feat(bridge): the world verbs an event owns (protocol 7, Phase 12a) #23
Reference in New Issue
Block a user
No description provided.
Delete Branch "feature/events-p12a-world-verbs"
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 12a, shard half. One of four:
servuo-plugins,link(#—),Module-uo(#—),docs(docs#221) — all ontoedge. Spec of record:docs/link/v7.md.overlay.toml→protocol = 7. 12b amends 7 in place rather than bumping again, so an overlay and a sidecar both declaring 7 are interchangeable only within one side of that merge — tolerable for the same single reason 6 was, and no other: nothing is released fromedge.One command family, five author verbs
world.spawn/world.despawn/world.owned, with awhatdiscriminator (creature, boss, npc, gate, decor). Every verb ends in the same sentence — an object exists, and this run owns it — so the per-verb differences (a boss's multipliers, an oracle's lines, a gate's destination and deadline) are fields rather than kinds. One ledger shape, one teardown path, one reconcile.The ownership registry is persisted, and that is forced rather than chosen
A spawned creature is in the world save, so unlike a crier line it survives a restart — which already rules out reconcile-by-boot-stamp on the website's side. But the record of which run owns which serial has nowhere else to live:
world.despawnwould delete whatever serial it was handed. "An event never touches a creature it did not create" — the sentence the boss verb is built on — would be an intention with no mechanism, and a bug in a step or a stolen sidecar token would be a delete-anything primitive.So the Bridge gains its second persisted file,
Saves/Bridge/Owned.bin, beside 11b'sParticipation.bin. Same mechanism, and the same rule about attaching hooks inConfigure()rather thanInitialize().It cannot get out of step with the objects: the registry is written by
EventSink.WorldSave, the same save that writes what it describes. That is what makes "anything not listed is gone" safe rather than hopeful.Three answers to a despawn, and only one is a failure:
removed,gone(a success — players killing event creatures is what spawning them is for),refused(not this run's; nothing is touched).The oracle is ours, and ServUO's own dialogue engine is why
XmlSpawner2.XmlDialogalready implements exactly the vocabulary this verb wants —Textplus comma-separatedKeywords, a keyword-less entry as the greeting, a proximity range, a conversation lock. That is the evidence the shape is right. It is also the reason not to build on it:SpeechEntrycarries anActionstring, XmlSpawner's command-scripting language, and routing authored dialogue through it would leave an arbitrary-command field one step from an event author on a web form — the[set§G excludes, arriving by the back door, in a subsystem we do not own and an operator can switch off.Mobile.OnMovement(delivered to every mobile in range — theHandlesOnMovementfilter applies only to Items,Server/Mobile.cs:3369against:3375) andMobile.HandlesOnSpeech/OnSpeechare native virtuals and are all it needs. Nothing executable crosses the wire.Ceilings, and the graphic problem
Every
Bridge.EventsMax*refuses rather than clamps, onLeaseMaxDurationSec's argument from 11b unchanged.EventsMaxOwnedPerRunis the one that is not per-call: it bounds a run calling a verb in a loop, which is the shape a runaway schedule takes.Bridge.EventsEnabledgates all of it — spawning is the same consent 11b introduced that switch for, not a third one.Decoration carries an
itemId, because measured on this treeStaticalone accounts for 5031 placements under 1992 different graphics: for that class the graphic is the identity, and a barenew Static()is never what the author picked. Never applied to aBaseAddon, whose ownItemIDis not what a player sees. Containers are refused outright — teardown would delete whatever a player had left inside.Scaffolding
tools/scaffolding/BridgeRigDriver.csgainsworldgone <serial>: delete an object behind the registry's back. It is the one outcome the rig cannot reach by asking the bridge — every bridge verb that removes an object also drops its row, so the two never disagree — and it is exactly what a player's sword does. Same reason 11b addedconfigset.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 (206,557 items / 42,785 mobiles) against the release sidecar:world.ownedreports each with its type and deadline;Bag) and an unknown type refuse, both 400;rig2cannot despawnrig1's boss →refused, nothing touched;save+ cleanshutdown+ restart,pruned: 0, and the world reloads with 0 errors (soBridgeOracle's serialization round-trips);gone, notremoved:{"removed":["0x314","0x316","0x317"],"gone":["0x315"],"refused":[]};world.expiredon the wire.Not driven by the rig: the oracle's greeting and keyword reply need a connected client — the same limit 11b hit with presence. The serialization round trip is proved; the speech behaviour is not.
🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4