diff --git a/modules/rust/PLAN_REDESIGNS.md b/modules/rust/PLAN_REDESIGNS.md index 2eb5410..b3cc494 100644 --- a/modules/rust/PLAN_REDESIGNS.md +++ b/modules/rust/PLAN_REDESIGNS.md @@ -10,7 +10,8 @@ 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). 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). **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). 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 the same day. **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 @@ -25,6 +26,7 @@ document is later and wins. Its decisions continue PLAN_FIXES' numbering at **D1 | 5 | Chat titles: twenty-three conditions | §4.6, D172–D175 | Rust-Plugins, Module-Rust | | 6 | NPCs — now RunicNPC, its own plan ([`runicnpc/PLAN.md`](../../runicnpc/PLAN.md)) | §4.7 | **runicnpc-rust**, Rust-Plugins, Rust-Link, Module-Rust, installer | | 11 | First-class optional plugins: Economics, Backpacks, RaidableBases (Kits stays required) | — (added 2026-09-28) | Rust-Plugins, Module-Rust, installer, Android-app | +| 12 | Permissions as event rewards: until the event ends, or for good | — (added 2026-10-12) | **core** (website `main`, MODULE_API 1.14.0), 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 @@ -1015,6 +1017,8 @@ module does not have, and R9 keeps item icons out of the asset bridge. | **D340** | **A backpack slot shows the item's name, not an icon.** The plugin sends each stack's English name from the game (`display`), so "Wood ×1000" and "Assault Rifle + Weapon Lasersight" need no item catalogue and nothing loads from outside the site. R9 stands. | Icons from a third-party host keyed by shortname; shortnames alone. | 11.3.4 | | **D341** | **A blueprint names what it makes.** Backpacks stores a blueprint as `blueprintbase` with a target; the stack carries `blueprint` (the target's shortname), its `display` is the target's name, and the slot reads "Assault Rifle blueprint". | Every blueprint shown as the same anonymous "Blueprint". | 11.3.4 | +Permission rewards, added on 2026-10-12, were decided as D342–D346; they are tabled with their section, in §12.8. + ## 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 @@ -1646,3 +1650,167 @@ The org lead answered §11.4.4's three questions the same day: These are new verbs, events and hello fields. **They join protocol 14 (D332)**, which §2 opens: protocol 13 shipped on 2026-10-09, before §9 reached item 7. + +--- + +## 12. Permissions as event rewards (D342–D346) + +The org lead asked for this on 2026-10-12, during the Backpacks build: an event reward that grants **any +permission the server's plugins registered**, so a perk nobody planned for (night vision, a chat colour, a +`/remove` command) is a reward without code written for it. The picker sorts by mod, then by permission +inside the mod. It is its own item, after Backpacks, and planned first. Five decisions, D342–D346, were answered +the same day (§12.8). + +Two steps come out of it. **Permissions until the event ends** lends them for the run, as a kit reward does. +**Permissions for good** gives them for keeps, as a staff member's grant in the permission manager does. Both +take several permissions in one step, which needs one small addition to core's step editor (§12.3). + +### 12.1 What it builds on + +Nothing below is new machinery; both steps are rows in tables the permission manager already pushes. + +- **The lease is a kit reward's** (PLAN.md §29, D84). `rust.kit.entitle` and `rust.backpack.size` (D337) write + one `rust_perm_run_grants` row per recipient, the permission mirror pushes it in the next `perm.sync`, and the + step's revert deletes the rows so the next sync revokes them. The push reads the **union** of these rows and + the standing grants, so a player who already held the permission keeps it after the revert. +- **A standing grant is the permission manager's own** (§1): a `rust_perm_grants` row for a website user, + scoped to a server, reaching every account the user has linked (D28). It is `UNIQUE` on (user, permission, + scope), so granting one twice leaves the first. +- **The catalogue** is `rust_perm_catalogue`: per server, every registered permission and the plugin that + registered it (`owner`, the plugin's `Name`). A permission no plugin owns, as Carbon's built-in modules + register theirs, has no owner, and the permission manager groups it by its prefix (§1). +- **The message is D333's notice**, which rides `perm.sync` and is told once, at once or on the player's + next wake. +- **So there is no plugin code, no sidecar code and no protocol change.** Protocol 14 is untouched. + +### 12.2 The picker: `rust.options.permissions` (D342) + +- **Every permission in the catalogue of every enabled server, once each.** Its `group` is the plugin that + registered it. A permission with no owner is grouped by its prefix, labelled *"<prefix> (not + registered by a plugin)"*, the same rule the permission manager draws. +- **Sorted by group, then by permission inside the group**, both case-insensitively. The editor keeps a + source's order. +- **A row says where it is missing.** A permission that is not in every enabled server's catalogue carries + a row `note` (MODULE_API 1.13.0): *"Only on Main, PvP"*. +- **Not `searchable`.** A server registers a few hundred permissions; the multi-select's own filter box + (§12.3) narrows them in the browser. If a deployment ever passes the 2,000-row bound, the source says so, as + every bounded source does. +- **No blocklist and no allowlist** (D343). The bridge's own `runicgateway.*` permissions are in the list like + any other. +- **The step checks the server it is given.** Every picked permission must be in that server's catalogue; one + that is not is refused by name: *"PvP has no permission nightvision.allowed — its plugins did not register + it"*. The check runs in the dry run too. + +### 12.3 The core addition: a param that holds several values (D344, MODULE_API 1.14.0) + +The step editor has six param types, all single values. A step that takes several permissions needs a +list, so core gains one declaration flag. **It is an addition only**: a module that does not use it is +unchanged, and Module-uo's `coreApi: "^1.10.0"` still resolves. + +- **`multiple: true` on an action param** of type `string` or `int`, with an optional `max` (1 to 100, 20 if + left out). It is refused on any other type, on a `duration` param, and on a template's inputs, which this + version does not extend. +- **The value is a JSON array**: each element checked as the type's literal, duplicates dropped, order kept, at + most `max`. `required` means at least one. A bare value is read as a list of one, so a spec written by hand + or over the API does not trip on it. The module's `perform()` and `cost()` receive the array. +- **The catalog serves `multiple` and `max`** beside the rest of the declaration (`GET + /admin/events/catalog`). +- **The editor draws a multi-select.** + - With a `source`: the chosen values as chips above a grouped checklist, with a filter box over it. Ticking + past `max` is refused with the number. + - With a `searchable` source: the typeahead adds a chip per pick. + - With no source: a tag box, a value per Enter. +- **The definition view and the run console** show a list as its values joined by commas. +- **Docs:** `website/MODULE_API.md` gains 1.14.0's entry and the flag in the declaration reference; + `website/EVENTS.md` §F names the control. + +### 12.4 Permissions until the event ends: `rust.perm.lend` (D345, D346) + +*"Lends the permissions you pick to the people a mode picks from this run's tally — or every linked player of +the server — until the run ends. Waits for them if they are offline. Someone who already held one keeps it +when the event ends."* + +- **Params:** + - the server; + - the permissions (`multiple`, `max` 10, from §12.2); + - the recipients and count (`rust.kit.entitle`'s modes, `linked` included); + - a message and its delivery; + - **`confirm`**, a required boolean: *"I am sure I want to give these permissions as a reward"* (D343). +- **One row per recipient per permission** in `rust_perm_run_grants`, with no kit and no credit; the notice + goes on one row per recipient, so each is told once. +- **Unticked, it refuses** in the dry run and at run time, naming what it would have given: *"Tick "I am sure" + to make nightvision.allowed, betterchat.color a reward"*. Being a required toggle (D334), it cannot be left + unset. +- **`risk: 'change'`, `reversible: 'ledger'`**, as a kit reward is. Its revert and reconcile are + `rust.kit.entitle`'s, and core reverts it at the end of every run, a normal finish included. +- **The budget is new: `rust.permissions`, *Permissions lent*.** One per recipient per permission; a mode + that is not a count is priced at the recipient bound times the permissions, as kit rewards are. + +### 12.5 Permissions for good: `rust.perm.give` (D345, D346) + +*"Gives the permissions you pick to the people a mode picks, for good. Ending or cancelling the event does not +take them back; staff remove them in the permission manager."* + +- **The same params as §12.4**, the confirm included. +- **A standing grant per website user per permission**, scoped to the step's server, with source `event` and + the note *"event run <run>, step <step>"*. A grant already there is left alone and counted as + *already held*. +- **`risk: 'irreversible'`, `reversible: 'none'`**, as `rust.coins.pay` is (D335). Like every step that changes + the world it arrives off until an admin turns it on under Actions; unlike §12.4's, its failure aborts the run + by default instead of pausing it, and Actions lists it as irreversible, so an admin enables the two + separately. +- **The budget is new too: `rust.permissions.kept`, *Permissions given for good***, priced as §12.4's. +- **Taking one back is the permission manager's.** The grant shows there with its source and note, and staff + delete it like any other. +- **The message rides a notice-only row** in `rust_perm_run_grants`: no permission, no kit, no credit. The + push grants nothing for it and tells the notice once. Nothing reverts the step, so the row stays; it is a + sentence that has been said. + +### 12.6 Settled in this plan, and overrulable + +- **A blank message tells nobody**, as a kit reward's does (D333), rather than a default sentence. A + permission's name (`nightvision.allowed`) means nothing to a player, so only the author can say what it does: + *"You can use /nv until the event ends"*. `{permissions}` puts the names in. +- **At most 10 permissions in one step**: enough for a perk bundle, and the price stays readable. +- **A permission the server's catalogue lacks is refused, not skipped.** The plugin may be unloaded for the + wipe, and a reward that silently gives less than it says is the worse failure. +- **The for-good grant is scoped to the step's server**, not every server. An author who wants it everywhere + adds a step per server. + +### 12.7 Where the work lands, and in what order + +1. **Website (core), on `main`:** §12.3, MODULE_API **1.13.0 → 1.14.0**, with its tests (`registries`, + `spec`, the editor's multi-select) and Module-uo's frozen-manifest proof against the new core, as 1.11.0's and + 1.13.0's were run. +2. **Module-Rust, on `edge`**, after the website PR merges: the option source, the two actions, the two + budgets; `coreApi` `^1.14.0`; `ci/core-ref.json` re-pinned to the merge; `routes.manifest` regenerated. +3. **docs:** this section's *Built* notes; `website/MODULE_API.md`; `website/EVENTS.md`. + +**Not touched:** Rust-Plugins, Rust-Link, the protocol, the installer, Android-app (the app does not author +events) and Module-uo. The Integration Kit's pin is at 1.12.0 and moves only when someone re-reads its +chapters for a newer core; it skipped 1.13.0 the same way. + +### 12.8 Decisions of record + +Answered by the org lead on 2026-10-12. + +| # | Decision | Rejected | § | +|---|---|---|---| +| **D342** | **An event reward may be any permission the server's plugins registered**, picked from the catalogue grouped by the plugin that registered it and sorted by plugin, then permission. `rust.backpack.size` stays beside it, with its sizes and its take-back warning. | A fixed list of perks; folding the backpack step into the generic one. | 12.2 | +| **D343** | **No blocklist and no allowlist. The safety is a required "I am sure" toggle in the step**; unticked, the dry run and the run refuse, naming the permissions. | A pop-up when the step is saved (a core change); a blocklist of dangerous permissions. | 12.2, 12.4 | +| **D344** | **One step gives several permissions, through a multi-select picker**: a `multiple` flag on an action param, added to core as MODULE_API 1.14.0. | One permission per step; five fixed picker slots; a comma-separated text box. | 12.3 | +| **D345** | **A permission reward lasts until the run ends, or for good**, the author's choice. | Until the run ends only. | 12.4, 12.5 | +| **D346** | **The two durations are two steps**, `rust.perm.lend` (`change`) and `rust.perm.give` (`irreversible`), each with its own budget, because core gives an action one risk class. | One step with a "keep it" switch, rated `irreversible` throughout. | 12.4, 12.5 | + +### 12.9 The walk (deferred to the end, D331) + +On both rigs, with a plugin that registers a perk (NightVision or similar): + +- open the picker and see it grouped by plugin, sorted inside each, a permission missing from a server noted, + and the unowned ones grouped by prefix; +- run a *Permissions until the event ends* step with two permissions, unticked (refused) and ticked; see the + perks work online and offline at the grant, and gone after the run ends, except for a player who already held + one; +- run a *Permissions for good* step; see it stay after the run ends, show in the permission manager with its + note, and go when staff delete it; +- unload the plugin and see the step refuse the permission by name.