euca-script, via mlua): a ScriptComponent names
a .lua file whose update function runs each tick through script_tick_system, reading and writing the
world through a small euca API. The script runs in a sandbox with an instruction budget, and the VM is
shared across forks. The one mental model to hold: /script/load only works on a host
that registered a ScriptEngine resource — a bare headless host doesn’t, so this
page shows the real error you’ll hit and exactly what a scripting host changes.
Attach a script to an entity
Spawn an entity, then point/script/load at a .lua file and the entity to run it on:
No ScriptEngine resource in world is the real response from a bare headless host — and it’s
the expected one. The handler looks up an Arc<Mutex<ScriptEngine>> resource and bails before it ever
touches the file, because a bare host never registers one (the same “register the resource on your
host” pattern as templates and input bindings). The
entity exists; nothing was attached.
On a server build that does register a ScriptEngine, the same call loads the file, derives the script
name (spin.lua), inserts a ScriptComponent, and returns {"ok":true,"message":"Script 'spin.lua' attached to entity 1"}. The attached entity then shows up in /script/list:
update(id) function called once per tick for the attached entity:
Behavior and gotchas
The things you only find out by running it:- A bare headless host has no
ScriptEngine, so/script/loadalways returns{"ok":false,"message":"No ScriptEngine resource in world"}. This is not a misconfiguration — Lua is a host-side capability you opt into by registering the engine in your project or your own host build./script/listreturns{"count":0,"entities":[]}there for the same reason. - Missing entities are reported before the engine check fails differently:
/script/loadwith an unknownentity_idreturns{"ok":false,"message":"Entity 999 not found"}. A real entity plus no engine is what produces theNo ScriptEnginemessage. - Scripts are sandboxed with an instruction budget (default 100,000). Dangerous globals are removed and
each
updatecall is capped, so a runaway loop can’t hang the tick. - The
eucaglobal is the ECS bridge:spawn(),despawn(id),get_position(id)/set_position(id,x,y,z),get_health(id)/set_health(id,v),delta_time(), plus an event APIon("event", fn)/emit("event", …). - Hot reload is built in — a file watcher reloads changed scripts at runtime.
- The VM is fork-safe. It’s shared as
Arc<Mutex<ScriptEngine>>, so a forked world reuses the same VM instead of cloning it.
Endpoints
To enable these end to end, register a
ScriptEngine on your server build — see
Hosting & deployment for which subsystems need host-side resources.
Status
- Shipped — sandboxed Lua VM, instruction budget, ECS bridge, per-entity scripts, hot reload, and fork-safe sharing.
- Caveat — a bare headless host does not register a
ScriptEngine, so/script/loadreturnsNo ScriptEngine resource in worlduntil you wire one into your project or host.
Script endpoints
Load and list endpoints, with request and response schemas.