Limits and security
Script limits
| Limit | Value | What happens past it |
|---|---|---|
| Running without waiting | 1 second | The script is stopped with an error. Put task.wait() in long loops |
| All a server's scripts, one step | 1 second | Threads still due wait for the next step; that step's remaining event handlers are skipped once |
| Memory, per script runtime | 64 MB | The script stops with an error; the game and its other scripts go on |
| Handlers on one event | 10,000 | connect is an error (usually a connect inside a loop) |
| A ClientScript or Module players get | 64 KB of text | It never reaches the players. Split it into Modules |
A net message | 16 KB | |
A data value | 16 KB | |
data requests in flight | 64 per server | |
| JSON | 100 levels deep, 16 MB | An error |
World limits
| Limit | Value |
|---|---|
| Parts (Models and Folders count) | 10,000 |
| A world going live | 16 MB |
| A part's size | 0.05 to 2,048 units a side |
| Positions | within a million units of the middle |
| Models (MeshPart meshes) | 64 a world, 65,535 points each |
| Collision groups of the world's own | 13 |
| Players per server | 1 to 100 |
| Uploaded pictures | 64 a world, 1,024 px a side |
| Uploaded sounds | 32 a world, 20 MB and two minutes each, .wav |
| Achievements | 100 a game |
What players can send
A player's app sends the server only what the game asks for, and the server holds it to:
- 120 messages a second (
net.send,net.call), 20 clicks and 20 tool actions. The rest are dropped. - Numbers must be real:
NaNor infinity refuses the whole message. - A part from ServerService or StorageService, or one that is gone, arrives as
nil. - Movement is the server's: it moves every character with its own
walkSpeedandjumpPower, so a player can't speed or fly by changing their app. - Prompts, Clickables, gameplay bricks and building are checked on the server - reach, the part being on, the player alive.
Writing secure games
A player can run a modified app and send any arguments with any message name your scripts listen for. So:
- Never take a result from a player. Not an amount, a price, a damage, a target, a score. The client says what it wants to do; the server works out what happens.
- Check that they may do it now: alive, near enough, the cooldown passed, owns the item, can afford it.
- Keep secrets on the server. Every ClientScript - and every Module it can require - is sent to every player (without its comments). Codes, admin lists and server-only logic go in ServerService or StorageService.
- Validate types. A message's arguments can be anything: check
typeof(x) == "string"before usingxas a string.
-- Bad: the client decides the price and the reward
net.on("buy", function(player, item, price)
player.stats.Coins -= price
end)
-- Good: the server knows the price
local PRICES = {Sword = 50}
net.on("buy", function(player, item)
if typeof(item) ~= "string" then return end
local price = PRICES[item]
if not price or player.stats.Coins < price then return end
player.stats.Coins -= price
shared.Items[item]:clone().parent = player.backpack
end)Numbers that reach the engine are checked where they go in: a NaN position is an error your script can catch with pcall, and huge values are held within the world's limits. Health stops at a billion, speeds at 100,000 units a second.
The sandbox
Scripts run sandboxed: no files, no network, no other processes. HttpService's web requests are an error - a KHIM game doesn't reach the web. print from a ClientScript shows in the player's F3 panel; from a Script, in the server's console (the Workshop's Live > Console for a live game). Script errors never reach the game's chat.