diff --git a/src/games/blockilization/server.ts b/src/games/blockilization/server.ts index c1da412..b9364b3 100644 --- a/src/games/blockilization/server.ts +++ b/src/games/blockilization/server.ts @@ -496,12 +496,19 @@ function begin(game: GameContext, g: Game, withSeats: LobbySeat[]) { sendLobby(game); } -/** The map's ground is loaded on the server: a point every chunk across it (an unloaded one collides). */ -function mapLoaded(game: GameContext, g: Game): boolean { +/** + * Where the map's ground isn't loaded on the server yet, if anywhere: a point every chunk across it + * (an unloaded one collides). Null: all of it is. + */ +function unloadedSpot(game: GameContext, g: Game): { x: number; z: number } | null { const size = extent(g.grid); // (Its last column is just inside: a world that wraps ends at a chunk's edge.) - for (let x = 0; x < size.w + 16; x += 16) for (let z = 0; z < size.d + 16; z += 16) if (game.world.collisionHeight(Math.min(x, size.w - 0.5), SEA_Y + 100, Math.min(z, size.d)) > 0) return false; - return true; + for (let x = 0; x < size.w + 16; x += 16) + for (let z = 0; z < size.d + 16; z += 16) { + const at = { x: Math.min(x, size.w - 0.5), z: Math.min(z, size.d) }; + if (game.world.collisionHeight(at.x, SEA_Y + 100, at.z) > 0) return at; + } + return null; } /** Somewhere far off: screens looking there let the map's ground go. */ @@ -1555,7 +1562,15 @@ export default defineServer(shared, { const size = extent(civGame.grid); const middle = { x: size.w / 2, y: SEA_Y + 40, z: size.d / 2 }; if (stage === 'gather') { - if (!mapLoaded(game, civGame)) return; + // The server loads the world round where people are, 12 chunks at most: a map wider than that + // (the large Earth's 14 from its middle) is loaded by taking them to its far parts in turn, + // which stay loaded once made. Then back to the middle. + const spot = unloadedSpot(game, civGame); + if (spot) { + for (const p of humans(game)) p.teleport({ x: spot.x, y: SEA_Y + 40, z: spot.z }); + return; + } + for (const p of humans(game)) p.teleport(middle); stage = 'away'; stageAt = game.clock.now; for (const p of humans(game)) lookAt(p, awayFrom(middle)); diff --git a/tests/headless/blockilization-build.ts b/tests/headless/blockilization-build.ts new file mode 100644 index 0000000..42ee05d --- /dev/null +++ b/tests/headless/blockilization-build.ts @@ -0,0 +1,38 @@ +import { readFileSync } from 'node:fs'; +import { GameHost } from '../../src/platform/host/game'; +import type { Game } from '../../src/games/blockilization/rules/state'; +import { extent } from '../../src/games/blockilization/geometry'; +import { check, games } from './_harness'; +import { devGames } from '../../src/games/server'; + +/** + * Blockilization builds every map, the widest too: the server loads the world 12 chunks round its + * people at most (as far as a screen asks), and the large Earth reaches 14 from its middle, so the + * build takes them to its far parts first. (It waited at 0% for them, for good, before.) + */ +export default async function blockilizationBuild() { + const def = [...games, ...(await devGames())].find((g) => g.id === 'blockilization')!; + for (const size of ['large', 'huge'] as const) { + // (As in a room: 8 columns round each person, raised to what their screens ask, a few columns a tick.) + const host = new GameHost(def, { engine: readFileSync('engine/pkg/voxel_engine_bg.wasm'), seed: 3, remote: true, radius: 8, cheats: true, player: { id: 'p1', name: 'Player' } }); + const civ = (globalThis as unknown as { __civ: { game: () => Game | null; phase: () => string } }).__civ; + const step = (seconds: number) => { + for (let t = 0; t < seconds; t += 1 / 30) host.step(1 / 30); + }; + const send = (id: string, name: string, data: unknown) => { + host.command(id, { t: 'message', msg: { t: 'game', player: id, name, data } }); + step(2 / 30); + }; + const ann = host.connect('Ann'); + host.command(ann.id, { t: 'start' }); + host.command(ann.id, { t: 'radius', columns: 12 }); + step(0.5); + send(ann.id, 'lobby', { a: 'settings', type: 'earth', size, cityStates: 4 }); + send(ann.id, 'lobby', { a: 'bots', count: 7 }); + send(ann.id, 'lobby', { a: 'start' }); + for (let i = 0; i < 240 && civ.phase() !== 'playing'; i++) step(0.5); + const across = extent(civ.game()!.grid).w / 32; + check(across > 12, `the ${size} Earth is wider than the server loads round one place (${across} chunks from its middle)`); + check(civ.phase() === 'playing', `the ${size} Earth is built and the game begins (${civ.phase()})`); + } +}