docs(rust): stage 6 built — Guarded Crate (PROTOCOL §20.9, PLAN_REDESIGNS §14.3.19, D380)

- rust-link/PROTOCOL.md §20.9: hello's guardedCrate with npcSpawn and events,
  gc.started/ended/failed, gc.place and gc.error's reasons, guardedWon, map.live's
  guarded rows and how they are kept, POST /gc/place, and the website side.
  §17.3's world row gains guarded.
- modules/rust/PLAN_REDESIGNS.md: D380 (the kept crate's run-log note at
  placement), §14.3.19 the stage record, pointers from §9 and §14.3.5.
- modules/rust/OPERATING.md: Runic Gateway's own section on Guarded Crate and
  NpcSpawn (install, images, what it contacts at load), after the uMod mirror.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-15 12:26:53 -05:00
parent 966ba41deb
commit a305e98e05
3 changed files with 248 additions and 2 deletions

View File

@@ -5,6 +5,8 @@ configure and permission plugins. This is the surface our own deployment story h
of, the way [`installer/INSTALL.md`](../../installer/INSTALL.md) sits on top of ServUO. of, the way [`installer/INSTALL.md`](../../installer/INSTALL.md) sits on top of ServUO.
> **Mirrored verbatim from uMod on 2026-09-15** — 6 pages. uMod is upstream and normative. > **Mirrored verbatim from uMod on 2026-09-15** — 6 pages. uMod is upstream and normative.
> The last section, [Guarded Crate and NpcSpawn](#guarded-crate-and-npcspawn), is Runic Gateway's own and
> is not part of the mirror.
--- ---
@@ -16,6 +18,7 @@ of, the way [`installer/INSTALL.md`](../../installer/INSTALL.md) sits on top of
- [Plugin configuration](#plugin-configuration-plugins-configuration) — Editing a plugin's config file - [Plugin configuration](#plugin-configuration-plugins-configuration) — Editing a plugin's config file
- [Data Files](#data-files-plugins-data-files) — Where plugin data lives on disk - [Data Files](#data-files-plugins-data-files) — Where plugin data lives on disk
- [Permissions](#permissions-plugins-permissions) — Granting permissions and managing groups in-game - [Permissions](#permissions-plugins-permissions) — Granting permissions and managing groups in-game
- [Guarded Crate and NpcSpawn](#guarded-crate-and-npcspawn) — Runic Gateway's note on an optional plugin that needs a Codefling extension
--- ---
@@ -369,3 +372,41 @@ That's the basics to permissions for Oxide. Permissions give you a fantastic way
--- ---
<sub>Mirrored from uMod, 2026-09-15.</sub> <sub>Mirrored from uMod, 2026-09-15.</sub>
---
<a id="guarded-crate-and-npcspawn"></a>
## Guarded Crate and NpcSpawn
<sub>Runic Gateway's own section, not mirrored. Design: [PLAN_REDESIGNS.md](PLAN_REDESIGNS.md) §14.3.5 (D361,
D364); wire: [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.9.</sub>
Bazz3l's **Guarded Crate** (uMod) is one of the optional plugins Runic Gateway reports and drives: a guarded
crate landing and being cleared reach the site's feed, map and *Warden* title, and an event can place one. It
**needs NpcSpawn** for its guards, and NpcSpawn is not on uMod. Runic Gateway never ships or modifies either
(D351, D361); an operator who wants guarded crates installs both.
**Installing NpcSpawn.**
1. Download NpcSpawn from Codefling (<https://codefling.com/extensions/npc-spawn>), a free download.
2. Put `NpcSpawn.cs` in the plugins directory (`oxide/plugins`, or `carbon/plugins` on Carbon).
3. Put the archive's `data/Images/` files in the data directory's `Images` folder (`oxide/data/Images`, or
`carbon/data/Images`). **Without them NpcSpawn unloads itself.**
4. Leave `data/NpcSpawn/` for NpcSpawn to create. On Carbon, a folder made by a panel's file manager may not be
writable by the game, and NpcSpawn then fails until that folder is deleted and left to it.
5. Install Guarded Crate from uMod as any other plugin.
**What NpcSpawn does at load: it contacts its author's server.** It calls a version server at a bare IP
address (seen as `37.153.157.216:5000` in the §14.2 test) and downloads clothing images. An operator whose
server may not reach the internet, or who does not want it to, should know this before installing it.
**Without NpcSpawn,** Guarded Crate unloads itself the next time its own schedule starts an event, and Runic
Gateway's *Place a guarded crate* step is refused with *"Guarded Crate is loaded but NpcSpawn is missing: its
events cannot start"*. The bridge refuses that placement itself, because Guarded Crate's `position` command does
not check and would drop a crate with no guards. Admin → Rust servers shows NpcSpawn beside Guarded Crate.
**Its events** are rows in Guarded Crate's **data** file, `data/GuardedCrate.json` (Easy, Medium, Hard and Elite
as it ships), not its config. The site offers whatever names that file holds. A crate placed by an event is
**kept when the event ends** (D373): Guarded Crate has no way to end one event, and `gcrate stop all` ends every
one on the server.

View File

@@ -930,7 +930,7 @@ pulls) is research, not a walk, so it still comes first:
answered before code: **written on 2026-10-14 as §14.3**, from D361–D369, and answered as D370–D376. Then the answered before code: **written on 2026-10-14 as §14.3**, from D361–D369, and answered as D370–D376. Then the
build, in §14.3.11's nine stages (two of them core), §13 first. **Stage 1, §13, was built on 2026-10-15 build, in §14.3.11's nine stages (two of them core), §13 first. **Stage 1, §13, was built on 2026-10-15
(§14.3.14)**, with D378 and D379. **Stage 2, core's `retryAfterMs` (MODULE_API 1.15.0), was built on (§14.3.14)**, with D378 and D379. **Stage 2, core's `retryAfterMs` (MODULE_API 1.15.0), was built on
2026-10-15 (§14.3.15).** **Stage 3, TruePVE in the zone step, was built on 2026-10-15 (§14.3.16)**, and **stage 4, Dynamic PVP, the same day (§14.3.17)**, and **stage 5, Dangerous Treasures, the same day (§14.3.18).** 2026-10-15 (§14.3.15).** **Stage 3, TruePVE in the zone step, was built on 2026-10-15 (§14.3.16)**, and **stage 4, Dynamic PVP, the same day (§14.3.17)**, and **stage 5, Dangerous Treasures, the same day (§14.3.18)**, and **stage 6, Guarded Crate, the same day (§14.3.19).**
**Then one walk, on Oxide and Carbon, of everything still owed (D331):** §2.7; §11's rows; §12.9; §13's and **Then one walk, on Oxide and Carbon, of everything still owed (D331):** §2.7; §11's rows; §12.9; §13's and
§14's rows; the in-game rows §14's rows; the in-game rows
@@ -1094,6 +1094,7 @@ The org lead answered §14.3's Q26–Q32 on 2026-10-14:
| **D377** | **The leaderboard changes only for something new and substantial.** A new title may add its column (*Safecracker*, *Scavenger*, *Treasure Hunter*, *Warden*, *Hoarder* do), but a small plugin's own numbers, such as ZLevels' levels, never become columns (org lead, 2026-10-14, confirming D374 beside D375). | Adding a column for every number a plugin reports. | 14.3.1, 14.3.6 | | **D377** | **The leaderboard changes only for something new and substantial.** A new title may add its column (*Safecracker*, *Scavenger*, *Treasure Hunter*, *Warden*, *Hoarder* do), but a small plugin's own numbers, such as ZLevels' levels, never become columns (org lead, 2026-10-14, confirming D374 beside D375). | Adding a column for every number a plugin reports. | 14.3.1, 14.3.6 |
| **D378** | **The Chinook is called with no position.** It drops only at the map's own drop zones (seven on the rigs' map, all at monuments), and the one route that sends it to a chosen zone borrows the Airfield call terminal's shared state, so a player at the real terminal and a step calling at once would race for one Chinook (stage 1's spike, org lead 2026-10-15). The airdrop and the patrol helicopter take a position. | A position snapped to the nearest drop zone through the terminal's state, refused while a real Airfield call is active. | 14.3.1, 14.3.14 | | **D378** | **The Chinook is called with no position.** It drops only at the map's own drop zones (seven on the rigs' map, all at monuments), and the one route that sends it to a chosen zone borrows the Airfield call terminal's shared state, so a player at the real terminal and a step calling at once would race for one Chinook (stage 1's spike, org lead 2026-10-15). The airdrop and the patrol helicopter take a position. | A position snapped to the nearest drop zone through the terminal's state, refused while a real Airfield call is active. | 14.3.1, 14.3.14 |
| **D379** | **`chinook.downed` is a 24th world event**, with the credit block and the operator's rule, and counts toward *Rotorbane*: D174 counted Chinook kills there, and the plan's first draft would have dropped them (org lead, 2026-10-15). | Counting Chinook kills through the old tally, with no line; patrol helicopters only. | 14.3.1, 14.3.14 | | **D379** | **`chinook.downed` is a 24th world event**, with the credit block and the operator's rule, and counts toward *Rotorbane*: D174 counted Chinook kills there, and the plan's first draft would have dropped them (org lead, 2026-10-15). | Counting Chinook kills through the old tally, with no line; patrol helicopters only. | 14.3.1, 14.3.14 |
| **D380** | **A guarded crate left standing is noted in the run log when it is placed, not when the run ends:** the *Place a guarded crate* step's success line says the crate is kept when the run ends and how it ends (org lead, 2026-10-15). Core gives a module no hook at a run's end for a step that is kept, so D373's note at the end would need new core work. | A run-end hook in core now (a MODULE_API minor, with the Module-uo proof), or filed for later. | 14.3.5, 14.3.19 |
## 11. First-class optional plugins (D199–D202) ## 11. First-class optional plugins (D199–D202)
@@ -2627,6 +2628,10 @@ the bridge already reads Kits' and Economics' (PROTOCOL.md §16.3), and absent w
column and the trigger; let one time out; cancel the run and see the crate stay; unload NpcSpawn and see the column and the trigger; let one time out; cancel the run and see the crate stay; unload NpcSpawn and see the
step refused with the reason. On Carbon: refused (D363). step refused with the reason. On Carbon: refused (D363).
> **Built on 2026-10-15** (§14.3.19), specified in PROTOCOL.md §20.9. The events are in Guarded Crate's data
> file, not its config; the location is a world call's (monument or point, no grid); the run-log note is written
> at placement (D380); and the Carbon row now expects the step to work. §14.3.19 says how.
#### 14.3.6 ZLevels Remastered (D369, D374) #### 14.3.6 ZLevels Remastered (D369, D374)
**Hello.** `integrations.zLevels`: `{ loaded, version }`. **Hello.** `integrations.zLevels`: `{ loaded, version }`.
@@ -3098,6 +3103,81 @@ actions, 11 budgets, 21 option sources; the 89 routes unchanged.
hotload the bridge while a chest stands and see it still on the map (the seed); and start one from a run and hotload the bridge while a chest stands and see it still on the map (the seed); and start one from a run and
read its `dt.started` row's `key` from the staff events API (the web client has no staff feed view). read its `dt.started` row's `key` from the staff events API (the web client has no staff feed view).
#### 14.3.19 Stage 6, built: Guarded Crate, with NpcSpawn (2026-10-15)
Built in Rust-Plugins, Rust-Link and Module-Rust, specified in [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.9
and §17.3, with the operator's section in [OPERATING.md](OPERATING.md). `PROTOCOL_VERSION` stays 14. Builds:
Rust 25902425 (changeset 167781), Oxide 2.0.7830, Carbon 2.0.262 production (the latest of each that day); the
code read is Guarded Crate 2.0.17, still the newest on uMod, and NpcSpawn 3.4.8. **D380** answers the one point of
§14.3.5 that could not be built as written. No MODULE_API change: `coreApi` stays `^1.15.0`.
**What §14.3.5 had wrong or unsaid, found in Guarded Crate's source and on the rigs:**
1. **The events are in its data file, not its config.** `data/GuardedCrate.json`'s `CrateEventEntries`, each
name under the key `event display name)` (the stray bracket is Guarded Crate's). Its console command is a
config value, read from the config.
2. **`gcrate position` does not check NpcSpawn** (only `start` does). Without NpcSpawn a placed crate lands with
no guards and nothing stops a hack, so `npcspawn-missing` is the bridge's own refusal, not a courtesy.
3. **The key's join is exact**, as Dangerous Treasures' is (§14.3.18): the start hook fires inside the call that
creates the event. §14.3.9's "first start within ten seconds" is not needed for either.
4. **Its unload and `stop all` raise no hook**, and the ended hook does not say which event ended. The bridge
reads Guarded Crate's private list of running events by reflection (it has no API that lists them) to seed
the map after a hotload, to drop events `stop all` ended, and to tell which event a clearing ended.
5. **Its cargo plane was an airdrop.** Stage 1 announces every cargo plane's spawn as `airdrop.called`, so each
guarded crate also said *"An airdrop was called"*; found on the rig, fixed here with Guarded Crate's own
`API_IsGuardedCrateCargoPlane`. §14.3.1's walk row *"a Guarded Crate crate raising no Rust frame"* now holds
for the plane as well as the crate.
6. **The run-log note is written at placement (D380):** core has no run-end hook for a kept step.
7. **The location is a world call's**: a monument with offsets, or a point. §14.3.5 said "monument, grid or
point", but no step takes a grid; the zone and call steps take these two.
8. **The Carbon row is out of date**, as §14.3.18 found for DT: the console probe is true on the Carbon rig, and
`gcrate` ran there. The step is expected to work on Carbon.
**Settled in the code, and overrulable:**
1. **A clearing counts only for a player.** A last guard killed by another NPC (RunicNPC's, say) or by nothing
is `gc.ended` with no winner: no *Warden* count, no trigger.
2. **`rust.gc.ended` fires only for a crate a player cleared**; an ending with nobody, and a timeout, raise no
trigger. Both are still feed lines (*"A guarded crate ended with nobody clearing it"* public, the timeout
staff's).
3. **Whether anybody cleared it (`won`) is public**; the clearer's name is the presence audience's, the Steam
id, the position and a step's key staff's.
4. **The event param is a name, not `server/name`.** The location already names the server; the list offers
every server's names once each, with the servers that have them, and Guarded Crate's four defaults where no
server has said. Verify refuses a name the chosen server's report does not list.
5. **`gc-too-close` is retried** on core's ordinary delay (the other crate may be cleared); every other plugin
refusal is permanent.
6. **A height is not sent**: the crate falls to the ground and its guards stand on it.
7. **The column is *Crates cleared***, shown where the server reports Guarded Crate or a row counts one, as
Treasures is.
8. **Where Guarded Crate's list cannot be read** (its shape changed), the hooks alone keep the set, and the log
says so once.
**Tests.** Bridge: `checkPlugin.js` (84 hooks, every one void and listed), 18 tests. Sidecar: 73 tests (one new,
the stamp), `fmt` and `clippy` clean. Module-Rust server: `test/guardedCrate.test.js`, 16 new (the public read
the grid, never the position or the key; the clearer's name for the presence audience and the Steam id for
staff; an ending with nobody is not a win; the feed reads a stored clearing per viewer; the report read
leniently; the step kept, priced and without a revert; the event list from every server and the defaults; the
dry run's refusals for Guarded Crate absent, NpcSpawn missing, the console unavailable and an unknown event,
ignoring case, and a server that has not said left to the plugin; the key, event and location on the wire and
the kept note; retries and permanent refusals; a repeat; the tally's count; the column's sort; the triggers and
their variables; a crate nobody cleared and a timeout firing nothing; an old crate telling nobody), and the
catalogue, title and registration tests extended: 643/643, imports, bundle, swagger and engagement checks.
Client: three new in `feed.test.js`, 95/95, the build and externals. Booted against the pinned core: 47 triggers,
14 actions, 12 budgets, 22 option sources; the 89 routes unchanged.
**On the rigs**, both: the bridge compiled and loaded; with Guarded Crate 2.0.17 and NpcSpawn 3.4.8 loaded,
`rg.world` read the four events from the data file and Guarded Crate's list as readable; `gcrate start` and
`gcrate position` (in the fully quoted form `ConsoleSystem.Run` sends) each counted a running crate and said
`gc.started`, with no `airdrop.called` after the fix; a hotload of the bridge found the running crate again
(*"1 guarded crate(s) after load"*); and `gcrate stop all` took it off. Both rigs were then cleaned of the two
plugins and their files, and put back on the protocol-13 bridge. The clearing, the timeout, the step through
the sidecar and the site are §14.3.5's walk rows, at the end walk (D331, D359).
**Walk rows** are §14.3.5's, with the Carbon row expecting the step to work, and three more: a Guarded Crate
placement raising no airdrop line; `gcrate stop all` taking a crate off the map; and a crate's guards killed by
a RunicNPC NPC, which clears it for nobody.
### 14.4 Questions for the org lead ### 14.4 Questions for the org lead
**Answered on 2026-10-14:** Q17 as proposed, out (D358). **Q18 against the proposal: the end walk waits for **Answered on 2026-10-14:** Q17 as proposed, out (D358). **Q18 against the proposal: the end walk waits for

View File

@@ -1805,7 +1805,7 @@ drawing a second time. A new map key makes the file stale, and it is ignored.
| Field | Rows | | Field | Rows |
|---|---| |---|---|
| `world` | `kind` (`cargo`, `heli`, `chinook`, `bradley`, `supply`, `crate`, `boss`, `treasure`), `x`, `z`; a locked crate being hacked adds `hackLeftSec`, a hacked one `hacked: true`; a `boss` (protocol 13, RunicNPC D302) is one of RunicNPC's bosses placed outside any event, with its `name`; a `treasure` (protocol 14, §20.8) is a running Dangerous Treasures chest | | `world` | `kind` (`cargo`, `heli`, `chinook`, `bradley`, `supply`, `crate`, `boss`, `treasure`, `guarded`), `x`, `z`; a locked crate being hacked adds `hackLeftSec`, a hacked one `hacked: true`; a `boss` (protocol 13, RunicNPC D302) is one of RunicNPC's bosses placed outside any event, with its `name`; a `treasure` (protocol 14, §20.8) is a running Dangerous Treasures chest; a `guarded` (protocol 14, §20.9) is a running Guarded Crate event |
| `events` | what this site's events placed (§15.2): `kind` (`zone`, `crate`, `npc`), `runId`, `x`, `z`, and a zone's `radius` and `name` or a thing's `prefab` and `key` (the step that placed it). Protocol 13 adds, for one of RunicNPC's NPCs, its `name`, and for a boss `boss: true` and `health` (0–1); and a boss's **adds**, which no step placed, under their boss's run with `add: true` | | `events` | what this site's events placed (§15.2): `kind` (`zone`, `crate`, `npc`), `runId`, `x`, `z`, and a zone's `radius` and `name` or a thing's `prefab` and `key` (the step that placed it). Protocol 13 adds, for one of RunicNPC's NPCs, its `name`, and for a boss `boss: true` and `health` (0–1); and a boss's **adds**, which no step placed, under their boss's run with `add: true` |
| `players` | `steamId`, `name`, `x`, `z`, `sleeping`, `online`. Connected players, then offline sleepers | | `players` | `steamId`, `name`, `x`, `z`, `sleeping`, `online`. Connected players, then offline sleepers |
| `bases` | `kind` (`tc`, `vending`), `x`, `z`. **Positions only**: no owner, no authorised list, no shop name | | `bases` | `kind` (`tc`, `vending`), `x`, `z`. **Positions only**: no owner, no authorised list, no shop name |
@@ -3249,3 +3249,128 @@ events like any other.
shown where the server reports DT or a row counts one (`dangerousTreasures: true` on the answer). shown where the server reports DT or a row counts one (`dangerousTreasures: true` on the answer).
- The map draws a running chest on the world layer. Server settings' plugin line says whether DT is loaded, and - The map draws a running chest on the world layer. Server settings' plugin line says whether DT is loaded, and
whether a step can start one; `GET /admin/rust/servers/{id}/integrations` gains `console` for it. whether a step can start one; `GET /admin/rust/servers/{id}/integrations` gains `console` for it.
### 20.9 Guarded Crate — `gc.started`, `gc.ended`, `gc.failed`, `gc.place` (§14.3.5, D361, D363, D364, D373, D375, D380)
Bazz3l's **Guarded Crate** (2.0.17, uMod), optional like Economics (D199) and not in `overlay.toml`, with its
dependency **NpcSpawn** (3.4.8, Codefling; D361), which spawns its guards. A cargo plane drops a locked crate at
the event's spot, ringed by guards (8 to 20 by event); **whoever kills the last guard has cleared it**, and the
crate unlocks for them. An event nobody clears ends after its time (1,800 s as it ships, counted from the last
guard killed). The bridge reports its events, and places one for a run's step through Guarded Crate's own
console command. The design is [`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §14.3.5; what the
§14.2 test found is §14.2.1 there, and what the build found in its source is §14.3.19.
**Hello and status.** `integrations` gains `guardedCrate`, with NpcSpawn beside it (D364):
```json
"guardedCrate": { "loaded": true, "version": "2.0.17",
"npcSpawn": { "loaded": true, "version": "3.4.8" },
"events": ["Easy", "Medium", "Hard", "Elite"] }
```
`npcSpawn` is always sent, `{ "loaded": false }` when it is absent. **`events` are the event names in Guarded
Crate's DATA file** (`data/GuardedCrate.json`, each `CrateEventEntries` row's `event display name)`, the key's
stray bracket and all), not its config; it is absent while Guarded Crate is not loaded or when the file cannot
be read. Hello is sent again when Guarded Crate or NpcSpawn loads or unloads. Placing one also needs hello's
`console.plugins` (§20.5, D363). `rg.world` prints `guardedCrate=`, `npcSpawn=`, `guarded=` (the running
events the bridge knows), `gcPlaces=` (keys remembered), `gcEvents=`, `gcCommand=` and `gcListReadable=`.
**Three events**, from Guarded Crate's own hooks:
```json
{"kind":"gc.started","type":"event","x":-604.0,"z":-342.0,"key":"12:4"}
{"kind":"gc.ended","type":"event","steamId":"76561198000000001","name":"Alice","x":-604.0,"z":-342.0}
{"kind":"gc.failed","type":"event","x":-604.0,"z":-342.0}
```
- **`gc.started`** is `OnGuardedCrateEventStart(position)`. **`key` is set when the start answers one of our
`gc.place` commands**, and the join is exact: Guarded Crate raises the hook inside the call that creates the
event, as Dangerous Treasures does (§20.8).
- **`gc.ended`** is `OnGuardedCrateEventEnded(player, crate)`, raised when the last guard dies. **`steamId` and
`name` are the player who killed it, and are absent when nobody did**: the guards went another way (NpcSpawn
reloaded under them, or they died of falls or drowning), or another NPC killed the last one. The hook does not
say which event, so the bridge finds it in Guarded Crate's own list (the one marked complete), else at the
crate's position or the player's, within 100 m, else the only one running; `x`, `z` are absent if none can be
told.
- **`gc.failed`** is `OnGuardedCrateEventFailed(position)`: its time ran out with guards standing.
- **Guarded Crate's unload and `gcrate stop all` end every event and raise no hook.** Nothing is sent for them;
the map's set is corrected instead (below).
- **None is sent while the world is not live** (§20.5's rule). A clearing is still counted.
- **Guarded Crate's cargo plane is not an airdrop.** The plane that delivers its crate raises no
`airdrop.called` (§20.5): the bridge asks Guarded Crate's own `API_IsGuardedCrateCargoPlane`. Its crate is
never Rust's own either, by §20.5's rule for crates.
**The tally** gains **`guardedWon`**, the events a player cleared (*Warden*, D375).
**`map.live`'s `world`** (§17.3) gains `kind: "guarded"` for each running event, `x`, `z`. The bridge keeps the
set by the hooks, and **checks it against Guarded Crate's own list** (its private `EventManager.Instances`, read
by reflection, since it has no API that lists them) at its load, before every `map.live` and on `rg.world`: a
hotload of the bridge hears no starts, and `stop all` ends events with no hook. Where the list cannot be read
(said once in the log), the hooks alone keep the set. It is cleared when Guarded Crate unloads.
**`gc.place` — place one guarded crate for a run (D364, D373).**
```json
{"cmd":"gc.place","reqId":"r-62","key":"12:4","event":"Elite","monument":"powerplant_1","offsetX":10,"offsetZ":-5}
```
`event` is one of `events`, matched ignoring case as Guarded Crate matches it. **The location is a world
call's** (§20.5): a `monument` with `offsetX`/`offsetZ`, or `x` and `z` (and `y`, else the ground). The bridge
runs `<command> position <event> x,y,z` through `ConsoleSystem.Run`, where `<command>` is Guarded Crate's
console command from its config (`gcrate` unless the operator renamed it). The answer is:
```json
{"kind":"gc.placed","type":"reply","reqId":"r-62","key":"12:4","event":"Elite","repeat":false,"x":-604.0,"z":-342.0}
```
**A key already answered is answered again with its first placement, `repeat: true`, and places nothing.** Keys
are kept seven days in `data/RunicGateway/gc-places.json`. **Nothing removes a placed crate** (D373): Guarded
Crate cannot end one event alone. Refusals are `{"kind":"gc.error","reason":…,"message":…}`:
| `reason` | When |
|---|---|
| `malformed` | No key, or one over 128 characters; or a location that is both or neither (§20.5) |
| `bad-event` | No event named, a name not in `events`, or Guarded Crate's own "event not found" |
| `gc-missing` | Guarded Crate is not loaded |
| `npcspawn-missing` | NpcSpawn is not loaded. **Guarded Crate's `position` does not check this itself** (only its `start` does), so without the bridge's refusal the crate would land unguarded and free to hack (D364) |
| `console-unavailable` | Hello's `console.plugins` is false (D363, Rust-Plugins#34) |
| `no-monument`, `out-of-range`, `off-map` | The location, as §20.5 |
| `gc-too-close` | Another guarded crate is running within 80 m (Guarded Crate's own rule) |
| `bad-position` | Guarded Crate's own "position invalid" |
| `refused` | Nothing was placed and Guarded Crate answered something else, in its own words; or its console command is switched off in its config |
`events-disabled` and the world's `not-ready` come first, as `world.error`, as for every world verb (§15).
"Placed" is the start hook firing; Guarded Crate's replies are recognised by reading its own messages from its
lang file, as §20.8 does for DT.
**Hooks.** `OnGuardedCrateEventStart`, `OnGuardedCrateEventEnded` and `OnGuardedCrateEventFailed` join
`rg.hooks`' list.
**The sidecar** adds `POST /gc/place`, a thin forward that keeps nothing, and stores and forwards the three
events like any other.
**The website (Module-Rust).**
- **`gc.started` and `gc.ended` are public kinds, `gc.failed` a staff kind.** Each row is read per viewer by
`guardedCrate.shapeFor`, as §20.8's are: everyone gets `grid`, and for an ending `won` (whether a player
cleared it); **the clearer's `name` only for the presence audience**; **`x`, `z`, `key` and the clearer's
`steamId` only for staff**. The lines read *"A guarded crate landed · M4"*, *"Alice cleared the guarded crate ·
M4"* (*"The guarded crate was cleared"* below the presence audience), *"A guarded crate ended with nobody
clearing it"*, and for staff *"A guarded crate timed out with its guards standing"*. The first two are in the
feed's *World events* filter.
- **Triggers `rust.gc.started` and `rust.gc.ended`** (§14.3.9): public, `everyone` the ceiling, the cooldown
subject the server; `byEvent` on a placement one of the site's steps made; **`rust.gc.ended` fires only for a
crate a player cleared**, with the clearer by in-game name only. A timeout raises nothing.
- **The step `rust.gc.place`**, *Place a guarded crate* (`risk: 'change'`, `reversible: 'none'`, budget
`rust.gc.crates`, one per step): the event (option source `rust.options.gc_events`, every server's own names,
or Guarded Crate's four defaults where none has said) and a world call's location. It is refused at verify
where the server said Guarded Crate is not loaded, that NpcSpawn is missing (*"Guarded Crate is loaded on Main
but NpcSpawn is missing: its events cannot start"*), that `console.plugins` is false, or that it has no event
of that name; a server that has not said is left to the plugin. **Its success line in the run log says the
crate is kept when the run ends** and how it ends (D373, D380). `gc-too-close` and a game that is down retry on
core's ordinary delay; every other refusal is permanent.
- **Warden (D375):** `guardedWon` → `rust_player_wipe_stats.guarded_won`, the title category `guardedwon`
(default *Warden*), and the leaderboard's **Crates cleared** column (`sort=guardedWon`), shown where the
server reports Guarded Crate or a row counts one (`guardedCrate: true` on the answer).
- The map draws a running event on the world layer. Server settings' plugin line names Guarded Crate and
NpcSpawn, the events, and whether a step can place one.