How to stop players filling the server with objects
A cooldown limits how fast players create things, not how many. How to cap objects per player and clean them up.
What it looks like
Players can create things in the shared world: placed blocks, spawned vehicles, pets, drawings, dropped items or projectiles. Each request has a cooldown, but nothing limits how many a player can have at once, or removes them when they leave.
Luau
remotes.PlaceBlock.OnServerEvent:Connect(function(player, position) if onCooldown(player) then return end makeBlock(position).Parent = workspace end)
How to detect it
- Find every handler that creates instances in a replicated place, such as
Workspaceor a player's plot, because of a player's request. - Roblox's security tactics guide asks, for every feature, how much it could use in the worst case. A cooldown limits the rate, not the total. One request a second for an hour is 3,600 objects from one player.
- Look for a per-player cap, such as a count of what they own that's checked before creating more, and a server-wide cap.
- Check that objects are removed when the player leaves, when they despawn, or when a newer one replaces them, such as one vehicle per player.
- Check what each object costs. Unanchored parts with physics, lights, particles and sounds cost far more than a few anchored parts.
- If one request can create many objects, report Client decides how much work a call does instead.
What an exploiter could do
They keep sending requests at the allowed rate until the server holds thousands of their objects. Every player's game slows down, physics stutters, and joining players take longer to load, until the server crashes or everyone leaves. Several accounts together reach the limit faster.
How to fix it
- Keep a count per player and refuse new objects at the limit, or remove the oldest one.
- Set a server-wide limit for anything with physics.
- Remove a player's objects when they leave, and give temporary objects a lifetime.
Vulnerable example
Vulnerable
local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local cooldown = 0.5 local lastPlaced: { [Player]: number } = {} ReplicatedStorage.Remotes.PlaceBlock.OnServerEvent:Connect(function(player: Player, position: unknown) if typeof(position) ~= "Vector3" or position.X ~= position.X or position.Y ~= position.Y or position.Z ~= position.Z then return end local now = os.clock() if now - (lastPlaced[player] or 0) < cooldown then return end lastPlaced[player] = now local block = Instance.new("Part") block.Size = Vector3.new(4, 4, 4) block.Position = position block.Anchored = true block.Parent = Workspace end) Players.PlayerRemoving:Connect(function(player) lastPlaced[player] = nil end)
Fixed example
Fixed
local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local Workspace = game:GetService("Workspace") local cooldown = 0.5 local maxBlocks = 200 local lastPlaced: { [Player]: number } = {} local blocks: { [Player]: { BasePart } } = {} ReplicatedStorage.Remotes.PlaceBlock.OnServerEvent:Connect(function(player: Player, position: unknown) if typeof(position) ~= "Vector3" or position.X ~= position.X or position.Y ~= position.Y or position.Z ~= position.Z then return end local now = os.clock() if now - (lastPlaced[player] or 0) < cooldown then return end lastPlaced[player] = now local owned = blocks[player] or {} blocks[player] = owned if #owned >= maxBlocks then local oldest = table.remove(owned, 1) if oldest then oldest:Destroy() end end local block = Instance.new("Part") block.Size = Vector3.new(4, 4, 4) block.Position = position block.Anchored = true block.Parent = Workspace table.insert(owned, block) end) Players.PlayerRemoving:Connect(function(player) lastPlaced[player] = nil local owned = blocks[player] blocks[player] = nil if owned then for _, block in owned do block:Destroy() end end end)
Common false positives
- Objects inside a player's own plot with a building limit the server enforces are already capped.
- Objects the server removes after a short lifetime, such as projectiles with
Debrisor atask.delaydestroy, can't pile up faster than the cooldown allows. - Where placement is also unchecked, such as blocks placed anywhere on the map, report Placement the server doesn't keep inside the player's area as well.