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:
2026-10-12 14:35:56 -05:00
parent 634db5b30e
commit 8c3975dddd
3 changed files with 103 additions and 2 deletions

View File

@@ -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.