TRXTD/scripts/test-mp.mts

72 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Lockstep determinism check: two engines receiving the same actions at the
* same ticks must produce identical game state (the foundation of multiplayer
* without state sync).
*/
import { GameEngine } from '../src/game/engine.ts'
const e1 = new GameEngine()
const e2 = new GameEngine()
for (const e of [e1, e2]) {
e.startGame('normal')
e.mpGameActive = true
e.mpMode = 'coop'
e.coopHpMul = 1.7
}
// scripted action stream (tick, action) like the relayed server order
const script: [number, Parameters<GameEngine['applyAction']>[0]][] = [
[5, { type: 'build', kind: 'arrow', tx: 8, ty: 5 }],
[5, { type: 'build', kind: 'frost', tx: 3, ty: 0 }],
[10, { type: 'wave' }],
[200, { type: 'build', kind: 'cannon', tx: 7, ty: 0 }],
[400, { type: 'upgrade', towerId: 1 }],
[400, { type: 'build', kind: 'tesla', tx: 12, ty: 6 }],
[700, { type: 'wave' }],
[900, { type: 'obstacle', tx: 19, ty: 0 }],
[1200, { type: 'build', kind: 'laser', tx: 18, ty: 4 }],
[1500, { type: 'speed', s: 2 }],
[1800, { type: 'wave' }],
]
let idx = 0
const TOTAL_TICKS = 3600 // ~2 Minuten Spielzeit @30 Ticks/s
let p0: ReturnType<GameEngine['applyAction']> = true
let p1 = p0
for (let tick = 1; tick <= TOTAL_TICKS; tick++) {
while (idx < script.length && script[idx][0] <= tick) {
const a = script[idx][1]
p0 = e1.applyAction(a, idx % 2) // alternate acting players
p1 = e2.applyAction(a, idx % 2)
idx++
}
e1.update(1 / 30)
e2.update(1 / 30)
}
function state(e: GameEngine): string {
return JSON.stringify({
money: Math.floor(e.money * 1000),
lives: e.lives,
score: e.score,
kills: e.kills,
wave: e.waveNo,
phase: e.phase,
enemies: e.enemies.map((x) => [x.id, Math.round(x.hp * 1000), x.wp]),
towers: e.towers.map((t) => [t.id, t.kind, t.level, t.kills, Math.round(t.damageDealt)]),
queue: e.spawnQueue.map((s) => [Math.round(s.t * 1000), s.kind]),
blocked: e.blockedCells.size,
})
}
const s1 = state(e1)
const s2 = state(e2)
console.log('Aktionen ausgeführt:', idx, '| identisch:', p0 === p1)
console.log('Zustand identisch:', s1 === s2)
if (s1 !== s2) {
console.log('e1:', s1.slice(0, 400))
console.log('e2:', s2.slice(0, 400))
process.exit(1)
}
console.log(`OK: beide Engines identisch nach ${TOTAL_TICKS} Ticks | Welle ${e1.waveNo} | ❤ ${e1.lives} | 🪙 ${Math.floor(e1.money)} | Türme ${e1.towers.length}`)