Files
servuo-plugins/tools/scaffolding/README.md
colby 7ea7570e5a Phase 3: polled sweeps (vitals, house decay, economy)
BridgeSweeps runs three repeating Core-thread timers for the state that has no
EventSink. Cost measured in Phase 1 is why they can run on the main thread: a
full pass of all three is well under a millisecond at the seeded scale.

  - Vitals: online players only. Small and volatile; the sidecar diffs snapshots.
    Offline characters do not move, so they are served on demand as full profiles
    instead, not swept.
  - House decay: emits only on a level transition. A silent baseline on
    ServerStarted records every house's current stage, so a restart does not
    re-announce them. Payload carries from/to, coords, nested ban location,
    region, sign name, owner serial+account, and built/refreshed timestamps, all
    null-guarded.
  - Economy supply: periodic sum of every account's currency as a snapshot. The
    level; AccountGoldChange and the vendor events are the flow.

All three re-arm on `[bridge reload`; `[bridge sweepnow` runs one of each on
demand; `[bridge status` reports sweep counters. Sweeps skip emitting while the
sidecar is disconnected, since their state is perishable and re-emitted next
tick anyway (unlike events, which queue through an outage).

Verified on the seeded world with 8s intervals: baseline recorded 29 houses
silently, a probe bumped one Somewhat->Fairly, and the next sweep emitted exactly
one house.decay and none for the other 28. Economy emitted a supply snapshot per
interval. Vitals emitted nothing, correctly, since all seeded characters are
offline. Evidence in docs/PLAN.md §13.

Adds tools/scaffolding/BridgeSweepProbe.cs (never deployed) to force a decay
transition on demand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:59:21 -05:00

70 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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. |
| `BridgeEventProbe.cs` | `Scripts/Custom/BridgeEventProbe.cs` | Fires gold/fame/karma/save events through their real code paths so the emit path can be verified without a game client. **Mutates the world and saves.** Flag: `EventProbeOnStart`. |
| `BridgeSweepProbe.cs` | `Scripts/Custom/BridgeSweepProbe.cs` | Bumps one seeded house's decay stage after baseline so the decay sweep's transition detection can be observed without waiting a real IDOC stage. Flag: `SweepProbeOnStart`. Pair with short `*SweepSeconds` overrides. |
## Deploy overwrites Bridge.cfg
`deploy.ps1` copies `overlay/Config/Bridge.cfg`, which deliberately omits the scaffolding flags. So **every deploy strips `SeedOnStart` / `EventProbeOnStart` / etc.** Re-append the flag you need after deploying, or the probe silently does nothing on the next boot. (This bit once during Phase 2 testing.)
## 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` 100125 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 24× for a fully-kitted character.