docs(rust): protocol 14 — kits.uses and reward notices (PROTOCOL §20, PLAN_REDESIGNS §2.9)
The wire half of PLAN_REDESIGNS §2 (D332, D333): PROTOCOL.md §2 says 14, and §20 specifies kits.uses (counts only, bounded by the sync's bound, POST /kits/uses) and perm.sync notices (the whole untold set, told once when the player is awake, told keys kept on disk, noticesTold in the report). PLAN_REDESIGNS §2.9 records the Rust half as built and its compile on both rigs. Stacked on docs/event-step-editor (docs#335) for §2.8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -409,6 +409,32 @@ Everything in §2.1–§2.3 and §2.5's core half, with three things settled whi
|
||||
registers its 12 actions), the server suite 723/723 and the client suite 45/45 — the same numbers as 1.11.0's.
|
||||
The Module-Rust half (the kit weekend, `linked`, `kits.uses`, the reward message) follows on `edge`.
|
||||
|
||||
### 2.9 As built: the Rust half, protocol 14 (2026-10-12)
|
||||
|
||||
Branches `feat/protocol-14-step-editor` in Rust-Plugins, Rust-Link and Module-Rust, into `edge`; the wire is
|
||||
`rust-link/PROTOCOL.md` §20.
|
||||
|
||||
- **The kit weekend** is `rust.template.kit_weekend`: inputs kit, minutes (a duration), message and delivery.
|
||||
It reads the kit from its server when it is filled in, so the lease names the kit's real permission. A kit
|
||||
with no permission is open to everyone already and gets step 2 alone; a kit with no use limit has no
|
||||
"one more use" and gets step 1 alone. The phase is the weekend: `advance: { after }` equals the minutes.
|
||||
Run through the real 1.13.0 core (website#212), it registers, and the plan it builds validates as a spec.
|
||||
- **`linked`** is "every linked account that has played on this server" — a stats row there — because a link
|
||||
is fleet-wide and the server is what narrows it. It reads no tally, and past the server's recipient bound it
|
||||
is refused with the bound's name (`EventsMaxRecipients`), never trimmed.
|
||||
- **The reward message** is checked with the kit's name in place of `{kit}`, against the chat bound, so a
|
||||
long kit name cannot push past what the game will say. It is stored on each grant row
|
||||
(`rust_perm_run_grants.notice`, `notice_delivery`, `notice_told_at`).
|
||||
- **The kit warning** is the `note` on `rust.options.grouppermissions` rows. That source is searchable, so the
|
||||
editor may not hold the chosen row; core asks for that one value when it needs its note (website#212).
|
||||
- **Timed params:** `rust.zone.open` and `rust.participation.open` declare `minutes` a duration, and the
|
||||
group-permission lease gives `true` as its value example.
|
||||
- **Compiled on both rigs, 2026-10-12.** Carbon 2.0.262 on Rust 25823813 (the latest of both): the bridge
|
||||
compiled and loaded in 3.8 s, protocol 14, its told-notices store read at load. Oxide 2.0.7820 on the same
|
||||
Rust build (the rig was behind on both, and was updated first: its first restart's steamcmd update did not
|
||||
finish, the second did): compiled in 5.3 s and loaded. Both rigs were put back on their protocol-13 bridge afterwards, to match their sidecars until
|
||||
the end-of-plan walk sets up protocol 14 (D331).
|
||||
|
||||
---
|
||||
|
||||
## 3. Zones: ZoneManager's options, and a dome (§4.4; D166, D167)
|
||||
|
||||
@@ -50,7 +50,7 @@ it is listening without one.
|
||||
|
||||
## 2. Versioning
|
||||
|
||||
The wire version is a single integer — **13** as of the first player walk's fixes (§19) — declared in
|
||||
The wire version is a single integer — **14** as of the step editor (§20) — declared in
|
||||
**four** places that must agree:
|
||||
|
||||
| Where | Repo |
|
||||
@@ -2638,3 +2638,66 @@ unaffected. The refusal is checked before anything is spawned, with the two reas
|
||||
The site checks the same thing first, from the hello's `integrations.runicNpc`, and refuses the step before it
|
||||
is sent; the bridge's check is for a site that missed the hello. `rg.npc` says the same when RunicNPC is not
|
||||
loaded.
|
||||
|
||||
---
|
||||
|
||||
## 20. Protocol 14 — the step editor
|
||||
|
||||
Opened by [`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §2 after protocol 13 shipped with RunicNPC
|
||||
v1.0.0 (D332). It is built on `edge` in Rust-Plugins, Rust-Link and Module-Rust and released once, after the
|
||||
end-of-plan walk (D331); the first-class optional plugins (§11 there) join it. **This section grows as the
|
||||
pieces land.** `PROTOCOL_VERSION` becomes 14 in all four places (§2).
|
||||
|
||||
### 20.1 `kits.uses` — how many have used a kit up (§2.6)
|
||||
|
||||
The warning beside a kit weekend: *"Kit vip: 2 uses, 24 h cooldown — 9 of 41 linked players have used it up."*
|
||||
The website asks about the players it already knows — the linked players of the server — and the answer is
|
||||
**counts, never a list**.
|
||||
|
||||
```json
|
||||
{"cmd":"kits.uses","reqId":"r-12","kit":"vip","steamIds":["76561190000000001","76561190000000002"]}
|
||||
{"kind":"kits.uses","type":"reply","reqId":"r-12","kit":"vip","max":2,"cooldown":86400,"asked":2,"exhausted":1,"onCooldown":0}
|
||||
```
|
||||
|
||||
- **Main thread**, two Kits reads per player (`GetPlayerKitUses`, `GetPlayerKitCooldown`), plus `GetKitMaxUses` and
|
||||
`GetKitCooldown` once. `exhausted` counts players whose uses have reached `max`; a kit with no use limit
|
||||
(`max` 0) is never used up. `onCooldown` counts players with any cooldown remaining. `asked` is how many ids
|
||||
parsed.
|
||||
- **Bounded by the sync's own bound** (`PermMaxOps`, 20,000): the site sends no more players than a sync
|
||||
carries.
|
||||
- Refusals are `kits.error`, as for `kits.list`: `kits-missing` (Kits is not loaded), `unknown-kit`,
|
||||
`too-large`.
|
||||
- **The sidecar route is `POST /kits/uses`**, a thin forward like `/kits`; the list rides in the body because it
|
||||
can be as long as a server's linked players. Nothing is kept.
|
||||
- **The website** asks once per kit that a permission gates, per server, at most 25 kits, and keeps the answer
|
||||
a minute. It attaches the sentence as the `note` of every `rust.options.grouppermissions` row whose
|
||||
permission is that kit's (core's option note, MODULE_API 1.13.0). A failure is no note.
|
||||
|
||||
### 20.2 `notices` on `perm.sync` — the reward's message (§2.4, D333)
|
||||
|
||||
"You earned kit vip — type /kit vip", said once to the player a reward step picked. `rust.kit.entitle` gains an
|
||||
optional `message` and `delivery`; the site writes them on each grant row, and every sync carries the rows'
|
||||
**whole untold set** for that server, beside `credits`:
|
||||
|
||||
```json
|
||||
{"cmd":"perm.sync", …, "notices":[{"key":"7:3:11","steamId":"76561190000000001","message":"You earned kit vip — type /kit vip","delivery":"popup"}]}
|
||||
```
|
||||
|
||||
- **`key`** is `<runId>:<stepId>:<userId>`: one reward step tells one person once, whichever of their linked
|
||||
accounts it picked. **`steamId`** is that account, while it is still linked to the person.
|
||||
- **When it is said is the plugin's.** At the end of the sync, to a player who is connected and **awake** —
|
||||
a sleeping or loading player sees no popup and reads no chat — and otherwise on `OnPlayerSleepEnded`, the
|
||||
next time they wake on this server. A popup goes through PopupNotifications when `delivery` is `popup` and
|
||||
the plugin is loaded, and chat otherwise; the reward was granted long before this moment, so the player is
|
||||
told either way. At most 2,000 notices per sync; a message is cut at the chat bound (`EventsMaxChat`).
|
||||
- **Told keys are kept on disk** (`data/RunicGateway/notices.json`) until the site stops sending them, so a
|
||||
reload between telling a player and the report that says so cannot tell them twice. Each sync forgets every
|
||||
told key the set no longer carries.
|
||||
- **The report lists them back**: `perm.report` gains `noticesTold`, every key of this sync's set that has been
|
||||
told — at this sync or before it. Present only when the sync carried `notices`, like the credit counts. The
|
||||
site marks those rows told, and they leave the next sync's set.
|
||||
- **A withdrawn reward takes its message with it.** Teardown deletes the grant row, so the notice leaves the set
|
||||
untold, and nobody is told about a reward that is no longer theirs.
|
||||
- **Absent is not empty**, as for `credits`: a sync with no `notices` field leaves the plugin's set alone. The
|
||||
site sends the field on every sync from protocol 14, empty when there is nothing to say. The notices are part
|
||||
of the sync's digest, so a new one is pushed at once.
|
||||
|
||||
Reference in New Issue
Block a user