docs(rust): phase 17 as built and walked — protocol 12, D144 (PLAN §33.5-§33.7, M19)
- PLAN.md §33.5 as built (D144: the plugin keeps its titles on disk, after the walk found a hot reload emptied them), §33.6 the walk on both rigs, §33.7 findings; the phase row marked built. - PROTOCOL.md §18: titles.set, a group's BetterChat style on perm.sync, chat.say delivery and format, integrations, rg.titles, POST /titles. The header's current version moves from a stale 10 to 12. - PLAYER_WALK.md: the steps that need a person in the game. - android/PLAN.md M19 as built. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -378,3 +378,23 @@ signed in to the app and a staff member at the website.
|
||||
|
||||
**What counts as a pass:** a player's own dot moves on the phone, a site-event zone opens its event in
|
||||
the app, and a tally counted from a real person reaches *My events* with its fractional score.
|
||||
|
||||
## Chat titles, styles and popups (phase 17, protocol 12)
|
||||
|
||||
Added 2026-09-25. The parts of [`PLAN.md`](../modules/rust/PLAN.md) §33.3 that need somebody typing
|
||||
in the game (§33.6). Everything else was walked with a probe that asks BetterChat to format a line.
|
||||
Both rigs have BetterChat 5.2.15 and PopupNotifications 0.2.1 installed. Use player A from the walks
|
||||
above, and a staff member at the website.
|
||||
|
||||
| # | Do this | You should see |
|
||||
|---|---|---|
|
||||
| 1 | Staff add a title rule on the server A plays on, e.g. top 3 playtime, and A plays a few minutes | A's chat lines carry the title after BetterChat's own, in its colour, within a minute of A reaching the top 3 |
|
||||
| 2 | Staff give a site group a chat style, and put A in the group | A's lines take the group's title and colours after the next sync |
|
||||
| 3 | Staff choose that group as the announcement voice and publish a news post with the server's news on | The post appears in chat with the group's title and colours and **no player's name** |
|
||||
| 4 | Staff set the server's news to *as a popup* and publish another | A popup on A's screen with the post's title, as plain text |
|
||||
| 5 | Run `oxide.reload RunicGateway` (or `c.reload`) and A types a line | A's title is still there (D144) |
|
||||
|
||||
**Run steps 1 and 4 on Carbon as well.**
|
||||
|
||||
**What counts as a pass:** a real chat line carries a site title and a site style, the voice speaks
|
||||
without a sender, and a popup appears on a real screen.
|
||||
|
||||
@@ -50,7 +50,7 @@ it is listening without one.
|
||||
|
||||
## 2. Versioning
|
||||
|
||||
The wire version is a single integer — **10** as of the rewards (§16) — declared in
|
||||
The wire version is a single integer — **12** as of the optional mods (§18) — declared in
|
||||
**four** places that must agree:
|
||||
|
||||
| Where | Repo |
|
||||
@@ -1833,3 +1833,104 @@ database (D111).
|
||||
| `GET /map/chunk?mapKey=&sha256=&n=` | `map.fetch` | All three required |
|
||||
| `POST /map/render` | `map.render` | Opaque object; `cmd` and `reqId` written over the caller's |
|
||||
| `GET /map/live` | `map.live` | |
|
||||
|
||||
## 18. Protocol 12 — the optional mods
|
||||
|
||||
Added in phase 17 ([`PLAN.md`](../modules/rust/PLAN.md) §33). BetterChat and PopupNotifications
|
||||
become **optional**: the plugin looks each up when it needs it and answers with a reason when it is
|
||||
not there (R3). **One new command, two widened commands, one field on the server body, and one console
|
||||
command.** `overlay.toml`'s `requires_plugins` loses PopupNotifications (D141).
|
||||
|
||||
### 18.1 `titles.set`
|
||||
|
||||
```json
|
||||
{"cmd":"titles.set","reqId":"r-40","setId":"<sha256>","titles":[{"steamId":"76561198800000001","text":"[#ff4400]Top Killer[/#]"}]}
|
||||
```
|
||||
|
||||
The chat titles each player holds, **as one whole set** that replaces the last. `text` is already in
|
||||
BetterChat's markup (`[#hex]…[/#]`, space-separated for a player with several); the site composes it
|
||||
from the current wipe's standings (PLAN.md §33.2).
|
||||
|
||||
- **Answered `titles.ok`** with `count` and `betterChat`: whether BetterChat, as loaded right now,
|
||||
holds the plugin's getter.
|
||||
- **Refused `titles.error`** with a `reason` and a `message`: `malformed` (no `titles` array, a
|
||||
Steam id that is not a number, or an empty or over-long `text` — the whole set is refused, never
|
||||
half of it), or `too-large` past 500 entries.
|
||||
- **The plugin swaps the set in by reference**, never editing the one BetterChat is reading, and keeps
|
||||
it in `titles.json` with the wipe it was pushed on (D144). A hot reload reads the file back when that
|
||||
wipe is still current; the site re-sends on a new boot id or wipe id as well.
|
||||
- **The getter** is registered with `API_RegisterThirdPartyTitle` at `OnServerInitialized`, and again
|
||||
on `OnPluginLoaded` for BetterChat, whose registrations die with its instance. It does one
|
||||
dictionary lookup and returns null for a player with no title. BetterChat appends it **after** its own
|
||||
titles and after its `MaxTitles` cut.
|
||||
|
||||
### 18.2 `perm.sync`: a group's BetterChat style
|
||||
|
||||
A group may carry `chat`, the twelve BetterChat group fields, each with the value the site wants and
|
||||
the value it last pushed **to this server**:
|
||||
|
||||
```json
|
||||
{"name":"staff","title":"Staff","rank":5,"permissions":[],"members":[],
|
||||
"chat":{"TitleColor":{"value":"#ff2200","expect":null},"Title":{"value":"[Staff]","expect":"[Staff]"}}}
|
||||
```
|
||||
|
||||
Values are text in the form BetterChat's own setter parses: `true`/`false`, decimal integers, a colour
|
||||
as `#rrggbb` or a word. The plugin, per field:
|
||||
|
||||
- **leaves it** when the game already holds the value (`same`);
|
||||
- **writes it** through `API_SetGroupField` when the game holds `expect`, or holds BetterChat's
|
||||
default and `expect` is null, or the group was created by this sync;
|
||||
- **reports it** as drift, and does not write it, when the game holds anything else — a hand edit (D138).
|
||||
|
||||
`API_SetGroupField` never saves, so **one `chat group set <group> Priority <current>` per group
|
||||
touched**, run as the server console, writes BetterChat's file (D143). A retirement of kind
|
||||
**`chat-group`** removes the group with `chat group remove`, BetterChat's only way (D139). Both
|
||||
commands' effects are read back through `API_GroupExists` / `API_GetGroupFields`, never assumed.
|
||||
|
||||
The report gains **`chat`**. With BetterChat absent it is `{ "loaded": false }` and no style op is
|
||||
compiled; the permissions sync as before. Otherwise:
|
||||
|
||||
| Field | |
|
||||
|---|---|
|
||||
| `version` | BetterChat's |
|
||||
| `applied` | fields written |
|
||||
| `saved` | groups saved by the console write, read back |
|
||||
| `same` | fields already right |
|
||||
| `drift` | `[{ group, field, game }]`, with the game's value |
|
||||
| `removed` | groups `chat group remove` took out, or that were already gone |
|
||||
| `failed` | `[{ group, field?, reason }]`: `InvalidField`, `InvalidValue`, `no-answer`, `not-created`, `not-saved`, `still-present`, `betterchat-unloaded`, `malformed` |
|
||||
|
||||
### 18.3 `chat.say`: delivery and a voice
|
||||
|
||||
Two optional fields. A protocol-11 caller sends neither and is answered as before.
|
||||
|
||||
- **`delivery`**: `chat` (the default) or `popup`. A popup is said with PopupNotifications'
|
||||
`CreatePopupNotification(message, null, 0)`: everybody connected, the plugin's default duration, and
|
||||
nothing queued for anybody offline. **Without PopupNotifications it is refused
|
||||
`chat.error { reason: "popup-unavailable" }`**, with a sentence.
|
||||
- **`format`**: BetterChat markup with exactly one `{message}`, at most 512 characters. The plugin puts
|
||||
the flattened line in after the 256-character bound (so the bound is the words', not the markup's),
|
||||
strips `[` and `]` from words that carry `[#` or `[+` (BetterChat's own rule), and turns the markup
|
||||
into rich text with `covalence.FormatText`. The line has no sender (D140). A popup ignores it.
|
||||
|
||||
`chat.ok` gains `delivery`. **Every line said is logged** (`said (chat): …`), because the game logs
|
||||
neither a broadcast nor a popup.
|
||||
|
||||
### 18.4 `integrations`, and the console
|
||||
|
||||
`server.hello` and `server.status` carry
|
||||
`integrations: { betterChat: { loaded, version? }, popupNotifications: { loaded, version? } }`;
|
||||
`version` only for a plugin that is loaded.
|
||||
|
||||
**`rg.titles [steamId]`** prints how many titles are held, BetterChat's version and whether the getter
|
||||
is registered, and for a Steam id what is held and what BetterChat renders — through
|
||||
`covalence.Players`, so only for a player the server has seen.
|
||||
|
||||
### 18.5 The sidecar
|
||||
|
||||
`PROTOCOL_VERSION` becomes 12. One route; `perm.sync`'s and `chat.say`'s new fields pass through
|
||||
untouched.
|
||||
|
||||
| Route | Command | |
|
||||
|---|---|---|
|
||||
| `POST /titles` | `titles.set` | Opaque object; `cmd` and `reqId` written over the caller's |
|
||||
|
||||
Reference in New Issue
Block a user