diff --git a/overlay/Config/Bridge.cfg b/overlay/Config/Bridge.cfg index 69ae6ec..f1ddd8f 100644 --- a/overlay/Config/Bridge.cfg +++ b/overlay/Config/Bridge.cfg @@ -1,14 +1,25 @@ -# Settings for the ServUO/sidecar bridge test scaffolding. +# uo-link bridge settings. +# +# Key scope is the filename: Bridge.cfg + StatSweepSeconds => "Bridge.StatSweepSeconds". +# Read in Configure(), which runs before World.Load. -# When true, BridgeSeeder populates the world with synthetic accounts, characters, -# houses and player vendors on ServerStarted, then saves. It refuses to run twice: -# if the account "seed_000" already exists it does nothing. -# Set back to False once the world is seeded. -SeedOnStart=False +# Loopback only. The socket being local is the trust boundary for inbound commands; +# if the sidecar ever moves off-host, add a shared secret first. +Host=127.0.0.1 +Port=7788 -# Read-only census of the seeded world on startup (see BridgeSeeder.Census). -CensusOnStart=False +# Outbound queue cap. On overflow the plugin drops oldest and counts the drops, +# because a stalled sidecar must never OOM the shard. +QueueCap=10000 -# Times the bridge reads against the loaded world (see BridgeProbe). -ProbeOnStart=False +# Sweep intervals, seconds. Measured on a 150-character shard: a vitals sweep costs +# 0.0015 ms/char, so 1000 online players is ~1.5 ms per sweep. See docs/PLAN.md §1. +StatSweepSeconds=30 +DecaySweepSeconds=60 +EconomySweepSeconds=300 + +# The test scaffolding in tools/scaffolding/ reads its own flags from this file +# (SeedOnStart, CensusOnStart, ProbeOnStart). They are absent here on purpose: +# Config.Get returns the default of false when a key is missing, so a deployed +# server never runs the scaffolding even if its .cs files are present. diff --git a/overlay/Scripts/Custom/BridgeProbe.cs b/tools/scaffolding/BridgeProbe.cs similarity index 100% rename from overlay/Scripts/Custom/BridgeProbe.cs rename to tools/scaffolding/BridgeProbe.cs diff --git a/overlay/Scripts/Custom/BridgeSeeder.cs b/tools/scaffolding/BridgeSeeder.cs similarity index 100% rename from overlay/Scripts/Custom/BridgeSeeder.cs rename to tools/scaffolding/BridgeSeeder.cs diff --git a/tools/scaffolding/README.md b/tools/scaffolding/README.md new file mode 100644 index 0000000..08ae85d --- /dev/null +++ b/tools/scaffolding/README.md @@ -0,0 +1,63 @@ +# Test scaffolding + +**Not part of the bridge. Never deployed.** `deploy.ps1` only copies `overlay/`, so nothing here reaches a server unless you put it there by hand. + +These two scripts produced the measured budget in `docs/PLAN.md` §1. They are kept because those numbers should be reproducible, and because re-running the probe is the only honest way to check whether a change to the plugin's read path got more expensive. + +| File | Server path when testing | What | +|------|--------------------------|------| +| `BridgeSeeder.cs` | `Scripts/Custom/BridgeSeeder.cs` | Populates a synthetic world: 50 accounts, 150 characters, 30 houses, 30 player vendors with 40 listings each. | +| `BridgeProbe.cs` | `Scripts/Custom/BridgeProbe.cs` | Times every read the plugin performs, on the Core thread. Read-only. | + +## Using them + +Copy both into `Scripts/Custom/`, then append the flags to `Config/Bridge.cfg`: + +```ini +SeedOnStart=True +CensusOnStart=False +ProbeOnStart=False +``` + +Boot once to seed and save, then set `SeedOnStart=False`. `CensusOnStart` reports what the loaded world actually contains; `ProbeOnStart` prints timings two seconds after `ServerStarted`. + +Because `Config.Get` returns `false` for a missing key, a server whose `Bridge.cfg` lacks these keys never runs the scaffolding — even if the `.cs` files are sitting in `Scripts/Custom/`. That is the safety net, not an excuse to ship them. + +In-game, `[seedworld` and `[unseedworld` (Administrator) do the same work on a live shard. + +## Back up `Saves/` first + +`[seedworld` and `SeedOnStart` **write to the live world**. Copy `Saves/` somewhere outside the repo before running either. `Backups/Automatic` is rotated by `AutoSave.cs` and `Backups/Temp` is deleted outright, so neither is a safe destination. + +`[unseedworld` deletes every `seed_*` account, which takes their characters and houses with it — but not necessarily their `PlayerVendor` mobiles. Restoring a backup is the reliable reset. + +## What the seeder had to work around + +Worth knowing before you trust its output: + +- **Plate needs strength.** `BaseArmor.CanEquip` rejects when `from.Str < strReq` (`PlateChest` needs 95). A rejected `EquipItem` leaves the item parentless, and the `Cleanup` pass later deletes it en masse. The seeder gives characters `Str` 100–125 and deletes any item whose equip is refused, rather than orphaning it. +- **`VendorItem.Price` is get-only** and `PlayerVendor.SetVendorItem` is private. Dropping an item into a vendor's pack fires `OnSubItemAdded`, which registers the item at the default price of 999. The seeder reaches `SetVendorItem` by reflection to set a real price. Acceptable in throwaway scaffolding; do not do this in the plugin. +- **Houses only decay when condemned.** `BaseHouse.CanDecay` is true only for `DecayType.Condemned` or `ManualRefresh`. An active owner's newest house is `AutoRefresh` and never decays. The seeder backdates 18 accounts past `Account.InactiveDuration` (180 days) to condemn them, then forces stages with `SetDynamicDecay` — not by backdating `LastRefreshed`, because `DynamicDecay.Enabled` is true on this expansion and `GetOldDecayLevel` is unreachable. + +## Reference output + +Census after a fresh load of the seeded world: + +``` +[BridgeSeeder] houses=35 +[BridgeSeeder] decay Ageless 13, Slightly 3, Somewhat 7, Fairly 3, Greatly 3, IDOC 6 +[BridgeSeeder] seeded chars=150 avgEquipped=8.00 naked=0 +[BridgeSeeder] playervendors=30 +``` + +Probe, best-of-20 on the Core thread: + +``` +[BridgeProbe] char.profile 0.069 ms/char 2386 bytes json +[BridgeProbe] vitals sweep 0.223 ms for 150 chars (0.0015 ms/char) +[BridgeProbe] decay sweep 0.007 ms for 35 houses (0.0002 ms/house) +[BridgeProbe] economy sweep 0.001 ms for 51 accounts (supply 110,478,209 gold) +[BridgeProbe] vendor snap 0.343 ms for 30 vendors (1200 listings) +``` + +Seeded characters carry 8 items with ~6 mods each and ~12 trained skills. A real endgame character has more of both, so profile cost and payload are a **floor** — budget 2–4× for a fully-kitted character.