feat(rust): RunicNPC is required for every Place NPCs step (runicnpc stage 9, D310)

A Place NPCs step on a server without RunicNPC, or with one older than the
bridge needs, is refused before it is sent: "<server> needs RunicNPC to place
NPCs, Rust's own scientists included: <why>". Rust's own scientists were the
fallback until now (D243); they need RunicNPC too. Crates are unaffected.

- npcs.model: API_NEEDED rises from 4 to 6, the bridge's RunicNpcApiNeeded, so a
  server the site calls ready is one the bridge will not refuse as runicnpc-old.
  The profile push follows the same floor.
- eventWorld: profileMissing becomes npcMissing, asked for every NPC step.
- Admin → Rust → Servers: each server carries `runicNpc` ({ready, absence,
  version, api}) and a server without one that will do reads "Incomplete" with
  the reason. Swagger documents the field.

The event picker is unchanged in this commit (an open question on the PR).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-06 16:58:30 -05:00
parent 72f6c557d2
commit d140d5c66d
7 changed files with 107 additions and 20 deletions

View File

@@ -25,6 +25,7 @@ const core = require('../core')
const client = require('../sidecarClient')
const serversDb = require('../model/servers/servers.db')
const servers = require('../model/servers/servers.model')
const npcsDb = require('../model/npcs/npcs.db')
const world = require('../eventWorld')
const action = (id) => world.ACTIONS.find((a) => a.id === id)
@@ -47,9 +48,13 @@ function stub(t, { zone, place, revert, owned, monuments, polling } = {}) {
worldRevert: client.worldRevert,
worldOwned: client.worldOwned,
worldMonuments: client.worldMonuments,
listNpcServers: npcsDb.listNpcServers,
}
serversDb.getServer = async (id) => ROWS[id] || null
// Runicnpc stage 9 (D310): an NPC step asks for RunicNPC first; every server here has it.
npcsDb.listNpcServers = async () =>
Object.keys(ROWS).map((id) => ({ id, name: ROWS[id].name, runicNpc: { loaded: true, version: '1.0.0', api: 6 } }))
servers.listForPolling = async () =>
(polling || ['main']).map((id) => ({ id, name: ROWS[id] ? ROWS[id].name : id, baseUrl: `http://${id}:1`, token: 't' }))
const ok = (data) => ({ ok: true, status: 'ok', data })
@@ -86,6 +91,7 @@ function stub(t, { zone, place, revert, owned, monuments, polling } = {}) {
t.after(() => {
serversDb.getServer = saved.getServer
servers.listForPolling = saved.listForPolling
npcsDb.listNpcServers = saved.listNpcServers
})
return calls