Menu
CoddyTech

Thời gian chờ của máy chủ

Bài học 8 trên 25 trong khóa học Trò chơi Roblox: Đấu trường của Coddy.

Kẻ khai thác quyền kiểm soát thiết bị của chính mình, nên có thể kích hoạt Remotes.Ability hàng trăm lần mỗi giây, với bất kỳ tên nào. Thời gian hồi chiêu trong LocalScript chỉ ngăn được những người chơi trung thực: máy chủ phải thực thi giới hạn này.

Máy chủ lưu thời điểm mỗi người chơi có thể dùng lại từng kỹ năng dưới dạng thuộc tính của người chơi, bài học tiếp theo sẽ hiển thị thông tin này trên màn hình. workspace:GetServerTimeNow() là đồng hồ mà máy chủ và mọi máy khách đều thống nhất:

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

Máy chủ cũng kiểm tra nội dung được gửi: đó phải là chuỗi chỉ tên một kỹ năng có thật, được gửi từ một người chơi còn sống.

challenge icon

Thử thách

Trung bình

Khiến máy chủ từ chối những gì kẻ khai thác có thể gửi.

  1. Trong trình xử lý Ability, trả về nếu typeof(name) ~= "string", kỹ năng không tồn tại hoặc người chơi không có nhân vật còn sống.
  2. Trả về nếu workspace:GetServerTimeNow() nhỏ hơn player:GetAttribute(name .. "ReadyAt") (hoặc 0).
  3. Nếu không, đặt thuộc tính đó thành thời điểm hiện tại + Settings.Cooldowns[name], rồi chạy kỹ năng.

Nhấn Chơi rồi nhấn Q hai lần: lần lướt thứ hai phải chờ 3 giây. 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 lastSwing = {} -- player -> thời điểm vung kiếm gần nhất

-- Hoạt ảnh vung kiếm của Roblox
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"

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

-- Một lần vung kiếm: phát hoạt ảnh, rồi đánh trúng mọi người trong vùng phía trước bạn
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()
	-- Vùng phía trước bạn, không bao gồm cơ thể bạn
	local params = OverlapParams.new()
	params.FilterDescendantsInstances = { character }
	local box = root.CFrame * CFrame.new(0, 0, -3)
	local hit = {} -- Mỗi nhân vật chỉ trúng đòn một lần trong mỗi lần vung kiếm
	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)
			print(humanoid.Parent.Name .. " is at " .. humanoid.Health)
		end
	end
end

-- Các kỹ năng theo tên và tác dụng của từng kỹ năng
local abilities = {}

-- Thiết bị của người chơi điều khiển nhân vật của họ, nên thao tác lướt diễn ra trên thiết bị đó
function abilities.Dash(player)
	remotes.Ability:FireClient(player, "Dash")
end

-- Máy khách yêu cầu kỹ năng theo tên; máy chủ thực hiện kỹ năng đó
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
	-- Bài học 8: Thêm thời gian chờ kỹ năng trên máy chủ vào đây
end)

-- Mỗi lần hồi sinh, người chơi nhận một thanh kiếm
Players.PlayerAdded:Connect(function(player)
	player.CharacterAdded:Connect(function()
		local sword = ServerStorage.Sword:Clone()
		sword.Parent = player.Backpack
		sword.Activated:Connect(function()
			swing(player)
		end)
	end)
end)

-- Hình nộm tập luyện: thanh máu thay đổi theo lượng máu, và khi bị hạ,
-- hình nộm sẽ xuất hiện lại sau một lúc dưới dạng bản sao mới ở cùng vị trí
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)

Tất cả bài học trong Trò chơi Roblox: Đấu trường

Tự luyện tập: Playground Roblox