Menu
CoddyTech

서버 쿨다운

Coddy의 Roblox 게임: 배틀그라운드 코스 레슨. 20개 중 7번째.

익스플로잇 사용자는 자신의 기기를 제어하므로, 어떤 이름이든 사용해 Remotes.Ability를 초당 백 번 호출할 수 있습니다. LocalScript의 쿨다운은 정직한 플레이어만 막을 수 있습니다. 서버에서 이를 강제해야 합니다.

서버는 플레이어와 능력별로 다시 사용할 수 있게 되는 시각을 플레이어의 속성으로 저장합니다(다음 레슨에서는 이 값이 화면에 표시됩니다). workspace:GetServerTimeNow()는 서버와 모든 클라이언트가 같은 값을 기준으로 삼는 시계입니다.

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

또한 서버는 전달된 내용도 확인합니다. 실제 능력의 이름인 문자열인지, 그리고 보낸 플레이어가 살아 있는지 확인합니다.

challenge icon

챌린지

중급

Ability 처리기에서, 능력을 실행하기 전에 다음 경우에는 반환합니다.

  1. 이름이 능력을 나타내는 문자열이 아닌 경우,
  2. 플레이어에게 캐릭터 루트가 없거나 Humanoid의 체력이 0인 경우,
  3. GetServerTimeNow()가 해당 능력에 대한 플레이어의 준비 시간보다 이전인 경우. 준비 시간은 name .. "ReadyAt" 특성입니다.

그렇지 않으면 해당 특성을 현재 시간에 Settings.Cooldowns[name]을 더한 값으로 설정한 다음 능력을 실행합니다. 플레이를 누른 다음 확인을 누르세요. 이 검사는 익스플로이터가 하듯 원격 이벤트를 발생시킵니다.

직접 해보기

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

local remotes = game.ReplicatedStorage.Remotes
local lastSwing = {} -- player -> 마지막으로 검을 휘두른 시각

-- Roblox 자체 검 애니메이션
local slash = Instance.new("Animation")
slash.AnimationId = "rbxassetid://522635514"

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

-- 검을 휘두르면 애니메이션이 재생된 뒤 앞쪽 판정 상자 안에 있는 모두가 공격받는다
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()
	-- 앞쪽 판정 상자에는 자신의 몸이 포함되지 않는다
	local params = OverlapParams.new()
	params.FilterDescendantsInstances = { character }
	local box = root.CFrame * CFrame.new(0, 0, -3)
	local hit = {} -- 검을 한 번 휘두를 때 각 humanoid는 한 번씩만
	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

-- 이름별 능력: 각 능력이 하는 일
local abilities = {}

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

-- 클라이언트가 이름으로 능력 사용을 요청하면 서버가 실행한다
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)

-- 모든 플레이어는 리더보드에 Kills를 받고, 부활할 때마다 검을 받는다
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)

-- 훈련용 더미: 체력 바는 체력에 따라 변하고, 쓰러진
-- 더미는 잠시 후 같은 자리에 새로운 복제본으로 다시 나타난다
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 icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

Roblox 게임: 배틀그라운드의 모든 레슨