Scripting with Rhai
Archipelago embeds Rhai through a Rustler NIF. Scripts receive a read-only snapshot of the world, emit declarative effects, and return. They have no filesystem, network, module-import, or dynamic-evaluation access.
The sandbox and its Elixir bridge are implemented. Script attachment and editing are not yet exposed as a complete operator workflow in the SSH client; /script checks the :author_scripts capability, but the rig> command surface is still incomplete.
Execution model
Archipelago.Core.ScriptRunner.run/5 joins three boundaries:
EntityViewbuilds a snapshot for the entity and room.Archipelago.Scriptingevaluates Rhai under fixed limits.ScriptBagchecks every emitted effect against the entity’s capabilities.
Accepted effects return to the caller for application by the Effect Interpreter. Rejected effects remain visible to operator tooling; a script cannot silently widen its authority.
if occupants().len() > 1 {
emit("say", #{
actor: self_id(),
message: "The room has changed since you arrived."
});
}
Read-only functions
| Function | Value |
|---|---|
self_id() | entity holding the script |
room_id() | current room ID |
occupants() | entity IDs in the room snapshot |
inventory(entity_id) | inventory entries for one entity |
property(entity_id, key) | property value or unit |
time() | ISO-8601 instant supplied to the snapshot |
random(min, max) | bounded integer using the invocation seed |
Emission functions
| Function | Result |
|---|---|
emit(type, args) | records a declarative effect |
abort(reason) | stops the invocation with an authored reason |
log(message) | appends an operator-audience log line |
ScriptRunner currently translates say, emote, set_property, and generic emit. Unknown effect types are rejected.
Limits
The defaults are 10,000 operations, 32 call levels, 10,000 bytes of string data, 1,000 array entries, 1,000 map entries, and expression depth 32. A caller may lower or raise them per invocation.
Budget exhaustion returns an operator diagnostic and a fiction-facing line such as “The spell unravels before completion.” Runtime and compile failures remain structured errors.
Rules and scripts
Declarative rules are stored as canonical CBOR in the content-addressed store and evaluated without crossing the NIF boundary. Use a rule for a stable predicate-and-effect response. Use Rhai when the behaviour needs branching, arithmetic, string work, or several reads from the entity snapshot.
Both paths end at effects. Neither path receives a mutable world object.