Four verbs (PLAN.md §30.2), all outside EventsEnabled because none of them
changes the world:
- map.info: the map key, the picture's source (the Rust+ cache, a render
kept on disk, or none), its size, SHA-256 and slice count, the game's own
grid, and the monuments the in-game map shows. The cache is read by
reflection by name only, never enumerated (it holds the Rust+ secret).
The hash is computed on the thread pool.
- map.fetch: one 512 KiB base64 slice, encoded on the thread pool, refused
`stale` when the key or hash moved.
- map.render: the admin fallback (D109). Answers `accepted` at once and
renders on a later frame, keeping the result in data/RunicGateway/map.jpg
beside its key so a restart within the map does not render again.
- map.live: every layer at once (world events, event-owned zones/crates/NPCs,
players and sleepers, TCs and player vending machines as positions only).
The sets are kept by OnEntitySpawned/OnEntityKill and seeded by one sweep
at OnServerInitialized. MapMaxSleepers/MapMaxBases bound the two layers.
Also fixes Grid() (D119): it used a 146.3 m cell, but the game fits whole
cells to the world (20 x 150 m on a 3000 map), so rows could be one off.
It now asks MapHelper.PositionToString, the in-game map's own labeller.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY