Five verbs an author sees -- creatures, an enhanced "boss", an oracle NPC, a temporary gate, decoration -- and ONE command family underneath them, because every one of them ends in the same sentence: an object exists, and this run owns it. `world.spawn` / `world.despawn` / `world.owned` carry a `what` discriminator, and the per-verb differences are fields rather than kinds. The ownership registry is PERSISTED, and that is forced rather than chosen. A spawned creature is in the world save, so it survives the restart that proves a town-crier line gone -- which already rules out reconcile-by-boot-stamp. But the record of which run owns which serial has nowhere else to live: in memory it is lost in the restart the creatures survive, and only in the website's ledger it is not held here at all, so `world.despawn` would delete whatever serial it was handed and "never touches a creature it did not create" would have no mechanism behind it. So the Bridge gains its second persisted file beside `Participation.bin` -- written by the same world save as the objects it describes, so the two cannot get out of step. The oracle is ours rather than `XmlSpawner2.XmlDialog`'s, and that engine is the reason for both halves of the decision. Its `SpeechEntry` is the evidence the shape is right -- `Text` plus comma-separated `Keywords`, a keyword-less entry as the greeting, a proximity range, a conversation lock. It is also why not to build on it: `SpeechEntry` carries an `Action` string, XmlSpawner's command-scripting language, which would leave an arbitrary-command field one step from an event author. `Mobile.OnMovement` (delivered to every mobile in range -- the `HandlesOnMovement` filter applies only to Items) and `Mobile.HandlesOnSpeech`/`OnSpeech` are native virtuals and are all it needs. Every `Bridge.EventsMax*` REFUSES rather than clamps, on `LeaseMaxDurationSec`'s argument from 11b: the shard's bound exists for the case where the website is wrong. `Bridge.EventsEnabled` gates all of it -- spawning is the same consent 11b introduced that switch for, not a third one. Decoration carries an `itemId`, because `Static` accounts for 5031 of the tree's decoration placements under 1992 different graphics: for that class the graphic IS the identity. Never applied to a `BaseAddon`, whose own ItemID is not what a player sees. Containers are refused outright -- teardown would delete whatever a player had left inside. `tools/scaffolding` gains `worldgone <serial>`, which deletes 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 -- and it is what a player's sword does every time they kill an event creature. Verified on a real ServUO 57.4 world (206k items, 42k mobiles) against the release sidecar: all five verbs place; every ceiling refuses; a container and an unknown type refuse; one run cannot despawn another's object; the registry and its objects both survive a save and a clean restart (`pruned: 0`); a creature deleted behind the registry's back comes back `gone` rather than `removed`; and a five-second gate is collected by the shard's own deadline with `world.expired` on the wire. Refs: docs/link/v7.md, docs/website/EVENTS_PLAN.md Phase 12a Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
43 lines
2.4 KiB
TOML
43 lines
2.4 KiB
TOML
# Release metadata for the deployable overlay.
|
|
#
|
|
# Consumed by .gitea/workflows/release.yml, which folds these values into the
|
|
# manifest.json shipped inside runicgateway-overlay-<ver>.tar.gz. The Runic
|
|
# Gateway installer reads that manifest to decide what it is deploying and
|
|
# whether it is compatible with the sidecar it is about to install
|
|
# (docs/installer/PLAN.md §5 Phase 0, §7.1).
|
|
#
|
|
# There is deliberately NO version key here. The release version is derived from
|
|
# git tags and conventional commits by the release workflow, so there is no bump
|
|
# commit to keep in sync and no way for this file to disagree with the tag.
|
|
|
|
# ── The loopback wire-protocol version this overlay speaks ───────────────────
|
|
#
|
|
# This is the plugin half of the compatibility contract. It MUST equal the
|
|
# sidecar's PROTOCOL_VERSION (link/sidecar/src/main.rs) for a deployment to
|
|
# work: the sidecar rejects a mismatch with 409 rather than mis-parsing.
|
|
#
|
|
# The C# plugin has no queryable version before ServUO boots — it does not
|
|
# announce one on the wire — so this declaration is the only thing that lets the
|
|
# installer's bundle CI check the pair BEFORE an operator installs them
|
|
# (docs/installer/PLAN.md §2.6, §7.1 gate 1). Keeping it honest is therefore a
|
|
# manual duty: when the protocol changes, bump it here in the same PR that
|
|
# changes the emitters, exactly as link bumps PROTOCOL_VERSION.
|
|
#
|
|
# Current: 6 — see docs/link/v6.md (idempotency keys on inbound commands, champ.boss.killed).
|
|
protocol = 7
|
|
|
|
# ── ServUO compatibility ─────────────────────────────────────────────────────
|
|
#
|
|
# The base overlay (Config/Bridge.cfg + Scripts/Custom/Bridge/*.cs) only ADDS
|
|
# files and is expected to work on any reasonably current ServUO. This is the
|
|
# oldest version it is known good on.
|
|
min_servuo_version = "57.4"
|
|
|
|
# The patches/ tier is a different matter: those are unified diffs against STOCK
|
|
# ServUO files, so they are verified against exactly one version and nothing
|
|
# else. On any other version the installer skips the whole tier with a warning
|
|
# and completes the base install (docs/installer/PLAN.md §1, §2.2) — losing
|
|
# vendor.sale events and in-game moderation-audit forwarding, but never
|
|
# half-patching an unknown tree.
|
|
patches_verified_against = "57.4"
|