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:
@@ -1805,7 +1805,7 @@ drawing a second time. A new map key makes the file stale, and it is ignored.
|
||||
|
||||
| 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` |
|
||||
| `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 |
|
||||
@@ -3249,3 +3249,128 @@ events like any other.
|
||||
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
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user