Files
OpenFrontIO/tests/GameViewCheckpoint.test.ts
scamiv a8ec56b5a4 feat(timeline): integrate timeline functionality into game client
- Added TimelineController to manage timeline events and state.
- Introduced TimelinePanel for user interaction with the timeline.
- Implemented LruCache for efficient storage of timeline records.
- Enhanced Transport and GameRenderer to support timeline features.
- Updated various layers to respond to timeline events, ensuring synchronization with game state.
- Added support for seeking and jumping within the timeline, improving user experience during gameplay.

This commit lays the groundwork for a more interactive and responsive timeline feature in the game client.
2026-02-20 20:11:51 +01:00

117 lines
3.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { Config } from "../src/core/configuration/Config";
import { UnitType } from "../src/core/game/Game";
import { GameMapImpl } from "../src/core/game/GameMap";
import {
GameUpdateType,
type GameUpdateViewData,
} from "../src/core/game/GameUpdates";
import { GameView } from "../src/core/game/GameView";
import type { TerrainMapData } from "../src/core/game/TerrainMapLoader";
import type { WorkerClient } from "../src/core/worker/WorkerClient";
function createEmptyGameUpdates() {
const updates: any = {};
for (const v of Object.values(GameUpdateType)) {
if (typeof v === "number") {
updates[v] = [];
}
}
return updates;
}
function createMinimalGameView(): GameView {
const w = 4;
const h = 3;
const terrain = new Uint8Array(w * h).fill(1 << 7); // land
const gameMap = new GameMapImpl(w, h, terrain, w * h);
const mapData: TerrainMapData = {
nations: [],
gameMap,
miniGameMap: gameMap,
};
return new GameView(
{} as unknown as WorkerClient,
{} as unknown as Config,
mapData,
"client1" as any,
"me",
"game1" as any,
[],
);
}
describe("GameView checkpoints", () => {
it("roundtrips a checkpoint (map, units, railroads, toDelete)", () => {
const view1 = createMinimalGameView();
const tileA = view1.ref(0, 0);
const tileB = view1.ref(1, 0);
const ownerA = 7;
const hasFallout = true;
const defenseBonus = true;
const stateA = ownerA;
const stateB =
3 /* owner */ | (hasFallout ? 1 << 13 : 0) | (defenseBonus ? 1 << 14 : 0);
const packedTileUpdates = new BigUint64Array([
(BigInt(tileA) << 16n) | BigInt(stateA),
(BigInt(tileB) << 16n) | BigInt(stateB),
]);
const updates = createEmptyGameUpdates();
updates[GameUpdateType.Unit].push({
type: GameUpdateType.Unit,
unitType: UnitType.City,
troops: 10,
id: 42,
ownerID: 1,
pos: tileA,
lastPos: tileA,
isActive: false,
reachedTarget: false,
retreating: false,
targetable: false,
markedForDeletion: false,
missileTimerQueue: [],
level: 1,
hasTrainStation: false,
});
updates[GameUpdateType.RailroadConstructionEvent].push({
type: GameUpdateType.RailroadConstructionEvent,
id: 99,
tiles: [tileA, tileB],
});
const gu: GameUpdateViewData = {
tick: 1,
packedTileUpdates,
updates,
playerNameViewData: {},
};
view1.update(gu);
const cp1 = view1.exportCheckpoint();
const view2 = createMinimalGameView();
view2.importCheckpoint(cp1);
const cp2 = view2.exportCheckpoint();
expect(view2.ticks()).toBe(1);
expect(view2.ownerID(tileA)).toBe(ownerA);
expect(view2.hasFallout(tileB)).toBe(true);
expect(((cp2.mapState[tileB] >> 14) & 1) === 1).toBe(true);
expect(cp2.toDeleteUnitIds).toEqual(cp1.toDeleteUnitIds);
expect(cp2.railroads).toEqual(cp1.railroads);
expect(Array.from(cp2.mapState)).toEqual(Array.from(cp1.mapState));
expect(cp2.numTilesWithFallout).toBe(cp1.numTilesWithFallout);
const unit = view2.unit(42);
expect(unit).toBeDefined();
expect(unit?.isActive()).toBe(false);
});
});