feat(rust): Backpacks — staff and player views, the Bigger backpack step (PLAN_REDESIGNS §11.3)
The site half of §11.3.2, on protocol 14:
- `rust.backpack.size` ("Bigger backpack"): a kit reward with a different
permission (D337) — one rust_perm_run_grants row per website user with
backpacks.size.N, no kit, no credit; the perm mirror pushes it and the run's
revert deletes it. risk change, reversible ledger, a new `rust.backpacks`
budget, rust.kit.entitle's recipient modes including `linked`. Refused with
"Backpacks is not installed on this server" in the dry run too (D199), and a
size the server does not offer is refused by name. Its message is never
silent: empty is the take-back warning (D338).
- `rust.options.backpacksizes`: each server's sizes above its smallest, from
the last hello, grouped by server.
- GET /player/rust/backpacks (own links only, 20 reads a minute) and
GET /admin/users/:id/rust/backpacks (that user's links only). Live, never
stored; a stranger's id is a 404 and never reaches the game.
- Client: a shared Backpack panel (pick server and account, open, read once)
on the account page and the admin user page; the grid draws pages of
pageSize, each slot named by the game (D340, no icons), blueprints by what
they make (D341), overflow marked. Backpacks joins the server's mod line.
Server 548/548 (13 new in test/backpacks.test.js), client 79/79 (5 new),
routes.manifest regenerated against the pinned core (89 routes), swagger
fragment regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
52
client/src/lib/backpack.js
Normal file
52
client/src/lib/backpack.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// ── A backpack as a grid (PLAN_REDESIGNS §11.3.2) ─────────────────────────
|
||||
//
|
||||
// Pure, like `format.js`, so the suite can ask it without a DOM. The page that
|
||||
// draws a backpack only maps over what these return.
|
||||
//
|
||||
// The read's `slot` runs across pages: page two of a 96-slot backpack starts at
|
||||
// slot 48 when the page size is 48 (§11.3.1). A stack at or past the capacity
|
||||
// is overflow — it waits for the owner's next open to be moved to their
|
||||
// inventory, or the ground (D338) — so the grid draws those slots too, after
|
||||
// the backpack's own, marked.
|
||||
|
||||
/**
|
||||
* The pages to draw: `[{ index, cells: [{ slot, stack, overflow }] }]`.
|
||||
* Enough slots for the capacity and for every stack, whichever reaches
|
||||
* further; a backpack that does not exist is no pages at all.
|
||||
*/
|
||||
export function backpackPages(backpack) {
|
||||
if (!backpack || !backpack.exists) return []
|
||||
|
||||
const items = Array.isArray(backpack.items) ? backpack.items : []
|
||||
const capacity = Math.max(0, Number(backpack.capacity) || 0)
|
||||
const pageSize = Math.max(1, Number(backpack.pageSize) || 48)
|
||||
const bySlot = new Map(items.map((s) => [Number(s.slot), s]))
|
||||
const furthest = items.reduce((max, s) => Math.max(max, Number(s.slot) + 1), 0)
|
||||
const total = Math.max(capacity, furthest)
|
||||
|
||||
const pages = []
|
||||
for (let start = 0; start < total; start += pageSize) {
|
||||
const cells = []
|
||||
for (let slot = start; slot < Math.min(start + pageSize, total); slot++) {
|
||||
cells.push({ slot, stack: bySlot.get(slot) || null, overflow: slot >= capacity })
|
||||
}
|
||||
pages.push({ index: pages.length, cells })
|
||||
}
|
||||
return pages
|
||||
}
|
||||
|
||||
/**
|
||||
* What a slot says: the game's name (D340), "… blueprint" for a blueprint
|
||||
* (D341), then the shortname, then the bare item id for an item the game no
|
||||
* longer knows.
|
||||
*/
|
||||
export function stackLabel(stack) {
|
||||
if (!stack) return ''
|
||||
const name = stack.display || stack.shortname || (stack.itemId !== undefined ? `item ${stack.itemId}` : 'unknown item')
|
||||
return stack.blueprint ? `${name} blueprint` : name
|
||||
}
|
||||
|
||||
/** How many stacks wait to be moved out at the owner's next open. */
|
||||
export function overflowCount(backpack) {
|
||||
return backpack && Array.isArray(backpack.items) ? backpack.items.filter((s) => s.overflow).length : 0
|
||||
}
|
||||
Reference in New Issue
Block a user