클라이언트를 절대 믿지 마세요
Coddy의 Roblox 게임: 펫 시뮬레이터 코스 레슨. 18개 중 7번째.
플레이어는 자신의 기기를 제어하므로, 악용자는 원하는 만큼 빠르게 어디서든 무엇이든 보내 어떤 원격 이벤트든 실행할 수 있습니다. 맵 반대편에서 Hit:FireServer(pile)을 초당 100번 호출하면 게임의 모든 더미가 비워집니다.
그러므로 서버는 요청을 처리하기 전에 모두 확인합니다:
- 실제 더미인지 (
workspace.Piles안의 Instance인지), - 플레이어가 더미에서
Settings.Reach이내에 있는지, - 그리고 플레이어가 마지막으로 타격한 지 최소
Settings.HitCooldown초가 지났는지.
if typeof(pile) ~= "Instance" or pile.Parent ~= piles then
return
endCheck를 누르면 테스트에서 악용자가 할 법한 동작을 수행합니다. 작성하는 모든 원격 이벤트의 원칙은 다음과 같습니다. 클라이언트가 요청하고, 서버가 결정합니다.
챌린지
중급PetServer에서 Hit 처리기의 시작 부분에 다음의 경우 공격하지 않고 반환하세요:
- 더미가
piles에 있는 Instance가 아닌 경우, - 플레이어에게 캐릭터가 없거나,
HumanoidRootPart가 더미에서Settings.Reach스터드보다 더 멀리 있는 경우, Settings.HitCooldown초가 지나기 전에 공격한 경우(위쪽 가까이에local lastHit = {}를 두고, 공격이 허용될 때time()을 기록하세요).
Play를 누른 다음 Check를 누르세요. 검사가 악용자를 플레이합니다.
직접 해보기
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 = {} -- 각 pile -> 해당 pile이 놓인 spot
print("A Meadow pile gives " .. Settings.PileValue .. " coins")
-- 모든 플레이어는 리더보드에 Coins를 받습니다
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)
-- spot에 놓인 코인 더미: ServerStorage.CoinPile의 복사본이며 체력은
-- 가치와 함께 속성으로 저장됩니다
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
-- 한 번 타격할 때마다 더미의 체력이 1 감소합니다;
-- 체력이 0이 되면 더미가 부서지고 해당 가치만큼 코인을 지급합니다
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()
-- 몇 초 후 같은 spot에 새 더미가 생성됩니다
task.delay(Settings.RespawnTime, spawnPile, spot)
end
-- Meadow의 모든 spot에 더미를 하나씩 생성합니다
for _, spot in workspace.Zones.Meadow.Spawns:GetChildren() do
spawnPile(spot)
end
-- 클라이언트가 더미를 타격하도록 요청합니다
remotes.Hit.OnServerEvent:Connect(function(player, pile)
hitPile(player, pile)
end)
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.