Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/games/blockilization/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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));
Expand Down
38 changes: 38 additions & 0 deletions tests/headless/blockilization-build.ts
Original file line number Diff line number Diff line change
@@ -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()})`);
}
}
Loading