docs(modules): the module-rust plan — 18 decisions of record and a 21-phase schedule #249
@@ -1,6 +1,6 @@
|
||||
# `module-rust` — the plan
|
||||
|
||||
**Status:** approved in outline 2026-09-15, not started. Three decisions of record settled; one
|
||||
**Status:** approved in outline 2026-09-15, not started. Five decisions of record settled; one
|
||||
question open (§3).
|
||||
|
||||
The [dry run](../rust-dryrun.md) designed this module on paper and deliberately did not build it.
|
||||
@@ -112,6 +112,60 @@ Two details from [kit][kit] ch. 5 that this depends on and are easy to get wrong
|
||||
`budgetMs`, our own `retry: false` is unreachable code. Derive one constant from the other and
|
||||
assert the inequality in a test. The first module this project shipped had exactly that pairing.
|
||||
|
||||
### R4 — Rust reaches an operator through the existing installer, behind `--game`
|
||||
|
||||
**Decided 2026-09-15 (org lead).** Not a second binary and not a shared-core refactor: the shipped
|
||||
[`installer`](../../installer/PLAN.md) grows a game dimension, `--game servuo|rust`, and keeps one
|
||||
release stream, one `doctor`, one `update`, one `uninstall`.
|
||||
|
||||
The shape of the work is set by where the coupling already is. **The reusable half is already
|
||||
game-agnostic** — `src/service.rs` and `src/main.rs` mention ServUO zero times, and `net.rs`,
|
||||
`diff.rs`, `paths.rs` and `ui.rs` barely more. **The UO-specific half is concentrated in four files**
|
||||
— `install.rs`, `doctor.rs`, `overlay.rs`, `tier.rs` — plus the bundle manifest, where
|
||||
`OverlayComponent` and `ServUoCompat` name the game in the schema itself.
|
||||
|
||||
So the change is: make the bundle's game payload a **variant** rather than an overlay, and
|
||||
`ServUoCompat` a per-game compat block. That is a schema change on the published **`bundles`**
|
||||
branch, and it is the part to design before touching code.
|
||||
|
||||
**The Rust payload is much simpler than the UO one**, which is what makes this affordable: no source
|
||||
tree to overlay, no `patches/`, no opt-in patch tier — a Rust install is a `.cs` file dropped into
|
||||
`oxide/plugins/`, plus the sidecar and its service, which the installer already knows how to do. What
|
||||
it gains instead is a **prerequisite check**: is Oxide installed, and is its build current enough.
|
||||
That is `doctor`'s shape, not a new concept.
|
||||
|
||||
The protocol pairing check generalises unchanged. `servuo-plugins/overlay.toml` declares the protocol
|
||||
the overlay speaks and the installer refuses to pair a disagreeing sidecar; `rust-plugins` needs the
|
||||
same declaration under whatever name the variant gives it, and the refusal is the same refusal.
|
||||
|
||||
### R5 — Teams come from Rust's first-party clans; the uMod Clans plugin is the richer tier
|
||||
|
||||
**Decided 2026-09-15 (org lead).** Basic functionality is built on **Rust's own clan system**; the
|
||||
third-party **uMod Clans plugin** is an optional layer for a much richer experience, and lands in
|
||||
phase 9 rather than phase 6.
|
||||
|
||||
The distinction matters more than the names suggest, so it is worth stating precisely:
|
||||
|
||||
- **Rust's first-party clans** are seven hooks in [`HOOKS.md`](HOOKS.md) — `OnClanCreated`,
|
||||
`OnClanDisbanded`, `OnClanMemberAdded`, `OnClanMemberKicked`, `OnClanMemberLeft`, plus colour and
|
||||
logo — each handing you a `LocalClan`. **All seven are "no return behavior"**, which is exactly
|
||||
what a read-only bridge wants: there is nothing to abstain from, so [kit][kit] ch. 4's
|
||||
return-`null`-from-every-veto rule has no work to do here. They need no plugin, and the rig is
|
||||
already running them (`D:\rust\server\server1\clans.287.db`).
|
||||
- **Rust's first-party *Teams* are a different system** — twelve hooks, mostly vetoable — and are the
|
||||
transient in-game squad, not the persistent organisation. They are **not** what core's Team
|
||||
provider should be fed. Clans is the right choice and this is why.
|
||||
- **The uMod Clans plugin's API is not in our mirror.** [`HOOKS.md`](HOOKS.md) is the *game's* 477
|
||||
hooks; a plugin's own API is its own documentation. It is reached through `[PluginReference]` and
|
||||
is null when absent, making it an R3-shaped dependency that must refuse with a reason. It is also
|
||||
not currently staged locally and would need pulling and mirroring.
|
||||
|
||||
**The one gap to design around:** the seven first-party hooks carry created, disbanded, added,
|
||||
kicked and left — but **no promote or leader-changed event**. Core's provider requires
|
||||
`getTeamLeaders`, so leadership is read off `LocalClan` at snapshot time rather than tracked from
|
||||
transitions. That makes phase 6 *partly* snapshot-driven where the dry run predicted it would be
|
||||
fully event-driven — a small correction to that document, recorded here rather than silently.
|
||||
|
||||
## 3. Open question
|
||||
|
||||
**Which other uMod plugins are in the required base set?** Kits is named. The rest of "a couple" is
|
||||
@@ -129,10 +183,13 @@ means R2's mechanism can be exercised on day one.
|
||||
|
||||
Two traps recorded here because both cost time before they were understood:
|
||||
|
||||
- **`D:\rust\start.bat` updates the wrong directory.** It runs
|
||||
`steamcmd +force_install_dir c:\rustserver\ +app_update 258550` and then launches
|
||||
`D:\rust\RustDedicated.exe`. The server that boots has never been updated by its own script. That
|
||||
- **`D:\rust\start.bat` updated the wrong directory — fixed 2026-09-15.** It ran
|
||||
`steamcmd +force_install_dir c:\rustserver\ +app_update 258550` and then launched
|
||||
`D:\rust\RustDedicated.exe`. The server that boots had never been updated by its own script, which
|
||||
is why a second, never-booted install exists at `C:\rustserver` and why `D:\rust` is a wipe behind.
|
||||
Now reads `+force_install_dir d:\rust\`; the original is kept at `D:\rust\start.bat.bak`.
|
||||
`D:\rust\steamapps\appmanifest_258550.acf` was already present at the same buildid, so the first
|
||||
corrected run is a delta to the current wipe rather than a 5.9 GB re-download.
|
||||
- **`C:\oxide_files` is a 2025-04-23 Oxide and must not be copied anywhere.** Oxide ships a patched
|
||||
`Assembly-CSharp.dll`; that bundle's is 6,842,880 bytes against the live 9,780,224, so copying it
|
||||
over a real install is a hard downgrade. `D:\rust` is already correct and needs nothing from it.
|
||||
@@ -143,22 +200,24 @@ re-checks it.
|
||||
|
||||
## 5. The phases
|
||||
|
||||
Phases 0–3 produce a working read-only Rust site. 4–5 are the permissions product. 7–8 are events.
|
||||
9 is the optional tier. Each phase ends with its findings written down, as every workstream here does.
|
||||
Phases 0–3 produce a working read-only Rust site. 4–5 are the permissions product. 6 is Teams. 7–8
|
||||
are events. 9 is the optional tier. 10 is how any of it reaches somebody who is not us. Each phase
|
||||
ends with its findings written down, as every workstream here does.
|
||||
|
||||
| # | Phase | Repos | Done when |
|
||||
|---|---|---|---|
|
||||
| 0 | **The rig.** Fix `start.bat`, update to the current wipe, confirm the Oxide build still matches, install the base set, prove a grant made at the console is visible to a plugin | docs | A current server boots with the base mods loaded and `oxide.grant` demonstrably gates something |
|
||||
| 0 | **The rig.** Update to the current wipe (the script is fixed), confirm the Oxide build still matches, install the base set, prove a grant made at the console is visible to a plugin | docs | A current server boots with the base mods loaded and `oxide.grant` demonstrably gates something |
|
||||
| 1 | **Protocol 1 and three skeletons.** Bounded drop-oldest queue, one writer thread, reconnect with a tagged epoch, dial-out; sidecar listener + SQLite + always-on token auth + version header; module from `template/` | all 3 + docs | One hello line travels game → sidecar → module, and killing the sidecar does not stall the game |
|
||||
| 2 | **The read path.** First hook wave from [`HOOKS.md`](HOOKS.md); events and snapshots kept distinct at the wire; `wipe_id` everywhere; every board re-emitted on connect | all 3 + docs | A restarted sidecar is fully populated within one connection, with no negotiation |
|
||||
| 3 | **The first pages.** Server list, per-server status, killfeed, leaderboard; `capabilities`; nav | Module-rust | The site renders the last thing the game said while the game is off |
|
||||
| 4 | **Identity** (R1) | 3 + docs | A player links an account in-game and the site names their Steam ID |
|
||||
| 5 | **Site-owned permissions** (R2). Groups and grants authored on the site; full set pushed on connect, deltas after; drift reported | all 3 + docs | A grant made on the website gates a third-party plugin in-game, and survives a wipe |
|
||||
| 6 | **Teams provider.** Event-driven with a baseline on connect — Rust delivers membership transitions in real time, so no sweep | Module-rust + 2 | Core's reconciler is answered from live transitions, `complete` claimed only per reachable server |
|
||||
| 6 | **Teams provider** (R5). Rust's first-party clans; membership event-driven, leadership read off `LocalClan` at snapshot | Module-rust + 2 | Core's reconciler is answered from live transitions, `complete` claimed only per reachable server |
|
||||
| 7 | **Events: one budget, one lease.** [kit][kit] ch. 5's own ordering — the lease before the action | Module-rust + 2 | The leased value is observed changing in the running game and restored, per key |
|
||||
| 8 | **Events: the Kits reward action** (R3) | all 3 | A retried step grants loot once, and the ledger and the world agree |
|
||||
| 9 | **Optional mod integrations.** One adapter per plugin, each detecting via `[PluginReference]` and degrading to absent | rust-plugins + docs | A server missing every optional mod still runs the module |
|
||||
| 10 | **Docs, kit feedback, cutover** | docs + Integration-kit | `docs/` describes what shipped; R2's missing chapter answered either way |
|
||||
| 9 | **Optional mod integrations**, the uMod **Clans** plugin first (R5). One adapter per plugin, each detecting via `[PluginReference]` and degrading to absent | rust-plugins + docs | A server missing every optional mod still runs the module, Teams included |
|
||||
| 10 | **The installer** (R4). `--game servuo|rust`, the bundle payload as a variant, an Oxide prerequisite check in `doctor`, the protocol pairing refusal carried over | installer + docs | An operator sets a Rust server up with the released binary and nothing hand-copied |
|
||||
| 11 | **Docs, kit feedback, cutover** | docs + Integration-kit | `docs/` describes what shipped; R2's missing chapter answered either way |
|
||||
|
||||
### Why the lease comes before the reward action
|
||||
|
||||
|
||||
Reference in New Issue
Block a user