Menu
CoddyTech

Czasy odnowienia na serwerze

Lekcja 7 z 20 w kursie Gra Roblox: Areny walki w Coddy.

Osoba wykorzystująca exploity kontroluje własne urządzenie, więc może wywoływać Remotes.Ability sto razy na sekundę, podając dowolną nazwę. Cooldown w LocalScript zatrzymałby tylko uczciwych graczy: musi go wymuszać serwer.

Serwer przechowuje dla każdego gracza i każdej zdolności informację, kiedy będzie ona ponownie gotowa, jako atrybut gracza (w następnej lekcji zobaczysz go na ekranie). workspace:GetServerTimeNow() to zegar, którego wskazania są zgodne na serwerze i u każdego klienta:

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

Serwer sprawdza też, co zostało mu wysłane: ciąg znaków będący nazwą istniejącej zdolności, od gracza, który żyje.

challenge icon

Wyzwanie

Średni

W obsłudze Ability, przed uruchomieniem umiejętności zakończ działanie, gdy:

  1. nazwa nie jest ciągiem znaków określającym umiejętność,
  2. gracz nie ma głównej części postaci lub zdrowie jego Humanoida wynosi 0,
  3. GetServerTimeNow() wskazuje czas wcześniejszy niż czas ponownej gotowości gracza dla tej umiejętności, zapisany w atrybucie name .. "ReadyAt".

W przeciwnym razie ustaw ten atrybut na bieżący czas plus Settings.Cooldowns[name], a następnie uruchom umiejętność. Naciśnij Play, a potem Check: testy wywołują zdalne zdarzenie tak, jak zrobiłby to exploiter.

Spróbuj swoich sił

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

local remotes = game.ReplicatedStorage.Remotes
local lastSwing = {} -- player -> kiedy ostatnio wykonał zamach

-- Własne animacje miecza Roblox
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"

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

-- Zamach: najpierw animacja, potem trafienie wszystkich w obszarze przed tobą
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()
	-- obszar przed tobą, nigdy twoja własna postać
	local params = OverlapParams.new()
	params.FilterDescendantsInstances = { character }
	local box = root.CFrame * CFrame.new(0, 0, -3)
	local hit = {} -- każdy humanoid tylko raz na zamach
	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

-- Umiejętności według nazw: co robi każda z nich
local abilities = {}

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

-- Klient prosi o umiejętność, podając jej nazwę; serwer ją wykonuje
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)

-- Każdy gracz otrzymuje Kills na tablicy wyników i miecz przy każdym odrodzeniu
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)

-- Kukła treningowa: jej pasek zdrowia odzwierciedla stan zdrowia, a pokonana
-- kukła po chwili powraca jako nowa kopia w tym samym miejscu
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 iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Gra Roblox: Areny walki