docs(rust): record the answers to Q26-Q32 (D370-D376)
D370 a trigger can start a run: core's `starts` (trigger, conditions, cooldown, one run at a time), written as §14.3.12. D371 a step's retryAfterMs, written as §14.3.13, used by DT's warm-up. D372 the operator picks the helicopter/Bradley credit rule (killing blow, last hit, most damage, everyone); the plugin sends every candidate and the site counts, amending D174. D373 a guarded crate outlives its run. D374 no leaderboard change for ZLevels, so its levels wire and table are dropped. D375 Treasure Hunter, Warden, Hoarder. D376 the public line names a player only for the presence audience. Build order is now nine stages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -927,8 +927,8 @@ pulls) is research, not a walk, so it still comes first:
|
|||||||
8. **Permissions as event rewards** (§12), built on 2026-10-12.
|
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
|
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,
|
all of them on both rigs (§14.2, D352); **it ran on 2026-10-14 (§14.2.1)**. Then one integration plan,
|
||||||
answered before code: **written on 2026-10-14 as §14.3**, from D361–D369, with Q26–Q32 open. Then the
|
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 seven stages, §13 first.
|
build, in §14.3.11's nine stages (two of them core), §13 first.
|
||||||
|
|
||||||
**Then one walk, on Oxide and Carbon, of everything still owed (D331):** §2.7; §11's rows; §12.9; §13's and
|
**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
|
§14's rows; the in-game rows
|
||||||
@@ -1078,6 +1078,18 @@ The org lead answered the phase's Q19–Q25 on 2026-10-14:
|
|||||||
| **D368** | **TruePVE in the zone step (Q24):** a *"no PvP protection in this zone"* checkbox (`exclude`), plus an optional ruleset name typed by staff, which the bridge validates by `AddOrUpdateMapping`'s own `false`. | Checkbox only; reading TruePVE's config file. | 14.2.1, 3 |
|
| **D368** | **TruePVE in the zone step (Q24):** a *"no PvP protection in this zone"* checkbox (`exclude`), plus an optional ruleset name typed by staff, which the bridge validates by `AddOrUpdateMapping`'s own `false`. | Checkbox only; reading TruePVE's config file. | 14.2.1, 3 |
|
||||||
| **D369** | **Wipes mirror the plugin (Q25):** the site shows what ZLevels and Server Rewards hold and does nothing at a wipe; ZLevels columns are *current level*, not *this wipe*. | Per-wipe snapshots of our own; a site-side wipe reset. | 14.2.1 |
|
| **D369** | **Wipes mirror the plugin (Q25):** the site shows what ZLevels and Server Rewards hold and does nothing at a wipe; ZLevels columns are *current level*, not *this wipe*. | Per-wipe snapshots of our own; a site-side wipe reset. | 14.2.1 |
|
||||||
|
|
||||||
|
The org lead answered §14.3's Q26–Q32 on 2026-10-14:
|
||||||
|
|
||||||
|
| # | Decision | Rejected | § |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **D370** | **A trigger can start a run (Q26).** Core gains a definition's `starts` (a trigger, conditions and a cooldown), one run at a time per definition, `event.*` triggers excluded; core work on the website's own plan, its own PR with the Module-uo proof. | Only phase gates; an event that waits for the trigger in its first phase. | 14.3.12 |
|
||||||
|
| **D371** | **A failed step may name its retry delay (Q27):** `retryAfterMs`, 1–30 min, counted as one attempt, a MODULE_API minor bump. DT's warm-up retries ten minutes later through it. | The bridge holding the request and starting DT itself later. | 14.3.4, 14.3.13 |
|
||||||
|
| **D372** | **The operator chooses who is credited with a helicopter or a Bradley (Q28):** killing blow (the default; for the helicopter, the crippling hit, else the last hit), last hit, most damage, or everyone who hurt it. The plugin sends every candidate; the site applies the server's rule to the feed line and to *Rotorbane* / *Tankbreaker* alike. **This amends D174.** | The crippling hit as the one fixed rule; D174's 60 s last-hitter. | 14.3.1 |
|
||||||
|
| **D373** | **A guarded crate outlives its run (Q29):** nothing is removed at teardown; the step says so and the run log notes a crate left standing. | Ending every guarded crate on the server with `gcrate stop all`. | 14.3.5 |
|
||||||
|
| **D374** | **The leaderboard is not changed for ZLevels (Q30):** no column and no title, because most of its skills already line up with counts of ours. ZLevels is reported at hello, and its XP boosts are lent as permissions (§12). | One total column; six columns; one column with a skill picker. | 14.3.6 |
|
||||||
|
| **D375** | **Three new titles (Q31):** *Treasure Hunter* (DT treasures won), *Warden* (guarded crates cleared), *Hoarder* (the most RP, above 0), renamable, each with a column on servers that report the plugin. | No titles for the plugins' counts. | 14.3.4, 14.3.5, 14.3.7 |
|
||||||
|
| **D376** | **A public line names a player only for the presence audience (Q32):** the event and its grid are public (*"Patrol helicopter downed at G8"*), and the name shows only to the audience the operator chose for presence. | The name always public (D353 as written); the whole line hidden below the presence audience. | 14.3.1 |
|
||||||
|
|
||||||
## 11. First-class optional plugins (D199–D202)
|
## 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
|
The org lead named four plugins the module supports as first-class integrations. Kits already is one. The other
|
||||||
@@ -2339,7 +2351,7 @@ Written on 2026-10-14 from §14.2.1 and D353–D369, against the code on `edge`
|
|||||||
at protocol 14 with §2, §11.2, §11.3 and §12 in), and **answered by the org lead before any code**. For §13 and
|
at protocol 14 with §2, §11.2, §11.3 and §12 in), and **answered by the org lead before any code**. For §13 and
|
||||||
each of the six it says what hello reports, what goes on the wire, what the site does, which helper (none), the
|
each of the six it says what hello reports, what goes on the wire, what the site does, which helper (none), the
|
||||||
tests and the walk rows. §14.3.9 gathers the points the plan settles itself, so they can be overruled, and
|
tests and the walk rows. §14.3.9 gathers the points the plan settles itself, so they can be overruled, and
|
||||||
§14.3.10 asks Q26–Q32.
|
§14.3.10 asks Q26–Q32, answered as D370–D376; §14.3.12 and §14.3.13 are the two pieces of core work they add.
|
||||||
|
|
||||||
**Everything here is protocol 14 (D357)**, specified in [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20 as each
|
**Everything here is protocol 14 (D357)**, specified in [PROTOCOL.md](../../rust-link/PROTOCOL.md) §20 as each
|
||||||
piece lands, and released with the rest after the end walk. **No helper plugin is needed (D351):** every route
|
piece lands, and released with the rest after the end walk. **No helper plugin is needed (D351):** every route
|
||||||
@@ -2362,9 +2374,9 @@ player is `steamId` and `name`.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `heli.arrived` | `OnEntitySpawned(PatrolHelicopter)` | `x`, `z` |
|
| `heli.arrived` | `OnEntitySpawned(PatrolHelicopter)` | `x`, `z` |
|
||||||
| `heli.left` | `OnHelicopterRetire` | `x`, `z` (last seen) |
|
| `heli.left` | `OnHelicopterRetire` | `x`, `z` (last seen) |
|
||||||
| `heli.downed` | the first `OnPatrolHelicopterKill` that names a player, else D174's last-hitter at the crash (Q28) | who, `x`, `z` |
|
| `heli.downed` | the crash's `OnEntityDeath`, with the first `OnPatrolHelicopterKill`'s player kept as the killing blow | every credit candidate (D372, below), `x`, `z` |
|
||||||
| `bradley.spawned` | `OnBradleyApcInitialize`. **Road Bradleys come twelve at once, so their spawns in one second are one frame with `count`** | `where: launch_site \| road`, `count` |
|
| `bradley.spawned` | `OnBradleyApcInitialize`. **Road Bradleys come twelve at once, so their spawns in one second are one frame with `count`** | `where: launch_site \| road`, `count` |
|
||||||
| `bradley.destroyed` | `OnEntityDeath(BradleyAPC)` (D366) | who, `where`, `x`, `z` |
|
| `bradley.destroyed` | `OnEntityDeath(BradleyAPC)` (D366) | every credit candidate (D372), `where`, `x`, `z` |
|
||||||
| `cargo.arrived` / `cargo.docking` / `cargo.docked` / `cargo.undocked` | `OnEntitySpawned(CargoShip)`, `OnCargoShipHarborApproach`, `…HarborArrived`, `…HarborLeave` | `x`, `z`; the three harbour types add `harbour` (the nearest `harbor_*`) |
|
| `cargo.arrived` / `cargo.docking` / `cargo.docked` / `cargo.undocked` | `OnEntitySpawned(CargoShip)`, `OnCargoShipHarborApproach`, `…HarborArrived`, `…HarborLeave` | `x`, `z`; the three harbour types add `harbour` (the nearest `harbor_*`) |
|
||||||
| `cargo.left` | `OnCargoShipEgress`, which did not fire within §14.2's window; else the ship's `OnEntityKill`. Stage 1 confirms which | `x`, `z` |
|
| `cargo.left` | `OnCargoShipEgress`, which did not fire within §14.2's window; else the ship's `OnEntityKill`. Stage 1 confirms which | `x`, `z` |
|
||||||
| `chinook.arrived` | `OnEntitySpawned(CH47HelicopterAIController)`, **except the oil-rig reinforcement**, which is told by the crate hack it answers | `x`, `z` |
|
| `chinook.arrived` | `OnEntitySpawned(CH47HelicopterAIController)`, **except the oil-rig reinforcement**, which is told by the crate hack it answers | `x`, `z` |
|
||||||
@@ -2394,9 +2406,37 @@ player is `steamId` and `name`.
|
|||||||
**The tally** gains `cratesHacked` and `dropsLooted` (D356): a hack counts for its starter at `crate.hack.started`,
|
**The tally** gains `cratesHacked` and `dropsLooted` (D356): a hack counts for its starter at `crate.hack.started`,
|
||||||
once per crate, from any location except `other`; a drop counts for its first looter. New columns
|
once per crate, from any location except `other`; a drop counts for its first looter. New columns
|
||||||
`crates_hacked`, `drops_looted` in `rust_player_wipe_stats`; title categories **`crateshacked` (*Safecracker*)** and
|
`crates_hacked`, `drops_looted` in `rust_player_wipe_stats`; title categories **`crateshacked` (*Safecracker*)** and
|
||||||
**`dropslooted` (*Scavenger*)**, `sum` sources, renamable (D175). `heli.downed` and `bradley.destroyed` keep
|
**`dropslooted` (*Scavenger*)**, `sum` sources, renamable (D175).
|
||||||
counting `heliKills` and `apcKills` through the same credit, so the feed line and *Rotorbane* never disagree
|
|
||||||
(Q28).
|
**Who gets the credit for a helicopter or a Bradley is the operator's choice (D372).** The plugin no longer
|
||||||
|
decides. For each patrol helicopter and Bradley it keeps a small damage table from `OnEntityTakeDamage`, the
|
||||||
|
hook that already feeds D174's last-hitter, after the type test it already does, and for those two classes
|
||||||
|
only. `heli.downed` and `bradley.destroyed` carry every candidate:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"kind":"world.event","type":"heli.downed","x":812.0,"z":-340.5,
|
||||||
|
"credit":{"blow":"76561190000000001","last":"76561190000000002","most":"76561190000000002",
|
||||||
|
"damagers":[{"steamId":"76561190000000002","damage":6120.0},{"steamId":"76561190000000001","damage":3880.0}]},
|
||||||
|
"names":{"76561190000000001":"Alice","76561190000000002":"Bob"}}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `blow` is the killing blow's player: for the helicopter, the player the first `OnPatrolHelicopterKill` names at
|
||||||
|
the crippling hit; for a Bradley, its death's `InitiatorPlayer`. `last` is the last player to hurt it, with no
|
||||||
|
60 s window. `most` is the player who dealt the most damage. Each is absent when nobody qualifies.
|
||||||
|
- **The site applies the server's rule**, a setting beside the server's other Rust settings:
|
||||||
|
|
||||||
|
| Rule | Credits | For example (Bob did 61 %, Alice the crippling hit) |
|
||||||
|
|---|---|---|
|
||||||
|
| **Killing blow** (default) | `blow`, else `last` | Alice |
|
||||||
|
| **Last hit** | `last` | whoever hit it last: Alice or Bob |
|
||||||
|
| **Most damage** | `most` | Bob |
|
||||||
|
| **Everyone who hurt it** | every `damagers` entry, one each | Alice and Bob, one each |
|
||||||
|
|
||||||
|
- **The same rule writes the feed line and the count**, so the line and *Rotorbane* / *Tankbreaker* never
|
||||||
|
disagree. Under protocol 14 the site counts `heli_kills` and `apc_kills` from these frames, and the tally's
|
||||||
|
`heliKills` / `apcKills` are no longer sent. Under *Everyone*, the line reads *"Patrol helicopter downed by
|
||||||
|
Alice, Bob and 2 others at G7"*.
|
||||||
|
- Changing the rule applies from then on; past counts are not recounted.
|
||||||
|
|
||||||
**The step that calls one: `world.call`, site → game (D354, D367).**
|
**The step that calls one: `world.call`, site → game (D354, D367).**
|
||||||
|
|
||||||
@@ -2423,13 +2463,15 @@ counting `heliKills` and `apcKills` through the same credit, so the feed line an
|
|||||||
|
|
||||||
- **The feed.** `world.event` joins the catalogue, filtered **per `type`**. Each line is written from the type, e.g.
|
- **The feed.** `world.event` joins the catalogue, filtered **per `type`**. Each line is written from the type, e.g.
|
||||||
*"Patrol helicopter downed by Alice at G7"*, *"Bob started hacking the locked crate on Large Oil Rig"*,
|
*"Patrol helicopter downed by Alice at G7"*, *"Bob started hacking the locked crate on Large Oil Rig"*,
|
||||||
*"12 road Bradleys are out"*, *"The Deep Sea is open"*. **Public with grid and name, staff adds the Steam id
|
*"12 road Bradleys are out"*, *"The Deep Sea is open"*. **The event and its grid are public; the player's name
|
||||||
(D353)**; how that meets the presence rule is Q32.
|
shows only to the operator's presence audience (D376)**, so with presence left at staff a signed-out visitor
|
||||||
|
reads *"Patrol helicopter downed at G8"*. Staff see the name and the Steam id (D353). A line with nobody in it
|
||||||
|
(the cargo docking, the Deep Sea) is the same for everyone.
|
||||||
- **Triggers (D355).** One engagement trigger per type, `rust.world.<type>` (`rust.world.heli.downed`,
|
- **Triggers (D355).** One engagement trigger per type, `rust.world.<type>` (`rust.world.heli.downed`,
|
||||||
`rust.world.crate.hack.started` …), `kind: 'event'`, ceiling `everyone`, cooldown subject the server and type
|
`rust.world.crate.hack.started` …), `kind: 'event'`, ceiling `everyone`, cooldown subject the server and type
|
||||||
(the crate types add `where`), with typed variables (`server`, `grid`, `player`, `where`, `harbour`,
|
(the crate types add `where`), with typed variables (`server`, `grid`, `player`, `where`, `harbour`,
|
||||||
`monument`, `count`). Because a phase gate's `{ on: … }` reads the same catalogue, **"advance when Bradley is
|
`monument`, `count`). Because a phase gate's `{ on: … }` reads the same catalogue, **"advance when Bradley is
|
||||||
destroyed" works with no new code**. Starting a run from a trigger is not something core does today (Q26).
|
destroyed" works with no new code**. **Starting a run from one is new core work (D370, §14.3.12).**
|
||||||
- **The step `rust.world.call`**, *"Call a world event"*: server, event, count, and the location params of the
|
- **The step `rust.world.call`**, *"Call a world event"*: server, event, count, and the location params of the
|
||||||
place verbs where the event allows a position (above).
|
place verbs where the event allows a position (above).
|
||||||
- **Titles and columns:** *Safecracker* and *Scavenger* (above); the leaderboard gains the two columns.
|
- **Titles and columns:** *Safecracker* and *Scavenger* (above); the leaderboard gains the two columns.
|
||||||
@@ -2438,10 +2480,12 @@ counting `heliKills` and `apcKills` through the same credit, so the feed line an
|
|||||||
**Tests.** Bridge: `checkPlugin.js` (hook names, the console probe). Module-Rust: the catalogue holds every type
|
**Tests.** Bridge: `checkPlugin.js` (hook names, the console probe). Module-Rust: the catalogue holds every type
|
||||||
against PROTOCOL.md's table (default deny for an unknown type); each feed line; the trigger declarations and
|
against PROTOCOL.md's table (default deny for an unknown type); each feed line; the trigger declarations and
|
||||||
their examples; the `world.call` verify and bounds; the two title categories and columns; the ingest of the two
|
their examples; the `world.call` verify and bounds; the two title categories and columns; the ingest of the two
|
||||||
tally fields.
|
tally fields; each of D372's four rules against one `credit` block (one with no `blow`, one with nobody at all),
|
||||||
|
for the line and the count alike; a presence-gated line with and without the name (D376).
|
||||||
|
|
||||||
**Walk rows (D331).** On both rigs, with timers shortened as §14.2 did: force each type and read its feed line,
|
**Walk rows (D331).** On both rigs, with timers shortened as §14.2 did: force each type and read its feed line,
|
||||||
staff line and trigger; down a helicopter with a crash long after the hit; hack a crate at each location and
|
staff line and trigger, signed out and as staff; down a helicopter with a crash long after the hit, and a
|
||||||
|
Bradley with two players, under each of the four credit rules; hack a crate at each location and
|
||||||
see *Safecracker* count; loot a drop first and see *Scavenger*; call an airdrop at a position and a patrol
|
see *Safecracker* count; loot a drop first and see *Scavenger*; call an airdrop at a position and a patrol
|
||||||
helicopter from a run, then cancel the run and see both stay; a Guarded Crate crate raising no Rust frame.
|
helicopter from a run, then cancel the run and see both stay; a Guarded Crate crate raising no Rust frame.
|
||||||
|
|
||||||
@@ -2512,13 +2556,14 @@ both go when the zone lasts out; cargo's ring for the ship's life; one of our zo
|
|||||||
- **The step `rust.dt.start`**, *"Start a Dangerous Treasures event"*: server and difficulty; DT picks the spot.
|
- **The step `rust.dt.start`**, *"Start a Dangerous Treasures event"*: server and difficulty; DT picks the spot.
|
||||||
`reversible: 'none'` (DT despawns its own), `risk: 'change'`, a new budget `rust.dt.events`. It refuses at
|
`reversible: 'none'` (DT despawns its own), `risk: 'change'`, a new budget `rust.dt.events`. It refuses at
|
||||||
verify where DT is absent or `console.plugins` is false, naming Rust-Plugins#34 for the second.
|
verify where DT is absent or `console.plugins` is false, naming Rust-Plugins#34 for the second.
|
||||||
- **D365.** `dt-not-ready` is reported as *"Dangerous Treasures is not ready yet"*. **The retry after ten minutes is
|
- **D365.** `dt-not-ready` is reported as *"Dangerous Treasures is not ready yet"*, and the step answers
|
||||||
Q27**: core's step retry today is a flat 60 s, three times, so ten minutes needs either a small core addition or
|
`{ ok: false, retry: true, retryAfterMs: 600000 }`: **core's new per-failure retry delay (D371, §14.3.13)**, so
|
||||||
the bridge holding the request. The manual retry is core's own: the step's `on_failure` pauses the run, and
|
the run tries once more ten minutes later. `dt-no-spot` retries on core's ordinary 60 s. The manual retry is
|
||||||
staff press **Retry** on the paused step ([EVENTS.md](../../website/EVENTS.md) §I), which re-sends the same key.
|
core's own: the step's `on_failure` pauses the run, and staff press **Retry** on the paused step
|
||||||
|
([EVENTS.md](../../website/EVENTS.md) §I), which re-sends the same key.
|
||||||
- **Feed and triggers.** *"A Dangerous Treasures event started at K12"*; *"Alice won the treasure at K12"*;
|
- **Feed and triggers.** *"A Dangerous Treasures event started at K12"*; *"Alice won the treasure at K12"*;
|
||||||
ended is staff-only. `rust.dt.started` and `rust.dt.won` are triggers (§14.3.9).
|
ended is staff-only. `rust.dt.started` and `rust.dt.won` are triggers (§14.3.9).
|
||||||
- **A column and a title**, `treasureswon`, default *Treasure Hunter* (Q31).
|
- **A column and a title**, `treasureswon`, default *Treasure Hunter* (D375).
|
||||||
- The map draws a treasure marker.
|
- The map draws a treasure marker.
|
||||||
|
|
||||||
**Tests.** The step's verify (absent, console unavailable), each error's wording, the key join on `dt.started`,
|
**Tests.** The step's verify (absent, console unavailable), each error's wording, the key join on `dt.started`,
|
||||||
@@ -2541,13 +2586,15 @@ the bridge already reads Kits' and Economics' (PROTOCOL.md §16.3), and absent w
|
|||||||
`IsAdmin`, which the server console passes). It refuses with `npcspawn-missing`, `bad-event`,
|
`IsAdmin`, which the server console passes). It refuses with `npcspawn-missing`, `bad-event`,
|
||||||
`console-unavailable` or `gc-missing`.
|
`console-unavailable` or `gc-missing`.
|
||||||
- `map.live`'s `world` gains `kind: "guarded"` while one runs.
|
- `map.live`'s `world` gains `kind: "guarded"` while one runs.
|
||||||
- The tally gains **`guardedWon`** for the player `gc.ended` names (Q31).
|
- The tally gains **`guardedWon`** for the player `gc.ended` names: a column and a title, `guardedwon`, default
|
||||||
|
*Warden* (D375).
|
||||||
|
|
||||||
**The website.**
|
**The website.**
|
||||||
- **The step `rust.gc.place`**, *"Place a guarded crate"*: server, event (the names from hello), and the place
|
- **The step `rust.gc.place`**, *"Place a guarded crate"*: server, event (the names from hello), and the place
|
||||||
verbs' location params (monument, grid or point). **Kept: nothing is removed at teardown (Q29).** There is no
|
verbs' location params (monument, grid or point). **Kept: nothing is removed at teardown (D373).** There is no
|
||||||
way to end one instance, and `gcrate stop all` would end other people's and raise no hook. A new budget
|
way to end one instance, and `gcrate stop all` would end other people's and raise no hook. The step's
|
||||||
`rust.gc.crates`.
|
description says so, and when a run ends with one still standing, the run log notes *"a guarded crate at M4
|
||||||
|
was left standing; it ends when cleared or after its own time"*. A new budget `rust.gc.crates`.
|
||||||
- **D364:** it refuses at verify where NpcSpawn is absent: *"Guarded Crate is loaded but NpcSpawn is missing: its
|
- **D364:** it refuses at verify where NpcSpawn is absent: *"Guarded Crate is loaded but NpcSpawn is missing: its
|
||||||
events cannot start."* The servers page shows NpcSpawn beside Guarded Crate.
|
events cannot start."* The servers page shows NpcSpawn beside Guarded Crate.
|
||||||
- **Feed and triggers.** *"A guarded crate landed at M4"*; *"Alice cleared the guarded crate at M4"*; failed is a
|
- **Feed and triggers.** *"A guarded crate landed at M4"*; *"Alice cleared the guarded crate at M4"*; failed is a
|
||||||
@@ -2562,27 +2609,25 @@ the bridge already reads Kits' and Economics' (PROTOCOL.md §16.3), and absent w
|
|||||||
column and the trigger; let one time out; cancel the run and see the crate stay; unload NpcSpawn and see the
|
column and the trigger; let one time out; cancel the run and see the crate stay; unload NpcSpawn and see the
|
||||||
step refused with the reason. On Carbon: refused (D363).
|
step refused with the reason. On Carbon: refused (D363).
|
||||||
|
|
||||||
#### 14.3.6 ZLevels Remastered (D369)
|
#### 14.3.6 ZLevels Remastered (D369, D374)
|
||||||
|
|
||||||
**Hello.** `integrations.zLevels`: `{ loaded, version }`.
|
**Hello.** `integrations.zLevels`: `{ loaded, version }`.
|
||||||
|
|
||||||
**The wire.** The tally gains **`levels`**: `{ acquire, crafting, mining, skinning, woodcutting, fishing }`, each a
|
**The leaderboard does not change (D374).** It gains no ZLevels column and no ZLevels title. Most of its six
|
||||||
value read with **`GetLevel` only** (never `api_GetPlayerInfo`, which creates records). It is read when a player
|
skills already line up with counts of ours: woodcutting with *Timberwright*, mining with *Prospector*, crafting
|
||||||
connects and at each flush for the players in it (six calls of about 3 µs each), and sent when one moved.
|
with *Seamster* and *Armorer*, acquire (plants) with *Greenhand*, and skinning sits beside the animal kills
|
||||||
There is no level-up hook, so a level is as fresh as the last flush.
|
(*Beastbane*). Fishing has no count of ours, and stays that way. So **no levels go on the wire
|
||||||
|
and nothing is stored**: the `levels` tally field and the `rust_player_levels` table this plan first proposed are
|
||||||
|
dropped. D369 still holds in what remains: nothing is done at a wipe.
|
||||||
|
|
||||||
**The website.**
|
**What the integration is:**
|
||||||
- **Stored per server, not per wipe (D369):** a new table `rust_player_levels` (`server_id`, `steam_id`, the six
|
- **Hello reports it**, and the servers page shows it beside the other optional plugins.
|
||||||
levels, `updated_at`), a value written, never summed, and nothing done at a wipe.
|
|
||||||
- **The leaderboard and titles** are Q30. Whatever the answer, the column is "current level", on servers that
|
|
||||||
report ZLevels, and shown for every wipe view because it does not belong to one.
|
|
||||||
- **XP boosts need no code** (§14.2.1): `rust.perm.lend` already lends `zlevelsremastered.vip1`–`vip3`, and its
|
- **XP boosts need no code** (§14.2.1): `rust.perm.lend` already lends `zlevelsremastered.vip1`–`vip3`, and its
|
||||||
picker lists them once ZLevels registers them (§12.2).
|
picker lists them once ZLevels registers them (§12.2). A *Double XP weekend* is a lend step.
|
||||||
|
|
||||||
**Tests.** The tally's `levels` ingest as values; the new table's upsert; the columns and categories Q30 decides.
|
**Tests.** The hello field and the servers page.
|
||||||
|
|
||||||
**Walk rows.** Gain a level and see it after the next flush; lend `vip2` from a run and see the XP rate change;
|
**Walk rows.** Lend `vip2` from a run and see the XP rate change at once, and back at the run's end.
|
||||||
wipe and see the levels stay.
|
|
||||||
|
|
||||||
#### 14.3.7 Server Rewards (D369)
|
#### 14.3.7 Server Rewards (D369)
|
||||||
|
|
||||||
@@ -2611,7 +2656,9 @@ player is at 0.
|
|||||||
per server and not per wipe (D369). The player's Rust page, the user's admin page and the app (R10) show it beside
|
per server and not per wipe (D369). The player's Rust page, the user's admin page and the app (R10) show it beside
|
||||||
the coins.
|
the coins.
|
||||||
- **Staff add and take** from the user's admin page, each an activity row, like Economics.
|
- **Staff add and take** from the user's admin page, each an activity row, like Economics.
|
||||||
- **A column and a title** (Q31).
|
- **A column and a title**, `points`, default *Hoarder*, a value like the richest (D375): ranked only above 0, on
|
||||||
|
servers that report Server Rewards, and shown for every wipe view because the points do not belong to one
|
||||||
|
(D369).
|
||||||
|
|
||||||
**Tests.** The same set as Economics': the amount bounds (0, negative, fractional), a repeat key, `insufficient`,
|
**Tests.** The same set as Economics': the amount bounds (0, negative, fractional), a repeat key, `insufficient`,
|
||||||
the value ingest, the admin route and its activity row, the app's screen.
|
the value ingest, the admin route and its activity row, the app's screen.
|
||||||
@@ -2627,7 +2674,7 @@ in the app; wipe and see the points stay.
|
|||||||
| Hello | `console.plugins`; `integrations.truePve`, `dynamicPvp`, `dangerousTreasures`, `guardedCrate` (with `npcSpawn`, `events`), `zLevels`, `serverRewards` |
|
| Hello | `console.plugins`; `integrations.truePve`, `dynamicPvp`, `dangerousTreasures`, `guardedCrate` (with `npcSpawn`, `events`), `zLevels`, `serverRewards` |
|
||||||
| Events | `world.event` (23 types); `pvp.zone.created` / `deleted`; `dt.started` / `won` / `ended`; `gc.started` / `ended` / `failed` |
|
| Events | `world.event` (23 types); `pvp.zone.created` / `deleted`; `dt.started` / `won` / `ended`; `gc.started` / `ended` / `failed` |
|
||||||
| Commands | `world.call`; `dt.start`; `gc.place`; `points.pay` / `balances` / `adjust`; `world.zone`'s `truePve` |
|
| Commands | `world.call`; `dt.start`; `gc.place`; `points.pay` / `balances` / `adjust`; `world.zone`'s `truePve` |
|
||||||
| Tally | `cratesHacked`, `dropsLooted`, `treasuresWon`, `guardedWon`; `levels` and `points` (values) |
|
| Tally | `cratesHacked`, `dropsLooted`, `treasuresWon`, `guardedWon`; `points` (a value). **`heliKills` and `apcKills` leave the tally**: the site counts them from `world.event`'s `credit` (D372) |
|
||||||
| `map.live` | `world` kinds `treasure` and `guarded`; the `pvp` layer |
|
| `map.live` | `world` kinds `treasure` and `guarded`; the `pvp` layer |
|
||||||
| Sidecar | `POST /world/call`, `/dt/start`, `/gc/place`, `/points/pay`, `/points/balances`, `/points/adjust`, thin forwards that keep nothing |
|
| Sidecar | `POST /world/call`, `/dt/start`, `/gc/place`, `/points/pay`, `/points/balances`, `/points/adjust`, thin forwards that keep nothing |
|
||||||
|
|
||||||
@@ -2651,6 +2698,10 @@ in the app; wipe and see the points stay.
|
|||||||
|
|
||||||
#### 14.3.10 Questions for the org lead
|
#### 14.3.10 Questions for the org lead
|
||||||
|
|
||||||
|
**All seven were answered on 2026-10-14 (D370–D376, §10).** Q26, Q27, Q29, Q31 and Q32 as proposed (Q26 is
|
||||||
|
written as §14.3.12, Q27 as §14.3.13). **Q28 differs:** the operator chooses the credit rule, from four options
|
||||||
|
(D372). **Q30 differs:** the leaderboard is not changed for ZLevels at all (D374).
|
||||||
|
|
||||||
- **Q26 — Can a world event start a run?** D355 makes every world event a trigger, and §13.4's example was
|
- **Q26 — Can a world event start a run?** D355 makes every world event a trigger, and §13.4's example was
|
||||||
*"when anyone destroys Bradley, start the Launch Site Lockdown event"*. Today a trigger can **advance a phase**
|
*"when anyone destroys Bradley, start the Launch Site Lockdown event"*. Today a trigger can **advance a phase**
|
||||||
of a run already going (`{ on: 'rust.world.bradley.destroyed' }`) and can send a notice, but **core cannot start a
|
of a run already going (`{ on: 'rust.world.bradley.destroyed' }`) and can send a notice, but **core cannot start a
|
||||||
@@ -2693,15 +2744,74 @@ Each stage is built on `edge`, compiled on both rigs, and PR'd with its PROTOCOL
|
|||||||
until the end (D331, D359). The org lead is asked before each stage starts.
|
until the end (D331, D359). The org lead is asked before each stage starts.
|
||||||
|
|
||||||
1. **§13, the world events**, starting with the placement spike for `world.call`. It has no plugin dependency
|
1. **§13, the world events**, starting with the placement spike for `world.call`. It has no plugin dependency
|
||||||
and carries the console probe the later stages use.
|
and carries the console probe the later stages use, and D372's credit rule.
|
||||||
2. **TruePVE** in the zone step.
|
2. **Core: a step's retry delay** (§14.3.13, D371), a website PR with the Module-uo proof. It is small and comes
|
||||||
3. **Dynamic PVP** (it needs TruePVE).
|
before stage 5, which needs it.
|
||||||
4. **Dangerous Treasures**, after Q27's core change if that is the answer.
|
3. **TruePVE** in the zone step.
|
||||||
5. **Guarded Crate**.
|
4. **Dynamic PVP** (it needs TruePVE).
|
||||||
6. **ZLevels**.
|
5. **Dangerous Treasures**, on stage 2's `retryAfterMs`.
|
||||||
7. **Server Rewards**.
|
6. **Guarded Crate**.
|
||||||
|
7. **ZLevels** (hello only, D374).
|
||||||
|
8. **Server Rewards**.
|
||||||
|
9. **Core: starting a run from a trigger** (§14.3.12, D370), a website PR of its own with the Module-uo proof,
|
||||||
|
as §2 was. It can be built alongside any stage after 1; it is last here only because nothing else waits on it.
|
||||||
|
Module-Rust needs no code for it, because the world events are already triggers.
|
||||||
|
|
||||||
Q26's core work, if wanted, is a website PR of its own and can be built alongside any stage.
|
#### 14.3.12 Core: starting a run from a trigger (D370)
|
||||||
|
|
||||||
|
**What it gives.** *"When anyone destroys Bradley on Main, start Launch Site Lockdown"*; *"when the Deep Sea opens,
|
||||||
|
start Sea Raiders"*; on UO, *"when a champion's boss appears, start Champion's Call"*. Today a trigger can only
|
||||||
|
advance a phase of a run already going, or send a notice (§14.3.1). This is core work on the website's own plan
|
||||||
|
([EVENTS.md](../../website/EVENTS.md) gains it when it is built). It is written here because this plan asked for it,
|
||||||
|
and no module contract changes: the triggers it starts from are the ones modules already register.
|
||||||
|
|
||||||
|
**The design, to be settled again in EVENTS.md at build time:**
|
||||||
|
- **A definition's version gains `starts`**: a list of `{ on: <triggerId>, where: <conditions>, cooldown }`,
|
||||||
|
beside its schedule (a definition may have either, or both). `on` and `where` are a phase gate's own
|
||||||
|
(EVENTS.md §E), the same catalogue, operators and form, so *"`where: { server: 'main', where: 'launch_site' }`"* is
|
||||||
|
already a thing the editor can draw.
|
||||||
|
- **The emit path starts it.** When a trigger fires, core finds the `ready` definitions whose published
|
||||||
|
version starts on it and whose conditions match, and creates a run of that version **now**, with
|
||||||
|
`scheduled_for` = the firing's time, so the existing `UNIQUE (definition_id, scope, scheduled_for)` keeps one
|
||||||
|
firing from starting two runs. `started_by` is `trigger:<triggerId>`, and the firing's variables are stored on
|
||||||
|
the run, so its page says *"Started because Bradley was destroyed at G8"*, and the audit says who and what.
|
||||||
|
- **One run at a time, and a cooldown.** A firing while a run of that definition is going, or within its
|
||||||
|
`cooldown` (default 30 minutes, 0 to 7 days) of the last trigger-started run, starts nothing. It is counted,
|
||||||
|
and the definition's page shows *"2 firings ignored while running"*. This is what stops twelve road Bradleys dying
|
||||||
|
in a minute from starting twelve runs.
|
||||||
|
- **No loops.** Core's own `event.*` triggers cannot start a run, so a run starting can never start another.
|
||||||
|
A run whose own steps cause its start trigger (an event that calls a patrol helicopter and starts on one
|
||||||
|
being downed) is held by one run at a time and the cooldown.
|
||||||
|
- **Everything else is a run like any other:** caps and budgets, verify before publish, the same roles that may
|
||||||
|
schedule one (admin and editor), the same announcements and cleanup. A definition that starts from a trigger
|
||||||
|
whose module is not loaded is dormant, as a phase gate on one is.
|
||||||
|
|
||||||
|
**Tests (website).** A matching firing starts one run; a second firing during it, and one in the cooldown, start
|
||||||
|
nothing and are counted; two instances emitting the same firing make one run; an `event.*` trigger is refused in
|
||||||
|
`starts`; a dormant trigger; the run's origin on its page and in the audit. **Module-uo proof:** one definition
|
||||||
|
starting on `uo.champ.boss_up`.
|
||||||
|
|
||||||
|
**Walk rows.** On a Rust rig: *"start when Bradley is destroyed"* with a short cooldown. Destroy two in a minute and
|
||||||
|
see one run and one ignored firing; destroy one after the cooldown and see a second run.
|
||||||
|
|
||||||
|
#### 14.3.13 Core: a step's retry delay (D371)
|
||||||
|
|
||||||
|
**What it gives.** A failed step may say when to try again: `{ ok: false, retry: true, retryAfterMs }`. Today every
|
||||||
|
retry waits the flat `EVENT_STEP_RETRY_MS` (60 s). DT's warm-up (D365) is the first user: *"not ready yet; try in
|
||||||
|
ten minutes"*.
|
||||||
|
|
||||||
|
- **Bounded** 60 000 to 1 800 000 ms (one to thirty minutes). Outside that it is clamped, because this is the
|
||||||
|
module's hint, not an author's value (D95 is about the second). Absent, the flat delay as today.
|
||||||
|
- **It counts as one attempt** of `EVENT_STEP_MAX_ATTEMPTS`, and the run goes `degraded` on it as on any retry
|
||||||
|
(EVENTS.md §L). The run console shows *"retrying at 20:10"*.
|
||||||
|
- **A run that is paused or cancelled while it waits** does not retry; the existing staff Retry on a paused step
|
||||||
|
still works at any time.
|
||||||
|
- **MODULE_API minor bump** (additions only). [`STEAM_SIGNIN.md`](../../website/STEAM_SIGNIN.md) has claimed
|
||||||
|
1.15.0; whichever lands first takes it, the other the next minor. Module-uo needs no change. The Integration Kit's
|
||||||
|
`coreApi` equality check turns red on the bump, as it is meant to, and its pin moves when the chapters are re-read.
|
||||||
|
|
||||||
|
**Tests (website).** The delay honoured; clamped at both ends; counted as an attempt; no retry for a run paused
|
||||||
|
or cancelled in the wait; `MODULE_API.md` and the classify rules updated.
|
||||||
|
|
||||||
### 14.4 Questions for the org lead
|
### 14.4 Questions for the org lead
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user