From 2d04b4808b7b9a2650509e87ba979bba2351ecb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Jul 2026 11:27:13 -0500 Subject: [PATCH] =?UTF-8?q?feat(protocol2):=20Town=20Cryer=20news-gump=20i?= =?UTF-8?q?ntegration=20(=C2=A716,=20Protocol=202.1)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Website news articles now land in the modern Town Cryer News gump (TownCryerSystem.NewsEntries), separate from the scrolling-crier lines. Overlay BridgeNews (new): news.add / news.remove insert/remove a TownCryerNewsEntry directly in the public NewsEntries list (no stock edit), tracking our own id->entry map so stock uo.com news is left intact. Title, HTML body, image, and URL are all supported (the stock gumps already branch on TextDefinition.Number, so string content renders). On add the article title is also proclaimed via GlobalTownCrierEntryList (announce defaults on; set announce:false to suppress). Config caps: NewsMaxTitleLength/BodyLength/ External, NewsAnnounceDurationSec. Sidecar: POST /news (add/replace, id-correlated), DELETE /news/{id}; news table stores each article as its news.add command; on shard server.hello the sidecar replays the stored set with announce:false (the shard rebuilds NewsEntries each boot and does not persist ours, so the website is the source of truth). Docs: PROTOCOL_2 §16 (design + verified), INTEGRATION.md /news endpoints. Verified live: sidecar cargo check clean; overlay compiles in the full ServUO Scripts tree (0 errors); booted shard + sidecar and exercised add/replace/ remove/error paths and the reconnect replay end-to-end. Co-Authored-By: Claude Opus 4.8 --- link/INTEGRATION.md | 22 ++++++++++++++++++++++ link/PROTOCOL_2.md | 7 ++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/link/INTEGRATION.md b/link/INTEGRATION.md index 9b2f943..ef9105f 100644 --- a/link/INTEGRATION.md +++ b/link/INTEGRATION.md @@ -477,6 +477,28 @@ DELETE /towncrier/{id} Caps apply (line count/length, active entries, duration); an over-cap post returns `towncrier.error`. +### Publish / remove Town Cryer **news** (Protocol 2.1) + +Distinct from the scrolling-crier lines above: this puts a full article — title, HTML body, image, and a "more info" URL — into the in-game **Town Cryer News gump**, and (by default) has the criers proclaim the **title** in-world. + +``` +POST /news +{ "id": "42", "title": "Double XP Weekend", + "body": "
Double XP Weekend


Starts Friday 7PM.", + "image": 1614, "url": "https://yoursite/news/42" } +``` +→ **200** `{"kind":"news.ok","id":"42"}`. Re-posting the same `id` **replaces** the prior article in place. + +- `id`, `title` required. `body` (HTML supported), `image` (a UO gump id; a neutral scroll if omitted), `url` (a browser button in the gump) optional. +- `announce` defaults to **true** — the criers proclaim the title. Send `"announce": false` to post silently (e.g. a correction). + +``` +DELETE /news/{id} +``` +→ **200** `{"kind":"news.ok","id":"42"}`, or **404** `{"kind":"news.error","reason":"unknown id"}`. + +Caps apply (title/body length, max active articles). The **website is the source of truth**: the shard rebuilds its news list on restart and does not persist yours, so the sidecar automatically re-pushes your articles (silently) whenever the shard reconnects. Stock ServUO news is left intact — your articles are tracked separately. + ### Staff moderation — the write plane Account and session moderation against the live shard. **These are privileged.** The sidecar does diff --git a/link/PROTOCOL_2.md b/link/PROTOCOL_2.md index a3f0d46..7da8a15 100644 --- a/link/PROTOCOL_2.md +++ b/link/PROTOCOL_2.md @@ -482,7 +482,7 @@ Deployed the overlay to the ServUO checkout, booted the shard and the real sidec ## 16. Town Cryer news — website articles into the news gump (Protocol 2.1) -**Status:** Design, grounded in the shard's `Scripts/Services/Town Cryer/` files. Not yet built. +**Status:** **Built and smoke-tested live** (2026-07-17). `BridgeNews.cs` (pure overlay, no stock edit) + `POST /news` / `DELETE /news/{id}` + reconnect replay. Verified against a booted shard: `news.add` (full + title-only) → `news.ok`, missing title → 400, idempotent replace, `news.remove` → `news.ok`, unknown id → `news.error`, no shard exceptions, and the **reconnect replay** confirmed (after a shard restart the stored article was re-pushed with `announce:false` and re-accepted). The gump rendering itself is verified by source inspection (needs a UO client to view). There are **two** distinct town-crier surfaces in ServUO, and 2.0 has so far touched only the first: @@ -514,7 +514,7 @@ Everything else in the pasted note stands, and the "this is one of the easier in On an inbound article the bridge does two things on the Core thread: 1. **News gump** — build `new TownCryerNewsEntry(new TextDefinition(title), new TextDefinition(body), image, null, url)` and `Insert(0, …)` at the top of `TownCryerSystem.NewsEntries`, tracking it in `_ours`; trim `_ours` past the cap by removing the oldest (from both `_ours` and `NewsEntries`). -2. **Say the title** — reuse the scrolling-crier path (`GlobalTownCrierEntryList`, as `BridgeTownCrier` does) to announce a single line, the **title only**, for a short duration, so the crier proclaims it in-world. Optional per article (`announce: true`), so silent corrections don't re-proclaim. +2. **Say the title** — reuse the scrolling-crier path (`GlobalTownCrierEntryList`, as `BridgeTownCrier` does) to announce a single line, the **title only**, for a short duration, so the crier proclaims it in-world. **On by default**; set `announce: false` on an article to suppress it (e.g. a silent correction that should not re-proclaim). ### 16.4 Protocol @@ -522,7 +522,8 @@ On an inbound article the bridge does two things on the Core thread: // website → sidecar → shard {"kind":"news.add","id":"42","title":"Double XP Weekend", "body":"
Double XP Weekend


Starts Friday 7PM.", - "image":1614,"url":"https://uomysticmoon.com/news/42","announce":true} + "image":1614,"url":"https://uomysticmoon.com/news/42"} +// announce defaults to true; add "announce":false to suppress the crier proclamation {"kind":"news.remove","id":"42"} ```