fix(shard): answer with the instance name when the shard is unnamed #119
Reference in New Issue
Block a user
No description provided.
Delete Branch "fix/ruleset-shard-name"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What & why
From the live Protocol 3.0 smoke test (ServUO + sidecar + website + AVD).
The rules page read "My Shard" under a header reading "UOMysticmoon". ServUO ships
Server.cfgwithName=My Shard, so an operator who never edited it publishes that verbatim. That value is the shard saying unnamed, not naming anything — so the site now answers with its own name.Three things in here
1. The fallback. New
settings.getInstanceName()resolvessite_title || BRAND_NAME— the same resolutiongetPublic().brand.namealready uses, so an install that set only the site title can never show two different names on two pages. Barebrand.namewould have been wrong for exactly that case (this test install hasBRAND_NAMEunset andsite_titleset, and would have printed "Runic Gateway").Matched case- and padding-insensitively, but only as a whole value: a shard genuinely called "My Shard Reborn" has named itself and keeps it.
Applied at ingest, not on read —
world.rulesetis also broadcast live, and the same object is handed to the SSE fan-out, so a read-time substitution would be undone by the next reconnect's frame. Both halves are asserted.2. A second ruleset writer, found on the way.
uoLinkSocket.backfill()calledshardState.setRulesetdirectly instead of going throughshardIngest.ingest()asingestEachdoes for the array-shaped boards — so the boot/reconnect snapshot silently skipped the normalization the live frame got. Symptom was the fix appearing to not work on a fresh boot. The two arrival orders have to produce the same stored frame; routed through the dispatcher so there is one writer.Worth generalising: any state a kind can arrive by two orders (live frame vs backfill snapshot) must funnel through the dispatcher, or the two paths drift.
3. Unscored leaderboards render a row. Most systems on a young shard have
top: [], and a page of blank cards reads as broken rather than as new. An empty board now shows a single placeholder bearing the instance name with an em dash where a score goes — deliberately not shaped like an entry (no medal, no bar, muted), because a placeholder that looked like a real standing would be a fabricated one. The first real entry replaces it outright.Presentation only: the API still sends an empty
top, so no consumer ever receives an invented row. The app renders it the same way (RunicGateway/Android-app#32).How it was tested
npm test(server) — 679 tests, 0 failures;node --test(client) — 47, 0 failures.shardIngest.ruleset.test.jscover the stock name, blank/missing, the live-broadcast half, the whole-value match, a shard that named itself, and a settings-read failure. One of them caught a real slip while writing this (a wrong variable name that silently skipped the write).GET /public/shard/rulesetreturns"shard": "UOMysticmoon"after a cold boot through the snapshot path, and/site/rules+/site/leaderboardsrender correctly in the browser.npm run swaggerregenerates with no content change (no route signature changed), so the committed spec is untouched.Docs: RunicGateway/docs#76.
Checklist
AI-assisted contributions (required)
Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with aCo-Authored-By/Assisted-Bytrailer.License
🤖 Generated with Claude Code
https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP