feat(rust): Economics — pay coins, balances, staff adjust, the richest (PLAN_REDESIGNS §11.2)
All checks were successful
PR Checks / server-tests (pull_request) Successful in 31s
PR Checks / client-build (pull_request) Successful in 29s
PR Checks / frozen-manifest (pull_request) Successful in -1m29s

The site half of §11.2.2, on protocol 14:

- `rust.coins.pay` ("Pay coins"): irreversible, reversible none (D335). The
  recipient modes of rust.kit.entitle including `linked`, one payment per
  website user (D103's rule), an optional message with {amount}, a new
  `rust.coins` budget. Refused with "Economics is not installed on this
  server" in the dry run too (D199). Core's key goes to the plugin, so a
  retried step answers `repeat` and pays nobody twice.
- `rust_player_wipe_stats.balance`: the tally's balance, written as a value.
  A nameless balance frame (an offline player) moves nobody's last-seen.
  `economics.wiped` clears the wipe's balances, except at a new save.
- The richest: a Coins leaderboard column on one wipe of a server that
  reports Economics, `sort=balance`, and a `richest` title category
  (Magnate); both rank only above the starting balance (D336).
- GET /player/rust/economics (live, falling back to the last known), and
  GET/POST /admin/users/:id/rust/economics for staff — scoped to the user's
  own links, keyed per submission, an activity row with before and after.
- Client: the account page's Coins, the admin user page's Coins panel with
  its form, the leaderboard column, and Economics in the server's mod line
  (saying when its config could not be read).

Server 535/535 (14 new in test/economics.test.js), client 74/74,
routes.manifest regenerated against the pinned core (87 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:
2026-10-12 07:07:05 -05:00
parent 1777cb6ce1
commit 845caef817
32 changed files with 1907 additions and 35 deletions

View File

@@ -129,6 +129,12 @@ export const playerPermissions = {
list: () => req('/player/rust/permissions'),
}
// Your coin balances on every server, per linked account (PLAN_REDESIGNS §11.2.2):
// live where a server answers, the last one it reported where it does not.
export const playerEconomics = {
list: () => req('/player/rust/economics'),
}
// ── admin ─────────────────────────────────────────────────────────────────
// **`sidecarToken` goes up and never comes back.** The list answers `hasToken`,
// and a save that omits the field leaves the stored credential alone — so an
@@ -323,6 +329,14 @@ export const adminUserPermissions = {
),
}
// The same panel's Economics half (§11.2.2): this person's coins, and staff's
// hand on them. `key` is one per submission, so a resent form moves nothing.
export const adminUserEconomics = {
list: (userId) => req(`/admin/users/${encodeURIComponent(userId)}/rust/economics`),
adjust: (userId, body) =>
req(`/admin/users/${encodeURIComponent(userId)}/rust/economics`, { method: 'POST', body }),
}
// ── core's public event calendar ───────────────────────────────────────────
// The one CORE route this module reads, and core published `runId` on it for
// exactly this: a map marker holds a run id and nothing else, and the calendar is
@@ -342,6 +356,7 @@ export default {
playerServers,
playerLinks,
playerPermissions,
playerEconomics,
admin,
adminPermissions,
adminConfig,
@@ -351,6 +366,7 @@ export default {
playerNpcKills,
adminUserLinks,
adminUserPermissions,
adminUserEconomics,
coreEvents,
BASE,
}