This page covers the sandbox, the limits each plugin has, and what happens past each one. Limits apply to every plugin, whatever its permissions, and to each plugin on its own: one plugin going over stops that plugin, never the game or another plugin.
The sandbox
Each plugin runs in its own Luau VM, made for it when it loads and closed when it unloads.
- Its own globals. A global one plugin sets is invisible to every other plugin. Plugins share nothing but what they pass through
reskate.expose, reskate.emit and messages, and those cross as JSON copies: tables, strings, numbers and booleans, never functions.
- Read-only libraries. The
reskate table, its sub-tables and Luau’s libraries (string, table, math and the rest) cannot be changed or given a metatable.
- No files, programs or code loading. There is no
io, package, dofile, loadfile, loadstring or load. os has no execute, remove, rename, getenv or exit.
- Source only. ReSkate compiles the plugin’s own
.lua and .luau text. It never loads bytecode.
- Its own package.
require and reskate.package read only inside the plugin’s folder. A path with .., a drive, \ or a link that leads outside is refused.
- Through the API only. Nothing reaches the game except the
reskate functions, and each checks its arguments and permission first.
Stopped or refused
Going over a limit does one of two things.
The plugin is stopped. ReSkate closes its VM at once:
- Its unload handlers do not run. Its storage is saved.
- Its console commands go, and so does everything it held of the game: its clips and trick replacements, its sounds, the items it put on the outfit, its board changes, a drop it hosts, the camera, a freeze, a hidden HUD, its nametags and its binds. Only its own: on a lobby’s host, the server copy (
<folder>@server) and the client copy hold their things apart, so one stopping leaves the other’s alone.
- The log says
[<id>] stopped: <why>, and plugin list shows stopped (<why>). For a server copy on a lobby’s host, <id> is <folder>@server.
- It stays stopped until you change one of its files (which reloads it) or run
plugin on <id>.
The call is refused. The plugin keeps running. The function either:
- raises a Lua error, which you can catch with
pcall (uncaught, it counts towards the error limit), or
- returns
false and a reason (or false alone, where the API page says so), with nothing raised.
Halts cannot be caught
When a plugin goes over its memory or its time for a call, ReSkate halts it. A halt raises the plugin was stopped at every point Luau checks: every loop, call and return. A pcall in the script cannot catch it and carry on. Every reskate function the halted plugin calls raises the same error, and nothing it asks for is done. The plugin is then stopped.
Limits that stop a plugin
Memory
The VM may hold 64 MB. An allocation past that fails, and the plugin stops, even when the script catches the out-of-memory error.
Time for one call
Every call ReSkate makes into a plugin has 50 ms: running the entry file, each event handler, each timer, each console command, each page, panel or screen build, each exposed function another plugin calls, and each web request’s callback. <what> names the call, such as the tick handler or the command speedo.
When a plugin unloads (its mod switched off, its files changed, plugin off or plugin reload), its unload handlers have 20 ms between them. Once they are spent, the rest are skipped.
Time a tick
Everything a plugin runs counts towards its tick: its timers, its tick handlers, its events, its draw handlers and its page and panels. ReSkate averages that time over each second. A plugin that averages more than 2 ms a tick for 3 seconds in a row is stopped. plugin list shows each plugin’s last average:
A spike is fine; a habit is not. Spread heavy work over ticks with reskate.time.every, or cache what you read.
Errors
Every error a plugin does not catch is logged with where it happened and a traceback:
More than 5 of them within 10 seconds stops the plugin. An error in the entry file as it first runs stops it at once. Errors you catch with pcall do not count.
Logging
reskate.log, print and reskate.console.print share 50 lines a second. Past that, the rest of that second’s lines are left out, with one warning:
A line longer than 2000 characters is cut, with ... at its end.
Limits that refuse a call with an error
These raise a Lua error. The plugin runs on.
Limits that refuse a call with false
These return false and a reason, and raise nothing. Check the result.
Limits on what a plugin draws and sends
These neither stop the plugin nor raise an error.
- Shapes. A plugin draws at most 4000 shapes a frame, with at most 64 KB of text between them. The rest of that frame’s shapes are left out. The log warns at most once every 10 s, naming the limit it hit:
more than 4000 shapes in a frame: the rest are left out, or more than 64 KB of text in a frame's shapes: the rest are left out.
- Emitted events. ReSkate holds at most 1024 emitted events a tick for all plugins together.
reskate.emit returns false past that.
- Messages out. At most 64 messages wait to go out each tick on a player’s game (256 on a dedicated server).
reskate.net.send returns false past that, and without a session.
- Messages in transit. The session passes at most 200 plugin messages a player every 5 s, from all that player’s plugins together; past that they are dropped. Messages reach only players on the same map, and a lobby’s host passes on none from a player whose mods change scoring or physics.
- Web requests at once. ReSkate runs at most 8 web requests at a time for all plugins together.
reskate.http.request returns nil and web requests cannot be made now past that.
- Web answers. A request has 10 s, follows no redirects and sends no cookies. An answer over 1 MB comes back with no body and the error
its answer is larger than 1 MB. A host on a private or local network is refused.
File sizes