Files
Module-Rust/server/guardedCrate.js
wtclaude da1d74bdff
All checks were successful
PR Checks / client-build (pull_request) Successful in 30s
PR Checks / server-tests (pull_request) Successful in 34s
PR Checks / frozen-manifest (pull_request) Successful in -1m34s
feat(rust): Guarded Crate — feed, map, Warden, Place a guarded crate (PLAN_REDESIGNS §14.3.5)
Stage 6 of the §14.3 integration plan, on protocol 14. Spec: docs
rust-link/PROTOCOL.md §20.9.

- gc.started and gc.ended public, gc.failed staff's; guardedCrate.js reads
  each row per viewer (the clearer's name for the presence audience, Steam id,
  position and key for staff, `won` for everyone).
- Triggers rust.gc.started and rust.gc.ended (a crate a player cleared only).
- The step rust.gc.place: an event from rust.options.gc_events and a world
  call's location; refused at verify for Guarded Crate absent, NpcSpawn missing
  (D364), console unavailable, or an unknown event. Kept (D373); the run log's
  success line says so (D380). Budget rust.gc.crates.
- Warden (D375): guarded_won, title category guardedwon, Crates cleared column.
- Map marker, server settings line, swagger, engagement manifest, bundle, README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-10-15 12:26:50 -05:00

98 lines
3.8 KiB
JavaScript

// ── Guarded Crate, with NpcSpawn (protocol 14, PLAN_REDESIGNS §14.3.5) ─────
//
// Bazz3l's Guarded Crate drops a locked crate from a cargo plane and rings it
// with NpcSpawn's scientists; whoever kills the last guard has cleared it. Its
// events reach this module as three kinds: `gc.started` (public), `gc.ended`
// (public) and `gc.failed` (staff's, the timeout). The crate cleared is
// *Warden*'s count, from the tally's `guardedWon` (D375). A run's step places
// one (`eventGuardedCrate.js`).
//
// ── Who may read what ──────────────────────────────────────────────────────
//
// As Dangerous Treasures' are (`dangerousTreasures.js`, D376): the event and its
// grid are public; the clearer's name shows only to the operator's presence
// audience, so with presence left at staff a signed-out visitor reads "The
// guarded crate was cleared · M4"; the Steam id, the position and a step's key
// are staff's. Whether a player cleared it at all is public (`won`), since an
// ending with nobody named (NpcSpawn reloaded under it) names nobody either.
const { gridOf } = require('./worldEvents')
const KINDS = Object.freeze(['gc.started', 'gc.ended', 'gc.failed'])
const STEAM_ID = /^\d{17}$/
function isKind(kind) {
return KINDS.includes(kind)
}
/**
* One `gc.*` frame for one viewer: `{ staff, presence, geometry }`. Passes the
* envelope, adds `grid`; an ending adds `won`, and `name` for the presence
* audience. Staff also get `x`, `z`, the step's `key` and the clearer's
* `steamId`. Any other frame is returned as is.
*/
function shapeFor(frame, { staff = false, presence = false, geometry = null } = {}) {
if (!frame || !isKind(frame.kind)) return frame
const out = {}
for (const key of ['kind', 'type', 't', 'serverId', 'wipeId']) {
if (frame[key] !== undefined) out[key] = frame[key]
}
const grid = gridOf(geometry, frame.x, frame.z)
if (grid) out.grid = grid
if (frame.kind === 'gc.ended') {
out.won = STEAM_ID.test(String(frame.steamId || ''))
if (out.won && (presence || staff) && typeof frame.name === 'string' && frame.name) out.name = frame.name
if (out.won && staff) out.steamId = String(frame.steamId)
}
if (staff) {
for (const key of ['x', 'z', 'key']) {
if (frame[key] !== undefined) out[key] = frame[key]
}
}
return out
}
/**
* A server's last report about Guarded Crate and its console, from
* `servers.db.gcReportFor`: `{ guardedCrate, consolePlugins }`, each null where
* the plugin never said (no state yet, or a plugin before protocol 14).
* `guardedCrate` is `{ loaded, version, npcSpawn: { loaded, version }, events }`;
* `npcSpawn` is null where it was not said, and `events` null where the
* plugin could not read Guarded Crate's data file.
*/
function reportOf(row) {
const parse = (value) => {
if (value === null || value === undefined) return null
if (typeof value !== 'string') return value
try {
return JSON.parse(value)
} catch {
return null
}
}
const integration = (body) =>
body && typeof body === 'object' ? { loaded: body.loaded === true, version: typeof body.version === 'string' ? body.version : null } : null
const gc = row ? parse(row.guardedCrate) : null
const probe = row ? parse(row.console) : null
return {
guardedCrate:
gc && typeof gc === 'object'
? {
...integration(gc),
npcSpawn: integration(gc.npcSpawn),
events: Array.isArray(gc.events) ? gc.events.filter((e) => typeof e === 'string' && e.trim()) : null,
}
: null,
consolePlugins: probe && typeof probe === 'object' && typeof probe.plugins === 'boolean' ? probe.plugins : null,
}
}
module.exports = { KINDS, isKind, shapeFor, reportOf }