docs(runicnpc): stage 9b built; INSTALL and COMMANDS; RunicNPC required (D315, D316) #325
109
runicnpc/COMMANDS.md
Normal file
109
runicnpc/COMMANDS.md
Normal file
@@ -0,0 +1,109 @@
|
||||
# RunicNPC — command reference
|
||||
|
||||
Every command RunicNPC answers, with the permission it needs. This page is the reference; how the commands came
|
||||
to be is in [PLAN.md](PLAN.md) §5 and stage 3, and installing RunicNPC is in [INSTALL.md](INSTALL.md).
|
||||
|
||||
## How commands are reached
|
||||
|
||||
- **In chat:** `/rnpc <verb> ...`. `/rnpc` alone lists the verbs you are allowed to use.
|
||||
- **In a console:** `rnpc.<verb> ...`, in a player's F1 console, the server console or RCON. The same code answers
|
||||
both, with the same permissions.
|
||||
- **Console-only verbs** (`profile`, `faction`) answer only in a console. In chat they tell you to open F1.
|
||||
- **The server console has every permission.** It has no position, so `place`, `here` and `path point` take
|
||||
`at=x,y,z` (and `yaw=`) there. In game those two options are refused.
|
||||
|
||||
## Permissions
|
||||
|
||||
| Permission | Grants |
|
||||
|---|---|
|
||||
| `runicnpc.place` | Placing, removing, renaming and inspecting NPCs, recording routes, and `follow`. |
|
||||
| `runicnpc.admin` | Everything, `runicnpc.place` included: teleporting, respawning, clearing an owner, and the console-only verbs. |
|
||||
|
||||
On a Runic Gateway server, both permissions show in Admin → Rust → Permissions like any plugin's, and the site
|
||||
grants them there.
|
||||
|
||||
## Placements
|
||||
|
||||
A **placement** is a spot where a profile's NPCs stand and come back after they die. It is saved on the server
|
||||
(`data/RunicNPC/placements.json`) and survives restarts. Placements made in game are named after their profile and
|
||||
a number (`bandit-1`) and can be renamed.
|
||||
|
||||
| Command | Does | Permission |
|
||||
|---|---|---|
|
||||
| `/rnpc place <profile> [options]` | Places the profile's NPCs where you are looking, and answers with the placement's name and the cost warning. | `runicnpc.place` |
|
||||
| `/rnpc here <profile> [options]` | The same, where you stand. | `runicnpc.place` |
|
||||
| `/rnpc remove [placement]` | Removes a placement and its NPCs: the one you name, or the one the NPC you are looking at belongs to. | `runicnpc.place` |
|
||||
| `/rnpc rename <placement> <new name>` | Renames a placement. Its live NPCs stay where they are. | `runicnpc.place` |
|
||||
| `/rnpc near [radius]` | Lists the placements and live NPCs around you, with distances and any note (waiting, off the navmesh). | `runicnpc.place` |
|
||||
| `/rnpc info` | The NPC you are looking at: name, profile, placement or owner, health, state and target. | `runicnpc.place` |
|
||||
| `/rnpc profiles` | The profiles this server has, and any it refuses with the reason. | `runicnpc.place` |
|
||||
| `/rnpc follow <placement> <player\|me\|off>` | The placement's live NPCs escort a player. They fight whoever attacks that player, and walk back to their spot if the player dies or leaves. `off` ends it. | `runicnpc.place` |
|
||||
| `/rnpc tp <placement>` | Teleports you to a placement. | `runicnpc.admin` |
|
||||
| `/rnpc respawn <placement\|all>` | Respawns a placement's NPCs now, or every placement's. | `runicnpc.admin` |
|
||||
| `/rnpc clear <owner>` | Removes every NPC an owner has, for example a stuck event run (`run:42`) or another plugin (`plugin:Name`). | `runicnpc.admin` |
|
||||
|
||||
**Options for `place` and `here`,** in any order:
|
||||
|
||||
| Option | Means | Default |
|
||||
|---|---|---|
|
||||
| `count=<n>` | How many NPCs stand there. | 1 |
|
||||
| `respawn=<seconds>` | How long after a death before the NPC comes back. | 300 |
|
||||
| `mode=each\|group` | `each`: every NPC comes back on its own timer. `group`: none comes back until all are dead, then all at once. | `each` |
|
||||
| `move=wander\|monument\|route:<name>` | How the NPCs move: wander around the spot, roam the monument they stand in, or walk a recorded route. | the profile's |
|
||||
| `radius=<m>` | How far a wanderer strays. | the profile's |
|
||||
| `tether=<zone>` | A ZoneManager zone the NPCs never leave. Needs ZoneManager. | none |
|
||||
|
||||
**Every placement is checked against Rust's navmesh.** A roamer must stand on it. Only a sentry may stand off it.
|
||||
On a player-built floor the placement is made with a warning: if the floor is destroyed, the NPCs fall back to the
|
||||
nearest navmesh, and return to their spot once it is rebuilt.
|
||||
|
||||
## Routes
|
||||
|
||||
A route is a line of points an NPC walks. It is recorded in game where you walk, one point at a time.
|
||||
|
||||
| Command | Does | Permission |
|
||||
|---|---|---|
|
||||
| `/rnpc path record <name>` | Starts recording a route. | `runicnpc.place` |
|
||||
| `/rnpc path point` | Adds the spot you stand on. A point the previous one has no walkable path to is refused. | `runicnpc.place` |
|
||||
| `/rnpc path undo` | Drops the last point. | `runicnpc.place` |
|
||||
| `/rnpc path save [loop\|back]` | Saves the route: `loop` walks back to the first point and round again, `back` walks it back and forth. | `runicnpc.place` |
|
||||
| `/rnpc path cancel` | Abandons the recording. | `runicnpc.place` |
|
||||
| `/rnpc path list` | The saved routes. | `runicnpc.place` |
|
||||
| `/rnpc path delete <name>` | Deletes a route. A placement that walks it waits until a route of that name exists again. | `runicnpc.place` |
|
||||
|
||||
A recording is kept in memory only: a reload of RunicNPC discards it.
|
||||
|
||||
## Console only
|
||||
|
||||
| Command | Does | Permission |
|
||||
|---|---|---|
|
||||
| `rnpc.profile list` | The same as `/rnpc profiles`. | `runicnpc.admin` |
|
||||
| `rnpc.profile show <name>` | A profile in full, as JSON, and why it is refused if it is. | `runicnpc.admin` |
|
||||
| `rnpc.profile create <name> [from=<profile>]` | A new profile, empty or copied from another. | `runicnpc.admin` |
|
||||
| `rnpc.profile set <name> <field> <value>` | Sets one field of a profile. | `runicnpc.admin` |
|
||||
| `rnpc.profile delete <name>` | Deletes a profile. Placements that use it wait until a profile of that name exists again. | `runicnpc.admin` |
|
||||
| `rnpc.faction list` | The faction table: which factions fight, ignore or help each other. | `runicnpc.admin` |
|
||||
| `rnpc.faction set <a> <b> hostile\|neutral\|allied` | Sets one pair, both ways. | `runicnpc.admin` |
|
||||
| `rnpc.faction clear <a> <b>` | Removes a pair. | `runicnpc.admin` |
|
||||
| `rnpc.status` | The version and API, the hooks that have fired, the navmesh, the swap's field list, NPC counts by owner, profiles, placements, routes, what fighting costs a think, the caps, and the cost warning. | server console, RCON, or an admin's F1 |
|
||||
| `rnpc.reload` | Re-reads profiles and routes after a hand edit of their files. | server console, RCON, or an admin's F1 |
|
||||
| `rnpc.help` | The same list as `/rnpc` alone. | anyone; it lists only what they may use |
|
||||
|
||||
**On a Runic Gateway server the website manages the profiles and the faction table** (Admin → Rust → NPC
|
||||
profiles). There, `rnpc.profile create|set|delete` and `rnpc.faction set|clear` are refused with "managed by its
|
||||
website", and `show` and `list` still answer. On a server without a website they are how profiles are made.
|
||||
|
||||
## Reading `rnpc.status`
|
||||
|
||||
```
|
||||
RunicNPC 1.0.0 api=6 hooks=12 fired=12 silent=0
|
||||
framework=oxide navmesh=ready swap fields: npc=55/55 brain=32/32 missing=- added=-
|
||||
npcs=14 awake=9 goingHome=0 asleep=5 owners: placement:bandit-1=3 run:42=11
|
||||
```
|
||||
|
||||
- **`silent`** names a hook that has never fired. After a Rust or framework update, a hook that stays silent is the
|
||||
first sign it was renamed: neither framework reports a hook that matches nothing.
|
||||
- **`navmesh=building`** after a Rust update means Rust is still building the map's navmesh (about ten minutes on a
|
||||
large map). Placements wait for it.
|
||||
- **`swap fields`**: `missing` or `added` other than `-` means Rust's scientist has changed since this RunicNPC was
|
||||
released. Update RunicNPC.
|
||||
132
runicnpc/INSTALL.md
Normal file
132
runicnpc/INSTALL.md
Normal file
@@ -0,0 +1,132 @@
|
||||
# Installing RunicNPC
|
||||
|
||||
RunicNPC is Runic Gateway's own NPC plugin for Rust, on Oxide and Carbon. On a Runic Gateway server it is
|
||||
**required**: every NPC an event places is RunicNPC's, Rust's own scientists included (D310). It also runs on any
|
||||
Rust server on its own, driven by its `/rnpc` commands and by other plugins through its API.
|
||||
|
||||
Every command is in [COMMANDS.md](COMMANDS.md), the API is in [API.md](API.md), and the design of record is
|
||||
[PLAN.md](PLAN.md).
|
||||
|
||||
## What it needs
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| **Oxide 2.0.7726 or Carbon 2.0.259**, or newer | The builds it was tested on. Its release manifest states them, and the installer checks them. |
|
||||
| **[Kits](https://umod.org/plugins/kits)** | **Required.** A profile equips its NPCs with Kits kits, and RunicNPC declares `// Requires: Kits`, so neither framework loads it without Kits. |
|
||||
| **[ZoneManager](https://umod.org/plugins/zone-manager)** | Optional. Only a tether (`tether=` on a placement, or an event's "keep inside the zone") uses it. |
|
||||
|
||||
**Kits and ZoneManager are reported, never installed.** The installer and `doctor` say when either is missing, and
|
||||
you install them from uMod. Runic Gateway does not fetch third-party plugins.
|
||||
|
||||
**Do not reload Kits while an event runs.** RunicNPC requires Kits, so both frameworks reload RunicNPC with it, and
|
||||
every NPC it had spawned is gone. Placements come back on their own; an event's NPCs do not (D283).
|
||||
|
||||
## On a Runic Gateway server
|
||||
|
||||
There is nothing to do by hand. RunicNPC is part of the Rust **bundle**, the exact set of releases CI has checked
|
||||
together, beside the bridge plugin and the sidecar:
|
||||
|
||||
- **The installer** (`install --game rust`, `update --game rust`) places `RunicNPC.cs` beside the bridge, checked
|
||||
against the bundle's checksum. It refuses a bundle without RunicNPC. `doctor --game rust` fails when RunicNPC is
|
||||
missing or was never installed, and warns when the file was edited by hand. `uninstall` removes `RunicNPC.cs`
|
||||
and keeps `data/RunicNPC/`. The installer's own guide is [../rust-link/INSTALL.md](../rust-link/INSTALL.md).
|
||||
- **The Pterodactyl egg** places it the same way on every install and reinstall.
|
||||
|
||||
Once the server has started, **Admin → Rust → Servers** shows the server as complete. A server without RunicNPC, or
|
||||
with a RunicNPC older than the bridge needs, reads "Incomplete" with the reason, and every Place NPCs step on it is
|
||||
refused with "needs RunicNPC". **Admin → Rust → NPC profiles** shows each server's RunicNPC version and API.
|
||||
|
||||
On such a server the website owns the NPC profiles and the faction table. It pushes them to RunicNPC, and the
|
||||
console commands that would edit them are refused. Placements stay on the server: they can be made in game with
|
||||
`/rnpc place` or from the site's map, and both see the same list.
|
||||
|
||||
## Without Runic Gateway
|
||||
|
||||
1. Download `runicnpc-<version>.tar.gz` and `SHA256SUMS` from the
|
||||
[releases](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust/releases), and check the tarball:
|
||||
`sha256sum -c SHA256SUMS`.
|
||||
2. Install Kits from uMod if the server does not have it.
|
||||
3. Copy `runicnpc/RunicNPC.cs` from the tarball into `oxide/plugins/` or `carbon/plugins/`. The framework compiles
|
||||
and loads it at once.
|
||||
4. In the server console: `rnpc.status`. It should say `navmesh=ready` (a fresh map builds it for several minutes
|
||||
after the first boot) and `swap fields: ... missing=- added=-`.
|
||||
|
||||
On its first load RunicNPC writes four example profiles (a raider, a camp guard, a sniper and the Juggernaut boss)
|
||||
and their kits into Kits' data file, so a fresh server has something to place at once. Grant `runicnpc.place` or
|
||||
`runicnpc.admin` to whoever places NPCs, then `/rnpc place raider` where you look.
|
||||
|
||||
**Do not create `data/RunicNPC/` yourself.** RunicNPC makes it on first load. A directory made from outside the game,
|
||||
for example in a panel's file manager, is not writable by the game.
|
||||
|
||||
## Its files
|
||||
|
||||
| File | What it holds | Who writes it |
|
||||
|---|---|---|
|
||||
| `plugins/RunicNPC.cs` | The plugin. | The installer, the egg or you. |
|
||||
| `config/RunicNPC.json` | Caps (below) and the spawn budget. | You. |
|
||||
| `data/RunicNPC/profiles.json` | The profiles and the faction table. | The website on a Runic Gateway server (`"managed": true`); otherwise `rnpc.profile` and `rnpc.faction`. |
|
||||
| `data/RunicNPC/placements.json` | The placements. | `/rnpc`, the API, and the website's map. |
|
||||
| `data/RunicNPC/routes.json` | The recorded routes. | `/rnpc path`. |
|
||||
| `data/RunicNPC/state.json` | Its own bookkeeping. | RunicNPC. |
|
||||
|
||||
After editing `profiles.json` or `routes.json` by hand, run `rnpc.reload`.
|
||||
|
||||
## The cost warning, and caps
|
||||
|
||||
**RunicNPC has no caps by default** (D227). Instead, every placement and every event step answers with what the
|
||||
server's RunicNPC NPCs cost, and `rnpc.status` ends with the same lines:
|
||||
|
||||
```
|
||||
RunicNPC: 100 NPC(s) on this server. Measured on a test server, they add about 3 ms to every server frame while
|
||||
idle with a player near enough to wake them, and about 0.5 ms once they sleep with nobody near. If all fight at
|
||||
once they add about 9 ms, and Rust's shared 2 ms AI budget then lets each react only every 3 s. ...
|
||||
```
|
||||
|
||||
What it means:
|
||||
|
||||
- **Idle NPCs are cheap, and sleeping ones cheaper.** NPCs that stand, wander or patrol with nobody to fight add a
|
||||
little to a frame. One with no player within its sleep distance (160 m by default) walks home and sleeps, and
|
||||
then costs about a sixth of what it did awake.
|
||||
- **Fighting NPCs hit Rust's AI budget before the frame rate.** All of Rust's human NPCs, its own scientists
|
||||
included, share `aithinkmanager.framebudgetms` (2 ms a frame). With many fighting at once, each one reacts less
|
||||
often. The server stays smooth, and the NPCs get slower to react.
|
||||
- **The figures are measured, not guessed.** Stage 9 measured the released NPC on both frameworks, on a 6000
|
||||
map, as what 100 add to the median server frame of the same session's empty server. Beside it is Rust's own
|
||||
scientist, unchanged but for RunicNPC's swap, measured the same day: fighting costs what Rust's own AI costs.
|
||||
|
||||
| 100 NPCs, over an empty server | RunicNPC on Oxide | RunicNPC on Carbon | Rust's scientist (Oxide / Carbon) |
|
||||
|---|---|---|---|
|
||||
| Idle, a player near enough to keep them awake | +2.7 to +2.9 ms | +1.5 ms | +1.0 / +1.0 ms |
|
||||
| Idle, asleep with nobody within 160 m | — | +0.5 ms | — |
|
||||
| Fighting 20 players | +7.2 ms | +9.0 ms | +7.5 / +8.0 ms |
|
||||
|
||||
The warning uses the worst of these, rounded up: 3 ms awake, 0.5 ms asleep and 9 ms fighting per 100. The awake
|
||||
figure on Oxide is higher than it should be and is being worked on
|
||||
([runicnpc-rust#13](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust/issues/13)). Your server's own
|
||||
figures depend on its CPU, its map and its players.
|
||||
|
||||
To cap them anyway, set any of these in `config/RunicNPC.json` and reload RunicNPC (0 means none):
|
||||
|
||||
| Key | Caps |
|
||||
|---|---|
|
||||
| `caps.total` | RunicNPC's NPCs on the server. |
|
||||
| `caps.perOwner` | NPCs of one owner: one placement, one event run, or one other plugin. |
|
||||
| `caps.perProfile` | NPCs of one profile. |
|
||||
| `caps.spawnsPerSecond` | How fast NPCs are spawned. |
|
||||
| `spawnBudgetMs` | Milliseconds of a frame the spawn queue may use (8 by default). One NPC takes about 5 ms to spawn, so a large placement is spread over frames instead of stalling one. |
|
||||
|
||||
A step or placement over a cap is refused with the cap named.
|
||||
|
||||
## After a Rust update
|
||||
|
||||
Rust's monthly update can change the scientist RunicNPC is built on. After the update:
|
||||
|
||||
1. **Wait for the navmesh.** `rnpc.status` says `navmesh=building` while Rust rebuilds it (about ten minutes on a
|
||||
large map). Placements wait for it and spawn when it is ready.
|
||||
2. **Check the swap.** `rnpc.status`'s `swap fields` line should end `missing=- added=-`. Anything else means Rust's
|
||||
scientist has changed since this RunicNPC was released: update RunicNPC (on a Runic Gateway server,
|
||||
`update --game rust` or an egg reinstall takes the current bundle).
|
||||
3. **Check the hooks.** A hook listed as `silent` long after the server has been played on may have been renamed.
|
||||
|
||||
Runic Gateway checks every new Rust staging build before it reaches the public branch (PLAN.md stage 9, the
|
||||
staging drill), so a RunicNPC release that needs fixing is usually out before the update is.
|
||||
@@ -12,7 +12,8 @@ and **its build's own questions answered 2026-10-01** (D267–D272). **Stage 5 b
|
||||
site 2026-10-05** on both rigs (§9); its API is [API.md](API.md) version 4. **Stage 6's design answered
|
||||
2026-10-05** (D273–D282, §0), **its spike measured 2026-10-05** on both rigs, and **its two questions answered the
|
||||
same day** (D283, D284, §9). **Its build's own questions were answered the same day too** (D285–D289), and **stage 6 was built and walked on the site 2026-10-05** on both rigs (§9); its API is [API.md](API.md) version 5. **Stage 7's design answered 2026-10-05** (D290–D299, §0). **Stage 9's design answered 2026-10-06**
|
||||
(D306–D314, §0 and §9).
|
||||
(D306–D314, §0 and §9). **Phase 9b was built 2026-10-06** (§9), with its two questions answered the same day (D315,
|
||||
D316).
|
||||
|
||||
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
|
||||
@@ -136,6 +137,8 @@ architectural or design decision is implemented.
|
||||
| **D312** | **The player walk is one checklist in one session per rig.** Every in-game check deferred so far is gathered with stage 9's own. I prepare the rigs and the walk site, the org lead plays, and I watch the logs and record the results (stage 9). | A checklist per stage; release without a player walk. |
|
||||
| **D313** | **v1.0.0 is one coordinated cutover.** After the walk passes, `edge` goes to `main` in all five Rust repos: RunicNPC v1.0.0 first, then the bridge, the sidecar, the installer and the bundle that pins them, and the module's requirement last (stage 9). | RunicNPC alone first, the rest later. |
|
||||
| **D314** | **Core's public phase label is fixed in stage 9**, in its own website PR. `eventPublic.phaseLabel` matches a phase by `key`, which specs use, and still accepts `id`; its test fixture moves to `key`. It has said "Under way" for every phase since Events Phase 14a, UO's events included (stage 9, from docs#323). | A website issue for later. |
|
||||
| **D315** | **The performance check records what 100 NPCs cost today, and the cost warning says it; the idle overhead is an optimisation issue, not a release blocker.** Stage 1's own bare NPC no longer meets stage 1's bar on today's rigs (fighting +7.5 / +8.0 ms against +5.2), and RunicNPC fights at the same cost; awake idle on Oxide is about 2 ms per 100 over the bare NPC (runicnpc-rust#13). Narrows D306 (stage 9b; the org lead: "Write a warning for it, log it as an optimization bug fix issue on gitea and keep going"). | A same-session bar against the bare NPC; fixing idle before v1.0; keeping D306's absolute bar. |
|
||||
| **D316** | **On a server without RunicNPC, the Place NPCs picker keeps listing Rust's own scientists, and the step is refused when saved, with the reason.** Core's option sources cannot show a module's reason (a refusal reads only "could not be read"), and Admin → Rust → Servers already says "Incomplete". Narrows 9b's "the picker shows the same reason instead of a list" (stage 9b). | A core change letting an option source answer a reason (MODULE_API minor); an empty list. |
|
||||
|
||||
**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
|
||||
@@ -1615,6 +1618,39 @@ is asked before the next one starts.
|
||||
harness of stages 1 and 5. It passes at about +1 ms and +5 ms of median frame over that session's empty baseline.
|
||||
The numbers go into INSTALL and the cost warning.
|
||||
|
||||
**9b built (2026-10-06).** Rust 25681086, Oxide 2.0.7801, Carbon 2.0.262 on both rigs.
|
||||
|
||||
- **Core's phase label (D314):** website PR. `phaseLabel` finds the phase by `key`, then `id`; the fixture uses
|
||||
`key`, and a unit test covers a UO-shaped spec, the fallback and key-before-id.
|
||||
- **RunicNPC is required (D310):** `module-rust` asks every Place NPCs step for RunicNPC first ("Main needs
|
||||
RunicNPC to place NPCs, Rust's own scientists included: RunicNPC is not loaded on it"), and its floor rises from
|
||||
API 4 to the bridge's 6, so a server the site calls ready is one the bridge will not refuse. Admin → Rust → Servers
|
||||
carries each server's `runicNpc` and reads "Incomplete" with the reason. The picker is unchanged (D316). The
|
||||
bridge refuses one of Rust's own NPC prefabs the same way, before anything spawns, and a profile placement now
|
||||
refuses an old RunicNPC up front (PROTOCOL §19.16; no message changes shape, protocol stays 13). The installer
|
||||
refuses a Rust bundle without RunicNPC, `doctor` fails without it, and the bundle CI composes no Rust bundle
|
||||
without a RunicNPC that answers the bridge (tested against the real releases: it refuses today's v0.1.1 bridge,
|
||||
which declares no RunicNPC API, and leaves ServUO alone). **The installer had no `edge` any more**, since its
|
||||
cutover deleted it; it is recreated from `main` for this, so `main`, which releases on every push, gets it at 9e.
|
||||
The egg follows the bundle and is unchanged.
|
||||
- **Operator documentation:** [INSTALL.md](INSTALL.md) and [COMMANDS.md](COMMANDS.md). The required note went into
|
||||
[`../rust-link/INSTALL.md`](../rust-link/INSTALL.md), the Rust operator guide that lists Kits:
|
||||
`modules/rust/OPERATING.md`, which this section named, is a verbatim mirror of uMod's pages.
|
||||
- **The update surface:** `runicnpc-rust`'s README lists every Rust type and member the swap and the brain depend
|
||||
on, where, and whether a change is caught by compiling, by the field list, or only on a running server.
|
||||
- **The performance check (D306, D315):** `rnt.cost` in the test harness measures RunicNPC's own NPCs, with stage
|
||||
1's `rnh.cost` beside it in the same session as the reference. 100 NPCs, increase of the median frame:
|
||||
|
||||
| | Oxide: bare NPC | Oxide: RunicNPC | Carbon: bare NPC | Carbon: RunicNPC |
|
||||
|---|---|---|---|---|
|
||||
| Idle, awake | +0.96 ms | +2.65 / +2.92 ms | +0.97 ms | +1.52 ms |
|
||||
| Idle, asleep | | | | +0.52 ms |
|
||||
| Fighting 20 stand-ins | +7.53 ms | +7.19 ms | +7.96 ms | +9.04 ms |
|
||||
|
||||
The rigs are slower than in September (an empty Oxide frame 22–30 ms, against 16.4 ms then), and between two
|
||||
empty windows the median moved by up to 7 ms, so every run ends with a second empty window and is judged against
|
||||
the lower. The cost warning now says 3 ms per 100 awake, 0.5 ms asleep and 9 ms fighting.
|
||||
|
||||
**9c. The staging drill (D307–D309).**
|
||||
|
||||
- **The rig:** a new Pterodactyl server, `rust-staging`, Oxide only, on a 2000 map at about 6 GB. It keeps a
|
||||
|
||||
@@ -7,13 +7,13 @@ the design of record is [`../modules/rust/PLAN.md`](../modules/rust/PLAN.md) §3
|
||||
## What this installs
|
||||
|
||||
Two components per Rust server, released together as a **bundle** — an exact pair CI has checked
|
||||
speaks one protocol, never "the latest of each" — and, from RunicNPC's stage 4, a third:
|
||||
speaks one protocol, never "the latest of each" — and a third, RunicNPC, which is required:
|
||||
|
||||
| Component | What it is | Released from |
|
||||
|---|---|---|
|
||||
| **The plugin** | `RunicGateway.cs`, one file that runs unchanged on Oxide and Carbon — and beside it, from protocol 13, the optional **ZoneManager helper** `RunicGatewayZones.cs` (PLAN_FIXES D181, D182), installed by default | [Rust-Plugins](https://gitea.whitlocktech.com/RunicGateway/Rust-Plugins/releases) |
|
||||
| **The sidecar** | `rust-link-sidecar`, which the plugin dials on loopback and the website reaches over HTTP | [Rust-Link](https://gitea.whitlocktech.com/RunicGateway/Rust-Link/releases) |
|
||||
| **RunicNPC** | `RunicNPC.cs`, Runic Gateway's NPC plugin ([`../runicnpc/PLAN.md`](../runicnpc/PLAN.md)), placed beside the bridge **when the bundle carries it**: from RunicNPC's stage 4, the latest RunicNPC release that answers the API the bridge needs (D224). Optional until RunicNPC's stage 9; it needs **Kits**, like the bridge | [runicnpc-rust](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust/releases) |
|
||||
| **RunicNPC** | `RunicNPC.cs`, Runic Gateway's NPC plugin ([`../runicnpc/PLAN.md`](../runicnpc/PLAN.md)), placed beside the bridge: the latest RunicNPC release that answers the API the bridge needs (D224). **Required** since RunicNPC's stage 9 (D310): without it the bridge refuses every NPC an event places, Rust's own scientists included, and Admin → Rust → Servers shows the server as incomplete. It needs **Kits**, like the bridge. Its own guide is [`../runicnpc/INSTALL.md`](../runicnpc/INSTALL.md) | [runicnpc-rust](https://gitea.whitlocktech.com/RunicGateway/runicnpc-rust/releases) |
|
||||
|
||||
The game server opens no port for the bridge: the plugin is the client and the sidecar the
|
||||
listener, on `127.0.0.1`. **One sidecar serves one Rust server.** A community running six servers
|
||||
@@ -32,7 +32,8 @@ There are three ways to set a server up. They produce the same result:
|
||||
- **Oxide or Carbon is installed, and the server has started once** with it, so its directories
|
||||
exist. The bridge is a plugin; a vanilla server has nothing to load it.
|
||||
- **Kits and ZoneManager** from uMod are what the reward and zone features use. The bridge works
|
||||
without them and names what is missing; nothing here installs them.
|
||||
without them and names what is missing; nothing here installs them. **RunicNPC needs Kits** and
|
||||
will not load without it, so without Kits no event can place NPCs.
|
||||
- **The website has the Rust module**, and you are an administrator on it. Servers are added at
|
||||
**Admin → Rust → Servers** (`/admin/rust/servers`).
|
||||
|
||||
@@ -122,8 +123,8 @@ When you create a server from it:
|
||||
|
||||
**The install** downloads the sidecar, its launcher and the plugin from the bundle, checks each
|
||||
against the bundle's checksum, and only then places them: the sidecar in `rust-link/`, the plugin in
|
||||
`oxide/plugins/` or `carbon/plugins/`. When the bundle carries RunicNPC, `RunicNPC.cs` goes beside the plugin
|
||||
(its data directory is left for RunicNPC to make). Any mismatch fails the install with the reason, before
|
||||
`oxide/plugins/` or `carbon/plugins/`. `RunicNPC.cs` goes beside the plugin (its data directory is left for RunicNPC to make); a bundle
|
||||
without RunicNPC is refused. Any mismatch fails the install with the reason, before
|
||||
anything is placed.
|
||||
|
||||
**The first boot** prints, in the console, the token the sidecar generated — **once** — and a line
|
||||
@@ -156,8 +157,8 @@ history, and the token is unchanged.
|
||||
[`v2/rust/current.json`](https://gitea.whitlocktech.com/RunicGateway/installer/src/branch/bundles/v2/rust/current.json)
|
||||
on the installer's `bundles` branch. It names the sidecar binary for your platform and the
|
||||
plugin tarball, each with a `sha256`.
|
||||
2. **Download and verify** both against those checksums (`sha256sum -c`, or `Get-FileHash`) — and, when
|
||||
the bundle has an `npc` entry, RunicNPC's tarball too. Copy `runicnpc/RunicNPC.cs` from it into the same
|
||||
2. **Download and verify** both against those checksums (`sha256sum -c`, or `Get-FileHash`) — and RunicNPC's
|
||||
tarball, the bundle's `npc` entry, too. Copy `runicnpc/RunicNPC.cs` from it into the same
|
||||
plugins directory as the bridge. **Do not create `data/RunicNPC/` yourself**: RunicNPC makes it on first
|
||||
load, and one made from outside the game (a panel's file manager) is not writable by it.
|
||||
3. **The plugin:** copy `runicgateway-rust-plugin/RunicGateway.cs` from the tarball into
|
||||
@@ -225,7 +226,7 @@ With the installer:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `doctor --game rust [--server-id <id>]` | Per server: the framework; whether the plugin file is still the one deployed; each helper deployed beside it, as a **warning** when missing or edited (the bridge runs without one, and the row says what that costs); RunicNPC, when the bundle carried it, the same way (without it the site's NPC profiles and placements and events' profile NPCs are off); that the plugin's config names this server; the required uMod plugins; the service; and `/health` through to **plugin connected**. A stopped server is a warning; a running one whose plugin never connected is a failure, printed with the framework versions the plugin is known good on |
|
||||
| `doctor --game rust [--server-id <id>]` | Per server: the framework; whether the plugin file is still the one deployed; each helper deployed beside it, as a **warning** when missing or edited (the bridge runs without one, and the row says what that costs); RunicNPC as a **failure** when it is missing or was never installed (it is required: without it every NPC an event places is refused), and a warning when edited by hand; that the plugin's config names this server; the required uMod plugins; the service; and `/health` through to **plugin connected**. A stopped server is a warning; a running one whose plugin never connected is a failure, printed with the framework versions the plugin is known good on |
|
||||
| `update --game rust` | Moves the sidecar and every server's plugin to the current bundle, and restarts the sidecars. Always all servers together — they share one binary |
|
||||
| `uninstall --game rust [--server-id <id>] [--purge]` | Removes the service, the plugin file, its helpers and RunicNPC. **Keeps the plugin's config** and RunicNPC's `data/RunicNPC/` (an admin's placements and routes) — it is the website's, and it names the server. `--purge` also removes the sidecar config (the token) and the database. Removing the last server removes the shared binary too |
|
||||
|
||||
|
||||
@@ -2401,8 +2401,9 @@ in-game rows (flags on a player, the messages) wait for the later in-game walk.
|
||||
RunicNPC is Runic Gateway's own NPC plugin ([`../runicnpc/PLAN.md`](../runicnpc/PLAN.md), [`API.md`](../runicnpc/API.md)).
|
||||
**The bridge is its only link to the site**: it calls RunicNPC's API for the site's commands and turns
|
||||
RunicNPC's hooks into frames, and RunicNPC never talks to the sidecar, so "the sidecar is a dumb forwarder"
|
||||
stays true. RunicNPC is optional until its stage 9. Without it every `npc.*` command answers `npc.error`
|
||||
**`runicnpc-missing`**, and an event places Rust's own scientists only (D243).
|
||||
stays true. Without it every `npc.*` command answers `npc.error` **`runicnpc-missing`**. Until its stage 9 an
|
||||
event then placed Rust's own scientists only (D243); since stage 9 RunicNPC is required, and an event places no
|
||||
NPCs at all without it (§19.16).
|
||||
|
||||
The bridge needs **RunicNPC API 3** (D249), `RunicNpcApiNeeded` in the code and **`runicnpc_api = 3` in
|
||||
`overlay.toml`**, which the release copies into its manifest and `checkPlugin.js` holds equal to the code.
|
||||
@@ -2614,3 +2615,19 @@ Stage 7's wire change **joins protocol 13** while it is unreleased (D299); no me
|
||||
`false` spawns the NPCs with RunicNPC's override `{"loot":{"dropTable":false}}`: they keep what their profile
|
||||
starts the corpse with, and roll no loot table. Absent or `true`, as before. The site sends it only when an event's
|
||||
Place NPCs step switches it off.
|
||||
|
||||
### 19.16 RunicNPC stage 9: RunicNPC is required (runicnpc PLAN.md stage 9)
|
||||
|
||||
**No message changes shape**, and `PROTOCOL_VERSION` stays 13. What changes is which `world.place` the bridge
|
||||
refuses (D310): **every placement of NPCs needs RunicNPC loaded and answering `RunicNpcApiNeeded` (6)**, a
|
||||
`profile` placement and one of Rust's own NPC prefabs (`npc.scientist`, `npc.bandit.guard`, …) alike. Crates are
|
||||
unaffected. The refusal is checked before anything is spawned, with the two reasons that already exist:
|
||||
|
||||
| `reason` | Means | Retried? |
|
||||
|---|---|---|
|
||||
| `runicnpc-missing` | RunicNPC is not loaded, so this server places no NPCs | no |
|
||||
| `runicnpc-old` | RunicNPC answers an API older than the bridge needs | no |
|
||||
|
||||
The site checks the same thing first, from the hello's `integrations.runicNpc`, and refuses the step before it
|
||||
is sent; the bridge's check is for a site that missed the hello. `rg.npc` says the same when RunicNPC is not
|
||||
loaded.
|
||||
|
||||
Reference in New Issue
Block a user