Merge pull request 'docs(rust): protocol 14 — Economics, as built (PROTOCOL §20.3, PLAN_REDESIGNS §11.2.4)' (#339) from docs/rust-protocol-14-economics into main
Reviewed-on: #339
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` |
|
||||
| **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) |
|
||||
|
||||
#### 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). **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). **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
|
||||
@@ -1173,6 +1173,27 @@ On both rigs, with Economics installed:
|
||||
- run `ecowipe`, and see the column clear;
|
||||
- read a balance on the account page and in the app.
|
||||
|
||||
#### 11.2.4 Built (2026-10-12)
|
||||
|
||||
Built to §11.2.2 on protocol 14, and specified in [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.3. The
|
||||
bridge compiled on both rigs (Rust 25877464; Oxide 2.0.7826, Carbon 2.0.262), which were then given back their
|
||||
protocol 13 bridge. Six points the design left open were settled in the code, and are named here so they can
|
||||
be overruled:
|
||||
|
||||
- **`economics.wiped` carries `newSave`.** After `ecowipe` the site clears the wipe's balances; at a new save it
|
||||
clears nothing, because the new map's balances arrive as players connect, and the last wipe's stay as the
|
||||
record of how it ended.
|
||||
- **A balance frame with no `name` is not play.** The plugin names only a connected player, and the site writes
|
||||
such a frame's balance without moving anybody's last-seen.
|
||||
- **The Coins column is per wipe.** A balance is a value, so there is no all-time one: the column and
|
||||
`sort=balance` exist only when one wipe of a server that reports Economics is shown.
|
||||
- **A payment's `failed` reasons** are `at-limit`, `refused`, `unmeasured` (deposited, but not measured — remembered
|
||||
with the key so a retry pays nothing more) and `bad-id`.
|
||||
- **Payments and adjustments keep their keys apart**, so a step's key can never be answered with a staff
|
||||
adjustment's reply. The admin form sends a fresh key with each submission.
|
||||
- **The step spends a new `rust.coins` budget**, priced like the kit reward: the amount times the count, or
|
||||
times the recipient bound for a mode that is not a count.
|
||||
|
||||
### 11.3 Backpacks (D201)
|
||||
|
||||
- **Read, never write (R2).** A new verb, `backpacks.read {steamId}`, returns
|
||||
|
||||
@@ -2641,7 +2641,7 @@ loaded.
|
||||
|
||||
---
|
||||
|
||||
## 20. Protocol 14 — the step editor
|
||||
## 20. Protocol 14 — the step editor and the optional plugins
|
||||
|
||||
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
|
||||
@@ -2701,3 +2701,109 @@ optional `message` and `delivery`; the site writes them on each grant row, and e
|
||||
- **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.
|
||||
|
||||
### 20.3 Economics — coins, balances, staff adjust (§11.2, D199, D335, D336)
|
||||
|
||||
Wulf's **Economics** (3.9.2, uMod), optional like BetterChat and PopupNotifications (D199): every verb below
|
||||
answers `economics-missing` without it, and nothing else in the bridge needs it. `requires_plugins` keeps Kits
|
||||
and ZoneManager only. The design is [`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §11.2.2; what the
|
||||
spike found, which shapes every call, is §11.2.1 there.
|
||||
|
||||
**How the plugin calls it.** Economics' API is private methods called by name, each with a `string` and an
|
||||
`object` overload. The bridge sends **only ids it has parsed as Steam ids** (17 digits, at or above the
|
||||
individual base), because Economics makes an account of any string it is given, and **boxes every amount as
|
||||
a `double`**: an `int` matches no overload, and `Call` answers null having done nothing. What a deposit paid is
|
||||
**the balance after less the balance before**: `Deposit` answers `true` while rounding to two places and
|
||||
clamping to Economics' balance limit.
|
||||
|
||||
**Hello and status.** `integrations` gains `economics`:
|
||||
|
||||
```json
|
||||
"economics": { "loaded": true, "version": "3.9.2", "startingBalance": 1000, "wipesWithMap": false }
|
||||
```
|
||||
|
||||
`startingBalance` and `wipesWithMap` come from `Economics.json` in the framework's config directory
|
||||
(`"Starting account balance (0 or higher)"`, `"Wipe balances on new save file"`), read again only when the file
|
||||
changes. **Each is absent when the file cannot say**; the site then assumes Economics' default of 1000 and the
|
||||
servers page says so. Hello is sent again when Economics loads or unloads.
|
||||
|
||||
**`economics.pay` — coins from an event step, kept once paid (D335).**
|
||||
|
||||
```json
|
||||
{"cmd":"economics.pay","reqId":"r-31","key":"7:4","amount":500,"steamIds":["76561190000000001"],
|
||||
"message":"You earned {amount} coins","delivery":"popup"}
|
||||
{"kind":"economics.paid","type":"reply","reqId":"r-31","key":"7:4","repeat":false,"amount":500,
|
||||
"paid":[{"steamId":"76561190000000001","before":1000,"after":1500}],"failed":[]}
|
||||
```
|
||||
|
||||
- **Main thread, one call for the list.** For each id: read the balance, deposit, read it again. `paid` holds
|
||||
each player's before and after. `failed` lists the rest by reason: `at-limit` (the deposit moved nothing —
|
||||
the balance is at Economics' limit), `refused` (`Deposit` answered false), `unmeasured` (deposited, but a
|
||||
balance read failed — not told an amount, and still remembered with the key), `bad-id` (not a Steam id).
|
||||
- **The key is remembered with its reply**, in `data/RunicGateway/payments.json`, for seven days (the tally
|
||||
ceiling, D108). A key already paid is answered from the first reply with `repeat: true`, and pays nobody. A
|
||||
refusal (`economics.error`) is not remembered: nothing moved.
|
||||
- **The message** (optional; blank tells nobody) is said once to each player paid, with `{amount}` replaced by
|
||||
**what that player received**: at once if they are connected and awake, otherwise on `OnPlayerSleepEnded`,
|
||||
the next time they wake on this server — §20.2's rule. Who has been told is kept in `payments.json` beside
|
||||
the key. A popup where `delivery` is `popup` and PopupNotifications is loaded, chat otherwise; cut at the
|
||||
chat bound.
|
||||
- **Bounds:** at most `EventsMaxRecipients` ids; an amount from 0.01 to 1,000,000. Behind `EventsEnabled`, as
|
||||
every event verb is.
|
||||
- Refusals are `economics.error`: `economics-missing`, `events-disabled`, `malformed` (no key), `bad-amount`,
|
||||
`too-large`.
|
||||
|
||||
**`economics.balances` — what some players hold.**
|
||||
|
||||
```json
|
||||
{"cmd":"economics.balances","reqId":"r-32","steamIds":["76561190000000001"]}
|
||||
{"kind":"economics.balances","type":"reply","reqId":"r-32","balances":[{"steamId":"76561190000000001","balance":1500}],"startingBalance":1000}
|
||||
```
|
||||
|
||||
Bounded by the sync's own bound (`PermMaxOps`), like `kits.uses`; ids that are not Steam ids are left out.
|
||||
**A balance at the starting value may belong to an account Economics has never seen**: it answers the
|
||||
starting balance for one and stores nothing (§11.2.1). `startingBalance` is absent where the config cannot be
|
||||
read. Refusals: `economics-missing`, `too-large`.
|
||||
|
||||
**`economics.adjust` — staff deposit, withdraw or set one balance.**
|
||||
|
||||
```json
|
||||
{"cmd":"economics.adjust","reqId":"r-33","key":"adj-3f2a9c1e-…","steamId":"76561190000000001","op":"withdraw","amount":500}
|
||||
{"kind":"economics.adjusted","type":"reply","reqId":"r-33","key":"adj-3f2a9c1e-…","repeat":false,"op":"withdraw","amount":500,
|
||||
"steamId":"76561190000000001","before":1500,"after":1000}
|
||||
```
|
||||
|
||||
- `op` is `deposit`, `withdraw` or `set`; an amount from 0.01 to 1,000,000, and from 0 for `set`. Not behind
|
||||
`EventsEnabled`: it is staff, not an event.
|
||||
- **A withdrawal above the balance is refused** with `insufficient` and the `balance`, because Economics takes
|
||||
all or nothing. A deposit that moved nothing is `at-limit`, with the balance.
|
||||
- The key is remembered seven days like a payment's, in its own namespace, so a resent form answers `repeat`
|
||||
and moves nothing. Other refusals: `economics-missing`, `malformed`, `bad-amount`, `refused`.
|
||||
|
||||
**The tally.** `player.tally` gains **`balance`: the current balance, a value and not a change**, like the
|
||||
`best*` fields (§19.10). It is set when a player connects (so a balance carried over from last wipe is known at
|
||||
once, D336), when Economics loads with players on, and from `OnEconomicsBalanceUpdated` for any id that parses
|
||||
as a Steam id. **The player need not be online**: a payment or an adjustment reaches an offline player, and
|
||||
that frame **carries no `name`** — the plugin names only a connected player, and the site reads a nameless
|
||||
balance frame as not play: it moves nobody's last-seen.
|
||||
|
||||
**`economics.wiped`**, an event from `OnEconomicsDataWiped` in both of its shapes (one method taking an
|
||||
`IPlayer`, null for the no-argument call):
|
||||
|
||||
```json
|
||||
{"kind":"economics.wiped","type":"event","newSave":false}
|
||||
```
|
||||
|
||||
`newSave` is true for the no-argument call — a new save, with Economics set to wipe balances with the map — and
|
||||
false after `ecowipe`. Balances not yet flushed are dropped. **The site clears the wipe's balances after
|
||||
`ecowipe`**; at a new save it does nothing, because the new map's balances arrive as players connect and the
|
||||
last wipe's stay as the record of how it ended.
|
||||
|
||||
**The sidecar** adds `POST /economics/pay`, `POST /economics/balances` and `POST /economics/adjust`, thin
|
||||
forwards like `/kits/uses`. It keeps nothing.
|
||||
|
||||
**The website (Module-Rust)** stores the tally's balance in `rust_player_wipe_stats.balance`, written over and
|
||||
never summed. It serves `GET /player/rust/economics` (live, falling back to the last known) and
|
||||
`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).
|
||||
|
||||
Reference in New Issue
Block a user