Ne jamais faire confiance au client
Leçon 7 sur 18 du cours Jeu Roblox : Simulateur d’animaux de Coddy.
Un joueur contrôle son propre appareil, donc un exploiteur peut lui faire déclencher n’importe quel remote, avec n’importe quoi, depuis n’importe où, aussi rapidement qu’il le souhaite. Hit:FireServer(pile) depuis l’autre bout de la carte, cent fois par seconde, viderait toutes les piles du jeu.
Le serveur vérifie donc chaque requête avant d’agir :
- il s’agit d’une vraie pile (une Instance dans
workspace.Piles), - le joueur se trouve à une distance inférieure ou égale à
Settings.Reachde celle-ci, - et le dernier coup porté remonte à au moins
Settings.HitCooldownsecondes.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
return
endQuand tu appuies sur Check, les tests font ce qu’un exploiteur ferait. La règle à suivre pour chaque remote que tu écris : le client demande, le serveur décide.
Défi
MoyenDans PetServer, au début du gestionnaire Hit, quitte sans frapper lorsque :
- la pile n’est pas une Instance dans
piles, - le joueur n’a pas de personnage, ou si son
HumanoidRootPartse trouve à plus deSettings.Reachstuds de la pile, - il a frappé il y a moins de
Settings.HitCooldownsecondes (gardelocal lastHit = {}près du début, et enregistretime()lorsqu’un coup est autorisé).
Appuie sur Play, puis sur Check : les vérifications jouent le rôle de l’exploiteur.
Essayez vous-même
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Settings = require(game.ReplicatedStorage.Settings)
local remotes = game.ReplicatedStorage.Remotes
local piles = workspace.Piles
local pileSpot = {} -- chaque pile -> l'emplacement qu'elle occupe
print("A Meadow pile gives " .. Settings.PileValue .. " coins")
-- Chaque joueur reçoit Coins dans le classement
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local coins = Instance.new("IntValue")
coins.Name = "Coins"
coins.Parent = leaderstats
end)
-- Une pile de pièces sur un emplacement : une copie de ServerStorage.CoinPile avec sa santé
-- et sa valeur comme attributs
local function spawnPile(spot)
local pile = ServerStorage.CoinPile:Clone()
pile.Position = spot.Position
pile:SetAttribute("MaxHealth", Settings.PileHealth)
pile:SetAttribute("Health", Settings.PileHealth)
pile:SetAttribute("Value", Settings.PileValue)
pileSpot[pile] = spot
pile.Parent = piles
return pile
end
-- Un coup retire 1 point de santé à une pile ;
-- à 0, la pile se brise et rapporte sa valeur
local function hitPile(player, pile)
local health = pile:GetAttribute("Health") - 1
if health > 0 then
pile:SetAttribute("Health", health)
return
end
player.leaderstats.Coins.Value += pile:GetAttribute("Value")
local spot = pileSpot[pile]
pileSpot[pile] = nil
pile:Destroy()
-- une nouvelle pile au même emplacement, quelques secondes plus tard
task.delay(Settings.RespawnTime, spawnPile, spot)
end
-- Une pile sur chaque emplacement dans Meadow
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
spawnPile(spot)
end
-- Le client demande à frapper une pile
remotes.Hit.OnServerEvent:Connect(function(player, pile)
hitPile(player, pile)
end)
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Jeu Roblox : Simulateur d’animaux
2Client et serveur
Pièces à l’écran