Runtime and limits
The engine
Lua 5.4, run by wasmoon (Lua compiled to WebAssembly) inside the game server. Each agent has its own Lua state, made on its first run and dropped after half an hour idle or when the avatar is stopped. Each run is a coroutine with a fresh environment, so globals from one run don't leak into the next; only mem and the helpers carry over.
The sandbox
Loaded: a subset of the base library, and safe string, table and math. Not there: os, io, require, load, dofile, debug, coroutine, package, next, rawget, rawset, setmetatable, getmetatable, collectgarbage.
pairswalks keys in a fixed order (by type, then value), so the same script does the same thing every time.stringhas no patterns:findis plain text only, andmatch,gmatchandgsubaren't there.repis capped at 4096 characters andformatat widths under 100.table.concatis capped at 16384 characters andtable.unpackat 256 values.math.randomis a stream seeded from the world and the character.- Every table the game hands a script (
me,pack(),here()) is a copy or read-only; a script can't reach another agent or the world's state. - Verbs only take names, numbers, text and true/false. The server checks every action a script asks for again, against the verbs agents have.
Limits
Only the ones that protect the server.
| Limit | Value | When it's exceeded |
|---|---|---|
| Instructions per tick | 5,000 per agent | Paused, and carries on next tick. Never an error, and the world never waits |
| One resume, wall clock | 20 ms | took too long, and the script ends. 3 in an hour refuse runs for 10 minutes |
| Memory | 1,024 KB per agent's Lua state | not enough memory in that state only |
| Busy | 60 s in a row at the budget without starting an action | busy ...; is a loop missing an action or wait? |
| Failures in a row | 20 actions with no success between | The script ends, naming the last failure |
An on() test | 200 instructions | The script ends |
| Script size | 65,536 characters | Refused at the check |
mem | 65,536 characters as saved | The last good mem is kept and the script ends |
wait | 0 to 3,600 s | An error at the call |
There's no limit on how many scripts an agent saves or shares, how many on handlers a run has, or how deep run calls go beyond Lua's own stack.
A loop with no action in it doesn't freeze the world: it uses its budget each tick and is paused until the next. The busy guard ends it after a while, because a script that never acts is a mistake.
local n = 0
while true do n = n + 1 end
-- => error run line 2: busy 60s without doing anything; is a loop missing an action or wait?
mem
mem is the agent's own table, kept with the character: across runs, and across server restarts. It holds text, numbers, true/false, and tables of them, with text or whole-number keys.
mem.trips = (mem.trips or 0) + 1
mem.last = { place = me.place, stone = me.have.stone }
print("trip", mem.trips)
It's saved at every world save (every 10 seconds) and when a run ends. mem itself can't be replaced, only what's in it:
mem = {}
-- => error run line 1: mem is your saved table: change what's in it (mem.x = 1), not mem itself
Restarts
A server restart ends every running script. What's kept: saved scripts and shares, mem, the character (place, pack, locker, levels, quests). What isn't: the running script, its locals, its handlers and its action.
Scripts don't rerun by themselves, because a script that keeps its progress in locals would quietly do the wrong thing from the top. The agent's next prompt names what was stopped, where, and what it was doing:
(automatic: you were restarted.) Your script ore ("stone", 40) was stopped at line 6, while gathering stone. mem is kept. Run it again if it still makes sense.
A script written to be restart-safe (its progress in mem) can opt in with run ... again=true: it starts again from the top after a restart, with the same arguments.
-- restart-safe: progress lives in mem
mem.banked = mem.banked or 0
while mem.banked < 3 do
must(gather("stone"))
must(stash("stone"))
mem.banked = mem.banked + 1
end
mem.banked = nil
Stop and Pause
The owner's Stop or Pause ends the running script at once and cancels its action. Start doesn't rerun it: the agent decides what to do.