From 5bc2ee87f41c76395e50c8af92c4521f6f50b006 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Fri, 16 Oct 2026 02:15:17 -0500 Subject: [PATCH] =?UTF-8?q?docs(rust):=20console=20commands=20as=20event?= =?UTF-8?q?=20steps=20and=20rewards=20(=C2=A716,=20D382-D384)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PLAN_REDESIGNS §16: two Module-Rust actions within the event system as it stands. `rust.command.run` runs typed console commands once on one server, with optional teardown commands; `rust.command.reward` runs them once per recipient from the run's tally, with {steamid} and {name} filled in by the bridge. Both irreversible, both behind an "I am sure" toggle, priced in a new `rust.commands` budget. - The wire: a `command.run` frame and POST /commands/run, joining the unreleased protocol 14; an at-most-once key file in the bridge; `unknown-command` stops a step at the first "not found". - D382 (the item), D383 (offline recipients skipped, not held), D384 (no allowlist: a free text box). Settled-and-overrulable points, the build order (one stage, before RaidableBases), and the walk rows, which join the end walk. - Pointers in the status paragraph, the summary table, §9 and §10. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- modules/rust/PLAN_REDESIGNS.md | 166 ++++++++++++++++++++++++++++++++- 1 file changed, 164 insertions(+), 2 deletions(-) diff --git a/modules/rust/PLAN_REDESIGNS.md b/modules/rust/PLAN_REDESIGNS.md index 49363a5..7814744 100644 --- a/modules/rust/PLAN_REDESIGNS.md +++ b/modules/rust/PLAN_REDESIGNS.md @@ -11,7 +11,7 @@ chose neither route: Runic Gateway writes its own NPC plugin, RunicNPC, planned [`../../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). Economics was built on 2026-10-12 too (§11.2.4). The Backpacks spike followed and raised D337–D339 (§11.3.1), and Backpacks was built the same day (D340, D341, §11.3.4). The org lead then added permission rewards (§12, -D342–D346), planned and built the same day. On 2026-10-14 RaidableBases was deferred until the current paid copy arrives (D347), with both editions to be supported (D348, §11.4.2). The same day the org lead added Rust's own world events to the base module (§13, D349) and six more first-class optional plugins (§14, D350–D352), to be tested together in one phase before their integration plan. Their questions were answered the same day (D353–D359, the end walk now waiting for RaidableBases), and Steam sign-in stays on the website's plan, linked as §15 (D360). **The §14.2 test phase ran the same day on both rigs (§14.2.1)**: the org lead ruled D361–D362 during it and answered its Q19–Q25 the same day (D363–D369). **Every item left is built, tested and PR'd first; every walk — +D342–D346), planned and built the same day. On 2026-10-14 RaidableBases was deferred until the current paid copy arrives (D347), with both editions to be supported (D348, §11.4.2). The same day the org lead added Rust's own world events to the base module (§13, D349) and six more first-class optional plugins (§14, D350–D352), to be tested together in one phase before their integration plan. Their questions were answered the same day (D353–D359, the end walk now waiting for RaidableBases), and Steam sign-in stays on the website's plan, linked as §15 (D360). **The §14.2 test phase ran the same day on both rigs (§14.2.1)**: the org lead ruled D361–D362 during it and answered its Q19–Q25 the same day (D363–D369). On 2026-10-16 the org lead added console commands as event steps and rewards (§16, D382–D384), to be built before RaidableBases. **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 @@ -30,6 +30,7 @@ document is later and wins. Its decisions continue PLAN_FIXES' numbering at **D1 | 13 | Rust's own world events (helicopter, Bradley, cargo, Chinook, airdrops, locked crates) in the base module | — (added 2026-10-14) | Rust-Plugins, Rust-Link, Module-Rust | | 14 | Five more optional plugins: TruePVE, Dynamic PVP, Dangerous Treasures, Guarded Crate, ZLevels (Server Rewards dropped, D381) | — (added 2026-10-14) | Rust-Plugins, Rust-Link, Module-Rust, Android-app | | 15 | Steam sign-in: a link to the website's own plan, [`website/STEAM_SIGNIN.md`](../../website/STEAM_SIGNIN.md) | §4.8, D171 | **core** (website), Android-app, Module-Rust | +| 16 | Console commands an admin types, as an event step and as a reward | — (added 2026-10-16) | Rust-Plugins, Rust-Link, Module-Rust | **Protocol.** §1, §3, §4 and §5 joined **protocol 13**, which shipped on 2026-10-09 with RunicNPC v1.0.0 (bundle 2026.10.09). The wire changes still to come, §2's and §11's, **open protocol 14** on `edge` and are @@ -933,10 +934,12 @@ pulls) is research, not a walk, so it still comes first: 2026-10-15 (§14.3.15).** **Stage 3, TruePVE in the zone step, was built on 2026-10-15 (§14.3.16)**, and **stage 4, Dynamic PVP, the same day (§14.3.17)**, and **stage 5, Dangerous Treasures, the same day (§14.3.18)**, and **stage 6, Guarded Crate, the same day (§14.3.19)**, and **stage 7, ZLevels, the same day (§14.3.20).** **Stage 8, Server Rewards, was taken off the plan on 2026-10-16 (D381).** **Stage 9, core's trigger starts, was built on 2026-10-16 (§14.3.21)**, which ends the build. +10. **Console commands as event steps and rewards** (§16, D382–D384), added on 2026-10-16: one stage across + Rust-Plugins, Rust-Link and Module-Rust (§16.8), built before RaidableBases. **Then one walk, on Oxide and Carbon, of everything still owed (D331):** §2.7; §11's rows; §12.9; §13's and §14's rows; the in-game rows -of §5.7 and §3.4; PLAN_FIXES §8's re-walk, including the Carbon pass of the first player walk. What it finds +of §5.7 and §3.4; §16.9; PLAN_FIXES §8's re-walk, including the Carbon pass of the first player walk. What it finds is fixed, protocol 14 is released, and the cutover follows. **The walk waits for RaidableBases (D359)**, so it runs only once §11.4 is built from the current paid copy (D347). @@ -1099,6 +1102,8 @@ The org lead answered §14.3's Q26–Q32 on 2026-10-14: | **D380** | **A guarded crate left standing is noted in the run log when it is placed, not when the run ends:** the *Place a guarded crate* step's success line says the crate is kept when the run ends and how it ends (org lead, 2026-10-15). Core gives a module no hook at a run's end for a step that is kept, so D373's note at the end would need new core work. | A run-end hook in core now (a MODULE_API minor, with the Module-uo proof), or filed for later. | 14.3.5, 14.3.19 | | **D381** | **Server Rewards is taken off the plan** (org lead, 2026-10-16: *"I do not want it"*). Stage 8 is not built. Gone with it: `integrations.serverRewards`, `points.pay` / `balances` / `adjust` and the sidecar's three routes, the tally's `points`, the step `rust.points.pay` and its budget, the `rust_player_points` table, staff add and take, the app's RP balance, and the *Hoarder* title and its column. It amends D350 (five plugins, not six), D369 (its wipe half now covers ZLevels only), D375 and D377 (no *Hoarder*). Nothing had been built. §14.2.1's test findings stay as a record. | Building §14.3.7 as planned. | 14, 14.3.7, 14.3.11 | +Console commands as event steps and rewards, added on 2026-10-16, were decided as D382–D384; they are tabled with their section, in §16.7. + ## 11. First-class optional plugins (D199–D202) The org lead named four plugins the module supports as first-class integrations. Kits already is one. The other @@ -3271,3 +3276,160 @@ What this module takes from it: is that plan's Q1, and what unlinking removes is its Q2. - When it lands, Module-Rust's account screens and the link flow are updated in their own PR. Rust's grants already follow the website user to every linked Steam id (D28), so nothing else changes. + +--- + +## 16. Console commands as event steps and rewards (D382–D384) + +The org lead asked for this on 2026-10-16, before the end walk and RaidableBases: **a step that runs console +commands an admin types**, and **a reward that runs them once per winner**. With these, a plugin with no +first-class support can still be used from the event scheduler: *"start a Raidable Base with `rbe …`"* as a step, +*"`sr add {steamid} 100`"* as a reward, now that Server Rewards is not first-class (D381). This saves writing +an integration for every small plugin. It is limited to this module and fits the event system as it stands: two +module actions, one budget, and no change to core. Three decisions, D382–D384, were answered the same day +(§16.7). + +### 16.1 What it builds on + +- **The two world-changing step shapes already in the module.** `rust.world.call` (§14.3.1) and Dangerous + Treasures' start (§14.3.4) send a command frame through the sidecar, which the bridge runs on the main thread + and answers. The new frame, `command.run`, is the same kind of frame. +- **The reward half is a kit reward's** (`rust.kit.entitle`): the run's tally from *Count who takes part*, and + the same recipient picker (everyone who scored, top N, a minimum score, N at random, the top X per cent), with + the same bound of 100 recipients per step. +- **The safety is core's, as it is for every world-changing step.** A `change` or `irreversible` action is off + until an admin switches it on in the actions switchboard, only an admin may author a step that uses it, and the + switchboard's per-run caps bound its budget (EVENTS.md §K). The text is saved with the definition, so every + run's commands are on its record. +- **How the bridge runs a console command is settled.** It already calls `ConsoleSystem.Run` for reloads (D180), + Dangerous Treasures and Guarded Crate. Stage 1 found that on Carbon this path, a `NextTick` queued by the + reader thread, reaches plugin commands (§14.3.14 point 7); the end walk confirms it. + +### 16.2 *Run console commands*: `rust.command.run` + +A step that runs the admin's commands once, on one server. + +- **Params:** + - **`server`**: one server, from the existing server list. + - **`commands`**: the commands, one per entry, in order. At most 10 per step and 512 characters each, typed + in a free text box; no allowlist and no blocklist (D384). For example RaidableBases' `rbe` with its arguments. + - **`teardown`**: optional commands, run when the run ends or is cancelled. For example a plugin's own + "stop" command. Same limits. + - **`confirm`**: a required boolean, *"I am sure I want to run these commands on the server"*. Unticked, the + dry run and the run refuse, as D343's toggle does. +- **`risk: 'irreversible'`, `reversible: 'ledger'`.** The site cannot know what a typed command does, so it + rates it the most dangerous class. That also gives it core's default failure rule for that class: retry, then + abort the run. The ledger row carries a **copy of the teardown commands**, so an edit to the definition + mid-run does not change what the teardown runs. A step with no teardown commands undoes nothing. +- **Priced** at one `rust.commands` unit per command, start and teardown together. + +### 16.3 *Reward by console command*: `rust.command.reward` + +A reward that runs the admin's commands once for each recipient. + +- **Params:** `server`, `recipients` and `count` exactly as the kit reward has them; `commands` (as §16.2, at + most 10); `confirm` (*"I am sure I want to run these commands for every recipient"*). +- **Placeholders**, filled in **by the bridge**, never by the site: + - **`{steamid}`**: the recipient's Steam id; + - **`{name}`**: the recipient's name, passed as one quoted argument so a name with spaces or quotes cannot + run anything else. + + For example `sr add {steamid} 100`, or `inventory.giveto {steamid} rifle.ak 1`. +- **Offline recipients are skipped, never held (D383).** The bridge runs the commands for each recipient + connected at that moment. The step's detail names everyone skipped as offline, as the kit reward names + those with no linked account. Holding commands until a player next joins is left for later. +- **A website account is not needed.** A command goes by Steam id, so it reaches players who never linked, + unlike a kit reward, which the site keeps for a website user. +- **`risk: 'irreversible'`, `reversible: 'none'`**, like `rust.perm.give`: nothing is undone at teardown. +- **Priced** at commands × recipients in `rust.commands`. A mode that is not a count is priced at the + 100-recipient bound, as kits are. + +### 16.4 The wire: `command.run` (protocol 14) + +- **Sidecar:** one more forwarding route, `POST /commands/run`, stamped `command.run` the way `world.call` and + `dt.start` are. The sidecar stays a forwarder. **It joins protocol 14**, which is still unreleased on `edge` and + goes out once after the end walk (D332). This is how every item since §2 has added to it. +- **Request:** `{ key, commands: [..], recipients?: [{ steamId, name }] }`. With no `recipients` the commands + run once (§16.2). With `recipients` they run once per online recipient, after the placeholders are filled + (§16.3). +- **Main thread, spread over frames.** A full reward is at most 100 × 10 commands, so the bridge runs a bounded + number per frame rather than all in one, and answers inside the step's 15 s budget (`BUDGET_MS`). +- **At most once, by the key.** A command cannot be asked whether it already ran. So the bridge records the + key in a small data file **before** it runs anything. A repeat with the same key (the site retrying after a + lost answer, or a run resumed) answers from that record and runs nothing again. Records are kept for 7 days. + This is the bridge's first at-most-once store; until now each feature's own registry did that job (§14.3.1). +- **The answer:** for each command, the console's reply (trimmed to 200 characters), plus who was skipped as + offline. **If the server does not know a command, the step fails as permanent `unknown-command`.** The game + answers *"Command '…' not found"* in that case. The bridge stops at the first such reply, before running it + for anyone else, so a typo or an unloaded plugin is caught at once instead of failing 100 times. +- **No new switch in the bridge's config.** The switchboard on the site is the switch (bridge principle 2), and + D384 rules out an allowlist. + +### 16.5 What it cannot do + +- **It knows only that the command ran, and what the console answered.** A plugin's reply is its own text, so + "it ran" is not "it worked". The run log shows the replies. +- **It learns nothing afterwards.** A Raidable Base started this way has no line in the feed, no marker on the + map, no winner credited, no title count and no trigger. Those need the plugin's hooks, and a hook cannot be + subscribed by name at runtime. **First-class support (§11.4's RaidableBases) is still the way to get them**; + this step is for "fire and forget". +- **Console commands only.** A plugin command registered only for chat (a `[ChatCommand]`) runs as a player and + cannot be reached from the console without reflecting into Oxide's and Carbon's command systems. Most + reward-style plugins also register a console command. +- **A reward does nothing for anyone offline** at the moment it runs (D383). + +### 16.6 Settled in this plan, and overrulable + +- **The text box is core's existing `multiple` string field**: a box where each command typed and entered + becomes one entry (MODULE_API 1.14.0, §12.3). So core is not changed. A real multi-line box would be a core + addition (a MODULE_API minor with the Module-uo proof, which 1.16.0 is already planned for Steam sign-in), and + can follow if the entry box is not enough. +- **One server per step**, as the world steps have. An author who wants every server adds a step per server. +- **Both steps are `irreversible`** and both carry the "I am sure" toggle (§16.2, §16.3). +- **The limits:** 10 commands per field, 512 characters each, records of keys kept 7 days, replies trimmed to + 200 characters. +- **The placeholders are `{steamid}` and `{name}`** only. A `{score}` can be added if a reward needs it. +- **No notification to the player.** A reward command does not send the website's "you were rewarded" + message, because the site does not know what the command gave. An author who wants a line in chat adds an + *Announce* step. +- **Built next, before RaidableBases**, and walked in the same end walk (D331, D359). + +### 16.7 Decisions of record + +Answered by the org lead on 2026-10-16. + +| # | Decision | Rejected | § | +|---|---|---|---| +| **D382** | **An event can run console commands an admin types**: a *Run console commands* step, with optional commands at teardown, and a *Reward by console command* reward run once per recipient. Both are module actions in Module-Rust within the event system as it stands. They are one item, planned here and built as one stage. | Writing a first-class integration for every plugin used in events; a game-agnostic command action in core (ADMIN_CONTROLS §8a admits only declared actions). | 16.2, 16.3 | +| **D383** | **A command reward skips recipients who are offline** and names them in the step's detail. It does not hold the commands until they next join, *"for now"*. | A queue in the bridge that runs held commands when the player connects and survives a restart. | 16.3 | +| **D384** | **No allowlist for these commands**: the admin types them into a free text box. What bounds them is core's: the step is off until an admin switches it on, only an admin may author it, the switchboard's caps, and the "I am sure" toggle. | An allowlist of command prefixes in the bridge's config, empty by default; an allowlist on the site. | 16.2, 16.4 | + +### 16.8 Where the work lands, and in what order + +One stage, after this plan merges: + +1. **Rust-Plugins, on `edge`:** the `command.run` handler, the placeholder filling, the per-frame runner, the + at-most-once key file, `unknown-command`; compiled on both rigs. +2. **Rust-Link, on `edge`:** `POST /commands/run`, with its stamp test. +3. **Module-Rust, on `edge`:** `server/eventCommands.js` with the two actions and the `rust.commands` budget, + reusing the kit reward's tally reading and recipient picker; its `ci/bundle.json` entry; tests. +4. **docs:** this section's *Built* notes; `rust-link/PROTOCOL.md` (the frame and route); + `website/EVENTS.md` (the two steps among the module's actions). + +**Not touched:** core (website), the installer (protocol 14 is already the pairing on `edge`), Android-app and +Module-uo. + +### 16.9 The walk (deferred to the end, D331) + +On both rigs, with a plugin that has console commands (RaidableBases once it is in, and something like +Economics' `deposit` meanwhile): + +- see both steps off in the switchboard, then switch them on; +- run a *Run console commands* step unticked (refused), then ticked; see the replies in the run log, and the + teardown commands run when the run ends and when it is cancelled; +- run a step with a misspelled command and see `unknown-command`, with nothing else run; +- run a *Reward by console command* reward with `{steamid}` and `{name}`, one recipient online and one offline; + see the online one rewarded, the offline one named as skipped, and a player whose name has spaces and a + quote get it right; +- retry a step after its answer is lost (stop the sidecar mid-step) and see nothing run twice; +- **on Carbon**, see a plugin's command run, the confirmation §14.3.14 point 7 asked for. -- 2.49.1