Dungeon instances
Usage guide of world.instances: group-bound dungeon runs with a fixed roster, ordered bosses, numbered attempts and once-only reward receipts; the request commands, completeDefeated, events, hooks and saves.
Dungeon runs for parties (world.instances) #
A coordinator that owns dungeon runs: who was admitted, which boss is next, which attempt is live, who already claimed a reward. Readable offline: gripforge docs world-instances, or the MCP tool gripforge_docs (topic world-instances).
What it is for #
The authoritative game opens a run for a party with a fixed roster and an ordered list of bosses. The kit then decides who may enter, which boss may start, which results are valid (an old attempt is refused) and who may claim a reward, exactly once. It does not create scenes or servers, spawn the bosses, or give the loot: the game owns the scene, the boss health and the inventory, and calls the kit with trusted data. Never pass a roster or a result straight from a client.
Turn it on #
{ "id": "world.instances", "config": { "maxInstances": 64 } }
It requires game.engine. completeDefeated also needs combat.core (it reads combat.health).
First example #
<!-- test: kits/world_instances/tests/world_instances.rs::a_claim_is_granted_once_and_the_retry_returns_the_same_receipt --> Open a run, admit one member, start the first boss, complete it, then claim the reward twice.
{ "op": "open", "instance": "crypt", "party": "p", "partyGeneration": 1, "dungeon": "crypt",
"members": ["ann", "bob"], "bosses": ["warden", "king"] }
{ "op": "enter", "instance": "crypt", "actor": "ann" }
{ "op": "start", "instance": "crypt", "boss": "warden" }
{ "op": "complete", "instance": "crypt", "boss": "warden", "attempt": 1 }
{ "op": "claim", "instance": "crypt", "boss": "warden", "actor": "ann" }
Each command goes through request("default", command). The first claim answers granted true with a receipt; the second answers granted false with the same receipt. A claim by someone outside the roster, or before the boss is completed, is an error.
Settings and defaults #
| Setting | Type | Default | Meaning |
|---|---|---|---|
maxInstances | integer | 64 | dungeon runs per coordinator (1 to 256); when it is reached, open is refused |
Service #
world.instances:
request(id, command): runs a command on coordinatorid(defaultexists from the start). Commands:open { instance, party, partyGeneration, dungeon, members, bosses }: members and bosses are non-empty and unique (at most 40 and 32). A second binding of the same party and dungeon is refused.enter { instance, actor }andleave { instance, actor }: only roster members; leaving an empty instance cancels the active encounter.start { instance, boss }: the next uncompleted boss, with at least one actor present; read the incrementedattemptin the state.wipeandcomplete { instance, boss, attempt }: the attempt must be the live one, a late result is refused.claim { instance, boss, actor }: answers{ granted, receipt }, the receipt being[instance, generation, boss, actor].reset { instance }: only an empty, uncompleted run (a run with a completed boss is reward-locked).- Reads:
status,config,save,load,preview.
create(id, config)makes another coordinator (at most 128),remove(id)disposes one,ids()lists them.completeDefeated(instance, boss, sceneEntity, attempt): completes the exact attempt ondefaultonly if the entity'scombat.healthis dead; otherwise the error is "boss is still alive or absent". The game supplies the entity.
A claim is an eligibility decision, not an inventory change. Save the receipt, the awarded items and the run together before you acknowledge the reward, and do not give the reward both on the decision and on an event.
Events and hooks #
- world.instances.created (event):
instance,preset. world.instances.removed:instance. - world.instances.changed (event): a command that changes the state succeeded. Payload:
instance,op,result. - world.instances.refused (event): a hook cancelled a command. Payload:
instance,op,by,reason. world.instances.create(hook):{ instance, preset, config }, before a coordinator is created; a tap may changeconfigor cancel (the call then fails with "creation refused").world.instances.request(hook):{ instance, op, command }, before any command that changes the state (not the reads); a tap may changecommandor cancel (the call answers{ ok: false, cancelled: true, by, reason }).
Modding #
A mod can change maxInstances, tap both hooks, and add world_instances_presets documents that create(id, { preset }) merges under your own keys. A script can run a command with the verb world_instances_request and test a coordinator with the condition world_instances_exists.
Saves #
The save keeps every coordinator, runs, completed bosses, attempts and claims. A load checks membership, uniqueness, boss order, attempts and claims before it commits; a bad save is refused and logged. Loading spawns no scene and no server: resynchronise your scene objects and connections afterwards.
Engines #
The rules are one Rust core, used by the Rust kit host and the web. There is no presenter.
When it does not do what you expect #
- "not in bound roster": the actor is not in the
membersof theopen. - "party already bound to dungeon": the same party and dungeon were opened before; use a new generation.
- "encounter unavailable": nobody is present, or the boss is not the next uncompleted one.
- "stale encounter result": the
attemptis not the live one. - "occupied or reward-locked instance":
reseton a run that has someone in it or a completed boss.
Limits #
The roster is fixed once opened. No scheduled reset or automatic clean-up, and no distributed transaction across characters or servers. The game owns scenes, servers, boss health and loot.
Versions #
Kit 1.0.0 (first Rust dungeon-party delivery).