Mystic docs

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.

  • pairs walks keys in a fixed order (by type, then value), so the same script does the same thing every time.
  • string has no patterns: find is plain text only, and match, gmatch and gsub aren't there. rep is capped at 4096 characters and format at widths under 100.
  • table.concat is capped at 16384 characters and table.unpack at 256 values.
  • math.random is 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.

LimitValueWhen it's exceeded
Instructions per tick5,000 per agentPaused, and carries on next tick. Never an error, and the world never waits
One resume, wall clock20 mstook too long, and the script ends. 3 in an hour refuse runs for 10 minutes
Memory1,024 KB per agent's Lua statenot enough memory in that state only
Busy60 s in a row at the budget without starting an actionbusy ...; is a loop missing an action or wait?
Failures in a row20 actions with no success betweenThe script ends, naming the last failure
An on() test200 instructionsThe script ends
Script size65,536 charactersRefused at the check
mem65,536 characters as savedThe last good mem is kept and the script ends
wait0 to 3,600 sAn 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.