A .patch file does not carry enough for an installer to run the tier safely. The installer additionally needs to know which patches form one all-or-nothing unit (the two vendor-sale patches are useless apart), which companion .cs may only be copied once that unit has landed, whether the change needs a core solution rebuild or just ServUO's dynamic script build, and what capability an operator loses by declining. None of that is derivable from the diffs. patches/tier.json declares it, and release.yml folds it into manifest.json as `patch_tier` — so a new or changed patch regenerates release metadata rather than requiring an installer release, which is the same rule §7.1 already applies to the bundle. The staged copy is removed from patches/ so the tarball carries exactly one statement of the table. The release gate now checks the table in both directions: every .patch described by exactly one feature, every named patch and companion present, every declared target equal to the file the diff actually edits, and every rebuild kind one the installer understands. All four were previously invisible until someone ran the tier on a live shard. Refs: docs/installer/PLAN.md §2.2, §7.0 Co-Authored-By: Claude <noreply@anthropic.com>
68 lines
5.7 KiB
Markdown
68 lines
5.7 KiB
Markdown
# patches
|
|
|
|
Unified diffs against stock ServUO 57.4 for files the bridge must **modify** rather than add. Anything that can be shipped as a whole file belongs in `overlay/` instead.
|
|
|
|
Apply from the server root:
|
|
|
|
```bash
|
|
git apply --check patches/<name>.patch # dry run
|
|
git apply patches/<name>.patch
|
|
```
|
|
|
|
## `tier.json` — adding or changing a patch
|
|
|
|
A `.patch` file does not say enough on its own. The Runic Gateway installer's patch tier also has to know which patches form **one all-or-nothing unit**, which companion `.cs` may only be copied once that unit has landed, whether the change needs a **core** solution rebuild or just the dynamic script build, and what the operator loses by declining. None of that is derivable from a diff, so it is declared in [`tier.json`](tier.json).
|
|
|
|
**Adding a patch means adding it there in the same PR.** The release workflow checks the table in both directions — every `.patch` described by exactly one feature, every named patch and companion present, every `target` equal to the file the diff actually edits — so a patch without an entry fails the release rather than shipping a tier that silently never offers it.
|
|
|
|
`tier.json` is folded into the tarball's `manifest.json` as `patch_tier` and removed from the staged `patches/` directory, so the artifact carries exactly one copy of the table and it is the one the installer reads. Installers older than this key ignore it; an installer newer than the overlay it is deploying falls back to a built-in copy. See `docs/installer/PLAN.md` §2.2 and §7.0.
|
|
|
|
## Phase 7 — player-vendor sale (a coupled unit)
|
|
|
|
Player-vendor purchases raise **no** EventSink. `ValidVendorPurchase` / `ValidVendorSell` cover NPC vendors only. The commit point is `PlayerVendorBuyGump.OnResponse`, the only place where buyer, vendor **owner**, price, and commission are all in scope — exactly what cheat detection needs. See [PLAN.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/PLAN.md) §6.
|
|
|
|
This is the one non-drop-in piece. Apply all three together:
|
|
|
|
| Item | Target | What |
|
|
|------|--------|------|
|
|
| `playervendor-sale-eventsink.patch` | `Server/EventSink.cs` | Adds the `PlayerVendorSale` delegate, `PlayerVendorSaleEventArgs { Buyer, Vendor, Owner, Item, Price, Commission }`, the event field, and `InvokePlayerVendorSale`. |
|
|
| `playervendor-sale-gump.patch` | `Scripts/Gumps/PlayerVendorGumps.cs` | One `InvokePlayerVendorSale(...)` call right after the committed `HoldGold +=`. |
|
|
| `BridgeVendorSale.cs` | copy to `Scripts/Custom/Bridge/` | The subscriber that emits `vendor.sale`. **Not** in `overlay/` because it references `PlayerVendorSaleEventArgs`, which does not exist until the EventSink patch is applied — shipping it in overlay would break the build on any unpatched install. |
|
|
|
|
```bash
|
|
cd <servuo root>
|
|
git apply --check patches/playervendor-sale-eventsink.patch patches/playervendor-sale-gump.patch # dry run
|
|
git apply patches/playervendor-sale-eventsink.patch patches/playervendor-sale-gump.patch
|
|
cp patches/BridgeVendorSale.cs Scripts/Custom/Bridge/BridgeVendorSale.cs
|
|
```
|
|
|
|
Both patches are `git`-format and verified with `git apply --check` against stock ServUO 57.4. Modifying `EventSink.cs` means the **core** rebuilds, so `ScriptCompiler`'s dynamic script build is not enough — rebuild the solution (`dotnet build ServUO.sln`) or the server binary.
|
|
|
|
Not applicable to a non-git shard? `git apply` works in a plain directory too. If `patch` is used instead, note the core files are CRLF; use `patch --binary`.
|
|
|
|
## In-game moderation audit (admin controls §5.5)
|
|
|
|
So the website's moderation log stays complete, in-game uses of the write-plane verbs are forwarded to it as `admin.audit` (`origin:"in-game"`). Broadcasts already surface through `EventSink.Command`, but resolved bans/kicks only carry their target inside the command's own `CommandLogging.WriteLine` call — which has no event to subscribe to. One small change fixes that:
|
|
|
|
| Item | Target | What |
|
|
|------|--------|------|
|
|
| `commandlogging-event.patch` | `Scripts/Commands/Logging.cs` | Adds a `public static event Action<Mobile,string> OnWrite`, raised in `WriteLine` **before** the `m_Enabled` guard so it fires even when file logging is off. |
|
|
| `BridgeModerationAudit.cs` | copy to `Scripts/Custom/Bridge/` | The subscriber: taps `OnWrite` for ban/kick (parsing the target out of the log line) and `EventSink.Command` for `[bcast`, emitting `admin.audit`. **Not** in `overlay/` because it references `CommandLogging.OnWrite`, which does not exist until the patch is applied. |
|
|
|
|
```bash
|
|
cd <servuo root>
|
|
git apply --check patches/commandlogging-event.patch # dry run
|
|
git apply patches/commandlogging-event.patch
|
|
cp patches/BridgeModerationAudit.cs Scripts/Custom/Bridge/BridgeModerationAudit.cs
|
|
```
|
|
|
|
`Logging.cs` is a **Scripts** file, so this is picked up by the dynamic script build — no core/solution rebuild needed (unlike the Phase 7 `EventSink.cs` patch). Verified end-to-end with `tools/scaffolding/BridgeAuditProbe.cs` (gated by `Bridge.AuditProbeOnStart`): a genuine `[bcast` plus simulated ban/kick log lines produced the expected `admin.audit` frames, target parsed, with non-moderation lines ignored.
|
|
|
|
## Note on `Scripts.csproj`
|
|
|
|
Phase 0 modifies an existing file but ships as a whole-file overlay (`overlay/Scripts/Scripts.csproj`) because the file is small, we own it operationally, and a copy is less fragile than a diff against a project file. Revisit if it starts drifting from upstream.
|
|
|
|
## Note on shard repairs
|
|
|
|
The deletions and edits described in [SHARD_PREREQS.md](https://gitea.whitlocktech.com/RunicGateway/docs/src/branch/main/link/SHARD_PREREQS.md) are one-time repairs to a specific broken install, not part of the bridge. They are not shipped here.
|