Délais de récupération côté serveur
Leçon 7 sur 20 du cours Jeu Roblox : Arènes de combat de Coddy.
Un exploiteur contrôle son propre appareil ; il peut donc déclencher Remotes.Ability cent fois par seconde, avec n’importe quel nom. Un temps de recharge dans le LocalScript n’arrêterait que les joueurs honnêtes : c’est au serveur de l’imposer.
Le serveur conserve, pour chaque joueur et chaque capacité, le moment où elle sera de nouveau prête, sous forme d’attribut du joueur (la leçon suivante l’affiche à l’écran). workspace:GetServerTimeNow() est une horloge sur laquelle le serveur et chaque client s’accordent :
local now = workspace:GetServerTimeNow()
if now < (player:GetAttribute(name .. "ReadyAt") or 0) then
return
end
player:SetAttribute(name .. "ReadyAt", now + Settings.Cooldowns[name])Et il vérifie ce qui lui a été envoyé : une chaîne désignant une capacité réelle, provenant d’un joueur en vie.
Défi
MoyenDans le gestionnaire Ability, avant d'exécuter la capacité, faites un retour lorsque :
- le nom n'est pas une chaîne désignant une capacité,
- le joueur n'a pas de racine de personnage, ou la santé de son Humanoid est de 0,
GetServerTimeNow()est antérieur au moment où le joueur pourra de nouveau utiliser cette capacité, l'attributname .. "ReadyAt".
Sinon, définissez cet attribut sur maintenant plus Settings.Cooldowns[name], puis exécutez la capacité. Appuyez sur Play, puis sur Check : les vérifications déclenchent le remote comme le ferait un pirate.
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 lastSwing = {} -- player -> moment de son dernier coup d’épée
-- Les animations d’épée propres à Roblox
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"
print("Swords deal " .. Settings.SwordDamage .. " damage")
-- Un coup d’épée : l’animation se joue, puis tous ceux qui se trouvent dans la boîte devant toi sont touchés
local function swing(player)
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if not root or character.Humanoid.Health <= 0 then
return
end
local now = time()
if lastSwing[player] and now - lastSwing[player] < Settings.SwingCooldown then
return
end
lastSwing[player] = now
character.Humanoid.Animator:LoadAnimation(slash):Play()
-- la boîte devant toi, jamais ton propre corps
local params = OverlapParams.new()
params.FilterDescendantsInstances = { character }
local box = root.CFrame * CFrame.new(0, 0, -3)
local hit = {} -- chaque humanoïde une seule fois par coup d’épée
for _, part in workspace:GetPartBoundsInBox(box, Settings.HitboxSize, params) do
local humanoid = part.Parent:FindFirstChildOfClass("Humanoid")
if humanoid and not hit[humanoid] then
hit[humanoid] = true
humanoid:TakeDamage(Settings.SwordDamage)
end
end
end
-- Les capacités, classées par nom : ce que chacune fait
local abilities = {}
function abilities.Dash(player, character, root)
root.AssemblyLinearVelocity = root.CFrame.LookVector * Settings.DashSpeed + Vector3.new(0, 15, 0)
end
-- Le client demande une capacité par son nom ; le serveur l’exécute
remotes.Ability.OnServerEvent:Connect(function(player, name)
local character = player.Character
local root = character and character:FindFirstChild("HumanoidRootPart")
if abilities[name] and root then
abilities[name](player, character, root)
end
end)
-- Chaque joueur reçoit Kills dans le classement, et une épée à chaque vie
Players.PlayerAdded:Connect(function(player)
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local kills = Instance.new("IntValue")
kills.Name = "Kills"
kills.Parent = leaderstats
player.CharacterAdded:Connect(function()
local sword = ServerStorage.Sword:Clone()
sword.Parent = player.Backpack
sword.Activated:Connect(function()
swing(player)
end)
end)
end)
-- Le mannequin d’entraînement : sa barre de vie reflète sa santé, et un mannequin
-- vaincu réapparaît après un instant, sous forme d’une nouvelle copie au même endroit
local function setupDummy(dummy)
local humanoid = dummy.Humanoid
humanoid.HealthChanged:Connect(function(health)
dummy.HealthBar.Back.Fill.Size = UDim2.fromScale(health / humanoid.MaxHealth, 1)
end)
humanoid.Died:Connect(function()
local spot = dummy:GetPivot()
task.wait(0.5)
dummy:Destroy()
task.wait(Settings.DummyRespawn)
local fresh = ServerStorage.Dummy:Clone()
fresh:PivotTo(spot)
fresh.Parent = workspace
setupDummy(fresh)
end)
end
setupDummy(workspace.Dummy)
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.