feat(bridge): what an event borrows, and the two one-shots (Phase 12b) #24
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/events-p12b-borrowed-and-oneshots"
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?
Events Phase 12b, shard half. One of five:
website(website#194),servuo-plugins,link(#—),Module-uo(#—),docs(docs#222). Spec of record:docs/link/v7.md§11–§14.overlay.tomlstays atprotocol = 7— 12b amends 7 in place, tolerable for the single reason 6 and 7 already are: nothing is released fromedge.A lease here must be persisted, and the config plane's must not
11b's fail-safe is stated plainly in its own header: a lease that never reaches disk means a shard restart is a free restore. That argument depends entirely on the leased value being memory-only too, and here it is not. A spawner is an
Itemand is in the world save; a seasonal entry is written toSaves/Misc/SeasonalEvents.binby ServUO's ownEventSink.WorldSave.So a restart does not put either back — it puts the change back and throws away the deadline timer that was going to undo it, leaving the world at the leased value with nothing here remembering it is borrowed. That is the exact failure the lease framing exists to make impossible.
So the Bridge gains its third save file,
Saves/Bridge/Leases.bin, written by the sameEventSink.WorldSavethat writes what it describes, with deadlines re-armed at load. A deadline that passed while the shard was down fires at once: the promise was "back at baseline by then", and extending it would silently turn a two-hour lease into however long the outage was. Config holds are still not written down — the same argument, applied to planes where its premise is false.A target is a serial or a UniqueId, and both are needed
A serial is what
[propsshows a GM. AnXmlSpawner.UniqueIdis what the shard's ownSpawns/*.xmlcarry — and it is not a convenience: a dropdown built from serials is impossible, because serials are assigned when the world is built and nothing off-shard knows them.SpawnerandXmlSpawnershare all four property names, which is a fact about this tree rather than a convenience: the spawn files load as XmlSpawners while[add spawnermakes the native one. And it isMaxCount, not theAmountEVENTS_PLAN.mdnamed — there is no such property.MinDelay/MaxDelayareTimeSpans, so the wire carries seconds.The allowlist is checked against the object's own type, which is the sentence the whole plane rests on: a serial is a number a caller chooses, so that check is all that stands between
Spawner.MaxCountand any item on the shard. Reflection is bounded three ways — the pair must be in the catalog, the property must carryCommandProperty(so this can never reach further than[setcould), and its CLR type must be one this file renders.The self-check, and the one failure no probe can catch
§N10 in full. A config key is probed live (write, read back, restore) because there is exactly one of it. A property cannot be — thousands of instances and no canonical one, so probing would mean writing to somebody's spawner at boot. What is verified instead is everything verifiable without touching the world.
And
TreasuresOfTokunois excluded by name, becauseIsActive()reads its ownDropErarather thanStatus: the write succeeds, the value reads back, a compare-and-set restore passes, and the capability does nothing at all. That is N10's "capability that lies" in its purest form, and the only way to find it is to read the source. §G also called this toggle "small and safe" — it is safe, butOnStatusChange()generates or removes world content for six of the eight.The one-shots
Who receives a grant is answered here. The website has the list too, but a module cannot read core's tables, so the alternative was a new core surface handing participants to a module. Not needed: 11b's participation ledger already holds them, keyed by the same serials. A run with no ledger is a 404; a run whose ledger is open and empty is a 200 with
granted: 0, because an event nobody attended still happened. An undeliverable grant is deleted rather than dropped —AddItemfailing on a full backpack would otherwise leave it in the world at (0,0).A save stops the world, so it is rate-limited rather than capped, counting ServUO's own autosave as the last one. Refused, never queued: a queued save would land at a moment nobody chose.
Verified
Compiles clean (
dotnet build Scripts.csproj -c Release: 0 warnings, 0 errors). Then a full walk on the real local ServUO 57.4 world against the release sidecar:currentis correctly absent on the targeted rows and filled when a target is named;lease.driftedand the world is left at 55, not reverted; a clean release restores to baseline;Inactive → Active;404, empty ledger200 granted: 0, unknown item400, over the stack bound400; save200then429inside the interval;Leases.binis 8 bytes and names nothing;ChainChestrefused as a spawner;00:01:30and the baseline reads back as18000;unreadableand releasestargetGone: true.The test world was never saved after the deliberate deletion, so it is intact.
Scaffolding
BridgeRigDriver.csgainsspawnerlist,propset,propreadandseasonlist.propsetis the one that matters: 11b addedconfigsetbecauseConfig.Sethas one caller in the whole tree, so nothing could drift a config lease. A spawner is the opposite — a GM drifts one with[propsin four seconds — but the rig has no client, so it needs the same door.🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4