fix(rust): protocol 13 step 2 — expiry, plugin loads, the loading hold, NPC names, the link fleet (F2 F5 F6 F7 F8 F13 F14)
The module's half of PLAN_FIXES §6 step 2 (decisions D181-D185, docs#288). - F13/F14 (D170, D183): `world.expired`, recognisable from protocol 13 by its `what`, is handed to core as the resource the zone step ledgered (`world`, `<serverId>:<id>`) through ctx.events.expired, which records it `expired`. coreApi moves to ^1.11.0 (website#209). - F8 (D184): `plugin.loaded` / `plugin.unloaded` mark the permission sync dirty when the plugin added or removed permissions, so an unresolved grant lands on the next tick instead of the fifteen-minute audit. - Catalogue: plugin.loaded/unloaded, world.expired and lease.expired are staff kinds. The last two were never classified (default deny kept them off public pages); the test now covers every event kind through protocol 13. - F7: permission and title pushes hold while the stored hello says `worldReady: false` (a human's "sync now" does not); a failed or refused permission sync now logs at warn. - F2 (D185): the killfeed names an NPC attacker — a family (Scientist, Bandit guard, Bradley APC…) or the prefab without its variant digits (wolf2 → Wolf). - F5/F6: a link code is asked of the servers that minted one in the last six minutes first, then of the rest, each group in parallel; "unsure" only when one of the minting servers is unreachable. - D182: the admin server list carries the ZoneManager helper's state from the hello, and the servers page says what a missing or failed helper costs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -43,7 +43,8 @@ test('the four attacker types are four different sentences', () => {
|
||||
const victim = { name: 'Bob' }
|
||||
|
||||
const npc = describe(row('player.death', { ...victim, attackerType: 'npc', attackerName: 'scientistnpc_full_any' }))
|
||||
assert.equal(npc.actor, 'scientistnpc full any')
|
||||
// A family is named, not spelled out as a prefab (PLAN_FIXES F2, D185).
|
||||
assert.equal(npc.actor, 'Scientist')
|
||||
assert.equal(npc.subject, 'Bob')
|
||||
|
||||
const self = describe(row('player.death', { ...victim, attackerType: 'self' }))
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { ago, clock, contrastInk, count, day, duration, nextWipe, prefab, shortId } from '../src/lib/format.js'
|
||||
import { ago, attacker, clock, contrastInk, count, day, duration, nextWipe, prefab, shortId } from '../src/lib/format.js'
|
||||
|
||||
const NOW = Date.parse('2026-09-16T12:00:00Z')
|
||||
|
||||
@@ -115,3 +115,28 @@ test('a title chip’s ink is whichever of black and white reads on its colour',
|
||||
assert.equal(contrastInk('#ff0000'), '#000000')
|
||||
assert.equal(contrastInk('red'), '#000000', 'not a hex colour: black, on the caller’s fallback')
|
||||
})
|
||||
|
||||
test('an NPC attacker reads as a name: a family, or the prefab without its variant (F2, D185)', () => {
|
||||
// The first walk's "killed by wolf2".
|
||||
assert.equal(attacker('wolf2'), 'Wolf')
|
||||
assert.equal(attacker('wolf'), 'Wolf')
|
||||
assert.equal(attacker('boar'), 'Boar')
|
||||
assert.equal(attacker('polarbear'), 'Polar bear')
|
||||
assert.equal(attacker('scientistnpc_full_any'), 'Scientist')
|
||||
assert.equal(attacker('scientistnpc_roam'), 'Scientist')
|
||||
assert.equal(attacker('scientistnpc_heavy'), 'Heavy scientist')
|
||||
assert.equal(attacker('npc_bandit_guard'), 'Bandit guard')
|
||||
assert.equal(attacker('bradleyapc'), 'Bradley APC')
|
||||
assert.equal(attacker('patrolhelicopter'), 'Patrol helicopter')
|
||||
assert.equal(attacker('autoturret_deployed'), 'Auto turret')
|
||||
assert.equal(attacker('sam_site_turret_deployed'), 'SAM site')
|
||||
// Anything unknown still reads as words, capitalised, with no suffix or variant.
|
||||
assert.equal(attacker('some_new_beast3'), 'Some new beast')
|
||||
assert.equal(attacker('spikes.floor.deployed'), 'Spikes floor')
|
||||
assert.equal(attacker(null), '')
|
||||
assert.equal(attacker(''), '')
|
||||
})
|
||||
|
||||
test('a weapon keeps the light touch — attacker() is for what did the killing', () => {
|
||||
assert.equal(prefab('rifle.ak'), 'rifle ak')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user