How to make a jail players can't reset out of
Jails and freezes that only move the character or set its walk speed end with a reset. How to hold players on the server instead.
What it looks like
A jail, freeze, time-out or penalty box is applied only to the player's current character: the server moves the character into a cell with PivotTo, or sets its WalkSpeed and JumpHeight to 0, and nothing else remembers the punishment. A new character spawns at a normal spawn with normal speed, so resetting ends it. Some games turn off the reset button with StarterGui:SetCore("ResetButtonCallback", false) to stop this.
character:PivotTo(jailCell) humanoid.WalkSpeed = 0
How to detect it
- Find every jail, freeze, stun, time-out, penalty box and similar hold, whether it comes from staff, a game rule or an anti-cheat.
- Check where the hold is recorded. A hold kept only on the character, or not kept at all, ends when the character is replaced. Look for a server table or attribute keyed by the
PlayerorUserId, and a server loop orCharacterAddedhandler that puts each new character back in place. - Players can respawn whenever they like with the Reset Character button in the settings menu.
SetCore("ResetButtonCallback", false)is called from client code, and an exploiter runs their own code on their client, so they can call it again withtrue. Hiding the button doesn't stop a reset. - Roblox's
Humanoidreference saysWalkSpeedis a client-replicated property that can be changed locally, and shouldn't be used as a security mechanism or the only way to stop a humanoid moving. A freeze that only setsWalkSpeedorJumpHeightis a finding even without a reset. - Holds that are meant to last past leaving, such as a moderation jail for hours, also need to be saved. Judge saving with Bans that don't last.
What an exploiter could do
They reset, or change their own walk speed, and walk out of jail or a freeze straight away. Staff punishments and game rules such as penalty boxes stop working for anyone willing to do it, and players who follow the rules are the only ones held.
How to fix it
- Record each hold on the server, keyed by the player, with its end time.
- Check held players on the server, every second or so, and move them back into the cell when they're outside it. This also catches each new character after a reset.
- Release them on the server when the time is up, and clear the record when they leave.
- Keep any reset-button change as a convenience only.
Vulnerable example
local ReplicatedStorage = game:GetService("ReplicatedStorage") local moderators: { [number]: boolean } = { [123456789] = true } local cell = CFrame.new(0, 500, 0) ReplicatedStorage.Remotes.JailPlayer.OnServerEvent:Connect(function(player: Player, target: unknown, minutes: unknown) if not moderators[player.UserId] then return end if typeof(target) ~= "Instance" or not target:IsA("Player") or target == player then return end if typeof(minutes) ~= "number" or minutes ~= math.floor(minutes) or minutes < 1 or minutes > 60 then return end local character = target.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not character or not humanoid then return end character:PivotTo(cell) humanoid.WalkSpeed = 0 task.delay(minutes * 60, function() if target.Parent and target.Character == character then target:LoadCharacterAsync() end end) end)
Fixed example
local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local moderators: { [number]: boolean } = { [123456789] = true } local cell = CFrame.new(0, 500, 0) local cellRadius = 12 local jailedUntil: { [Player]: number } = {} ReplicatedStorage.Remotes.JailPlayer.OnServerEvent:Connect(function(player: Player, target: unknown, minutes: unknown) if not moderators[player.UserId] then return end if typeof(target) ~= "Instance" or not target:IsA("Player") or target == player then return end if typeof(minutes) ~= "number" or minutes ~= math.floor(minutes) or minutes < 1 or minutes > 60 then return end jailedUntil[target] = os.clock() + minutes * 60 end) Players.PlayerRemoving:Connect(function(player) jailedUntil[player] = nil end) while true do task.wait(1) local now = os.clock() for player, releaseAt in jailedUntil do if now >= releaseAt then jailedUntil[player] = nil task.spawn(player.LoadCharacterAsync, player) continue end local character = player.Character local root = character and character:FindFirstChild("HumanoidRootPart") if character and root and root:IsA("BasePart") and (root.Position - cell.Position).Magnitude > cellRadius then character:PivotTo(cell) end end end
Common false positives
- Holds that are meant to end on death, such as a stun from an attack or a short slow, are fine when a reset costs the player more than waiting it out.
- A short freeze during a cutscene or countdown that the server ends on its own doesn't need to survive a reset unless escaping it gives an advantage.
- Holds that the server records and reapplies, as in the fixed example, aren't this finding even if they also change
WalkSpeedor the reset button for a smoother feel.