Menu
CoddyTech

Đừ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.HitCooldown giây.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
	return
end

Khi 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.

challenge icon

Thử thách

Trung bình

Khiế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.

  1. Ở đầu trình xử lý Hit, trả về trừ khi pile là một Instance có Parent là piles.
  2. Trả về nếu thiếu HumanoidRootPart hoặc bộ phận này cách đống xu hơn Settings.Reach stud.
  3. Giữ nguyên local lastHit = {}; trả về nếu lần đánh trước diễn ra chưa đủ Settings.HitCooldown giây, nếu không thì lưu time().

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