docs(rust-link): protocol 2 — the read path, and phase 3 as built
The specification the other three repositories are held against, plus the phase record. `PROTOCOL.md` §8 is the new contract. Its centre is one field: every frame now carries `type` — `event`, `snapshot`, `reply`, `control` — and the sidecar files on that and nothing else. That is the dumb-forwarder property made structural rather than intended: ten new event kinds are zero change in Rust-Link, and only a version adding an indexed column touches it at all. Also in §8: the fifteen-kind catalogue and what each frame carries; `wipeId` derived by the plugin, which REVERSES §3.2's "deriving one is the website's job" and says why; boards re-sent on connect and on a cadence; the aggregate rule (a hook that can fire more than once a second per player is a counter, not an event); the void rule that stops a read-path hook vetoing a death or a login; and `GET /feed`, a cursor route separate from `/events` because one route with two orderings serves the wrong one to every caller that forgets the parameter. §8.5 is the part to read twice. The classification of a kind as public or staff is NOT on the wire, deliberately: a boundary declared by the sender is one a compromised or out-of-date game host can widen, so the module holds a default-deny allowlist and this table is what its test holds it against. §8.8 corrects a catalogue rather than a defect: PLAN.md §10 sources `rust.login.denied` from `CanUserLogin`, and that hook fires on every attempt — the only way to learn of a denial from it is to be the denier. A denial is the absence of an approval, and protocol 2 emits both facts so phase 10 can pair them. `PLAYER_WALK.md` is new, and it exists because half this catalogue cannot fire without somebody holding a mouse. Ten steps, what each one should produce, and what counts as a pass — written so the walk can be run without watching the output live, and so the answer afterwards is readable as a transcript. PLAN.md §16 is phase 3 as built: the four decisions, the two defects only a server that BOOTED with the plugin could find (a wipe id that was null for every real session, and a two-second main-thread stall on unload), what was proven and how, and — stated plainly rather than implied — the three measurements still queued on the org lead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
This commit is contained in:
@@ -164,6 +164,17 @@ Two failures that look alike and are not:
|
||||
says what was true when it was written; nothing has written it since. Either the poll is failing
|
||||
(the website's log) or the sidecar stopped (its own).
|
||||
|
||||
### 3.0 `untyped_frames` on `/health` is not zero
|
||||
|
||||
**The plugin and the sidecar are on different protocol versions.** The game link has no handshake
|
||||
to catch that at connect time (`PROTOCOL.md` §2), so it shows up here instead: the sidecar files a
|
||||
frame by its `type`, a frame from the wrong version does not carry one it recognises, and it is
|
||||
dropped and counted rather than guessed at.
|
||||
|
||||
The symptom without this counter is the confusing one — a game server plainly up, a sidecar plainly
|
||||
healthy, and a website showing nothing. Check the plugin's `rg.link` (it prints its protocol) against
|
||||
the sidecar's `/health` (which prints its own) and upgrade whichever is behind.
|
||||
|
||||
### 3.1 The failures that are supposed to happen
|
||||
|
||||
Three things look like breakage and are the design:
|
||||
@@ -193,6 +204,29 @@ you get a warning naming both ids.
|
||||
|
||||
---
|
||||
|
||||
## 4.1 What the bridge sends, and how much of it is kept
|
||||
|
||||
From protocol 2 the plugin sends the read path: connects and disconnects, deaths, chat, gathering,
|
||||
bans and reports, and the wipe. Two things about the volume are worth knowing before you size
|
||||
anything.
|
||||
|
||||
**Gathering and NPC kills are counted, not forwarded.** `OnDispenserGather` fires on every swing at
|
||||
a tree; sending one frame per swing would make the bridge the most expensive thing on the server. The
|
||||
plugin keeps a per-player tally and flushes it once a minute as a single `player.tally` frame. So the
|
||||
leaderboard is exact and the wire is quiet.
|
||||
|
||||
**The sidecar's history is bounded; the website's is not.** `[store].retain_days` (default 14) is
|
||||
how long the sidecar keeps raw events. The permanent record — per-wipe totals that survive a wipe —
|
||||
lives in the website's own tables, so shortening this loses recent detail and never loses a player's
|
||||
history. Set it to `0` to keep everything, if the host's disk is yours to spend.
|
||||
|
||||
**Every row carries its wipe.** The plugin derives a `wipeId` from the save's creation time and
|
||||
stamps it on every frame, so a wipe splits the history rather than ending it. That is also why
|
||||
**the sidecar's database must never be in a wipe script's delete list** — see the Pterodactyl egg's
|
||||
`REMOVE_FILES`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Upgrading
|
||||
|
||||
The four declaration sites in [`PROTOCOL.md` §2](PROTOCOL.md#2-versioning) must agree. In practice
|
||||
|
||||
Reference in New Issue
Block a user