- Introduce 4 playable map environments: * Meadow (Grüne Lichtung): Classic balanced S-curve path with lush grasslands, flowers, and standard obstacles * Desert (Sonnendünen): Winding dune trail with sandstone rocks, oasis palms, and scrub * Frostland (Frostgipfel): Icy serpentine path through snowfields with frost-covered pines and crystal rocks * Volcano (Lavabruch): Tight aggressive layout through dark obsidian/basalt with glowing magma streams and floating ember sparks - Add comprehensive MapDef & MapTheme configuration for distinct waypoint routes, flying trajectories, biome color palettes, portal/keep styles, and obstacle densities - Extend Canvas 2D renderer to dynamically adapt backgrounds, path texturing, portal effects, and procedurally drawn biome foliage/decorations per map - Upgrade GameEngine with dynamic map loading, obstacle clearing logic, and per-map highscore persistence - Implement multiplayer map synchronization in WebSocket server (set-map event), room state, and lobby UI allowing host map selection - Update StartScreen and LobbyScreen components with interactive map selection cards and per-map highscore tracking - Add automated test suites for map path integrity, determinism, playability, and room map protocol synchronization
70 lines
2.8 KiB
JavaScript
70 lines
2.8 KiB
JavaScript
/**
|
|
* Smoke test for the map-aware multiplayer room protocol:
|
|
* create with a map, host changes the map, join sees the map, start carries it.
|
|
* Requires the server: PORT=3102 node server/server.mjs
|
|
*/
|
|
import { WebSocket } from 'ws'
|
|
|
|
const PORT = 3102
|
|
const URL = `ws://localhost:${PORT}`
|
|
|
|
function connect(origin) {
|
|
return new Promise((resolve, reject) => {
|
|
const ws = new WebSocket(URL, origin ? { origin } : undefined)
|
|
const queue = []
|
|
const waiters = []
|
|
ws.on('open', () => resolve({
|
|
ws,
|
|
next: () => new Promise((res) => {
|
|
const m = queue.shift()
|
|
if (m) return res(m)
|
|
waiters.push(res)
|
|
}),
|
|
close: () => ws.close(),
|
|
}))
|
|
ws.on('message', (d) => {
|
|
const m = JSON.parse(d.toString())
|
|
const w = waiters.shift()
|
|
if (w) w(m)
|
|
else queue.push(m)
|
|
})
|
|
ws.on('error', reject)
|
|
setTimeout(() => reject(new Error('timeout')), 4000)
|
|
})
|
|
}
|
|
|
|
const host = await connect('http://localhost:' + PORT)
|
|
host.ws.send(JSON.stringify({ t: 'create', mode: 'duel', name: 'Host', mapId: 'volcano' }))
|
|
const room = await host.next()
|
|
console.log('1) Raum erstellt mit Karte :', room.t === 'room' && room.mapId === 'volcano' ? 'OK (volcano)' : `FEHLER (${JSON.stringify(room)})`)
|
|
|
|
const guest = await connect('http://localhost:' + PORT)
|
|
guest.ws.send(JSON.stringify({ t: 'join', code: room.code, name: 'Gast' }))
|
|
const join = await guest.next()
|
|
const joinHost = await host.next()
|
|
console.log('2) Gast sieht Karte :', join.t === 'room' && join.mapId === 'volcano' ? 'OK' : `FEHLER (${JSON.stringify(join)})`)
|
|
console.log('3) Host-Sync bleibt Karte :', joinHost.t === 'room' && joinHost.mapId === 'volcano' ? 'OK' : `FEHLER (${JSON.stringify(joinHost)})`)
|
|
|
|
// guest may NOT change the map
|
|
guest.ws.send(JSON.stringify({ t: 'set-map', mapId: 'desert' }))
|
|
await new Promise((r) => setTimeout(r, 300))
|
|
console.log('4) Gast darf Karte nicht ändern: OK (Server ignoriert)')
|
|
|
|
host.ws.send(JSON.stringify({ t: 'set-map', mapId: 'frostland' }))
|
|
const m1 = await host.next()
|
|
const m2 = await guest.next()
|
|
console.log('5) Host wechselt Karte :', m1.mapId === 'frostland' && m2.mapId === 'frostland' ? 'OK (frostland)' : `FEHLER (${m1.mapId}/${m2.mapId})`)
|
|
|
|
host.ws.send(JSON.stringify({ t: 'start' }))
|
|
const s1 = await host.next()
|
|
const s2 = await guest.next()
|
|
console.log('6) Start trägt Karte :', s1.t === 'start' && s1.mapId === 'frostland' && s2.mapId === 'frostland' ? 'OK' : `FEHLER (${JSON.stringify([s1, s2])})`)
|
|
|
|
// invalid map id must fall back to meadow
|
|
const host2 = await connect('http://localhost:' + PORT)
|
|
host2.ws.send(JSON.stringify({ t: 'create', mode: 'coop', name: 'H2', mapId: 'hack' }))
|
|
const r2 = await host2.next()
|
|
console.log('7) Ungültige Karte -> Wiese :', r2.mapId === 'meadow' ? 'OK' : `FEHLER (${r2.mapId})`)
|
|
|
|
host.close(); guest.close(); host2.close()
|
|
process.exit(0)
|