From aec01342d5c2b7b0153dc4bc69a67056087e381c Mon Sep 17 00:00:00 2001 From: wtclaude Date: Tue, 29 Sep 2026 20:38:28 -0500 Subject: [PATCH] docs(runicnpc): record the org lead's answers, D221-D228 Standalone use too (D221), placements live on the server (D222), our own scientist subclass and brain (D223), installer and egg ship it plus a Gitea release and possibly other download sites (D224), per-profile kills public and in titles (D225), edge to main (D226), no default caps but a measured cost warning wherever NPCs are added (D227), and the names RunicNPC, /rnpc, runicnpc.* (D228). The sections they change are updated: standalone profiles, the console profile verb, cost warnings, stage 1's measurements, and publishing. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- modules/rust/PLAN_REDESIGNS.md | 2 +- runicnpc/PLAN.md | 42 ++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/modules/rust/PLAN_REDESIGNS.md b/modules/rust/PLAN_REDESIGNS.md index b2c3143..b4f5103 100644 --- a/modules/rust/PLAN_REDESIGNS.md +++ b/modules/rust/PLAN_REDESIGNS.md @@ -8,7 +8,7 @@ they can be read together. **Built so far:** §1, walked 2026-09-28 (§1.9); §5 rig walk still to come (§5.8, D209); §3, built and walked on the rigs and the site 2026-09-29, its in-game rows still to come (§3.4); §4, built 2026-09-29 (§4.1). **§6 was spiked on 2026-09-30, and the org lead chose neither route: Runic Gateway writes its own NPC plugin, RunicNPC, planned in -[`../../runicnpc/PLAN.md`](../../runicnpc/PLAN.md) (D214–D220), and the rest of this plan waits for it.** +[`../../runicnpc/PLAN.md`](../../runicnpc/PLAN.md) (D214–D228), and the rest of this plan waits for it.** This is a companion to [`PLAN_FIXES.md`](PLAN_FIXES.md) and [`PLAN.md`](PLAN.md). Where they disagree, this document is later and wins. Its decisions continue PLAN_FIXES' numbering at **D188**. diff --git a/runicnpc/PLAN.md b/runicnpc/PLAN.md index 39d76b4..53235ba 100644 --- a/runicnpc/PLAN.md +++ b/runicnpc/PLAN.md @@ -1,6 +1,6 @@ # RunicNPC — the plan -**Status:** plan, written 2026-09-30. No code yet. **Its questions (§11) are open.** +**Status:** plan, written 2026-09-30. No code yet. **Its eight questions (§11) were answered the same day: D221–D228 (§0).** RunicNPC is Runic Gateway's own NPC plugin for Rust servers, in its own repository, [`RunicGateway/runicnpc-rust`](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust). It runs on Oxide and @@ -30,6 +30,14 @@ architectural or design decision is implemented. | **D218** | **Loot tables are RunicNPC's own for now.** Links to other loot plugins can come later. | AlphaLoot, CustomLoot, Loottable or LootManager as a dependency. | | **D219** | **Rust's own navmesh is used. A custom navigation mesh is a later stage, built only if a real need appears** (§8, stage 10). | Shipping NpcSpawn-style point meshes from the start. | | **D220** | **The plugin is built and tested in stages before the Rust plan resumes, and it then becomes a plugin `module-rust` requires.** | Building it alongside the remaining redesigns. | +| **D221** | **RunicNPC works without Runic Gateway too** (Q1). Standalone, its profiles live in its data file and console commands edit them. On a Runic Gateway server, the site's profiles replace that file on every push, and in-game profile edits are refused with "this server's profiles are managed by its website". | Runic Gateway only. | +| **D222** | **Placements live on the server** (Q2). They keep respawning while the site is offline; the site catches up when it returns, and lists and edits them from then on. | The site as the only record. | +| **D223** | **Our NPC is its own subclass of Rust's scientist, with its own brain** (Q3). The brain is kept small and tested on Rust's `staging` branch before each forced wipe. | Rust's scientist plus patches. | +| **D224** | **The installer and the egg ship it from the bundle, pinned and checksummed** (Q6). It is also published as a release on Gitea, and possibly on other sites for anyone to download. | Operators installing it themselves. | +| **D225** | **Per-profile kills are public and usable in titles** (Q4), like the NPC kills column today. | Admins only. | +| **D226** | **`edge` → `main`**, like the other Rust repositories (Q5, D18). | PRs straight to `main`. | +| **D227** | **No default caps.** Instead, a realistic warning of what a number of NPCs costs and its impact on the server, measured in stage 1, shown wherever NPCs are added (Q7). Caps exist only when an admin sets them. | 100 / 50 / 10 per second by default. | +| **D228** | **`RunicNPC.cs`, console name `RunicNPC`, chat command `/rnpc`, permissions `runicnpc.*`** (Q8). | — | **Borrowing, not copying.** NpcSpawn states no licence at all, so its source grants us nothing and is read only as a description of *what* can be done in Rust. HumanNPC is MIT on uMod, which is GPL-compatible, but §1.2 rules out its @@ -132,6 +140,11 @@ Stage 1 proves the swap on both frameworks before anything is built on it. This split is what HumanNPC got wrong: the *placement* is persistent data, the *NPC* never is. There is exactly one save path, a flag marks it dirty on every change (including removal), and nothing else is written. +**Standalone or managed (D221).** Without a site, RunicNPC reads its profiles from its own data file and console +commands edit them. With a site, the bridge pushes the site's profiles, which replace the file; the file records that +it is managed, and in-game profile edits are refused with "this server's profiles are managed by its website". +Placements stay the server's either way (D222): the site reads them and edits them through the bridge. + **A profile is a named description of an NPC:** - **Appearance:** name (or a list to pick from), the kits it wears, body type, and the prefab it starts from. @@ -183,7 +196,7 @@ Borrowed ideas are marked with their source; everything else is new. Stage numbe | **Admin placement in game** by chat command (§5), persistent, with respawn delays; the site lists and edits the placements | 3, 4 | | **Correct accounting**: an NPC is never counted as a player; its name reaches the killfeed, event log and death screen; kills are counted per profile | 2, 4 | | **Per-profile stats and titles**: "Warden kills", boss kills; the killing blow and every player who did damage | 4 | -| **Caps**: per server, per owner, per profile, a spawn-rate limit, and a performance budget | 2 | +| **Cost warnings, not default caps (D227)**: what N NPCs cost the server, measured in stage 1, shown when an admin places them, on the site's profile and placement pages, and in `doctor`. Caps (per server, owner or profile, and a spawn rate) exist only when an admin sets them | 2, 4 | | **Event ownership**: run-owned NPCs with the bridge's restart and teardown guarantees | 4 | | **Waves and phases** without a core change: a phase advances on "n NPCs of profile X died", and on "the boss fell below 50%" | 4, 6 | | **Zone tethering**: an NPC held inside a ZoneManager zone (from redesign §3) | 5 | @@ -193,7 +206,7 @@ Borrowed ideas are marked with their source; everything else is new. Stage numbe | **Quest-giver or vendor**: passive, press E for a site-written message | 6 | | **The live map and the app**: markers for event NPCs and bosses, "guards left: 3/8" | 8 | | **Operator-friendly**: shipped in the bundle, no phone-home, no image files, no boot scan; `doctor` reports it; RunicNPC's permissions appear in the site's permission manager like any plugin's | 2, 4 | -| **An API for other plugins**, versioned, documented, and usable without Runic Gateway at all if §11 Q1 says so | 2, 9 | +| **An API for other plugins**, versioned, documented, and usable without Runic Gateway at all (D221) | 2, 9 | --- @@ -209,7 +222,7 @@ matched by accident. The first draft, fixed in stage 2 and published as `docs/ru | `RunicNpc_Spawn(Vector3 at, string profile, string owner, JObject overrides)` → `BasePlayer` | Spawns one NPC, or returns null and logs why. `overrides` may change any profile value for this NPC only. | | `RunicNpc_Despawn(ulong netId)` / `RunicNpc_DespawnOwner(string owner)` → `int` | Removes one, or all of an owner's. | | `RunicNpc_List(string owner)` → `List>` | The owner's live NPCs: id, profile, name, position, health. | -| `RunicNpc_Profiles()` / `RunicNpc_SetProfiles(JObject all)` | Reads, or replaces, the profile set (§11 Q1 decides who may). | +| `RunicNpc_Profiles()` / `RunicNpc_SetProfiles(JObject all)` | Reads, or replaces, the profile set. Replacing marks the server as managed by a site (D221). | | `RunicNpc_Placements()` / `RunicNpc_SetPlacement(...)` / `RunicNpc_RemovePlacement(string id)` | The persistent placements (§2). | | `RunicNpc_IsRunicNpc(BaseEntity e)` → `bool`, `RunicNpc_ProfileOf(BaseEntity e)` → `string` | Lets another plugin tell our NPCs apart. | @@ -236,11 +249,15 @@ permissions reach the site's permission manager through the inventory, like any | `/rnpc near [radius]` | Lists placements and live NPCs near you | `runicnpc.place` | | `/rnpc info` | The NPC you are looking at: profile, owner, health, target, state | `runicnpc.place` | | `/rnpc profiles` | The profiles this server has | `runicnpc.place` | +| `rnpc.profile ...` (console) | Edits a profile on a standalone server; refused when the site manages them (D221) | `runicnpc.admin` | | `/rnpc tp ` | Teleports you to a placement | `runicnpc.admin` | | `/rnpc respawn [placement\|all]` | Respawns a placement's NPC now | `runicnpc.admin` | | `/rnpc clear ` | Removes every NPC of an owner (e.g. a stuck run) | `runicnpc.admin` | -**Every placement is checked against Rust's navmesh (D219).** On the navmesh, the NPC roams, chases and fights +**Every placement is checked against Rust's navmesh (D219).** Every placement also answers with the +cost warning (D227): how many RunicNPC NPCs the server now has and what stage 1 measured that number to cost. + +On the navmesh, the NPC roams, chases and fights normally. Off it (a roof, inside a base, a pasted structure), a **stationary** profile is placed and a roaming one is refused with the reason. An event step gets the same answer, as a blocked RaidableBases spot does (D208). @@ -303,7 +320,7 @@ needs a player in game writes that walk down, and it is done before the stage cl - `LICENSE.md` (GPL-3.0-or-later), `CODE_OF_CONDUCT.md`, `CONTRIBUTING.md` (with the AI-disclosure rule), `SECURITY.md`, `README.md`, PR template. -- Branches `main` and `edge` (§11 Q5). +- Branches `main` and `edge` (D226). - **CI on every PR:** a static reader like Rust-Plugins' `checkPlugin.js`: every hook it declares is known, none returns a value where one would cancel a game action, and the API version in the source equals the manifest's. - **Release on `main`:** `runicnpc-.tar.gz` holding `runicnpc/RunicNPC.cs` and a `manifest.json` (version, API @@ -324,7 +341,8 @@ A harness plugin (`tools/RunicNpcHarness.cs`) answers, on **both** rigs, what st 2. `enableSaving = false` holds across a hard kill and restart (§1.3 did this for NpcSpawn only). 3. Which ranges and scales hold on the brain without our own states, and which need them. 4. Navmesh coverage: a placement check at every monument of the 6000 map, and on a roof. -5. The cost of spawning 1, 10 and 100, and of 100 idle and 100 fighting, as the server's frame time. +5. The cost of spawning 1, 10 and 100, and of 100 idle and 100 fighting, as the server's frame time. These numbers + are the cost warning's (D227), so they are measured on both rigs and on the 6000 map. 6. The name in the death screen, and what the bridge publishes for a kill by and of our NPC. **Done when** each has a measured answer written into this plan, and the org lead has picked anything the answers @@ -334,7 +352,8 @@ leave open. - The subclass, profiles (read from RunicNPC's own data file for now), kits (random pick), appearance, combat values, roamer and sentry roles, sleep. -- Owners and lifetimes (§2); placements persisted with one dirty flag; caps and a spawn-rate limit. +- Owners and lifetimes (§2); placements persisted with one dirty flag; standalone profiles and the managed flag + (D221); optional caps, off by default, and the cost warning from stage 1's measurements (D227). - The API (§4) and its hooks; `docs/runicnpc/API.md`. **Tested by** the harness asserting each call's result (PASS/FAIL lines read back over the panel), plus restart @@ -369,7 +388,7 @@ Across four repositories, split into 4a–4c if it grows: - per-profile stats and titles. - **Installer and egg:** - RunicNPC becomes a third artefact in the Rust bundle, pinned and checksummed like the plugin and the sidecar - (§11 Q6); + (D224); - `doctor` reports it missing or edited; - the egg installs it. @@ -415,6 +434,8 @@ in the Android app. - **The whole acceptance walk on both rigs, with a player.** - **Operator documentation:** `docs/runicnpc/` gets INSTALL and a command reference; `module-rust`'s OPERATING notes list it as required. +- **Publishing (D224):** the Gitea release is the source of record. Other download sites (uMod, Codefling) are + decided then, each against its own rules, for example uMod's review guidelines. - **v1.0.0 released, and `module-rust` then requires it:** - the bundle lists it; - the module refuses NPC steps on a server without it, saying why; @@ -444,6 +465,9 @@ does not wait for a bridge release, or the reverse. ## 11. Questions for the org lead +**All eight were answered on 2026-09-30, as recommended except Q6 (D224 adds Gitea and other download sites) +and Q7 (D227: no default caps, a cost warning instead). They are kept as asked.** + **Q1. Can RunicNPC be used without Runic Gateway?** Picture a server owner who installs only RunicNPC, from uMod. - **(a, recommended) Yes.** Without a site, profiles live in RunicNPC's data file and console commands edit them.