@@ -98,7 +98,7 @@ requires it once it releases (D220).
| Doc | What it covers |
| Doc | What it covers |
|---|---|
|---|---|
| [PLAN.md](runicnpc/PLAN.md) | **The plan** — what the 2026-09-30 spike found (route A, HumanNPC, NpcSpawn), the org lead's decisions D214–D238, the features, the API, the chat commands, and stages 0–10 with how each is tested and what each found |
| [PLAN.md](runicnpc/PLAN.md) | **The plan** — what the 2026-09-30 spike found (route A, HumanNPC, NpcSpawn), the org lead's decisions D214–D242, the features, the API, the chat commands, and stages 0–10 with how each is tested and what each found |
| [API.md](runicnpc/API.md) | **The API** other plugins call (version 2): owners, every `RunicNpc_*` call, the hooks it raises, and the profile, placement and route shapes |
| [API.md](runicnpc/API.md) | **The API** other plugins call (version 2): owners, every `RunicNpc_*` call, the hooks it raises, and the profile, placement and route shapes |
**Stage 0 closed 2026-09-30** (runicnpc-rust#1/#2, §9): v0.1.0 released and loaded on both rigs. D229–D230 were
**Stage 0 closed 2026-09-30** (runicnpc-rust#1/#2, §9): v0.1.0 released and loaded on both rigs. D229–D230 were
decided with it. **Stage 1 measured 2026-09-30** (§9): six answers on both rigs; D231 was decided with it.
decided with it. **Stage 1 measured 2026-09-30** (§9): six answers on both rigs; D231 was decided with it.
**Stage 2's design answered 2026-09-30:** D232–D238 (§0), which reshape stage 2 (§9). **Stage 2 built and tested
**Stage 2's design answered 2026-09-30:** D232–D238 (§0), which reshape stage 2 (§9). **Stage 2 built and tested
2026-09-30** on both rigs (§9); its API is [API.md](API.md).
2026-09-30** on both rigs (§9); its API is [API.md](API.md).**Stage 3's design answered 2026-09-30:** D239–D242
(§0).
RunicNPC is Runic Gateway's own NPC plugin for Rust servers, in its own repository,
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
[`RunicGateway/runicnpc-rust`](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust). It runs on Oxide and
@@ -52,6 +53,10 @@ architectural or design decision is implemented.
| **D236** | **Respawn is chosen per placement:**`each` (the default: every NPC returns its delay after its own death) or `group` (none return until all are dead, then all return together). | One fixed rule. |
| **D236** | **Respawn is chosen per placement:**`each` (the default: every NPC returns its delay after its own death) or `group` (none return until all are dead, then all return together). | One fixed rule. |
| **D237** | **A deleted profile leaves its placements waiting.** Their NPCs despawn; the placements are kept and shown as "profile missing" in game and on the site; they spawn again if the profile returns. | Refusing the delete; deleting the placements with it. |
| **D237** | **A deleted profile leaves its placements waiting.** Their NPCs despawn; the placements are kept and shown as "profile missing" in game and on the site; they spawn again if the profile returns. | Refusing the delete; deleting the placements with it. |
| **D238** | **A profile's shape** is §2's, as the org lead approved it: `names`, `kits`, `prefab`, `role`, `movement`, `health`, `damageDealt`, `damageTaken`, `aimCone`, `ranges`, `visionCone`, `sleepDistance`, `healthThresholds`, in `data/RunicNPC/profiles.json` with the `managed` flag. Placements and routes have their own files beside it; the optional caps (D227) are in the plugin's config. Stages 5–7 add their own sections. | A smaller stage-2 profile, combat values deferred to stage 5. |
| **D238** | **A profile's shape** is §2's, as the org lead approved it: `names`, `kits`, `prefab`, `role`, `movement`, `health`, `damageDealt`, `damageTaken`, `aimCone`, `ranges`, `visionCone`, `sleepDistance`, `healthThresholds`, in `data/RunicNPC/profiles.json` with the `managed` flag. Placements and routes have their own files beside it; the optional caps (D227) are in the plugin's config. Stages 5–7 add their own sections. | A smaller stage-2 profile, combat values deferred to stage 5. |
| **D239** | **A roamer may be placed wherever Rust's navmesh reaches, a player-built floor included, and the answer warns when it is one** ("this spot is on a player-built structure; if it is destroyed, the NPC falls back to the nearest navmesh"). Sentry-only stays the rule for a spot that is truly off the mesh. Stage 10 shrinks to pasted or custom prefabs and moving platforms (stage 2's finding). | Allowing it silently; keeping anything player-built sentry-only even where the mesh covers it. |
| **D240** | **A route is recorded point by point:**`/rnpc path record <name>` starts it, `/rnpc path point` adds where the admin stands (checked against the navmesh; a bad point is refused with the reason), `/rnpc path undo` drops the last, `/rnpc path save [loop\|back]` writes it, `/rnpc path cancel` discards it. Nothing is drawn on screen (D216). | The same flow with the points drawn on screen; a point dropped every few metres as the admin walks. |
| **D241** | **A placement made in game is named after its profile and a number** (`/rnpc place bandit` answers "Placed bandit-3"), and `/rnpc rename <id> <new>` renames it. `/rnpc near` lists the names. | Bare numbers; the admin naming every placement. |
| **D242** | **`/rnpc place` and `/rnpc here` take `key=value` options in any order after the profile:** `count=`, `respawn=`, `mode=each\|group`, `move=wander\|monument\|route:<name>`, `radius=`. Anything left out takes the profile's default. | `/rnpc place bandit 3 300 group route:gate`; `/rnpc place` then `/rnpc set`. |
**Borrowing, not copying.** NpcSpawn states no licence at all, so its source grants us nothing and is read only as a
**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
description of *what* can be done in Rust. HumanNPC is MIT on uMod, which is GPL-compatible, but §1.2 rules out its
@@ -290,10 +295,13 @@ permissions reach the site's permission manager through the inventory, like any
| Command | Does | Permission |
| Command | Does | Permission |
|---|---|---|
|---|---|---|
| `/rnpc place <profile> [count] [respawn]` | Places at the spot you are looking at, persistent | `runicnpc.place` |
| `/rnpc place <profile> [key=value ...]` | Places at the spot you are looking at, persistent, and answers with its name (D241). Options in any order (D242): `count=`, `respawn=` (seconds), `mode=each\|group`, `move=wander\|monument\|route:<name>`, `radius=` | `runicnpc.place` |
| `/rnpc here <profile> [count]` | The same, where you stand | `runicnpc.place` |
| `/rnpc here <profile> [key=value ...]` | The same, where you stand | `runicnpc.place` |
| `/rnpc remove` | Removes the placement of the NPC you are looking at | `runicnpc.place` |
| `/rnpc remove [placement]` | Removes a placement and its NPCs: the one named, or that of the NPC you are looking at | `runicnpc.place` |
| `/rnpc near [radius]` | Lists placements and live NPCs near you | `runicnpc.place` |
| `/rnpc near [radius]` | Lists placements (by name) and live NPCs near you | `runicnpc.place` |
| `/rnpc path record <name>` · `point` · `undo` · `save [loop\|back]` · `cancel` | Records a route where you walk, point by point (D240) | `runicnpc.place` |
| `/rnpc path list` · `delete <name>` | Lists or deletes routes; a placement on a deleted route waits, as for a deleted profile (D237) | `runicnpc.place` |
| `/rnpc info` | The NPC you are looking at: profile, owner, health, target, state | `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 profiles` | The profiles this server has | `runicnpc.place` |
| `rnpc.profile <create\|set\|delete> ...` (console) | Edits a profile on a standalone server; refused when the site manages them (D221) | `runicnpc.admin` |
| `rnpc.profile <create\|set\|delete> ...` (console) | Edits a profile on a standalone server; refused when the site manages them (D221) | `runicnpc.admin` |
@@ -305,8 +313,11 @@ permissions reach the site's permission manager through the inventory, like any
cost warning (D227): how many RunicNPC NPCs the server now has and what stage 1 measured that number to cost.
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
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
normally, and that includes a player-built floor, which Rust's mesh covers a moment after it is built (stage 2).
is refused with the reason. An event step gets the same answer, as a blocked RaidableBases spot does (D208).
**On a player-built structure the placement is made with a warning** (D239): if the structure is destroyed, the
NPC falls back to the nearest navmesh. Off the mesh (a pasted structure, a custom prefab), 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).
---
---
@@ -349,7 +360,8 @@ shipped no custom mesh. Where it does not reach, a profile is a sentry (§5).
**Stage 10, only if needed:** a point graph recorded in game. An admin walks the route, `/rnpc mesh record <name>`
**Stage 10, only if needed:** a point graph recorded in game. An admin walks the route, `/rnpc mesh record <name>`
samples it, and NPCs move between the points directly, as NpcSpawn's custom meshes do, for:
samples it, and NPCs move between the points directly, as NpcSpawn's custom meshes do, for:
- pasted or player-built bases, and roofs;
- pasted structures, and roofs the mesh does not reach (a player-built floor needs none: Rust's mesh covers it
| A player-built floor 4 m up | **not on the mesh** in the frame it spawned; **on it 0.1–0.3 s later** (stage 2) | the same |
| A player-built floor 4 m up | **not on the mesh** in the frame it spawned; **on it 0.1–0.3 s later** (stage 2) | the same |
| One check | 13.6 µs | 8.3 µs |
| One check | 13.6 µs | 8.3 µs |
Monument roofs (Launch Site, Airfield, Trainyard, the warehouses) are mostly walkable. Player-built floors and
Monument roofs (Launch Site, Airfield, Trainyard, the warehouses) are mostly walkable. This stage read
roofs never are: that is §5's sentry rule, and stage 10.
player-built floors and roofs as never walkable; stage 2 corrected that (the row above), and D239 follows from it.
**5. The cost (D227's numbers).** Seven 60 s phases on a field 400 m from any monument: a baseline, 1, 10 and 100
**5. The cost (D227's numbers).** Seven 60 s phases on a field 400 m from any monument: a baseline, 1, 10 and 100
of ours idle, the same 100 shooting 20 stand-ins (whose damage the harness zeroes, so none die), a fresh 100 set to
of ours idle, the same 100 shooting 20 stand-ins (whose damage the harness zeroes, so none die), a fresh 100 set to
@@ -584,7 +596,7 @@ What building it found:
- **Rust's navmesh covers a new player-built floor in 0.1–0.3 s** (2–3 frames), on both rigs. Stage 1's "a
- **Rust's navmesh covers a new player-built floor in 0.1–0.3 s** (2–3 frames), on both rigs. Stage 1's "a
player-built floor is never on the mesh" (Q4) sampled in the same frame the floor spawned, so it is wrong: a
player-built floor is never on the mesh" (Q4) sampled in the same frame the floor spawned, so it is wrong: a
roamer can stand on a player-built floor a moment after it exists. §5's sentry rule, §8 and stage 10 were
roamer can stand on a player-built floor a moment after it exists. §5's sentry rule, §8 and stage 10 were
written on stage 1's reading. **Open for the org lead.**
written on stage 1's reading; the org lead answered with D239, and those sections now follow it.
- **Rust's navmesh sampler reaches further down than across.** A roamer asked to stand on a roof found the
- **Rust's navmesh sampler reaches further down than across.** A roamer asked to stand on a roof found the
ground 4.7 m below. A roamer's spot must now be within 2 m, up or down, of the navmesh it is put on.
ground 4.7 m below. A roamer's spot must now be within 2 m, up or down, of the navmesh it is put on.
- **A brain can think before Unity has started it.** For that moment it has no navigator, so RunicNPC leaves that
- **A brain can think before Unity has started it.** For that moment it has no navigator, so RunicNPC leaves that
@@ -596,12 +608,15 @@ What building it found:
### Stage 3 — In game
### Stage 3 — In game
The chat and console commands (§5), their permissions, the navmesh placement check, respawn delays, and recording
The chat and console commands (§5), their permissions, the navmesh placement check with D239's warning on a
routes in game (`/rnpc path record`, then setting points, D234). A placement may name a movement override and a
player-built structure, respawn delays, and recording routes in game point by point (D234, D240). Placements made
respawn mode (D233, D236).
in game are named after their profile and a number, and renamable (D241). `/rnpc place` and `/rnpc here` take
`key=value` options, a movement override and a respawn mode among them (D233, D236, D242).
**Tested by**a written in-game walk: place, remove, look-at info, respawn, record a route and place a patrol on it,
**Tested by**the harness for everything a console can reach (the commands' parsing, refusals, names, and the
restart. **This is the first stage that needs a player on a rig.**
route file a recording writes), then a written in-game walk: place, remove, rename, look-at info, respawn, record a
route and place a patrol on it, place on a player-built floor and destroy it, restart. **This is the first stage
that needs a player on a rig.**
### Stage 4 — Runic Gateway integration
### Stage 4 — Runic Gateway integration
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.