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