mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 13:50:43 +00:00
775ae77e0a
## Description: When random spawn is active, human SpawnExecutions are pre-created in GameRunner.init() and fire on the same tick as NationExecution. Because humans were added first, their SpawnExecution ticked first, called endSpawnPhase() (in singleplayer), and NationExecution then saw inSpawnPhase()=false, found the nation not alive, and deactivated it before ever queuing a SpawnExecution. Two changes fix this: 1. GameRunner.init(): Move nationExecutions() before spawnPlayers() so NationExecution ticks first and queues its SpawnExecution before the human SpawnExecution can end the spawn phase. 2. NationExecution.tick(): After the spawn-phase block, add a guard that waits when spawnExecAdded is true but the nation hasn't actually spawned yet. This prevents NationExecution from deactivating on the very next tick (via !isAlive()) before its queued SpawnExecution has had a chance to fire and give the nation territory. I tested it in singleplaye with and without random spawn and also in public lobbies. Nations now always spawn. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin
106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { NationExecution } from "../../src/core/execution/NationExecution";
|
|
import { SpawnExecution } from "../../src/core/execution/SpawnExecution";
|
|
import { Cell, Nation, PlayerInfo, PlayerType } from "../../src/core/game/Game";
|
|
import { GameConfig, GameID } from "../../src/core/Schemas";
|
|
import { setup } from "../util/Setup";
|
|
import { executeTicks } from "../util/utils";
|
|
|
|
const gameID: GameID = "test_game_id";
|
|
|
|
async function createTestGame(
|
|
randomSpawn: boolean,
|
|
nationCells: { x: number; y: number }[],
|
|
) {
|
|
const game = await setup(
|
|
"plains",
|
|
{ randomSpawn } as Partial<GameConfig>,
|
|
[],
|
|
undefined,
|
|
undefined,
|
|
false,
|
|
);
|
|
|
|
const humanInfo = new PlayerInfo(
|
|
"human",
|
|
PlayerType.Human,
|
|
"client_1",
|
|
"human_id",
|
|
);
|
|
game.addPlayer(humanInfo);
|
|
|
|
const nations: { info: PlayerInfo; nation: Nation }[] = [];
|
|
for (let i = 0; i < nationCells.length; i++) {
|
|
const info = new PlayerInfo(
|
|
nationCells.length === 1 ? "TestNation" : `Nation${i}`,
|
|
PlayerType.Nation,
|
|
null,
|
|
nationCells.length === 1 ? "nation_id" : `nation_${i}`,
|
|
);
|
|
const nation = new Nation(
|
|
new Cell(nationCells[i].x, nationCells[i].y),
|
|
info,
|
|
);
|
|
game.addPlayer(info);
|
|
nations.push({ info, nation });
|
|
}
|
|
|
|
return { game, humanInfo, nations };
|
|
}
|
|
|
|
describe("Nation spawn ordering with random spawn", () => {
|
|
test("nation spawns in singleplayer with random spawn", async () => {
|
|
const { game, humanInfo, nations } = await createTestGame(true, [
|
|
{ x: 50, y: 50 },
|
|
]);
|
|
|
|
// Mirror GameRunner.init() ordering: nation first, then human.
|
|
game.addExecution(new NationExecution(gameID, nations[0].nation));
|
|
game.addExecution(
|
|
new SpawnExecution(gameID, game.player(humanInfo.id).info()),
|
|
);
|
|
|
|
executeTicks(game, 4);
|
|
|
|
expect(game.player(humanInfo.id).hasSpawned()).toBe(true);
|
|
expect(game.player(nations[0].info.id).hasSpawned()).toBe(true);
|
|
expect(game.player(nations[0].info.id).isAlive()).toBe(true);
|
|
});
|
|
|
|
test("multiple nations spawn in singleplayer with random spawn", async () => {
|
|
const cells = Array.from({ length: 5 }, (_, i) => ({
|
|
x: 20 + i * 15,
|
|
y: 20 + i * 15,
|
|
}));
|
|
const { game, humanInfo, nations } = await createTestGame(true, cells);
|
|
|
|
// Nation executions first (mirrors GameRunner.init()).
|
|
for (const { nation } of nations) {
|
|
game.addExecution(new NationExecution(gameID, nation));
|
|
}
|
|
// Human spawn execution second.
|
|
game.addExecution(
|
|
new SpawnExecution(gameID, game.player(humanInfo.id).info()),
|
|
);
|
|
|
|
executeTicks(game, 8);
|
|
|
|
expect(game.player(humanInfo.id).hasSpawned()).toBe(true);
|
|
for (const { info } of nations) {
|
|
const player = game.player(info.id);
|
|
expect(player.hasSpawned()).toBe(true);
|
|
expect(player.isAlive()).toBe(true);
|
|
}
|
|
});
|
|
|
|
test("nation spawns in singleplayer without random spawn", async () => {
|
|
const { game, nations } = await createTestGame(false, [{ x: 50, y: 50 }]);
|
|
|
|
game.addExecution(new NationExecution(gameID, nations[0].nation));
|
|
|
|
executeTicks(game, 4);
|
|
|
|
expect(game.player(nations[0].info.id).hasSpawned()).toBe(true);
|
|
expect(game.player(nations[0].info.id).isAlive()).toBe(true);
|
|
});
|
|
});
|