diff --git a/modules/rust/PLAN_REDESIGNS.md b/modules/rust/PLAN_REDESIGNS.md index 263ac71..b06ec30 100644 --- a/modules/rust/PLAN_REDESIGNS.md +++ b/modules/rust/PLAN_REDESIGNS.md @@ -930,7 +930,7 @@ pulls) is research, not a walk, so it still comes first: answered before code: **written on 2026-10-14 as §14.3**, from D361–D369, and answered as D370–D376. Then the build, in §14.3.11's nine stages (two of them core), §13 first. **Stage 1, §13, was built on 2026-10-15 (§14.3.14)**, with D378 and D379. **Stage 2, core's `retryAfterMs` (MODULE_API 1.15.0), was built on - 2026-10-15 (§14.3.15).** + 2026-10-15 (§14.3.15).** **Stage 3, TruePVE in the zone step, was built on 2026-10-15 (§14.3.16).** **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 @@ -2501,6 +2501,8 @@ helicopter from a run, then cancel the run and see both stay; a Guarded Crate cr #### 14.3.2 TruePVE in the zone step (D362, D368) +> **Built on 2026-10-15** (§14.3.16), specified in PROTOCOL.md §20.6. + **Hello.** `integrations.truePve`: `{ loaded, version }`. **The wire.** `world.zone` gains an optional **`truePve`**: `{ "exclude": true }` or `{ "ruleset": "pvp-arena" }` @@ -2933,6 +2935,47 @@ machine's installed `modules/`; client 422/422 and the build. **No walk rows of its own.** Stage 5's Dangerous Treasures rows exercise it: a step answering *"not ready"* shows *"retrying at …"* and runs then. +#### 14.3.16 Stage 3, built: TruePVE in the zone step (2026-10-15) + +Built in Rust-Plugins and Module-Rust as §14.3.2 planned it, specified in +[PROTOCOL.md](../../rust-link/PROTOCOL.md) §20.6. **No Rust-Link change**: the sidecar forwards `world.zone` +whole. `PROTOCOL_VERSION` stays 14. Builds: Rust 25902425, Oxide 2.0.7830, Carbon 2.0.262, TruePVE 2.4.6 on both +rigs. + +**Settled in the code, and overrulable:** + +1. **A ruleset name is trimmed and 1–64 characters**, checked by the site and the plugin alike; past that it + is passed as written, because TruePVE compares names case-sensitively (`r.name == ruleset` in 2.4.6). +2. **A server whose hello has not said whether it has TruePVE is left to the plugin** (a plugin before + protocol 14, or never reached), as a dome is, and the plugin answers `truepve-missing`. Only a hello that + says `loaded: false` is refused at verify. +3. **`exclude` refused by TruePVE answers `refused`, not `truepve-ruleset`.** It happens only when TruePVE has + not read its config or data yet; the zone is erased all the same. +4. **The mapping comes after the dome**, so a refusal erases both, as a failed dome erases its zone. +5. **Re-creation maps nothing again.** TruePVE keeps the mapping in its own data file, keyed by the zone id, + which re-creation keeps. +6. **Two erase paths beyond the plan's three are covered:** a zone `world.owned` finds ZoneManager no longer + holds, and a new wipe dropping the old map's entries. TruePVE's table outlives a wipe. +7. **Leftovers are also flushed at the bridge's own load**, not only at `OnPluginLoaded(TruePVE)`, so a bridge + reloaded after TruePVE came back does not wait for TruePVE's next reload. `truepve.json` is deleted when empty. +8. **Hello is sent again when TruePVE loads or unloads**, so the site's verify reads the current answer. +9. **`truepve-missing` and `truepve-ruleset` are permanent failures**, as `no-zone` is. +10. **Server settings' plugin line names TruePVE**: *"TruePVE 2.4.6 is loaded — a zone step may turn off PvP + protection or name a ruleset."* + +**One thing TruePVE does that we cannot change:** it saves its data file one second after a change, on its +own timer. A restart inside that second loses the change. For a removal that leaves a mapping for an `rg-…` id +no zone will have again, which changes nothing in game; D362's cleanup is otherwise complete. + +**Tests.** Module-Rust: five new in `test/zones.test.js` (exclude, `"true"` and a trimmed ruleset on the wire; +unset, off and blank send nothing; both, a long name and no TruePVE refused for good at verify and perform; a +hello that has not said is left to the plugin; `truepve-ruleset` and `truepve-missing` fail for good with the +plugin's words). Full suites: server 603/603 and every check (imports, bundle, swagger, engagement); client +84/84, the build and externals. The plugin compiled and loaded on both rigs, and `rg.world` read +`truePve=True truePveLeftover=0` on each; both rigs were put back on the protocol-13 bridge after. + +**Walk rows** are §14.3.2's, at the end walk (D331, D359). + ### 14.4 Questions for the org lead **Answered on 2026-10-14:** Q17 as proposed, out (D358). **Q18 against the proposal: the end walk waits for diff --git a/rust-link/PROTOCOL.md b/rust-link/PROTOCOL.md index ab148ec..ac95e50 100644 --- a/rust-link/PROTOCOL.md +++ b/rust-link/PROTOCOL.md @@ -1436,7 +1436,7 @@ five routes. | Command | Answers | | |---|---|---| | `world.monuments` | `world.monuments` | This map's monuments in one stable order (grouped by prefab short name, then by position). Each has `value` (`kind`, or `kind#n` when the kind repeats), `kind`, `instance`, `of`, `label` (the game's display phrase), `x`, `z` and `grid`. Also `worldSize`, the placeable `prefabs` (`key`, `kind`, `label`), `eventsEnabled`, `maxCrates`, `maxNpcs` and `zoneManager` | -| `world.zone` | `world.ok` or `world.error` | Opens a ZoneManager temporary zone owned by the bridge. Needs `runId`, `key`, a location, `radius` (5–150 m) and `holdMs` (1 minute to 7 days); `name` is optional. Protocol 13 adds `flags`, `settings`, `enterMessage`, `leaveMessage`, `delivery`, `format` and `dome` (§19.11) | +| `world.zone` | `world.ok` or `world.error` | Opens a ZoneManager temporary zone owned by the bridge. Needs `runId`, `key`, a location, `radius` (5–150 m) and `holdMs` (1 minute to 7 days); `name` is optional. Protocol 13 adds `flags`, `settings`, `enterMessage`, `leaveMessage`, `delivery`, `format` and `dome` (§19.11); protocol 14 adds `truePve` (§20.6) | | `world.place` | `world.ok` or `world.error` | Places `count` of one allowlisted `prefab` at a location, scattered within `spread` m (0–50, 10 by default for a group). All or nothing: if the game refuses one, the ones already made are killed. Protocol 13 adds `profile`, a RunicNPC profile in place of a prefab (§19.12), and for a profile `escort`, `ally` and `tether` (§19.13) | | `world.revert` | `world.ok` | Gives back what a run owns: the named `ids`, or else everything under `key`, or else everything the run owns. The answer lists `removed`, `gone` and `refused` | | `world.owned` | `world.owned` | What the world still holds of what events made, **looked for** by net id or zone id, narrowed by `runId`. Anything gone is pruned from the registry as the walk passes it | @@ -3019,3 +3019,59 @@ is every website command's path. On both rigs it reads `true`. Hello is sent aga - **The tally's two counts** in `rust_player_wipe_stats.crates_hacked` and `.drops_looted`; title categories `crateshacked` (*Safecracker*) and `dropslooted` (*Scavenger*); leaderboard columns *Crates hacked* and *Drops looted*, sortable (`sort=cratesHacked`, `sort=dropsLooted`). + +### 20.6 TruePVE in a zone — `world.zone`'s `truePve` (§14.3.2, D362, D368) + +nivex's **TruePVE** (2.4.6, uMod), optional like Economics (D199) and not in `overlay.toml`. A zone an event +opens may ask TruePVE to leave it alone, or to use one of the operator's rulesets inside it. The design is +[`PLAN_REDESIGNS.md`](../modules/rust/PLAN_REDESIGNS.md) §14.3.2, and what §14.2's test found, which shapes it, +is §14.2 there. None of TruePVE's hooks is used. + +**Hello and status.** `integrations` gains `truePve`: + +```json +"truePve": { "loaded": true, "version": "2.4.6" } +``` + +Hello is sent again when TruePVE loads or unloads. + +**`world.zone` gains an optional `truePve`** (the rest of the zone is §15 and §19.11): + +```json +{"cmd":"world.zone","reqId":"r-4","runId":"13","key":"9c1e…","monument":"airfield_1","radius":40, + "holdMs":1800000,"truePve":{"exclude":true}} +{"cmd":"world.zone","reqId":"r-5","runId":"13","key":"0b7d…","monument":"launch_site_1","radius":60, + "holdMs":1800000,"truePve":{"ruleset":"pvp-arena"}} +``` + +- **Exactly one of `exclude` and `ruleset`.** `exclude` is `true` or left out; `ruleset` is 1–64 characters + after trimming, passed as written, because TruePVE matches its names case-sensitively. Both, neither, a + non-object or a blank name is **`malformed`**. +- **TruePVE not loaded** is **`truepve-missing`**, before anything is made. +- **All or nothing.** The bridge makes the zone, and its dome when asked, then calls + **`AddOrUpdateMapping(zoneId, "exclude" | ruleset)`**. TruePVE answers false for a ruleset it does not have, + and nothing else lists the names, so the call is the check (D368). A false erases the dome and the zone + just made and answers **`truepve-ruleset`** with the name, or **`refused`** for `exclude` (TruePVE not ready). +- The registry entry keeps the mapping it was given (`TruePve`), so a mapping is only ever removed for a zone + this plugin mapped. **Re-creating a zone** (a restart, a hot reload, a ZoneManager reload, §19.11) maps + nothing again: TruePVE keeps its table in its own data file, and the zone keeps its id. + +**The unmapping lives with the zone (D362).** TruePVE prunes a mapping whose zone is gone only when the zone +id is a number, and ours are `rg-…`. So every path that erases one of our zones calls **`RemoveMapping`** +first: its deadline (checked every second, with the site down too, §15.3), `world.revert`, a zone that +`world.owned` finds ZoneManager no longer holds, and a new wipe dropping the old map's entries. When TruePVE is not loaded at +that moment, the id goes into **`data/RunicGateway/truepve.json`**, and the bridge removes each one at +`OnPluginLoaded(TruePVE)` (TruePVE has read its table in `Init` by then) and at its own load. The file is +deleted when it is empty. + +**Status.** `rg.world` prints `truePve=` (loaded) and `truePveLeftover=` (ids waiting in `truepve.json`). + +**The sidecar** forwards `world.zone` whole, as before; nothing in it changed. + +**The website (Module-Rust).** `rust.zone.open` gains **`noPvpProtection`** (a boolean, *"No PvP protection in +this zone"*) and **`pvpRuleset`** (a string, *"or this TruePVE ruleset"*). Setting both is refused, as is a +name over 64 characters, and either one is refused where the server's last hello said TruePVE is not loaded +(*"TruePVE is not installed on this server"*); all three at verify too. A server whose hello has not said +(a plugin before protocol 14, or never reached) is left to the plugin. `truepve-missing` and +`truepve-ruleset` fail the step for good, with the plugin's words. Server settings' plugin line says whether +TruePVE is loaded.