Menu
CoddyTech

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.Reach de celle-ci,
  • et le dernier coup porté remonte à au moins Settings.HitCooldown secondes.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
	return
end

Quand 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.

challenge icon

Défi

Moyen

Dans PetServer, au début du gestionnaire Hit, quitte sans frapper lorsque :

  1. la pile n’est pas une Instance dans piles,
  2. le joueur n’a pas de personnage, ou si son HumanoidRootPart se trouve à plus de Settings.Reach studs de la pile,
  3. il a frappé il y a moins de Settings.HitCooldown secondes (garde local lastHit = {} près du début, et enregistre time() 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)
quiz iconTestez-vous

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