Menu
CoddyTech

Server-Abklingzeiten

Lektion 7 von 20 im Kurs Roblox-Spiel: Schlachtfelder von Coddy.

Ein Exploiter kontrolliert sein eigenes Gerät und kann daher Remotes.Ability hundertmal pro Sekunde mit einem beliebigen Namen auslösen. Eine Abklingzeit im LocalScript würde nur ehrliche Spieler aufhalten: Der Server muss sie durchsetzen.

Der Server speichert für jeden Spieler und jede Fähigkeit als Attribut des Spielers, wann sie wieder bereit ist (in der nächsten Lektion wird sie auf dem Bildschirm angezeigt). workspace:GetServerTimeNow() ist eine Uhr, auf die sich der Server und alle Clients einigen:

local now = workspace:GetServerTimeNow()
if now < (player:GetAttribute(name .. "ReadyAt") or 0) then
	return
end
player:SetAttribute(name .. "ReadyAt", now + Settings.Cooldowns[name])

Außerdem überprüft er, was ihm gesendet wurde: eine Zeichenfolge, die eine echte Fähigkeit benennt, von einem Spieler, der am Leben ist.

challenge icon

Aufgabe

Mittel

Im Ability-Handler returnst du vor dem Ausführen der Fähigkeit, wenn:

  1. der Name kein String ist, der eine Fähigkeit bezeichnet,
  2. der Spieler keine Charakterwurzel hat oder die Gesundheit seines Humanoids 0 beträgt,
  3. GetServerTimeNow() vor der Bereitschaftszeit des Spielers für diese Fähigkeit liegt, dem Attribut name .. "ReadyAt".

Setze andernfalls dieses Attribut auf jetzt plus Settings.Cooldowns[name] und führe die Fähigkeit dann aus. Drücke Play und dann Check: Die Prüfungen lösen das Remote aus, so wie es ein Exploiter tun würde.

Probier es selbst

local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local Settings = require(game.ReplicatedStorage.Settings)

local remotes = game.ReplicatedStorage.Remotes
local lastSwing = {} -- player -> Zeitpunkt des letzten Schlags

-- Roblox’ eigene Schwertanimationen
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"

print("Swords deal " .. Settings.SwordDamage .. " damage")

-- Ein Schwerthieb: Erst kommt die Animation, dann werden alle im Bereich vor dir getroffen
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()
	-- der Bereich vor dir, niemals dein eigener Körper
	local params = OverlapParams.new()
	params.FilterDescendantsInstances = { character }
	local box = root.CFrame * CFrame.new(0, 0, -3)
	local hit = {} -- jeden Humanoid nur einmal pro Schwerthieb
	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

-- Die Fähigkeiten, nach Namen geordnet: was jede einzelne bewirkt
local abilities = {}

function abilities.Dash(player, character, root)
	root.AssemblyLinearVelocity = root.CFrame.LookVector * Settings.DashSpeed + Vector3.new(0, 15, 0)
end

-- Der Client fordert eine Fähigkeit anhand ihres Namens an; der Server führt sie aus
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)

-- Jeder Spieler bekommt Kills auf der Bestenliste und in jedem Leben ein Schwert
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)

-- Die Trainingspuppe: Ihre Lebensanzeige passt sich ihrer Gesundheit an, und eine besiegte
-- Puppe erscheint nach kurzer Zeit als frische Kopie am selben Ort wieder
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)
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Roblox-Spiel: Schlachtfelder