feat(rust): the rewards — tally, kit reward, chat and the news leg (phase 13b, protocol 10)
Four event verbs and the announce leg, per PLAN.md §29: - rust.participation.open / .collect: the plugin counts who takes part (seconds, kills or both, in a zone this run opened or the whole server) and collect files them as the run's participants, keyed by Steam id. - rust.kit.entitle: the five recipient modes (D101), rows in the new rust_perm_run_grants (D84) unioned into the permission push, one extra use of the kit per reward as site-held credits on perm.sync (D103), and the rust.kit.entitled notice deferred from phase 10 (D64). - rust.announce: one server or every server (D105). - rust.chat announce leg, speaking only on servers whose new news switch is on (D104) - a card on Admin -> Rust visibility (D106). Budgets rust.grants and rust.announcements; the kit source and four fixed-choice sources (core has no enum param type). rust_perm_run_grants carries core's idempotency key so a revert of a lost answer can find its rows. Protocol 10. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
@@ -143,34 +143,58 @@ test('nothing is registered that has nothing behind it yet', () => {
|
||||
// leases and option sources, and kept budgets here on purpose (D79): a lease
|
||||
// spends none, and a dimension nothing spends is a dial that does nothing.
|
||||
// Phase 13a deleted the budgets and actions lines, and registered each budget
|
||||
// beside the verb that spends it (below). The announce leg is 13b's.
|
||||
assert.deepStrictEqual(api.record.legs, [])
|
||||
// beside the verb that spends it (below). Phase 13b registered the announce
|
||||
// leg (D104) — the post hook is still absent: nothing in game mirrors a post.
|
||||
assert.deepStrictEqual(api.record.legs.map((l) => l.leg), ['rust.chat'])
|
||||
assert.strictEqual(api.record.hooks.post, undefined)
|
||||
})
|
||||
|
||||
test('the world verbs are registered, and every budget has a verb that spends it (phase 13a)', () => {
|
||||
test('the event verbs are registered, and every budget has a verb that spends it (phases 13a, 13b)', () => {
|
||||
const { api } = register()
|
||||
|
||||
const actions = api.record.eventActions
|
||||
const budgets = api.record.eventBudgets
|
||||
assert.deepStrictEqual(actions.map((a) => a.id).sort(), ['rust.crate.place', 'rust.npc.place', 'rust.zone.open'])
|
||||
assert.deepStrictEqual(budgets.map((b) => b.id).sort(), ['rust.npcs', 'rust.prefabs', 'rust.zone.minutes'])
|
||||
assert.deepStrictEqual(actions.map((a) => a.id).sort(), [
|
||||
'rust.announce',
|
||||
'rust.crate.place',
|
||||
'rust.kit.entitle',
|
||||
'rust.npc.place',
|
||||
'rust.participation.collect',
|
||||
'rust.participation.open',
|
||||
'rust.zone.open',
|
||||
])
|
||||
assert.deepStrictEqual(budgets.map((b) => b.id).sort(), [
|
||||
'rust.announcements',
|
||||
'rust.grants',
|
||||
'rust.npcs',
|
||||
'rust.prefabs',
|
||||
'rust.zone.minutes',
|
||||
])
|
||||
|
||||
// D79/D89/D97: every dimension has a verb that spends it, and each verb spends
|
||||
// exactly ONE — priced from its own declared examples, which is how core
|
||||
// decides which cap boxes the switchboard shows. A verb whose dimension moved
|
||||
// with its params would hide the other dial from every operator.
|
||||
// D79/D89/D97: every dimension has a verb that spends it, and a verb that
|
||||
// spends anything spends exactly ONE — priced from its own declared examples,
|
||||
// which is how core decides which cap boxes the switchboard shows. A verb
|
||||
// whose dimension moved with its params would hide the other dial from every
|
||||
// operator. The two tally verbs spend nothing: counting people costs no loot.
|
||||
const spent = new Set()
|
||||
for (const a of actions) {
|
||||
const example = Object.fromEntries(a.params.map((p) => [p.name, p.example]))
|
||||
const dims = Object.keys(a.cost(example)).filter((id) => a.cost(example)[id] > 0)
|
||||
if (a.id.startsWith('rust.participation.')) {
|
||||
assert.strictEqual(dims.length, 0, `${a.id} prices ${dims.join(', ')}`)
|
||||
continue
|
||||
}
|
||||
assert.strictEqual(dims.length, 1, `${a.id} prices ${dims.join(', ')}`)
|
||||
spent.add(dims[0])
|
||||
}
|
||||
assert.deepStrictEqual([...spent].sort(), budgets.map((b) => b.id).sort())
|
||||
|
||||
// What a teardown can give back is declared honestly: a line said and a
|
||||
// collect filed are not undone.
|
||||
const once = new Set(['rust.announce', 'rust.participation.collect'])
|
||||
for (const a of actions) {
|
||||
assert.strictEqual(a.reversible, 'ledger')
|
||||
assert.strictEqual(a.reversible, once.has(a.id) ? 'none' : 'ledger', a.id)
|
||||
if (once.has(a.id)) continue
|
||||
assert.strictEqual(typeof a.revert, 'function')
|
||||
assert.strictEqual(typeof a.reconcile, 'function')
|
||||
// Every param source is one this module registers.
|
||||
|
||||
Reference in New Issue
Block a user