Merge pull request 'docs(rust): record the §14.2 test phase findings and answers (D361–D369)' (#346) from docs/rust-test-phase-14-2 into main
Reviewed-on: #346
This commit is contained in:
@@ -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
|
[`../../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
|
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,
|
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). **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). **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).**
|
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
|
This is a companion to [`PLAN_FIXES.md`](PLAN_FIXES.md) and [`PLAN.md`](PLAN.md). Where they disagree, this
|
||||||
@@ -926,8 +926,8 @@ pulls) is research, not a walk, so it still comes first:
|
|||||||
for the current paid copy (D347)** and is built for both editions (D348).
|
for the current paid copy (D347)** and is built for both editions (D348).
|
||||||
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). Then one integration plan, answered before code (§14.3). Then the
|
all of them on both rigs (§14.2, D352); **it ran on 2026-10-14 (§14.2.1)**. Then one integration plan,
|
||||||
build, §13 first.
|
answered before code (§14.3), which starts from D361–D369. Then the build, §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
|
||||||
@@ -1058,6 +1058,25 @@ The org lead answered §13's and §14's questions the same day, and added Steam
|
|||||||
| **D359** | **The end walk waits for RaidableBases** (Q18). D331's one walk runs once every item is coded, §11.4 included, and §11.4 waits for the current paid copy (D347). | Walking the rest without it and giving RaidableBases its own walk later. | 9, 14.4 |
|
| **D359** | **The end walk waits for RaidableBases** (Q18). D331's one walk runs once every item is coded, §11.4 included, and §11.4 waits for the current paid copy (D347). | Walking the rest without it and giving RaidableBases its own walk later. | 9, 14.4 |
|
||||||
| **D360** | **Steam sign-in is supported in the same manner as the other providers:** a built-in provider beside Google and Discord, link-only, through the one session service, configured on Admin → Auth, on the website and in the Android app. **It stays on the website's plan (D171)**, now written as [`website/STEAM_SIGNIN.md`](../../website/STEAM_SIGNIN.md) and on the roadmap as the `website` milestone *Steam sign-in*. §15 links it. | Planning it inside this document. | 15 |
|
| **D360** | **Steam sign-in is supported in the same manner as the other providers:** a built-in provider beside Google and Discord, link-only, through the one session service, configured on Admin → Auth, on the website and in the Android app. **It stays on the website's plan (D171)**, now written as [`website/STEAM_SIGNIN.md`](../../website/STEAM_SIGNIN.md) and on the roadmap as the `website` milestone *Steam sign-in*. §15 links it. | Planning it inside this document. | 15 |
|
||||||
|
|
||||||
|
During the §14.2 test phase (2026-10-14) the org lead ruled on two points it raised:
|
||||||
|
|
||||||
|
| # | Decision | Rejected | § |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **D361** | **Guarded Crate may depend on NpcSpawn.** NpcSpawn (Codefling) is downloaded and used as is, on the rigs and by operators; Runic Gateway never ships or modifies it (D351). | Treating NpcSpawn as a licence problem that rules Guarded Crate out. | 14.2.1 |
|
||||||
|
| **D362** | **Our teardown removes our TruePVE mappings.** TruePVE never prunes a mapping whose zone id is not numeric, and ours are `rg-…`, so the run that maps a zone unmaps it. | Relying on TruePVE to clean up. | 14.2.1 |
|
||||||
|
|
||||||
|
The org lead answered the phase's Q19–Q25 on 2026-10-14:
|
||||||
|
|
||||||
|
| # | Decision | Rejected | § |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **D363** | **Carbon's console route (Q19): watch Carbon's updates and keep an issue open in Rust-Plugins so it is not forgotten** ([Rust-Plugins#34](https://gitea.whitlocktech.com/RunicGateway/Rust-Plugins/issues/34)). No workaround is built for now; each new Carbon production build is re-tested. | Running commands through Carbon's `CommandManager` by reflection now. | 14.2.1 |
|
||||||
|
| **D364** | **A missing NpcSpawn is reported and refused (Q20):** the hello reports NpcSpawn beside Guarded Crate, the site refuses a guarded-crate step without it and says why, and the operator docs say what NpcSpawn does on load. | Documenting it only. | 14.2.1 |
|
||||||
|
| **D365** | **Dangerous Treasures' warm-up (Q21):** a step that fails because DT is not ready says so (*"Dangerous Treasures is not ready yet"*), retries once on its own after ten minutes, **and offers staff a manual retry** on the run. | Failing with DT's own text; the bridge pre-warming DT at boot. | 14.2.1 |
|
||||||
|
| **D366** | **One Bradley event, two places (Q22):** `bradley.destroyed` carries `where: launch_site | road`, and both count toward *Tankbreaker*. | Two separate events; Launch Site only. | 14.2.1, 13 |
|
||||||
|
| **D367** | **The newer events join §13 (Q23):** the satellite crash, the Deep Sea opening and closing, and the ghost ship's crate (a fourth crate location) are feed events and triggers. **The F-15 flyover is not an event; it can be added to an event as decoration**, a step that calls the flyover (D354). | The F-15 as a feed event or trigger. | 14.2.1, 13 |
|
||||||
|
| **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 |
|
||||||
|
|
||||||
## 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
|
||||||
@@ -2092,6 +2111,227 @@ not with the deferred walks (D331).
|
|||||||
**The phase ends with a findings section per item**, as §11.4.1 and §11.4.4 did, plus the questions it raises.
|
**The phase ends with a findings section per item**, as §11.4.1 and §11.4.4 did, plus the questions it raises.
|
||||||
It ships as one docs PR.
|
It ships as one docs PR.
|
||||||
|
|
||||||
|
#### 14.2.1 What the test phase found (2026-10-14)
|
||||||
|
|
||||||
|
**Builds.** Rust **25902425** (protocol 2634.289.1, public since 2026-10-13), **Oxide 2.0.7830**, **Carbon 2.0.262**
|
||||||
|
(the current production build, released before that Rust update). Both rigs were brought up to date first; the
|
||||||
|
first restart skipped the Rust update again, as on 2026-10-09, and a second start took it. The six plugins came
|
||||||
|
from uMod at the versions in §14.1; Guarded Crate's dependency **NpcSpawn 3.4.8** came from Codefling (D361);
|
||||||
|
Economics 3.9.2 and Backpacks 3.17.8 were added for the all-together run. A throwaway spike plugin logged every
|
||||||
|
hook named below with its arguments, forced the world events through the game's own schedule, and used
|
||||||
|
stand-in players with Steam-shaped ids (the RunicNPC 9d pattern) to down, hack, loot and throw. It, the plugins
|
||||||
|
and every file they wrote were removed afterwards and both rigs restored from backups taken first. Timers were
|
||||||
|
shortened for the test (crate hack 900 → 20 s, helicopter lifetime 30 → 2 min, vendor 1800 → 120 s) and put back.
|
||||||
|
|
||||||
|
**Oxide and Carbon raised the same hooks with the same arguments everywhere, with one exception that matters:
|
||||||
|
on Carbon a plugin's console command cannot be run from another plugin (below, and Q19).**
|
||||||
|
|
||||||
|
##### Rust's own world events (§13)
|
||||||
|
|
||||||
|
**The schedule.** Rust keeps one `EventSchedule` per scheduled event. This build has seven, and each runs the way
|
||||||
|
the game runs it by calling `RunEvent` on its `TriggeredEvent` components. That is the route for D354's step,
|
||||||
|
with no console command and no helper:
|
||||||
|
|
||||||
|
| Schedule (`Key`) | Spawns | Default gap (game hours) |
|
||||||
|
|---|---|---|
|
||||||
|
| `patrol_heli` | `patrolhelicopter` | 36–54 |
|
||||||
|
| `cargo` | `cargoshiptest` | 45–78 |
|
||||||
|
| `chinook` | `ch47scientists.entity` | 27–54 |
|
||||||
|
| `airdrop` | `cargo_plane` | 9–18 |
|
||||||
|
| `travelling_vendor` | `TravellingVendor` (with four NPC turrets) | 45–78 |
|
||||||
|
| **`bradley_road`** (`RoadBradleys`) | **12 Bradleys on the roads at once** | 7.5–18 |
|
||||||
|
| **`f15`** | one `F15` jet flying over at 150 m | 15–30 |
|
||||||
|
|
||||||
|
Two systems run outside the schedule: the **satellite** (players launch or steer it; `satellite.random`
|
||||||
|
crashes one at a random spot) and the **Deep Sea** (`deepsea.open` / `deepsea.close`, also on its own timer).
|
||||||
|
Launch Site's Bradley comes from `BradleySpawner` (`bradley.quickrespawn`).
|
||||||
|
|
||||||
|
**What each §13.2 row raised:**
|
||||||
|
|
||||||
|
| Event | Hook(s), as seen | Who and where |
|
||||||
|
|---|---|---|
|
||||||
|
| Patrol helicopter arrives | `OnEntitySpawned(PatrolHelicopter)` | Spawns at the map edge |
|
||||||
|
| **Helicopter downed** | **`OnPatrolHelicopterKill(heli, info)` fires twice**: at the crippling hit, naming the player, and again at the crash, naming nobody. `OnEntityDeath` fires at the crash, naming nobody | The crippled helicopter flew **4.5 s (Oxide) and 34.7 s (Carbon)** before crashing. D174's 60 s last-hitter rule credited the right player both times, but the first `OnPatrolHelicopterKill` names them outright, with no window |
|
||||||
|
| Helicopter leaves without dying | `OnHelicopterRetire(PatrolHelicopterAI)`, then `OnEntityKill` far off the map with full health (`IsDead()` false) | — |
|
||||||
|
| Launch Site Bradley destroyed | `OnEntityDeath(BradleyAPC, info)` with the killer; `OnCrateSpawned(bradley, crate)` ×3 | Killer named directly |
|
||||||
|
| **Road Bradleys** | `OnBradleyApcInitialize` + `OnEntitySpawned` per Bradley; death as above | **The same `BradleyAPC` class**: a road Bradley is told from Launch Site's only by where it is |
|
||||||
|
| Cargo ship arrives | `OnEntitySpawned(CargoShip)`, then `OnCargoShipSpawnCrate` + `OnCrateDropped` per crate | — |
|
||||||
|
| Cargo docks; leaves | `OnCargoShipHarborApproach(ship, CargoNotifier)` (43 min after spawn on the rig), `OnCargoShipHarborArrived` (5 min later, at Harbor, with a fresh crate), `OnCargoShipHarborLeave` (8 min docked). `OnCargoShipEgress` (leaving the map) did not come within the window | The harbour is the nearest `harbor_*` monument |
|
||||||
|
| Chinook arrives; drops its crate | `OnEntitySpawned(CH47HelicopterAIController)`; **`OnHelicopterDropCrate(ch47)`** then `OnCrateDropped` + `OnCrateLanded` (17 min after the call, at Water Treatment) | Monument = nearest to the crate |
|
||||||
|
| Airdrop called | **A supply signal:** `OnCargoPlaneSignaled(plane, signal)`, whose `signal.creatorEntity` is the thrower. **The schedule:** `OnAirdrop(plane, dropPosition)` only | — |
|
||||||
|
| Airdrop lands; first looted | `OnSupplyDropDropped(drop, plane)` and `OnSupplyDropLanded(drop)`; **the drop's owner is 0**, so "who called it" means remembering plane → thrower. **`OnSupplyDropLanded` fired twice for one drop.** `OnLootEntity(player, drop)` for the loot | — |
|
||||||
|
| **Locked crate** hack started; unlocked; looted | `CanHackCrate(player, crate)` names who; `OnCrateHack(crate)` names nobody; `OnCrateHackEnd(crate)`; `OnLootEntity(player, crate)` | **Where it is:** prefab `codelockedhackablecrate_oilrig` = an oil rig (nearest `oilrig_1` large / `oilrig_2` small); **parent `CargoShip`** = cargo; **`codelockedhackablecrate_ghostship`**, parent `GhostShip` = the Deep Sea; otherwise a Chinook drop. **`wasDropped` is true for cargo's crates too**, so it does not mean "Chinook" |
|
||||||
|
| Oil rig scientists called | Nothing of its own. Starting a hack on an oil-rig crate spawns a reinforcement `CH47HelicopterAIController` at once, which drops 11 `scientistnpc_heavy` within 2 s | The crate's prefab says which rig |
|
||||||
|
| Travelling Vendor arrives; leaves | `OnEntitySpawned(TravellingVendor)`; `OnEntityKill` when it goes. Carbon's catalogue lists `OnVendingShopOpen`/`Opened` for its shop (not exercised) | — |
|
||||||
|
| **Satellite crash** (newer) | `OnEntitySpawned(SatelliteCrash)` as it is launched (about 5 min before impact), then `SatelliteCrashRemains` + five `satellite_crate_N` at impact. **No hook of its own** | Crates burn, unlootable, for `satellite.crate_fire_duration` (300 s) |
|
||||||
|
| **Deep Sea** (newer) | `OnDeepSeaOpen` → `OnDeepSeaOpened` (2.5 min of staged spawning: a floating city, 6 islands, 4 ghost ships crewed by Gen2 `ScientistNPC2`) and `OnDeepSeaClose` → `OnDeepSeaClosed` (35 s). `OnDeepSeaTeleport` for crossings | **All of it is off the main map** (x −4,800 to −7,400 on a 6000 map), so a grid reference there means nothing |
|
||||||
|
| F-15 flyover (newer) | `OnEntitySpawned(F15)` | Cosmetic; nothing to report (Q23) |
|
||||||
|
|
||||||
|
**Hooks to avoid.** `OnHelicopterOutOfCrates` fires **every tick** for a Chinook that carries no crate (a
|
||||||
|
reinforcement Chinook raised it 668 times in 20 s). `OnPatrolHelicopterTakeDamage` fires per hit.
|
||||||
|
|
||||||
|
**Plugin crates are vanilla crates.** Guarded Crate drops an ordinary `HackableLockedCrate`, so every crate hook
|
||||||
|
above fires for it too. §13 must not count a plugin's crate as Rust's own (the integration plan says how).
|
||||||
|
|
||||||
|
##### TruePVE 2.4.6
|
||||||
|
|
||||||
|
- **The zone step works as planned, with no helper.** Two stand-ins in a zone made like §3's (`rg-spike-1`)
|
||||||
|
could not hurt each other under the default ruleset. After `AddOrUpdateMapping("rg-spike-1", "exclude")`
|
||||||
|
returned `true`, a 10-damage hit landed. Stand-ins outside the zone stayed protected, and
|
||||||
|
`RemoveMapping` returned `true` and protection came back at once. Same on both frameworks.
|
||||||
|
- **Persistence.** The mapping is written to `data/TruePVE.json` about 1 s after the call. It survived a TruePVE
|
||||||
|
reload and a full server restart on both rigs. **TruePVE prunes a mapping whose zone is gone only when the
|
||||||
|
zone id is numeric.** Ours are `rg-…`, so a leftover is never pruned, and our teardown removes our mappings
|
||||||
|
(D362).
|
||||||
|
- **`OnUpdatedMappings(Dictionary<string,string>)`** and `OnRemovedMappings` fire about 1 s after each change,
|
||||||
|
carrying the whole table, Dynamic PVP's entries included.
|
||||||
|
- **Ruleset names cannot be read.** There is no API and no console command that lists them (`tpve` with no
|
||||||
|
argument does nothing). `AddOrUpdateMapping` returns **`false` for a name that does not exist**, and the
|
||||||
|
special name `exclude` means "TruePVE does not apply here". The default config has one ruleset, `default`,
|
||||||
|
with "players cannot hurt players" (Q24).
|
||||||
|
- Memory: TruePVE held 123 MB after a reload, the largest of the six.
|
||||||
|
|
||||||
|
##### Dynamic PVP 5.2.0
|
||||||
|
|
||||||
|
- **Every event is off by default, and when switched on, Bradley, the helicopter and the others refuse to make
|
||||||
|
a zone** ("both radius and size are less than or equal to zero"): their default radius is 0. Only cargo
|
||||||
|
(sized to the ship) works out of the box. With a radius set, every general event made its zone.
|
||||||
|
- **Its zones.** `OnCreatedDynamicPVP(zoneId, eventName, position, duration)`. The id is the entity's net id
|
||||||
|
for parented events (cargo, a crate, a supply drop) or a generated number (Bradley, helicopter, satellite),
|
||||||
|
**always numeric**. Cargo's lives as long as the ship (`duration -1`); the others last the configured time,
|
||||||
|
then `OnDeleteDynamicPVP` / `OnDeletedDynamicPVP` and their mapping goes. Each zone is mapped in TruePVE,
|
||||||
|
to `exclude` by default.
|
||||||
|
- **Ours and theirs coexist.** Our mapping stayed in TruePVE's table through Dynamic PVP's creates and deletes,
|
||||||
|
and neither the bridge nor RunicGatewayZones touched its zones. RunicGatewayZones' startup sweep enters
|
||||||
|
connected players into every zone, theirs included, which is what Dynamic PVP needs anyway.
|
||||||
|
- `OnPlayerEnterPVP(player, zoneId)` fired as stand-ins arrived. **`OnPlayerExitPVP` fired twice per player**
|
||||||
|
when a zone was deleted.
|
||||||
|
- A guarded crate's zone was made where its crate spawned, 500 m up, before it fell. A satellite crash made one too.
|
||||||
|
|
||||||
|
##### Dangerous Treasures 3.0.0
|
||||||
|
|
||||||
|
- **`dtevent <level>` works from the bridge's console route on Oxide, and DT picks the spot.** The levels are
|
||||||
|
0 Easy, 1 Medium, 2 Hard. All three started; a success replies with an empty string.
|
||||||
|
- **The first request after every load fails for about ten minutes.** DT builds its spawn grid **lazily, on the
|
||||||
|
first request**, at 1 ms per frame, about 2,800 positions a minute. A 6000 map has 31,188, and every request
|
||||||
|
until the grid is done fails with *"Unable to obtain a valid position"*, the same text it gives when there
|
||||||
|
really is no spot (Q21).
|
||||||
|
- **Hooks, as read:** `OnDangerousEventStarted(Vector3)`, `OnPlayerEnteredDangerousEvent(player, pos, pvp)`,
|
||||||
|
`OnDangerousEventWon(looter, List<ulong> invaders)` when the chest is emptied, and `OnDangerousEventEnded(pos)`.
|
||||||
|
The chest unlocks after 300–480 s and despawns 900 s after that if nobody loots it. The map marker is a vending
|
||||||
|
marker. It allows PvP at its events through TruePVE. It can pay Economics or Server Rewards itself (off by default).
|
||||||
|
- **Its NPCs are `HumanoidNPC : ScientistNPC`**, so RunicNPC treats them as the built-in `scientists` faction.
|
||||||
|
- **Boot cost: its `OnServerInitialized` stalls the server for 8.8 s (Oxide) / 9.4 s (Carbon)**, at every load.
|
||||||
|
- It treats ZoneManager zones, ours included, as places not to spawn.
|
||||||
|
|
||||||
|
##### Guarded Crate 2.0.17 (with NpcSpawn 3.4.8)
|
||||||
|
|
||||||
|
- **It needs NpcSpawn**, a Codefling extension that §14.1 did not list, and it **unloads itself** if NpcSpawn is
|
||||||
|
missing when an event starts. NpcSpawn in turn needs its image files in `data/Images/`, or it unloads too,
|
||||||
|
and on load it **calls a version server at a bare IP address** (`37.153.157.216:5000`). Q20.
|
||||||
|
- **`gcrate position <event> x,y,z` works from the bridge's console route on Oxide.** The handler checks
|
||||||
|
`arg.IsAdmin`, not `IsRcon` as §14.1 said, and the server console passes. A success replies with an empty
|
||||||
|
string. `gcrate start <event>` picks a spot itself and replies "Event starting." The events are Easy (8 guards),
|
||||||
|
Medium, Hard and Elite; the default duration is 1,800 s.
|
||||||
|
- **The event completes when the last guard dies**, crediting that guard's killer:
|
||||||
|
`OnGuardedCrateEventEnded(player, instance)`. **It also fires with player = null** when the guards vanish some
|
||||||
|
other way (seen when NpcSpawn reloaded), which means "no winner". The crate is never locked. Looting it does not
|
||||||
|
end the event. `OnGuardedCrateEventFailed(pos)` is the timeout.
|
||||||
|
- **There is no way to end one instance.** `gcrate stop` with no second argument silently shows help;
|
||||||
|
`gcrate stop all` ends every event and **raises no hook at all**.
|
||||||
|
- **Its guards are NpcSpawn's `CustomScientistNpc : ScientistNPC`**, so RunicNPC's factions treat them as
|
||||||
|
`scientists` (D254, D255): a profile hostile to scientists hunts them, an allied one leaves them alone.
|
||||||
|
|
||||||
|
##### ZLevels Remastered 3.3.1
|
||||||
|
|
||||||
|
- **`GetLevel(ulong, skill)` costs about 3 µs** per call on both rigs, so a full server's six skills at tally
|
||||||
|
time is trivial. **`api_GetPlayerInfo` costs 60–80 µs and creates a record for a Steam id it has never seen.**
|
||||||
|
The bridge reads with `GetLevel` only.
|
||||||
|
- **Lent XP boosts take effect at once.** ZLevels reads the permission on every gain (`getXpMulti`). The default
|
||||||
|
config has `zlevelsremastered.vip1`–`vip3` (×2, ×3, ×4) as permissions and `zlevelsvip1`–`3` (×5–×7) as group
|
||||||
|
names, so §12's `rust.perm.lend` already lends one.
|
||||||
|
- **Levels survive a wipe by default** (`Wipe Data OnNewSave: false`); the `zlevelsremastered.nowipes`
|
||||||
|
permission keeps them when wiping is on. There is still no level-up hook.
|
||||||
|
|
||||||
|
##### Server Rewards 2.0.9
|
||||||
|
|
||||||
|
- **`AddPoints`, `TakePoints` and `CheckPoints` work for an offline player**, through both the `ulong` and `string`
|
||||||
|
overloads. `TakePoints` refuses (false) more than the balance. **`OnPointsUpdated(ulong, int balance)`**
|
||||||
|
follows every change.
|
||||||
|
- **The trap: a negative amount through the id overloads is not refused.** `TakePoints(id, -10)` adds 10 and
|
||||||
|
`AddPoints(id, -10)` takes 10, with no floor. Only the `BasePlayer` and `IPlayer` overloads check the sign.
|
||||||
|
The bridge must refuse a non-positive amount itself.
|
||||||
|
- **It has no wipe handling at all.** Balances carry across wipes until an operator clears them.
|
||||||
|
|
||||||
|
##### All together
|
||||||
|
|
||||||
|
- **Everything loaded at once on both rigs** (the six, NpcSpawn, Economics, Backpacks, RunicNPC, ZoneDomes,
|
||||||
|
ZoneManager, Kits and our three plugins): no errors on either. A full restart with all of them took about 8 min 50 s to
|
||||||
|
"Server startup complete" on Oxide and about 11 min on Carbon, against about 8 min 15 s and 10 min for the same
|
||||||
|
rigs restored (each start includes the egg's steamcmd check). That is under a minute of difference: noise
|
||||||
|
against steamcmd, so no boot cost worth designing around.
|
||||||
|
The slow starters were DT (8.8 s once), NpcSpawn 2.3 s, ZLevels 1.4 s and RunicNPC 0.26 s.
|
||||||
|
- **A busy minute** (two helicopters, three Bradleys, an airdrop, two hits, a DT and a guarded crate, a hack;
|
||||||
|
108 logged hooks) cost, in hook time: NpcSpawn 0.19 s, TruePVE 0.08 s, RunicNPC 0.06 s, Guarded Crate 0.05 s,
|
||||||
|
the bridge 0.04 s, Dynamic PVP 0.03 s, DT and ZoneManager 0.02 s each, the rest under 0.01 s.
|
||||||
|
|
||||||
|
##### Found on the way, outside this plan
|
||||||
|
|
||||||
|
- **Carbon cannot run another plugin's console command, or its own, through `ConsoleSystem.Run` on this Rust
|
||||||
|
build.** Every plugin command (`gcrate`, `dtevent`, `tpve`, `zl.info`) and Carbon's own (`c.version`,
|
||||||
|
`c.reload`) answered *"Command '…' not found"*, while the same text typed at the console worked. Rust's own
|
||||||
|
commands still run, with their replies. On Oxide everything ran. The same Rust update changed `ConsoleSystem`
|
||||||
|
(`arg.Args` is now `StringView[]`; the spike had to stop using `?? new string[0]` on it), and Carbon 2.0.262
|
||||||
|
predates it, so this is most likely Carbon not yet updated. **It also breaks the bridge's own Carbon reload
|
||||||
|
(D180), which runs `c.reload` exactly this way**: `c.reload PopupNotifications` run like the bridge runs it
|
||||||
|
answered *"Command 'c.reload' not found"* and reloaded nothing. Carbon's own `CommandManager` (`Find`, `Execute`) is
|
||||||
|
reachable by reflection. Q19.
|
||||||
|
- On Carbon, a folder the panel's file API creates is not writable by the game (the known rig trap): NpcSpawn
|
||||||
|
failed until its `data/NpcSpawn` folder was deleted and left for it to make.
|
||||||
|
- RunicNPC's `IsScientist` is `ScientistNPC` only, so the Deep Sea's Gen2 `ScientistNPC2` crews are not
|
||||||
|
`scientists` to its factions. Noted for RunicNPC, not this plan.
|
||||||
|
|
||||||
|
##### Questions the phase raises
|
||||||
|
|
||||||
|
**All seven were answered on 2026-10-14 (D363–D369, §10).** Q19, Q21 and Q23 differ from the proposals below; D363, D365 and D367 say how.
|
||||||
|
|
||||||
|
**Q19 — Carbon's console route.** On Carbon today the bridge cannot start a DT event or a guarded crate by
|
||||||
|
console command, and its own `c.reload` (D180) silently does nothing. For example, a *Treasure Hunt* event's
|
||||||
|
"start Dangerous Treasures" step would work on the Oxide rig and do nothing on the Carbon rig. **Proposed:** (a)
|
||||||
|
report it to Carbon and re-test on their next production build; (b) meanwhile, on Carbon only, run commands
|
||||||
|
through Carbon's `CommandManager` found by reflection (as D180 already reflects), keeping `ConsoleSystem.Run` on
|
||||||
|
Oxide; (c) file the reload breakage against Rust-Plugins now, since it affects what is released.
|
||||||
|
|
||||||
|
**Q20 — Saying NpcSpawn is missing.** D361 accepts the dependency; what is left is how an operator finds out. An
|
||||||
|
operator who installs Guarded Crate from uMod must also install NpcSpawn from Codefling (a free download, no licence
|
||||||
|
stated), which contacts its author's server at load. For example, the site would say *"Guarded Crate is loaded but
|
||||||
|
NpcSpawn is missing: its events cannot start."* **Proposed:** the hello reports NpcSpawn beside Guarded Crate; the
|
||||||
|
site refuses a guarded-crate step when NpcSpawn is absent, with that reason; the operator docs say what NpcSpawn does
|
||||||
|
on load.
|
||||||
|
|
||||||
|
**Q21 — Dangerous Treasures' warm-up.** After every restart, the first DT request starts a ten-minute grid build and
|
||||||
|
fails. For example, a *Treasure Hunt* event at 20:00 after a 19:55 restart would fail its step. **Proposed:** the
|
||||||
|
step's failure is reported as "Dangerous Treasures is not ready yet" (the bridge can tell, from DT's text plus a
|
||||||
|
retry), and the run retries the step once after ten minutes. Not proposed: the bridge pre-warming DT at boot, which
|
||||||
|
would need it to reach into DT.
|
||||||
|
|
||||||
|
**Q22 — Two kinds of Bradley.** A road Bradley and Launch Site's are one class. For example, *"Bradley destroyed by
|
||||||
|
Alice at Launch Site"* against *"Road Bradley destroyed by Alice at G7"*. **Proposed:** one event,
|
||||||
|
`bradley.destroyed`, carrying `where: launch_site | road`, and one *Tankbreaker* count for both.
|
||||||
|
|
||||||
|
**Q23 — The newer events.** **Proposed:** the satellite crash, the Deep Sea opening and closing, and a ghost ship's
|
||||||
|
crate join §13 as events and triggers. The F-15 flyover does not (cosmetic). For example, *"A satellite crashed at
|
||||||
|
M30"* and *"The Deep Sea is open"* in the feed.
|
||||||
|
|
||||||
|
**Q24 — TruePVE ruleset names.** They cannot be listed without reading TruePVE's config file. For example, a zone
|
||||||
|
step offering *"PvP rules: default / pvp-arena / exclude"* needs the names. **Proposed:** the step offers
|
||||||
|
"no PvP protection here" (`exclude`) as a checkbox, plus an optional ruleset name typed by staff, which the bridge
|
||||||
|
validates by the call's own `false`.
|
||||||
|
|
||||||
|
**Q25 — Wipes.** ZLevels keeps levels across wipes and Server Rewards keeps points across wipes, by default.
|
||||||
|
**Proposed:** the site shows what the plugin holds and does nothing at a wipe; ZLevels columns are "current level",
|
||||||
|
not "this wipe".
|
||||||
|
|
||||||
### 14.3 The integration plan
|
### 14.3 The integration plan
|
||||||
|
|
||||||
Written after §14.2, in one docs PR, and answered by the org lead before any code. For §13 and for each of the
|
Written after §14.2, in one docs PR, and answered by the org lead before any code. For §13 and for each of the
|
||||||
|
|||||||
Reference in New Issue
Block a user