import assert from "node:assert/strict"; import test from "node:test"; import { ROYALE_BOT_COUNT, ROYALE_BOT_ID_BASE, ROYALE_CHUNK_SIZE, ROYALE_MAP_SEED, collidesWithRoyaleMap, generateRoyaleChunk, hasRoyaleLineOfSight, royaleGame, } from "../dist/index.js"; test("the public world seed deterministically generates a 2 km chunked island", () => { const first = generateRoyaleChunk(-3, 4); const second = generateRoyaleChunk(-3, 4); const neighbor = generateRoyaleChunk(-2, 4); assert.deepEqual(first, second); assert.notDeepEqual(first, neighbor); assert.equal(ROYAALE_MAP_SEED_FOR_TEST(), ROYALE_MAP_SEED); assert.equal(ROYAALE_CHUNK_SIZE_FOR_TEST(), ROYALE_CHUNK_SIZE); assert.ok(first.obstacles.every((obstacle) => obstacle.id.startsWith("-3:4:"))); }); test("Syncer Royale simulates a complete authority battle headlessly", () => { const server = royaleGame.createServer(); const totals = { shots: 0, damage: 0, eliminations: 0, winners: 0 }; for (let tick = 0; tick < 6_000; tick += 1) { for (const event of server.step().events) { if (event.type === "shot") totals.shots += 1; if (event.type === "damage") totals.damage += 1; if (event.type === "elimination") totals.eliminations += 1; if (event.type === "winner") totals.winners += 1; } } assert.equal(server.currentState.players.length, ROYALE_BOT_COUNT); assert.ok(totals.shots > 150, `expected firefights, saw ${totals.shots}`); assert.ok(totals.damage > 50, `expected damage, saw ${totals.damage}`); assert.ok(totals.eliminations > 15, `expected eliminations, saw ${totals.eliminations}`); assert.ok(server.currentState.players.every((player) => Number.isFinite(player.x + player.z))); }); test("Royale projections hide authority secrets and distant combatants", () => { const server = royaleGame.createServer(); server.addPlayer(1); const authority = server.currentState; const viewer = authority.players.find((player) => player.id === 1); const hidden = authority.players.find((player) => player.id === ROYALE_BOT_ID_BASE); viewer.altitude = 0; viewer.x = 0; viewer.z = 0; hidden.x = 430; hidden.z = 0; hidden.alive = true; const snapshot = server.createSnapshot(1, 0).state; assert.equal(snapshot.mapSeed, ROYALE_MAP_SEED); assert.equal("secretSeed" in snapshot, false); assert.ok(snapshot.players.some((player) => player.id === 1)); assert.ok(!snapshot.players.some((player) => player.id === hidden.id)); assert.ok( snapshot.players .filter((player) => player.id !== 1) .every((player) => player.health === null && player.armor === null), ); const [sound] = server.createPerceptions(1, [ { id: 99, type: "shot", sourceId: hidden.id, x: hidden.x, y: 1.55, z: hidden.z, yaw: 0, pitch: 0 }, ]); assert.equal(sound.type, "gunfire"); assert.equal("sourceId" in sound, false); assert.equal("x" in sound, false); const [ownShot] = server.createPerceptions(1, [ { id: 100, type: "shot", sourceId: viewer.id, x: viewer.x, y: 1.55, z: viewer.z, yaw: viewer.yaw, pitch: viewer.pitch, }, ]); assert.equal(ownShot.type, "shot"); assert.equal(ownShot.sourceId, viewer.id); }); test("Royale enforces a viewer bandwidth budget after ordinary visibility", () => { const server = royaleGame.createServer(); server.addPlayer(1); const authority = server.currentState; const viewer = authority.players.find((player) => player.id === 1); viewer.altitude = 0; viewer.x = 0; viewer.z = 0; for (const bot of authority.players.filter((player) => player.bot)) { bot.x = 800; bot.z = 0; } for (let index = 0; index < 100; index += 1) { const item = authority.loot[index]; item.active = true; item.x = (index % 10) * 2 - 9; item.z = Math.floor(index / 10) * 2 - 9; } const snapshot = server.createSnapshot(1, 0).state; assert.ok(snapshot.stream.droppedCount > 0); assert.ok(snapshot.stream.usedBytes <= snapshot.stream.budgetBytes); assert.ok(snapshot.loot.length < 100); assert.equal(snapshot.players.filter((player) => player.id === 1).length, 1); }); test("Royale input and projected state round-trip through the generic protocol", () => { const server = royaleGame.createServer(); server.addPlayer(7); const client = royaleGame.createClient(); const welcome = server.createSnapshot(7, 10); client.initialize(7, welcome); const packet = client.createInput({ forward: 1, strafe: 0.25, yaw: 0.7, pitch: -0.2, fire: false, sprint: true, reload: false, }, 1); const decoded = royaleGame.protocol.decodeClient( royaleGame.protocol.encodeClient({ kind: "input", packet }), ); assert.equal(decoded.kind, "input"); assert.deepEqual(decoded.packet, packet); assert.equal(server.submitInput(7, decoded.packet).accepted, true); server.step(); assert.equal(server.currentState.players.find((player) => player.id === 7).pitch, -0.2); client.step(); client.reconcile(server.createSnapshot(7, 20)); assert.ok(client.currentState.players.some((player) => player.id === 7)); }); test("Royale pitch drives the authoritative three-dimensional hit ray", () => { const server = royaleGame.createServer(); server.addPlayer(7); server.addPlayer(8); const authority = server.currentState; for (const bot of authority.players.filter((player) => player.bot)) bot.alive = false; const shooter = authority.players.find((player) => player.id === 7); const target = authority.players.find((player) => player.id === 8); let origin = null; for (let x = -100; x <= 100 && !origin; x += 10) { for (let z = -100; z <= 60; z += 10) { if ( !collidesWithRoyaleMap(x, z) && !collidesWithRoyaleMap(x, z + 40) && hasRoyaleLineOfSight(x, z, x, z + 40) ) { origin = { x, z }; break; } } } assert.ok(origin, "expected an unobstructed public-map firing lane"); Object.assign(shooter, { x: origin.x, z: origin.z, altitude: 0, yaw: 0 }); Object.assign(target, { x: origin.x, z: origin.z + 40, altitude: 20, health: 100, armor: 0 }); const pitch = Math.atan2(20, 40); assert.equal(server.submitInput(7, { sequence: 1, targetTick: 1, observedTick: 0, input: { forward: 0, strafe: 0, yaw: 0, pitch, fire: true, sprint: false, reload: false, }, }).accepted, true); const result = server.step(); assert.ok(result.events.some((event) => event.type === "damage" && event.targetId === 8)); assert.ok(target.health < 100); }); test("Royale strafe-right matches the first-person camera's screen right", () => { const server = royaleGame.createServer(); server.addPlayer(7); const authority = server.currentState; for (const bot of authority.players.filter((player) => player.bot)) bot.alive = false; const player = authority.players.find((candidate) => candidate.id === 7); Object.assign(player, { x: 0, z: 0, altitude: 20, yaw: 0 }); assert.equal(server.submitInput(7, { sequence: 1, targetTick: 1, observedTick: 0, input: { forward: 0, strafe: 1, yaw: 0, pitch: 0, fire: false, sprint: false, reload: false, }, }).accepted, true); server.step(); assert.ok(player.velocityX < 0, "screen-right is world -X while facing world +Z"); }); function ROYAALE_MAP_SEED_FOR_TEST() { return ROYALE_MAP_SEED; } function ROYAALE_CHUNK_SIZE_FOR_TEST() { return ROYALE_CHUNK_SIZE; }