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:
@@ -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'])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user