feat: TruePVE in the zone step (PLAN_REDESIGNS §14.3.2, WIP)

Stage 3 of the §14.3 integration plan (D362, D368): world.zone's optional
truePve ({exclude: true} or {ruleset}), all or nothing, unmapped on every
path that erases one of our zones, with truepve.json for erases while
TruePVE is away. Docs and PRs still to come.

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-15 03:03:36 -05:00
parent 176ded67a8
commit 4af731288a
7 changed files with 197 additions and 7 deletions

View File

@@ -12,6 +12,9 @@
// the step's dropdown VALUE is the line itself, so picking copies it (D210)
// the zone crosses with its flags, settings, messages and dome
// a dome on a server without ZoneDomes or its helper is refused with the reason
// TruePVE's rules cross as exclude or one ruleset, never both, and never to a
// server that said it has no TruePVE (§14.3.2, D368); a ruleset TruePVE does
// not have fails for good, with the plugin's words
const test = require('node:test')
const assert = require('node:assert')
@@ -34,13 +37,14 @@ const source = (id) => world.OPTION_SOURCES.find((s) => s.id === id)
const FLAGS = ['AutoLights', 'NoBuild', 'NoPlayerLoot', 'PvpGod', 'NoChat', 'SomethingNew']
function server(id, { flags = FLAGS, domes = null } = {}) {
function server(id, { flags = FLAGS, domes = null, truePve = null } = {}) {
return {
id,
name: id.toUpperCase(),
enabled: true,
zoneManager: flags ? { version: '3.1.14', flags } : null,
zoneDomes: domes,
truePve,
}
}
@@ -287,6 +291,84 @@ test('a dry run checks the options and the dome and sends nothing', async (t) =>
assert.strictEqual(calls.length, 0)
})
// ── TruePVE in the zone (§14.3.2, D368) ────────────────────────────────────
const TRUEPVE = { loaded: true, version: '2.4.6' }
const zoneRun = (params, extra = {}) =>
action('rust.zone.open').perform({ runId: 7, idempotencyKey: 'k', params: { monument: 'main/a', radius: 40, minutes: 5, ...params }, ...extra })
test('no PvP protection crosses as exclude, and a ruleset by its name, trimmed', async (t) => {
for (const [params, wire] of [
[{ noPvpProtection: true }, { exclude: true }],
[{ noPvpProtection: 'true' }, { exclude: true }],
[{ pvpRuleset: ' pvp-arena ' }, { ruleset: 'pvp-arena' }],
]) {
await t.test(JSON.stringify(params), async (tt) => {
stubZones(tt, { servers: [server('main', { truePve: TRUEPVE })] })
const calls = stubWorld(tt)
const result = await zoneRun(params)
assert.strictEqual(result.ok, true, result.error)
assert.deepStrictEqual(calls[0].body.truePve, wire)
})
}
})
test('left unset, switched off or blank, a zone sends no truePve at all', async (t) => {
stubZones(t, { servers: [server('main', { truePve: { loaded: false } })] })
const calls = stubWorld(t)
for (const params of [{}, { noPvpProtection: false, pvpRuleset: '' }, { noPvpProtection: null, pvpRuleset: ' ' }]) {
const result = await zoneRun(params)
assert.strictEqual(result.ok, true, result.error)
}
assert.ok(calls.every((c) => !('truePve' in c.body)))
})
test('both, a long name, or a server without TruePVE is refused for good, at verify too', async (t) => {
for (const [truePve, params, words] of [
[TRUEPVE, { noPvpProtection: true, pvpRuleset: 'pvp-arena' }, /not both/],
[TRUEPVE, { pvpRuleset: 'x'.repeat(65) }, /64 characters/],
[{ loaded: false }, { noPvpProtection: true }, /TruePVE is not installed on this server/],
[{ loaded: false }, { pvpRuleset: 'pvp-arena' }, /TruePVE is not installed on this server/],
]) {
for (const verify of [true, false]) {
await t.test(`${words} verify=${verify}`, async (tt) => {
stubZones(tt, { servers: [server('main', { truePve })] })
const calls = stubWorld(tt)
const result = await zoneRun(params, { verify })
assert.strictEqual(result.ok, false)
assert.strictEqual(result.retry, false)
assert.match(result.error, words)
assert.strictEqual(calls.length, 0)
})
}
}
})
test('a server that has not said whether it has TruePVE is left to the plugin', async (t) => {
stubZones(t, { servers: [server('main', { truePve: null })] })
const calls = stubWorld(t)
const result = await zoneRun({ noPvpProtection: true })
assert.strictEqual(result.ok, true, result.error)
assert.deepStrictEqual(calls[0].body.truePve, { exclude: true })
})
test('a ruleset TruePVE does not have, or no TruePVE, fails for good with the plugin’s words', async (t) => {
for (const [reason, message] of [
['truepve-ruleset', "TruePVE on this server has no ruleset named 'pvp-arna' (names are case-sensitive), so the zone was not opened"],
['truepve-missing', 'TruePVE is not loaded on this server, so a zone cannot change its PvP rules'],
]) {
await t.test(reason, async (tt) => {
stubZones(tt, { servers: [server('main', { truePve: TRUEPVE })] })
stubWorld(tt)
client.worldZone = async () => ({ ok: true, status: 'ok', data: { kind: 'world.error', reason, message } })
const result = await zoneRun({ pvpRuleset: 'pvp-arna' })
assert.strictEqual(result.ok, false)
assert.strictEqual(result.retry, false)
assert.strictEqual(result.error, message)
})
}
})
test('the dome dropdown offers ZoneDomes’ five colours', async () => {
assert.deepStrictEqual((await source('rust.options.domes').resolve()).map((r) => r.value), ['standard', 'red', 'blue', 'green', 'purple'])
})