Nigdy nie ufaj klientowi
Lekcja 7 z 18 w kursie Gra Roblox: Symulator zwierzaków w Coddy.
Gracz kontroluje własne urządzenie, więc osoba wykorzystująca luki w zabezpieczeniach może sprawić, że będzie ono wywoływać dowolny zdalny obiekt, z dowolnymi danymi, z dowolnego miejsca i tak często, jak zechce. Hit:FireServer(pile) z drugiego końca mapy, sto razy na sekundę, opróżniłoby każdą stertę w grze.
Dlatego serwer sprawdza każde żądanie, zanim podejmie działanie:
- czy to prawdziwa sterta (obiekt Instance w
workspace.Piles), - czy gracz stoi w odległości nie większej niż
Settings.Reachod niej, - i czy od ostatniego trafienia minęło co najmniej
Settings.HitCooldownsekund.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
return
endPo naciśnięciu przycisku Check testy robią to, co zrobiłaby osoba wykorzystująca luki w zabezpieczeniach. Zasada dotycząca każdego zdalnego obiektu, który tworzysz: klient prosi, serwer decyduje.
Wyzwanie
ŚredniW PetServer, na początku obsługi Hit zakończ działanie bez uderzania, gdy:
- stos nie jest obiektem Instance w
piles, - gracz nie ma postaci lub jego
HumanoidRootPartznajduje się dalej od stosu niżSettings.Reachstudów, - uderzył mniej niż
Settings.HitCooldownsekund temu (zostawlocal lastHit = {}blisko początku i zapisujtime(), gdy uderzenie jest dozwolone).
Naciśnij Play, a następnie Check: testy odgrywają rolę osoby wykorzystującej exploity.
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 piles = workspace.Piles
local pileSpot = {} -- każda sterta -> miejsce, w którym stoi
print("A Meadow pile gives " .. Settings.PileValue .. " coins")
-- Każdy gracz otrzymuje Coins w tabeli wyników
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)
-- Sterta monet w danym miejscu: kopia ServerStorage.CoinPile z jej zdrowiem
-- i wartością zapisanymi jako atrybuty
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
-- Uderzenie zmniejsza zdrowie sterty o 1;
-- gdy spadnie do 0, sterta się rozpada i wypłaca swoją wartość
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()
-- nowa sterta w tym samym miejscu, kilka sekund później
task.delay(Settings.RespawnTime, spawnPile, spot)
end
-- Sterta w każdym miejscu w Meadow
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
spawnPile(spot)
end
-- Klient zgłasza chęć uderzenia sterty
remotes.Hit.OnServerEvent:Connect(function(player, pile)
hitPile(player, pile)
end)
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.