Files
runicnpc-rust/README.md
wtclaude fe20ca41af feat: API 3, placements from a website (stage 4, D249)
RunicNpc_AddPlacement creates a placement and names it as /rnpc place
does (D241, D246). A position without y is a map point: it is put on the
ground there (terrain and rock, never a building or a tree, D245), then
checked against the navmesh like an in-game placement. It answers the id,
the grounded position, whether the spot is player-built, and the cost
warning, or the in-game refusal sentence.

RunicNpc_RenamePlacement and RunicNpc_RespawnPlacement do what rnpc
rename and rnpc respawn do. OnRunicNpcPlacementChanged(id, change,
previous) is raised on every set, remove and rename, from the API or in
game, so the bridge can tell the site at once.

rnpc place and here now share CreatePlacement with the API, so both give
the same refusals. The test harness gains an api3 group; all 157 checks
pass on the Oxide and Carbon rigs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
2026-09-30 04:01:30 -05:00

5.8 KiB
Raw Blame History

RunicNPC

Runic Gateway's own NPC plugin for Rust, on Oxide and Carbon.

Other plugins drive it through an API, and admins use it directly in game through chat commands. It works on its own, and on a Runic Gateway server the website authors its NPC profiles and events use its NPCs. The plan of record, stage by stage, is docs/runicnpc/PLAN.md.

Status: stage 4. The NPC, its profiles, placements and routes, the API other plugins call, and the /rnpc commands admins use in game. On a Runic Gateway server, the website takes over the profiles and lists, edits and creates placements through the bridge (API 3).

Requirements

  • Kits — required. A profile names kits, and that is how every RunicNPC NPC is equipped (D217). The plugin declares // Requires: Kits, so neither framework loads it without Kits.
  • Oxide 2.0.7726 or Carbon 2.0.259, or newer: the builds it has been loaded on (plugin.toml).

Installing it

On a Runic Gateway server, the installer and the Pterodactyl egg will install it from the Rust bundle, pinned and checksummed (D224, from stage 4). Until then, and on any other server:

  1. Download runicnpc-<version>.tar.gz and SHA256SUMS from this repository's releases, and check the tarball against it (sha256sum -c SHA256SUMS).
  2. Copy runicnpc/RunicNPC.cs into oxide/plugins/ or carbon/plugins/. The framework compiles and loads it on the write.

runicnpc/manifest.json, beside it, states the release's version, commit, API version, framework floors, required plugins, and the sha256 of every file it ships.

Checking it

rnpc.status

Answers at the server console and over RCON: the version, the API version, and which of the plugin's hooks have fired. A hook that never fires is the first sign a Rust or framework update has renamed it, because neither framework reports a hook that matches nothing.

In game

/rnpc in chat, and the same verbs as rnpc.<verb> in a console (F1, the server console or RCON). /rnpc alone lists the ones you may use. Grant runicnpc.place to place and manage NPCs, and runicnpc.admin for everything (it includes runicnpc.place); the server console has both.

/rnpc place bandit count=3 respawn=300 mode=group move=route:gate
  > Placed bandit-1: 3 × 'bandit' (roamer, route:gate), respawn 300 s, group.
/rnpc path record gate      then walk, and at each point: /rnpc path point
/rnpc path save loop        (or back, to walk it back and forth)
/rnpc near · /rnpc info · /rnpc rename bandit-1 gateguards · /rnpc remove

place uses the spot you look at, here the spot you stand on; the server console gives at=x,y,z. A roamer must stand on Rust's navmesh; only a sentry may stand off it. Every placement answers with what the server's NPCs cost. On a standalone server, profiles are made from a console with rnpc.profile create|set|delete. The full list is in PLAN.md §5.

For other plugins

Every call is prefixed RunicNpc_ and reached through Call:

[PluginReference] private Plugin RunicNPC;

int api = RunicNPC?.Call<int>("RunicNpc_ApiVersion") ?? 0;

The API is version 3, documented in docs/runicnpc/API.md: spawning and removing NPCs by owner, profiles, placements (created and named as in game, from a map point), routes, the cost warning, and the hooks it raises. The API version moves when a call or a raised hook changes shape, not on every release.

Repository layout

Path What
plugin/RunicNPC.cs The plugin. The only file a server gets.
plugin.toml Its declarations: API version, framework floors, required plugins. The release copies them into the manifest.
scripts/checkPlugin.js The static checks run on every pull request and again before a release (see its header).
tools/ Developer scaffolding for the test rigs, never shipped: the panel scripts (see tools/rigs.example.json); RunicNpcHarness.cs, the stage 1 measurement plugin; RunicNpcTest.cs, the stage 2, 3 and 4 test harness (rnt.run all, then rnt.after after a reload or restart); and fieldlist/ with managed.js, which regenerate the swap's field list (below).

After a Rust update: the swap's field list

Our NPC is Rust's scientist with two components swapped, and the swap copies a fixed list of fields (D232), generated from Rust's unmodified assembly. rnpc.status reports it as swap fields: npc=64/64 brain=32/32 missing=- added=-. On Carbon, where the assembly is unmodified, added names any field Rust has added since the list was made. To regenerate it:

node tools/managed.js carbon <dir>                         # the Carbon rig's RustDedicated_Data/Managed
dotnet run --project tools/fieldlist -- <dir>              # rewrites the block in plugin/RunicNPC.cs

Use the Carbon rig: Oxide's patcher makes Rust's private fields public, so its assembly no longer says which fields Rust itself serialises.

Releases

Work lands on edge and is cut over to main; every releasable push to main tags and publishes a release (.gitea/workflows/release.yml). The version comes from Conventional Commits since the last tag. v1.0.0 is stage 9's release, the one module-rust then requires.

Contributing

See CONTRIBUTING.md — including the AI-disclosure rule — and report security problems privately as SECURITY.md describes.

License

GPL-3.0-or-later — see LICENSE.md.