docs(rust): protocol 14 — Backpacks, as built; D340, D341 (PROTOCOL §20.4, PLAN_REDESIGNS §11.3.4)
- PROTOCOL §20.4: hello's integrations.backpacks.sizes, backpacks.read and its reply (display, blueprint, overflow, pageSize), the refusals, the sidecar forward, and the site's routes and Bigger backpack step. - PLAN_REDESIGNS: D340 (a slot shows the game's name, no icons; R9 stands) and D341 (a blueprint names what it makes), answered before any code; §11.3.4 lists the points settled in code so they can be overruled; §11.3.3 notes the walk needs real Steam ids. - android PLAN: the account screen's Backpack section. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -1455,7 +1455,7 @@ push, and Play (M6–M8) follow the designed app.
|
||||
|
||||
| Screen | Route | Reads |
|
||||
| --- | --- | --- |
|
||||
| **My Rust account** | `player/rust` | `GET /player/rust/links`, `POST /player/rust/link`, `DELETE …/links/{steamId}`, `GET /player/rust/permissions`, `GET /player/rust/economics` (the Coins section, 2026-10-12, [`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §11.2: one card per server, "last known" when a server did not answer, the reason where it has no Economics, and left out on a 404 from an older module) |
|
||||
| **My Rust account** | `player/rust` | `GET /player/rust/links`, `POST /player/rust/link`, `DELETE …/links/{steamId}`, `GET /player/rust/permissions`, `GET /player/rust/economics` (the Coins section, 2026-10-12, [`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §11.2: one card per server, "last known" when a server did not answer, the reason where it has no Economics, and left out on a 404 from an older module), `GET /player/rust/servers` + `GET /player/rust/backpacks` (the Backpack section, 2026-10-12, §11.3: one card per server, an Open button per linked account that reads once per press, the slots as pages of four-wide rows named by the game, D340, overflow marked; "not installed" on a 409, and left out with Coins on that 404) |
|
||||
|
||||
#### The gate is `rust`, and `PLAYER` means staff too
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ rows still to come (§3.4); §4, built 2026-09-29 (§4.1). **§6 was spiked on 2
|
||||
chose neither route: Runic Gateway writes its own NPC plugin, RunicNPC, planned in
|
||||
[`../../runicnpc/PLAN.md`](../../runicnpc/PLAN.md) (D214–D314), and the rest of this plan waits for it.** RunicNPC v1.0.0 shipped on 2026-10-09, and the plan
|
||||
resumed with §2 the same day (D331–D334); §2 was built on 2026-10-12, and §11's Economics spike the same day
|
||||
raised D335–D336 (§11.2.1). Economics was built on 2026-10-12 too (§11.2.4). The Backpacks spike followed and raised D337–D339 (§11.3.1). **Every item left is built, tested and PR'd first; every walk —
|
||||
raised D335–D336 (§11.2.1). Economics was built on 2026-10-12 too (§11.2.4). The Backpacks spike followed and raised D337–D339 (§11.3.1), and Backpacks was built the same day (D340, D341, §11.3.4). **Every item left is built, tested and PR'd first; every walk —
|
||||
theirs and the ones still owed by §3 and §5 — happens together at the end (D331, §9).**
|
||||
|
||||
This is a companion to [`PLAN_FIXES.md`](PLAN_FIXES.md) and [`PLAN.md`](PLAN.md). Where they disagree, this
|
||||
@@ -1007,6 +1007,14 @@ The Backpacks spike raised three more, answered on 2026-10-12:
|
||||
| **D338** | **Taking the space back is Backpacks' own behaviour:** nothing moves at the revoke, and on the owner's next open the stacks past their size go to their inventory, or drop at their feet when it is full. Tested on both rigs at the org lead's request; the step's default message warns about it. | Holding the space until the extra slots are empty; never taking it back. | 11.3.1, 11.3.2 |
|
||||
| **D339** | **The Carbon permission leak found by the spike stays out of §11.3**: filed as Rust-Plugins#32, for later. | Fixing it in §11.3; only noting it. | 11.3.1 |
|
||||
|
||||
The build raised two more, answered on 2026-10-12 before any code: §11.3.2's grid named an "item catalogue" the
|
||||
module does not have, and R9 keeps item icons out of the asset bridge.
|
||||
|
||||
| # | Decision | Rejected | § |
|
||||
|---|---|---|---|
|
||||
| **D340** | **A backpack slot shows the item's name, not an icon.** The plugin sends each stack's English name from the game (`display`), so "Wood ×1000" and "Assault Rifle + Weapon Lasersight" need no item catalogue and nothing loads from outside the site. R9 stands. | Icons from a third-party host keyed by shortname; shortnames alone. | 11.3.4 |
|
||||
| **D341** | **A blueprint names what it makes.** Backpacks stores a blueprint as `blueprintbase` with a target; the stack carries `blueprint` (the target's shortname), its `display` is the target's name, and the slot reads "Assault Rifle blueprint". | Every blueprint shown as the same anonymous "Blueprint". | 11.3.4 |
|
||||
|
||||
## 11. First-class optional plugins (D199–D202)
|
||||
|
||||
The org lead named four plugins the module supports as first-class integrations. Kits already is one. The other
|
||||
@@ -1388,6 +1396,33 @@ On both rigs, with Backpacks installed and a real player:
|
||||
again with a full inventory, and see them on the ground;
|
||||
- remove Backpacks, and see the step and the panels refuse with a reason.
|
||||
|
||||
The walk's players must have real Steam ids: the bridge reads only a 17-digit id at or above
|
||||
76561197960265728, so the spike's `7656119000000000N` stand-ins are refused `bad-id` (§11.3.4).
|
||||
|
||||
#### 11.3.4 Built (2026-10-12)
|
||||
|
||||
Built to §11.3.2 on protocol 14, and specified in [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.4. The
|
||||
bridge compiled on both rigs (Rust changeset 167584; Oxide 2.0.7826, Carbon 2.0.262), which were then given back
|
||||
the bridge they ran before. Before any code the grid's "item catalogue" turned out not to exist; D340 and D341
|
||||
answered it. These points the design left open were settled in the code, and are named here so they can be
|
||||
overruled:
|
||||
|
||||
- **Each stack carries `display`**, the game's English name (D340); for a blueprint, the name of what it makes
|
||||
(D341). The grids on the site and in the app show it, then the shortname, then `item <id>` for an item the game
|
||||
no longer knows.
|
||||
- **A third refusal, `unreadable`**, for contents the bridge cannot parse. Backpacks writes the JSON itself, so it
|
||||
should never happen; it is a sentence rather than an exception if it does.
|
||||
- **The step's message is never silent.** Left empty, it is the default take-back warning, because a player
|
||||
nobody warned is the one surprised by D338. An author can reword it, with `{size}`.
|
||||
- **The size picker** offers each server's sizes above its smallest, grouped by server, from the last hello. The
|
||||
step checks the pair it is given, and names the sizes the server does offer when it refuses one.
|
||||
- **The panels offer every enabled server** and learn that one lacks Backpacks when the read answers `409`, so
|
||||
nothing about which mods a server runs reaches the public server list.
|
||||
- **The player route is rate-limited** to 20 reads a minute, because a read runs on the game's main thread.
|
||||
- **A skin id is text** on the site's API: workshop ids are labels, and some pass 2^53.
|
||||
- **The app gates the section on the Coins read's 404**: Backpacks and Economics ship in the same protocol, so an
|
||||
older module has neither.
|
||||
|
||||
### 11.4 RaidableBases (D202)
|
||||
|
||||
- **Live map and feed.** `OnRaidableBaseStarted` / `Ended` / `Despawn` / `PrivilegeDestroyed` become events
|
||||
|
||||
@@ -2807,3 +2807,69 @@ never summed. It serves `GET /player/rust/economics` (live, falling back to the
|
||||
`GET`/`POST /admin/users/:id/rust/economics`, pays through the `rust.coins.pay` step (`risk: 'irreversible'`,
|
||||
`reversible: 'none'`), and ranks the richest — a leaderboard column for one wipe, and the `richest` title
|
||||
category, *Magnate* — **only above the server's starting balance** (D336).
|
||||
|
||||
### 20.4 Backpacks — read a backpack, lend a bigger one (§11.3, D199, D337, D338, D340, D341)
|
||||
|
||||
WhiteThunder's **Backpacks** (3.17.8, uMod), optional like Economics (D199): `backpacks.read` answers
|
||||
`backpacks-missing` without it. `requires_plugins` keeps Kits and ZoneManager only. The design is
|
||||
[`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §11.3.2, and what the spike found, which shapes the
|
||||
read, is §11.3.1 there.
|
||||
|
||||
**Read, never write (R2).** `backpacks.read` is the only verb, and the bridge calls no Backpacks method that
|
||||
changes a backpack or a capacity. The bigger backpack an event lends is one of Backpacks' own
|
||||
`backpacks.size.N` permissions, leased like a kit reward through an ordinary `perm.sync` (§20.2, D337), so it
|
||||
adds nothing to this protocol beyond the sizes hello reports. None of Backpacks' hooks is used.
|
||||
|
||||
**Hello and status.** `integrations` gains `backpacks`:
|
||||
|
||||
```json
|
||||
"backpacks": { "loaded": true, "version": "3.17.8", "sizes": [6, 12, 18, 24, 30, 36, 42, 48, 96, 144] }
|
||||
```
|
||||
|
||||
`sizes` is the N of every registered `backpacks.size.N` permission, ascending and read from the permission
|
||||
store, so it is the operator's own *Permission sizes*. A `backpacks.size.profile.…` permission (a dynamic-size
|
||||
profile) is not a size and is left out. `sizes` is absent when Backpacks is not loaded. Hello is sent again
|
||||
when Backpacks loads or unloads.
|
||||
|
||||
**`backpacks.read` — one player's backpack, live.**
|
||||
|
||||
```json
|
||||
{"cmd":"backpacks.read","reqId":"r-40","steamId":"76561198000000001"}
|
||||
{"kind":"backpacks.contents","type":"reply","reqId":"r-40","steamId":"76561198000000001","exists":true,
|
||||
"capacity":12,"pageSize":48,
|
||||
"items":[{"slot":0,"shortname":"wood","display":"Wood","amount":1000},
|
||||
{"slot":2,"shortname":"rifle.ak","display":"Assault Rifle","amount":1,
|
||||
"contents":[{"slot":0,"shortname":"weapon.mod.lasersight","display":"Weapon Lasersight","amount":1}]},
|
||||
{"slot":3,"shortname":"hoodie","display":"Hoodie","amount":1,"skin":10129},
|
||||
{"slot":4,"shortname":"note","display":"Note","amount":1,"name":"spike-a"},
|
||||
{"slot":5,"shortname":"blueprintbase","display":"Assault Rifle","blueprint":"rifle.ak","amount":1},
|
||||
{"slot":14,"shortname":"note","display":"Note","amount":1,"overflow":true}]}
|
||||
```
|
||||
|
||||
- **One id per call, on the main thread.** Only an id parsed as a Steam id is read (17 digits, at or above the
|
||||
individual base, as §20.3). It calls `API_ReadBackpackContentsAsJson`, which loads an offline player's
|
||||
backpack from disk, and **only when that answered a backpack** reads `API_GetBackpackCapacityById`: the
|
||||
backpack is loaded by then, so the capacity follows a size granted later (§11.3.1's trap). A player who has
|
||||
never had a backpack is `"exists": false`, with no `capacity`, `pageSize` or `items`.
|
||||
- **Each stack is reduced** to `slot`, `shortname`, `display` (the game's English name, D340), `amount`, and
|
||||
`skin` and `name` (a renamed item's name) when set. An id no item definition knows is sent with
|
||||
`"shortname": null` and its `itemId`, and no `display`. **A blueprint** (Backpacks stores `blueprintbase` with
|
||||
a target) carries `blueprint`, the target's shortname, and its `display` is the target's name (D341).
|
||||
`contents` (a weapon's attachments, a container's items) keeps the same shape, one level deep, and is sorted
|
||||
by slot like the stacks.
|
||||
- **`slot` runs across pages:** the first slot of page two is `pageSize`, which is Backpacks' *Max size per
|
||||
page* from `Backpacks.json`, clamped to 1–48 as Backpacks clamps it, and 48 when the file does not say.
|
||||
- **`overflow: true`** marks a stack at or past `capacity`. Nothing moves it until the owner next opens the
|
||||
backpack; then Backpacks moves it to their inventory, or drops it at their feet when that is full (D338).
|
||||
- **Refusals** are `backpacks.error`: `backpacks-missing`, `bad-id`, and `unreadable` (contents the bridge could
|
||||
not parse, which Backpacks' own JSON should never be).
|
||||
|
||||
**The sidecar** adds `POST /backpacks/read`, a thin forward like `/kits/uses`. It keeps nothing.
|
||||
|
||||
**The website (Module-Rust)** serves `GET /player/rust/backpacks?serverId=&steamId=` for the caller's own
|
||||
linked accounts (rate-limited) and `GET /admin/users/:id/rust/backpacks?serverId=&steamId=` for staff, scoped to
|
||||
that user's links. **Each view is one read; nothing is stored, and nothing polls.** It lends a bigger backpack
|
||||
through the `rust.backpack.size` step (*Bigger backpack*, `risk: 'change'`, `reversible: 'ledger'`, budget
|
||||
`rust.backpacks`): one `rust_perm_run_grants` row per recipient with `backpacks.size.N` as its permission and no
|
||||
kit, pushed by the next `perm.sync` and revoked when the run's revert deletes the rows. Its message, a §20.2
|
||||
notice, defaults to a warning about the take-back.
|
||||
|
||||
Reference in New Issue
Block a user