c6c0c257dd2a69216a51852235c6fea8a97cfcf7
2 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 7c769ea8fd |
feat(atlas): serve the spawn atlas and give operators a panel for it
Protocol 3.0 order 3 (Part C), second of two website PRs. #112 built the data pipeline; this makes it reachable — six public routes, five admin ones, two public pages and an admin panel. Still website-only: no plugin, no sidecar, no new event kinds, no wire change. The API sits at /api/v1/public/atlas, not under /public/shard. Nothing here touches the sidecar, so the pages stay complete while the shard is down, and a /shard prefix would imply a dependency the atlas does not have. Unlike /shard/* it IS site-mode gated, like /posts and /wiki: a bestiary is site content. Every route carries requireFeature('atlas') and projects its response. The atlas feature declares no sensitive fields, so the projection is a no-op today — the call is there because v3.md 3.6.1's rule is that the FIRST field needing a gate should be covered by construction rather than by a retrofit. Two bugs the UI surfaced, both fixed here: Respawn delays were stored in the wrong unit, sometimes. XmlSpawner writes MinDelay/MaxDelay in minutes and switches to seconds only when a delay does not divide into whole minutes, flagging it per record with DelayInSec. A `5` means five minutes on one spawner and five seconds on the next, both plausible, and the pipeline stored the raw number. 170 of 6,455 stock spawners are second flagged. The parser normalises to seconds; the API and UI carry seconds. That exposed the hash gate as a trap. "Has the tree changed?" is the wrong question on its own: an install whose maps never change would have kept serving the old readings forever, because the only thing compared was the tree. PARSER_VERSION is now stored beside the source hashes and a mismatch counts as drift, so any future parse correction lands on the next boot. Also renamed the detail route's spawn-point array to `spawners` — it was `points`, which is the COUNT on the search route, so one key meant a number in one place and an array in the other. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP |
|||
| 2801ec8f4d |
refactor(atlas): derive the atlas from the shard's tree on every boot
Replaces the committed-artifact design from the first commit. Two problems with it, both raised in review: **Facets are not a fixed list.** The first pass carried a hardcoded table of the six stock UO facets to reconcile the spelling drift between sources. That is wrong: a shard may add facets, replace them outright, or rename them when its maps are updated, and a built-in list quietly mishandles all three. Nothing in the atlas names a facet any more. The facet set is discovered from the tree — spawn records and region definitions are the authority — and the loose spellings in Data/Locations are matched against it by key and prefix. Custom facets get identical treatment; the tests use `Sosaria` and `Underdark` precisely so a stock-facet assumption cannot creep back in. **A snapshot goes stale.** Maps change over a server's life, so a build-once artifact silently drifts from the world players actually see. The tree is now the single source of truth and the atlas is re-derived on every boot. ## What that changed - **The committed artifact is gone** — 1.41 MB of generated JSON removed, along with `scripts/buildSpawnAtlas.js` and the whole encode/decode seam it needed (`encodePoint`/`readPoint`, the tuple encoding, the omitted-defaults scheme and their round-trip tests). Nothing to keep in sync, nothing to go stale. - **NEW `src/utils/spawnAtlasSource.js`** — the only thing that touches a ServUO tree; shared by the boot path and the CLI. Parsers stay pure and fs-free. - **NEW `src/model/shardAtlas/`** — `.db.js` (the one-transaction replace) and `.model.js` (the refresh decision). - **`scripts/importSpawnAtlas.js`** is now a thin CLI over the model: `--servuo`, `--force`, `--approve`, `--reject`, `--status`. `atlas:build` is gone; `atlas:import` remains. - Path comes from the `spawn_atlas_servuo_path` admin setting, falling back to `SERVUO_PATH`. The setting wins, matching how the rest of the shard integration is admin-managed rather than env-configured. ## Two contracts on the boot path **It never blocks startup.** No path, an unreadable mount, a malformed file, a database error — every one is caught and logged, and the site comes up serving whatever atlas it already had. Verified by booting the real server with no path, a broken path, and a good path. **A facet disappearing is never applied automatically.** Losing a facet is the signature of a half-copied or mid-update tree as much as of a real map change, and boot cannot tell them apart. The refresh is staged in `shard_atlas_pending` for an admin to approve or reject, and startup continues regardless. Additions and every other change apply immediately, since none of them can destroy something an operator would miss. Only the decision is stored, not the parsed world: a few KB of source hashes and the facet diff. Approving re-parses, so what gets applied matches the tree at approval time rather than at boot. A rejection is remembered against those exact hashes, so a declined refresh does not re-prompt on every restart — changing the tree changes the hashes and asks again. Hash-gated, so the common case (restart, maps unchanged) reads and hashes the tree (~120 ms) and writes nothing. A real change costs a ~400 ms parse. The admin approve/reject UI is part of the second PR, with the rest of the routes and pages. Until then the CLI covers it. ## Verification - **564 server tests pass**, 28 new in `spawnAtlas.source.test.js` covering the custom-facet build, the spelling reconciliation, hash gating, and every branch of the refresh decision — including that `refreshOnBoot` survives a database that throws on every call. - End-to-end against the local MariaDB and the real ServUO tree: 6,455 points, 800 creatures, 23,927 point/type rows, 387 regions, 558 landmarks, 25 altars, 83.2% of points resolved to a place name. - The facet gate exercised against a real tree copy with `malas.xml` removed: staged rather than applied, atlas untouched with all 293 Malas points intact, reject then stays quiet on re-run, approve applies and drops the facet. - Booted the real server under all three source conditions; none blocked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP |