Files
runicnpc-rust/CONTRIBUTING.md
wtclaude 249f2e513f
All checks were successful
PR Checks / plugin-checks (pull_request) Successful in -1m44s
feat: stage 0, an empty RunicNPC that releases through CI
The repository RunicNPC is built in (docs/runicnpc/PLAN.md §9, stage 0):

- plugin/RunicNPC.cs: `// Requires: Kits` (D217), `[Info]` with the 0.0.0
  placeholder the release stamps, `RunicNpc_ApiVersion()` (API 1), and
  `rnpc.status`, which reports the version and which hooks have fired. It
  spawns nothing.
- plugin.toml: the API version, the framework floors it was loaded on
  (Oxide 2.0.7726, Carbon 2.0.259) and requires_plugins = ["Kits"].
- scripts/checkPlugin.js, adapted from Rust-Plugins': every hook listed and
  void unless written down; chat-command signatures; every RunicNpc_ call
  reachable by Call (the HumanNPC trap, PLAN.md §1.2); ApiVersion,
  `// Requires:` and [Info] agreeing with plugin.toml. 23 self-tests, including
  the real plugin and a CRLF checkout.
- PR Checks on PRs into main and edge; the release workflow on main, with
  Rust-Plugins' release engine unchanged and an adapter that ships
  runicnpc-<ver>.tar.gz (runicnpc/RunicNPC.cs + manifest.json) and SHA256SUMS.
  No bundle dispatch until stage 4.
- tools/: the rig panel scripts, with the panel and server ids moved into a
  git-ignored tools/rigs.json. `con.js` became `console.js`: CON is a reserved
  device name on Windows, and git there cannot open the file.
- README, CONTRIBUTING (edge-based flow, AI disclosure, borrow-not-copy),
  SECURITY, the code of conduct, issue and PR templates.

`feat:` so the cutover to main cuts the first release, 0.1.0.

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

4.7 KiB

Contributing to RunicNPC

Thanks for your interest in contributing! This repository is RunicNPC, Runic Gateway's NPC plugin for Rust on Oxide and Carbon. What it will become, and in what order, is planned in docs/runicnpc/PLAN.md. Read it before proposing a feature: most are already placed in a stage.

By participating you agree to abide by our Code of Conduct.

Ways to contribute

  • Report a bug or request a feature through the issue tracker (issue templates are provided).
  • Improve the code or docs by opening a pull request (see below).
  • Never report a security vulnerability in a public issue — see SECURITY.md.

Development setup

The plugin is deployed as source and compiled by Oxide or Carbon at load. There is no standalone build and no CI build, because compiling it needs Rust's own managed assemblies.

While developing, the loop is one copy and a wait:

cp plugin/RunicNPC.cs /path/to/rust/oxide/plugins/     # or carbon/plugins/
# The framework notices the write, recompiles, and reloads. Watch its log.

That is a developer's loop, not how a server is set up: servers get RunicNPC from a release. A change that only works when you copy it by hand is a change that does not ship.

Borrow behaviours, never code. RunicNPC may do what other NPC plugins do, but it is written from how Rust's own classes behave. NpcSpawn states no licence, so its source grants nothing and is read only as a description of what can be done (PLAN.md D214). Do not paste code from another plugin.

Checks

node scripts/checkPlugin.js
node --test scripts/checkPlugin.test.js

Dependency-free; any Node 20+ runs them. They are what CI runs on every pull request, and what the release runs again on the commit it ships. scripts/checkPlugin.js explains each check in its header. Two matter most when you add code:

  • A new hook goes in ExpectedHooks in the plugin, so rnpc.status can report whether it fires.
  • A hook that returns a value changes what the game does. Add it to ANSWERS_DELIBERATELY in the checker with the reason, and make it answer for RunicNPC's own NPCs only, returning null for everything else on the server.

Testing on a server

A hook binds by reflection and fails silently, so a running server is the only proof that one fires. Every stage is tested on an Oxide server and then a Carbon server before it is merged. tools/ holds the scaffolding used for that (see tools/rigs.example.json).

Declarations

plugin.toml and the plugin state some facts twice, and the checks hold them equal:

What In the plugin In plugin.toml
The API version ApiVersion api
Required plugins // Requires: lines requires_plugins

Bump the API version in both, in the same change, when a RunicNpc_* call or a raised hook changes shape.

Branch & PR workflow

  1. Branch from an up-to-date edge with a descriptive name (feat/…, fix/…, docs/…, chore/…).
  2. Keep changes focused; small PRs are easier to review.
  3. Open a pull request against edge. Fill out the PR template, including the AI-assisted contributions disclosure.
  4. A maintainer reviews it. edge is cut over to main for releases.

Commit messages

We use Conventional Commits — type(scope): summary (e.g. feat(api): add RunicNpc_Spawn). The release version is derived from them: feat is a minor release, fix and perf a patch, ! or BREAKING CHANGE a major; docs, chore, ci and test release nothing.

AI-assisted contributions (disclosure required)

This project is developed openly with AI assistance, and we ask the same transparency of everyone. If you used an AI tool (Claude, Copilot, ChatGPT, Cursor, etc.) to help produce a contribution, you must disclose it:

  • Tick the AI-usage box in the pull-request template and name the tool(s).
  • Mark AI-authored commits with a trailer, e.g. Co-Authored-By: Claude <noreply@anthropic.com> or Assisted-By: <tool>.
  • You remain responsible for every line you submit: review it, understand it, and make sure it is correct and that you have the right to contribute it.

Disclosed AI assistance is welcome. Undisclosed AI-generated contributions are not, and may be closed.

License

RunicNPC is licensed under the GNU General Public License v3.0 or later (see LICENSE.md). By submitting a contribution you agree that it is licensed under the same terms (inbound = outbound) and that you have the right to contribute it.