docs(rust): RaidableBases built (PROTOCOL §20.12, PLAN_REDESIGNS §11.4.6, D391, MODULE_API 1.16.0)

- rust-link/PROTOCOL.md §20.12: RaidableBases — hello's raidableBases, the
  five raid.* events, the tally's raidsCompleted, map.live's raids layer (and
  its §17.3 row), raid.spawn and raid.check with their refusals, the raids
  helper, the profiles in the configuration editor, the sidecar's two
  forwards and the website's step, feed, map layer and Warlord.
- modules/rust/PLAN_REDESIGNS.md: D391 (the editor's spot check is advice
  through core), §11.4.6 (what was built, the rig tests on both rigs, the
  Oxide rig's RaidableBases fault that a restart cleared, twelve points
  settled in code, the end walk's rows), and the order of work.
- website/MODULE_API.md, EVENTS.md: 1.16.0, a dry run's detail.warnings.
- website/STEAM_SIGNIN.md: its contract moves to 1.17.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-16 19:29:02 -05:00
parent a9bc2c5feb
commit 9eb6a2edc4
5 changed files with 340 additions and 7 deletions

View File

@@ -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). On 2026-10-16 the org lead added console commands as event steps and rewards (§16, D382–D384), to be built before RaidableBases; **they were built the same day (§16.10)**, with D385. The same day the paid RaidableBases copy arrived; **its spike ran on both rigs (§11.4.2)**, with D386 and four questions, Q33–Q36, answered the same day (D387–D390). **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; **they were built the same day (§16.10)**, with D385. The same day the paid RaidableBases copy arrived; **its spike ran on both rigs (§11.4.2)**, with D386 and four questions, Q33–Q36, answered the same day (D387–D390). **§11.4 was built the same day (§11.4.6)**, with D391, core's dry-run advice. **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
@@ -925,7 +925,7 @@ pulls) is research, not a walk, so it still comes first:
7. **The first-class optional plugins** (§11): Economics, then Backpacks, then RaidableBases, each starting
with its source pull and rig spike. Economics and Backpacks were built on 2026-10-12. **RaidableBases waited
for the current paid copy (D347)** and is built for both editions (D348). **The copy arrived on 2026-10-16
and was spiked on both rigs the same day (§11.4.2)**, with D386. Q33–Q36 were answered the same day (D387–D390).
and was spiked on both rigs the same day (§11.4.2)**, with D386. Q33–Q36 were answered the same day (D387–D390). **Built the same day (§11.4.6)**, with D391.
8. **Permissions as event rewards** (§12), built on 2026-10-12.
9. **Rust's own world events and six more optional plugins** (§13, §14). First comes one test phase covering
all of them on both rigs (§14.2, D352); **it ran on 2026-10-14 (§14.2.1)**. Then one integration plan,
@@ -1109,6 +1109,7 @@ The org lead answered §14.3's Q26–Q32 on 2026-10-14:
| **D388** | **The area check refuses for buildings, deployables, raid bases, players and water; rocks and cliffs are a warning (Q34).** The plugin's own reason is read from its message queue and shown in the editor and the run log, and a spot with only rocks or cliffs near it still spawns. This amends D208. | Refusing on every reason the plugin gives (D208 as written); advice only. | 11.4.2, 11.4.5 |
| **D389** | **"In water" is refused unless the chosen profile is built for it (Q35):** its seabed chance is above 0, or it allows bases to float above water. Both are read from the profile the step picked. This amends D208. | Always refusing water. | 11.4.2, 11.4.5 |
| **D390** | **Lockouts are dropped from the feed (Q36).** Neither edition raises a hook for them. On the paid edition the feed gains a line when a player buys a base (`OnRaidableBasePurchased`) and when an admin locks one to a player (`OnRaidableBaseLocked`), naming the player only for the presence audience (D376). This amends D202. | Dropping them with no new lines; reading lockouts out of the plugin's data by reflection. | 11.4.2 |
| **D391** | **The editor's spot check is advice through core (2026-10-16, asked during the build).** Core's dry run gains a warning channel, MODULE_API 1.16.0: a module answering `verify: true` with `{ ok: true, detail: { warnings } }` has each line shown as a warning finding that never fails the dry run. A raid base's spot refused now, and the rocks and cliffs beside it (D388), show there as advice; the run itself checks again. Steam sign-in moves to 1.17.0. | A blocked spot failing the dry run, with rocks and cliffs only in the run log; no editor check. | 11.4.6 |
Console commands as event steps and rewards, added on 2026-10-16, were decided as D382–D384, and D385 during the build; they are tabled with their section, in §16.7.
@@ -1969,6 +1970,110 @@ The org lead answered §11.4.4's three questions the same day:
runs the same check through the bridge when the step is saved. That is advice only, since the world
can change before the event runs.
#### 11.4.6 Built (2026-10-16)
Built the day the paid copy was spiked, on the org lead's go, in one pass across the repos. The branches are
`feat/raidable-bases` in Rust-Plugins, Rust-Link and Module-Rust (into `edge`) and in the installer (into
`edge`), `feat/dry-run-warnings` in the website (into `main`), with this record on `docs/raidablebases-build`.
The specification is [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.12. The walk rows join the end walk (D331,
D359), below.
**D391 was asked during the build.** D208 makes the editor's spot check advice, and D388 shows rocks and cliffs
as a warning in the editor; core's dry run took only errors from a module. So core gained a warning channel:
MODULE_API 1.16.0 (`detail.warnings` under `verify: true`), and Steam sign-in moves to 1.17.0. §10 has the
decision.
**What was built:**
- **The raids helper, `RunicGatewayRaids.cs`**, beside the bridge and installed by default. Three Harmony
patches (the spawn queue's `Add`, `OpenEvent`, each base's `BypassUseOwners()`), a spot route that checks
and pastes, one base's despawn, and the profiles' watched reload. Both editions: the paid spawn call where it
is, the free edition's private typed one otherwise, every member looked for by name and the state reported.
- **The bridge:** hello's `raidableBases`; five `raid.*` events from the lifecycle hooks; the tally's
`raidsCompleted`; the map's `raids` layer; `raid.spawn` and `raid.check`; a run's base in the world registry
so `world.revert`, `world.owned` and `world.expired` serve it; and the profile folder in the editor.
- **The sidecar:** two thin forwards.
- **Module-Rust:** the step `rust.raidbase.spawn`, its option sources and budget, the feed's lines, the map
layer with its own audience, Warlord (title category, leaderboard column), the servers-page line, and the
profile files in Mod configuration.
- **Core:** the dry run's advice (D391). **The installer:** the helper's line in `doctor`.
**Tested on the rigs** (Rust 25902425, Oxide 2.0.7830, Carbon 2.0.262, paid RaidableBases 3.2.2, CopyPaste 4.3.0, the
tier-3 pack), through a throwaway plugin calling the bridge's handlers (no sidecar):
| Row | Result |
| --- | --- |
| Hello: edition, the spawn call, difficulties and profiles as the pack names them, the helper patched, the grid | ✓ |
| A spawn refused while the grid builds (`rb-not-ready`), and an unknown difficulty (`bad-difficulty`) | ✓ |
| The plugin's pick, Hard, PvP off, 2 NPCs, despawn 30 min, locked: tagged, on the registry and the map with its run; PvP off despite the pack's *Convert PVE To PVP*; `BypassUseOwners()` false; despawn 30 min | ✓ |
| The shared profile untouched after it (PvP still on) | ✓ |
| A spot, `raidmed3`, +5 m, NPCs off, lock off: landed 1 m from the spot, blocks from 5.9 m on ground −0.3, no NPCs, the config's lock | ✓ |
| The spot check: a deployable refused with the plugin's reason; a rock a warning; 0.9 m of water refused for a profile not built for it | ✓ |
| A repeat key: nothing spawned | ✓ |
| `world.revert` of the run: its two bases despawned, an untagged base left standing | ✓ |
| A run's base the plugin despawned (`rbevent despawnall`): off the registry by the expiry path | ✓ |
| Profiles in the editor: listed under the virtual path; a good edit reloaded and settled; a bad one (`"Difficulty Level": "not a number"`) failed to load, rolled back and reloaded the old file | ✓ |
| A RaidableBases unload with a run's base out: the base expired with `raidablebases-unloaded` | ✓ |
**A finding, and a change made on it.** The first build copied a spawn's profile through a JSON round trip, as
the spike had, into a fresh `BaseProfile`. That loses the profile's loot list, its spawn table and every value
the plugin computes after reading a profile, so the copy is now member by member, with the three objects a
spawn's settings write into copied again (`CopyProfile`). It was found while chasing the Oxide rig's fault
below, which turned out not to be ours.
**The Oxide rig.** After several hours of uptime and the spike's own reloads, **paid RaidableBases broke on every spawn on the
Oxide rig, with none of our plugins loaded**: `rbevent Easy` and `rbevent Hard` each threw
`ArrayTypeMismatchException` from a coroutine and left a half-made base that made its damage, SAM site and
trigger hooks throw on every tick. The Carbon rig, the same plugin and pack, was clean. **A server restart
cleared it.** On the restarted rig the pick with every setting, a spot at an exact height of 30 m (blocks from
30.9), the spot check, revert and the profile editor (good and broken) all passed as on Carbon; the unload row
had passed on Oxide before the fault. The repeat key and the despawn-all expiry were run on Carbon only. The cause is RaidableBases', in that process; the end walk starts both rigs fresh. **`rb-busy`
was met for real**: a spot asked for while the plugin's pick was still pasting.
**Not tested on the rigs, and owed by the end walk:** a raid completed by a player (the tally, *Warlord*, the
feed's names), a purchase and an admin lock (`raid.purchased`, `raid.locked`), the never-started expiry, the
free edition (D348: the end walk covers a rig with each edition), and the whole path through the sidecar and
the site.
**Settled in code** (each can be overruled):
1. **"Queued" is the step's success.** The plugin pastes in its own time, so the step answers when the base is
on its way; where it went reaches the map, the feed and the run's resource when it opens. A run's base not
open fifteen minutes after it was asked for never will be: its request is dropped and the resource expires,
`never-started`.
2. **A base not made by the helper is not the run's.** Without the helper the step records no resource and
the run log says the base despawns on its own timer; with no difficulty named, the paid call picks one of
the plugin's own at random.
3. **A spot is a world call's location** (a monument with offsets, or x and z), so the step's location fields
are the other world steps'; `y` is ignored, and the height is `heightAdjust` or `height`.
4. **The spot check looks first, then asks the plugin.** The plugin's check stops at the first thing it finds,
so a rock found first would hide a player's hut. The helper's own look (players, a player's building or
deployable, a raid base within the profile's protection radius) runs before it. **Every reason the plugin
gives other than rocks and cliffs refuses**, roads, rivers and monuments among them, beyond D388's list.
A refused spot is permanent: the run pauses on its own `on_failure` rather than placing the base elsewhere.
5. **NPC count** is 0 to 20 (the bridge's world bound), in the profile's own mix of scientists and murderers,
and turns the profile's random amounts off. **Despawn minutes** sets both the total and the inactive timer.
6. **The base's file name is public** on the feed and the map, as its difficulty is: it is the operator's name
for a building, and the map's projection cannot tell staff apart. The run's key never leaves for any viewer.
7. **Warlord's credit** is the plugin's own participants (`GetRaiders()`), each once; the lock holder alone
when none is listed. The unload trap's completions (no owner, no raiders) are neither counted nor told.
8. **No engagement triggers** for raid bases: §11.4 asked for none. They can follow §14.3.9's pattern if wanted.
9. **`OnRaidableBaseDespawn` and `PrivilegeDestroyed` are not used**: `Ended` says a base is gone, and the
feed's completion carries the raid.
10. **The profile editor needs the patched helper and a built grid**, refuses a write that mixes profiles with
other files, and reloads with no player: the plugin then skips its base loot tables and its log line,
which a profile edit does not touch.
11. **`rb-busy`** (it pastes one base at a time) retries on core's ordinary delay; **`rb-not-ready`** in ten
minutes.
12. **A run's base id is the bridge's own**, `raid-<ms>-<n>`, so a zone's `rg-` id is never mistaken for one.
**The end walk's rows for §11.4**, added to §9's list, on both rigs and both editions: spawn by difficulty,
profile and base, where the plugin picks and at a spot with each height mode; each setting and the lock felt
in game; the dry run's advice for a blocked spot and a rocky one; teardown takes the run's base and no other; a
base despawned or raided first is marked `expired`; a raid completed by a player (feed, *Warlord*, the column);
a purchase and an admin lock on the paid edition; the map layer and its audience switch; a profile edited from
the site, good and broken; and the free edition without the helper (`rbevent`, which needs `console.plugins`).
### 11.5 Protocol
These are new verbs, events and hello fields. **They join protocol 14 (D332)**, which §2 opens: protocol 13