Sandbox
Scripts run in a restricted Lua 5.4 environment. Only a subset of the standard library is loaded. Anything that touches the filesystem, OS, or arbitrary module loading is blocked. Network access goes through a separate API that can be disabled from the menu.
Loaded libraries
base: core language functions (type,pairs,pcall,tostring,error, etc.)math: math functions (sin,cos,sqrt,floor,random, etc.)string: string manipulation (find,format,sub,gsub,match, etc.)table: table operations (insert,remove,sort,concat,unpack, etc.)coroutine: coroutine control (yield,resume,create,wrap, etc.)utf8: Unicode string handling (len,codes,char,codepoint, etc.)
Blocked libraries
io: no file read/writeos: noos.execute,os.remove,os.clock, etc.package: norequirepaths or module loadingdebug: nodebug.getinfo,debug.sethookor stack inspection
Removed functions
These are removed even though their library loads. Calling one fails with attempt to call a nil value.
- From
base:dofile,loadfile,require,rawget,rawset,rawlen,rawequalandcollectgarbage. string.dump.
load still works, but only for Lua source text. Given a precompiled (binary) chunk, it returns nil and an error message. Script files must be source text too, so a precompiled .lua file does not load.
Each script has its own globals, so a global you set is not visible to other scripts. _G is shared by every script. Code from load also runs in the shared _G unless you pass your own environment: load(src, "chunk", "t", _ENV).
Common replacements
- To persist data, use Storage instead of
io. To store a table, turn it into text withjson.encode. - For timing, use the global
clock()function instead ofos.clock. - For network access, use the HTTP library. It is opt-in and can be turned off from the menu.
- For modular code, use
exportandimport.requireis blocked, but these two globals let scripts share modules with each other:
-- In your library script (e.g. utils.lua):
export("utils", {
clamp = function(v, lo, hi) return math.max(lo, math.min(hi, v)) end,
})
-- In any other script:
local utils = nil
function on_tick()
utils = utils or import("utils")
if not utils then return end -- library not loaded yet
local x = utils.clamp(150, 0, 100) -- 100
endexport(name, module) registers a table under a name. import(name) fetches it, or returns nil until the exporting script has loaded. Scripts load one after another, so an import at the top of a script can run before the library does. Call it from on_tick until it returns a table.