docs(rust): plan fixes — D169-D172, the org lead's answers

D169 every fix and redesign lands before Module-Rust's cutover. D170 an
expired zone is marked expired on the site (F14 moves from "by design"
to a fix, amending D96). D171 Steam sign-in moves to the website's own
plan as part of the base website. D172 chat titles use all twenty-three
listed conditions under new names: §4.6 is now a table of condition,
title and the hook that feeds it, plus what the detailed plan must
settle (best-value columns, weapon classes, vehicle-kill credit, heals
of others, quests). PLAN.md §35 and the rust README follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-09-26 16:26:58 -05:00
parent dc6c5a72fd
commit 7834ef1a88
3 changed files with 90 additions and 44 deletions

View File

@@ -1,14 +1,15 @@
# `module-rust` — plan fixes
**Status:** plan, awaiting the org lead's approval, 2026-09-26. **The gate before Module-Rust's cutover
(phase 19, [`PLAN.md`](PLAN.md) §34, D145).** Everything here comes from the first walk of
**Status:** plan, awaiting the org lead's approval, 2026-09-26; its four open questions were answered the same
day (D169–D172). **Everything in it — fixes and redesigns alike — lands before Module-Rust's cutover
(phase 19, [`PLAN.md`](PLAN.md) §34, D145; D169).** Everything here comes from the first walk of
[`PLAYER_WALK.md`](../../rust-link/PLAYER_WALK.md) with a real player in the game — the Oxide pass, walked by
the org lead on the `rust-oxide` rig with every frame checked on the console, the sidecar and the site's
database. The Carbon pass has not been walked yet and may add to this document.
This is a companion to [`PLAN.md`](PLAN.md), not a replacement: its decisions continue PLAN.md's numbering
(D159 onward), and where the two disagree this one is later and wins. It has three kinds of content, and
they gate the cutover differently (§6):
all of it lands before the cutover (D169, §6):
- **Fixes** (§2) — the plugin, the sidecar or the site does something other than what the plan and the
protocol say. Fifteen, F1–F15.
@@ -51,8 +52,10 @@ Taken by the org lead during and straight after the walk.
| **D166** | **The zone step offers ZoneManager's own flags and settings**, read from the installed ZoneManager, not a hard-coded list. |
| **D167** | **ZoneDomes is an optional dependency.** The zone step gains a "show a dome" option; the bridge adds the dome and removes it with the zone. |
| **D168** | **When a plugin we depend on does not expose what we need, we may write a small helper plugin for it.** It ships from Rust-Plugins beside the bridge, is optional, and is detected at hello like the other integrations. The bridge keeps calling only public APIs (R2); the helper is where a gap in someone else's plugin gets bridged. |
**One more, leaning but not yet decided:** Steam as a first-class sign-in provider on the website (§4.8).
| **D169** | **Every fix and every redesign in this document lands before Module-Rust's cutover (phase 19).** Rejected: cutting over after the fixes and following with the redesigns. |
| **D170** | **An expired zone is marked expired on the site.** Once F13 makes `world.expired` recognisable, ingest marks the run's resource row `expired`. This amends D96's "the website maps it to nothing". F14. |
| **D171** | **Steam sign-in belongs to the website's own plan and is part of the base website**, exposed to modules however they need it. It leaves this document (§4.8). |
| **D172** | **Chat titles use all twenty-three conditions the org lead listed, under new names** (§4.6). Rejected: a first subset. |
## 2. Fixes
@@ -155,13 +158,11 @@ or map prefab to label on the site — whichever the killfeed page already expec
empty for players other plugins spawn; the plugin already avoids it for `entity.destroyed`. Use
`((ulong)player.userID).ToString()` at all 13 sites. Real connected players are unaffected.
### 2.4 By design — a question, not a fix
**F14 — the site never learns that a zone expired.** Expired zones stayed `confirmed` on the run console until
the runs were cancelled, when teardown found them "already gone" and counted that a success. PROTOCOL.md §15
says this is intended: "The website maps it to nothing. Core learns about it through `reconcile` and `revert`"
(D96). **The question:** should ingest mark the row `expired` once F13 makes the frame recognisable, so the
console stops showing a live zone that is gone? Recommended: yes.
**F14 — the site never learns that a zone expired.** *(Module-Rust)* Expired zones stayed `confirmed` on the run
console until the runs were cancelled, when teardown found them "already gone" and counted that a success. This
was the design — PROTOCOL.md §15: "The website maps it to nothing. Core learns about it through `reconcile` and
`revert`" (D96) — and D170 changes it. *Fix:* after F13, ingest maps `world.expired` to the run's resource row
and marks it `expired`; teardown treats `expired` as already reverted; amend PROTOCOL.md §15.
## 3. Interface and walk-doc corrections
@@ -257,15 +258,50 @@ A per-type marker setting for staff, defaulting to the major monuments. Decide i
monument sweep or the site does the filtering; the type (`monument_substation`, cave, …) is already in the
monument data.
### 4.6 Chat titles: more conditions
### 4.6 Chat titles: twenty-three conditions (D172)
The org lead's list, as inspiration (the names are examples): animal, bow, melee, blade, revolver, NPC, APC and
helicopter kills; headshots; players killed; PvP and PvE kill distance; wood, ore and plants gathered; clothes
and weapons crafted; structures built and repaired; players healed; rockets fired; explosives thrown; quests
completed. Most need new counters in the plugin — the weapon class on a kill, headshots, crafting by category,
heals, building and repair, explosives — each a hook, a tally field and a leaderboard column. D159's gathering
counters feed the gathering titles. Quests need a quest plugin, and none is installed. The plan picks the first
set; the rest follow.
All of the conditions the org lead listed, each under a new title. The titles are proposals; the conditions are
decided. Every hook below is in uMod's catalogue ([`HOOKS.md`](HOOKS.md)); each is declared `void`, as the read
path requires (PROTOCOL.md §8.7), and each must be checked on Carbon, whose catalogue renames some.
| Condition | Title | Where the count comes from |
|---|---|---|
| Animal kills | **Beastbane** | `OnEntityDeath`, victim a `BaseAnimalNPC` — today folded into `npcKills`; split it |
| NPC kills | **Exterminator** | `OnEntityDeath`, victim a human NPC (scientists, bandits) — the other half of that split |
| Players killed | **Reaper** | the existing `kills` column (`player.death` with a player attacker) |
| Bow kills | **Fletcher** | a kill whose weapon is a bow or crossbow — the weapon shortname the killfeed already carries |
| Melee kills | **Brawler** | a kill with any `BaseMelee` weapon |
| Blade kills | **Duelist** | a kill with a bladed melee weapon (machete, swords, knives) — a named list |
| Revolver kills | **Outlaw** | a kill with the revolver or the python |
| Headshots | **Crownbreaker** | a killing blow with `HitInfo.isHeadshot` |
| PvP kill distance | **Longshot** | the *longest* single player kill — the `distance` the killfeed already carries |
| PvE kill distance | **Marksman** | the longest single NPC or animal kill |
| APC kills | **Tankbreaker** | `OnEntityDeath`, victim a `BradleyAPC`, credited to the killing player |
| Helicopter kills | **Rotorbane** | `OnEntityDeath`, victim a patrol helicopter (and the CH47), credited to the killing player |
| Wood gathered | **Timberwright** | `gathered.wood`, all three sources (D159) |
| Ore gathered | **Prospector** | `gathered` metal, sulfur and high-quality ore, all three sources |
| Plants gathered | **Greenhand** | `OnGrowableGathered` plus picked-up plants (hemp, berries, mushrooms, corn, potatoes, pumpkins) |
| Clothes crafted | **Seamster** | `OnItemCraftFinished`, item category attire |
| Weapons crafted | **Armorer** | `OnItemCraftFinished`, item category weapon |
| Structures built | **Mason** | `OnEntityBuilt` placing a building block |
| Structures repaired | **Tinkerer** | `OnStructureRepair` |
| Players healed | **Mender** | healing *another* player — `OnHealingItemUse` on someone else, and `OnPlayerRevive` |
| Rockets fired | **Artillerist** | `OnRocketLaunched` |
| Explosives thrown | **Demolitionist** | `OnExplosiveThrown` and `OnExplosiveDropped` |
| Quests completed | **Wayfarer** | a quest plugin — none is installed; choose one, or a D168 helper |
What the detailed plan has to settle before building:
- **Two kinds of column.** Every existing leaderboard column is a sum; the two distance titles need a *best* (the
largest single value). The tally carries the interval's maximum, and the site keeps the running best.
- **What a weapon-class kill counts.** Recommended: any kill the player is credited with — players, NPCs and
animals — so Fletcher rewards a hunter as well as a raider. The weapon lists (bow, blade, revolver) are named
sets in one place, so a Rust update adds a weapon by editing a list.
- **Credit for a vehicle kill.** The APC and the helicopter are usually killed by several people. Credit the player
who dealt the killing blow (`HitInfo.InitiatorPlayer`) — simple, and the rule the killfeed already uses.
- **Healing someone else**, never yourself — otherwise Mender is earned by bandaging after every fight.
- **Counters are aggregates** (rule 2): all of these ride on `player.tally`, never one frame per shot, craft or swing.
- **Quests** wait on a quest plugin; the other twenty-two do not.
### 4.7 NPCs
@@ -273,12 +309,12 @@ Research before deciding: which free uMod NPC plugins are maintained and expose
name and a behaviour, against extending `rust.npc.place` to dress the stock scientist from a Kits kit and give it
a display name. The goal the org lead named is server customisation — NPCs with different kits and names.
### 4.8 Steam sign-in (website, not this module)
### 4.8 Steam sign-in (moved to the website's plan, D171)
Steam as a provider beside Google and Discord. It is OpenID 2.0, not OAuth2/OIDC, so it needs its own adapter.
Leaning toward both halves: link-only sign-in (SSO never provisions an account), and linking Steam also creates
the Rust link, since it proves the Steam id more strongly than a code does. Belongs in `website/` with its own
plan; noted here because it changes R1's identity story.
Steam as a sign-in provider beside Google and Discord is part of the base website and is planned there, not
here. What this module needs from it: a way to learn a signed-in account's verified Steam id, so linking by
Steam can stand beside the in-game code (R1). Steam is OpenID 2.0, not OAuth2/OIDC, so the website needs its
own adapter.
## 5. Protocol
@@ -290,20 +326,23 @@ once:
- A plugin-loaded / unloaded frame (F8).
- The permission inventory verb and its reply (§4.1).
- ZoneManager's flags at hello, zone options on `world.zone`, the dome option (§4.4).
- New tally fields for D159 and the first title counters (§4.6).
- New tally fields: D159's gathering sources, the animal / NPC split, weapon-class kills, headshots, crafting,
building, repairs, heals, rockets, explosives, and the two per-interval maxima for kill distance (§4.6).
- `world.expired` handled by the site (F14, D170) — no wire change beyond F13's.
F15 changes no message shape and ships ahead of the bump.
## 6. Order, and what gates the cutover
1. **Before anything else:** F15 (data corruption), and F9 + F10 (it throws away edits). Small, no protocol change
for F15.
2. **Protocol 13 with the remaining fixes:** F13, F12, F1, F3, F8, F7, F2, F4, and F5/F6 in the module.
3. **The redesigns** (§4.1, §4.3, §4.4) — each its own phase, planned in detail before code.
**All of it lands before Module-Rust's cutover (D169).** The order inside that:
**Recommended gate:** steps 1 and 2 land before Module-Rust's cutover (phase 19); the redesigns follow it.
Nobody outside the org runs module-rust yet, so the cutover should not carry known corruption or silent
rollbacks, but it need not wait for the new screens. Open for the org lead.
1. **First:** F15 (data corruption), and F9 + F10 (they throw away edits). Small; F15 changes no message shape.
2. **Protocol 13 with the remaining fixes:** F13, F14, F12, F1, F3, F8, F7, F2, F4 in the bridge and the module,
and F5/F6 in the module.
3. **The redesigns**, each planned in detail before code and walked on both frameworks: the permission manager
(§4.1), the event step editor with the kit-weekend template (§4.2, §4.3), zones and domes (§4.4), the map's
marker types (§4.5), the chat title conditions (§4.6), and the NPC research (§4.7).
4. **The cutover**, once the re-walk (§8) passes on Oxide and Carbon.
## 7. Helper plugins (D168)
@@ -327,12 +366,19 @@ After step 2 of §6, on both frameworks:
- A zone restarted with a player standing in it, and a zone opened around a player (F12).
- A zone left to expire, checked by `kind=world.expired` (F13).
- A plugin unloaded, a grant made, the plugin loaded again — the grant lands within a minute (F8).
- An expired zone reads `expired` on the run console (F14).
- Each redesign's own walk, and every title condition earned at least once.
- Then the steps still owed: the Carbon subset, player step 8 and the map walk with a second player, events step
7 with several players, the phone walks.
## 9. Open questions for the org lead
## 9. Answered, and what stays open
1. The cutover gate in §6 — fixes before phase 19, redesigns after?
2. F14 — should ingest mark an expired zone `expired` (recommended), or keep D96's "maps it to nothing"?
3. Steam sign-in (§4.8) — both halves, and does it belong to this workstream or to `website/`?
4. Which title conditions form the first set (§4.6)?
The four questions this plan first asked were answered on 2026-09-26: the gate (D169), F14 (D170), Steam sign-in
(D171) and the title conditions (D172).
Left for the detailed plans of §6 step 3, none of them blocking the fixes:
1. The quest plugin behind Wayfarer — or a D168 helper.
2. Whether the plugin or the site filters monument types (§4.5).
3. The ZoneDomes default type and stack, chosen by looking at each on the rig (§4.4).
4. The NPC route — a plugin, or extending `rust.npc.place` (§4.7).