Đừng bao giờ tin máy khách
Bài học 7 trên 20 trong khóa học Trò chơi Roblox: mô phỏng thú cưng của Coddy.
Người chơi kiểm soát thiết bị của mình, nên kẻ gian lận có thể khiến thiết bị gọi bất kỳ remote nào, với bất kỳ dữ liệu nào, từ bất kỳ đâu, nhanh tùy ý. Gọi Hit:FireServer(pile) từ bên kia bản đồ một trăm lần mỗi giây sẽ làm cạn mọi đống trong trò chơi.
Vì vậy, máy chủ kiểm tra mọi yêu cầu trước khi xử lý:
- đó là một đống hợp lệ (một Instance trong
workspace.Piles), - người chơi đứng cách đống đó không quá
Settings.Reach, - và lần đánh gần nhất diễn ra cách đây ít nhất
Settings.HitCooldowngiây.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
return
endKhi bạn nhấn "Kiểm tra", các bài kiểm tra sẽ mô phỏng hành vi của kẻ gian lận. Quy tắc áp dụng cho mọi remote bạn viết: máy khách yêu cầu, máy chủ quyết định.
Thử thách
Trung bìnhKhiến máy chủ từ chối những cú đánh mà một người chơi công bằng không thể thực hiện.
- Ở đầu trình xử lý
Hit, trả về trừ khipilelà một Instance có Parent làpiles. - Trả về nếu thiếu
HumanoidRootParthoặc bộ phận này cách đống xu hơnSettings.Reachstud. - Giữ nguyên
local lastHit = {}; trả về nếu lần đánh trước diễn ra chưa đủSettings.HitCooldowngiây, nếu không thì lưutime().
Nhấn Chơi rồi nhấp vào một đống xu từ xa: sẽ không có gì xảy ra. Sau đó nhấn Kiểm tra.
Tự làm thử
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 = {} -- mỗi đống xu -> vị trí đặt đống xu đó
print("A Meadow pile gives " .. Settings.PileValue .. " coins")
-- Mỗi người chơi có Coins trên bảng xếp hạng
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)
-- Một đống xu tại một vị trí: bản sao của ServerStorage.CoinPile với máu
-- và giá trị được lưu dưới dạng thuộc tính (attribute)
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
-- Mỗi lần đánh làm máu của đống xu giảm 1;
-- khi máu về 0, đống xu vỡ và trao số xu theo giá trị của nó
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()
-- vài giây sau, một đống xu mới xuất hiện tại cùng vị trí
task.delay(Settings.RespawnTime, spawnPile, spot)
end
-- Mỗi vị trí trong Meadow có một đống xu
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
spawnPile(spot)
end
-- Client yêu cầu đánh một đống xu
remotes.Hit.OnServerEvent:Connect(function(player, pile)
-- Thêm bài học 7: Không bao giờ tin client vào đây
hitPile(player, pile)
end)
Tất cả bài học trong Trò chơi Roblox: mô phỏng thú cưng
Tự luyện tập: Playground Roblox