diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index 3c7329d8a..66c09fcab 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -8,7 +8,7 @@ import { loadTerrainFromFile, loadTerrainMap } from "../core/game/TerrainMapLoad import { SendAttackIntentEvent, SendSpawnIntentEvent, Transport } from "./Transport"; import { createCanvas } from "./Utils"; import { MessageType } from '../core/game/Game'; -import { DisplayMessageUpdate } from "../core/game/GameUpdates"; +import { DisplayMessageUpdate, ErrorUpdate } from "../core/game/GameUpdates"; import { WorkerClient } from "../core/worker/WorkerClient"; import { consolex, initRemoteSender } from "../core/Consolex"; import { getConfig, getServerConfig } from "../core/configuration/Config"; @@ -121,9 +121,11 @@ export class ClientGameRunner { this.renderer.initialize() this.input.initialize() - this.worker.start((gu: GameUpdateViewData) => { - const size = gu.packedTileUpdates.length * 4 / 1000 - // console.log(`game update size: ${size}kb`) + this.worker.start((gu: GameUpdateViewData | ErrorUpdate) => { + if ('errMsg' in gu) { + showErrorModal(gu.errMsg, gu.stack, this.clientID) + return + } this.gameView.update(gu) this.renderer.tick() }) @@ -209,8 +211,8 @@ export class ClientGameRunner { } } -function showErrorModal(error: Error, clientID: ClientID) { - const errorText = `Error: ${error.message}\nStack: ${error.stack}`; +function showErrorModal(errMsg: string, stack: string, clientID: ClientID) { + const errorText = `Error: ${errMsg}\nStack: ${stack}`; consolex.error(errorText); const modal = document.createElement('div'); diff --git a/src/core/GameRunner.ts b/src/core/GameRunner.ts index ed9bbd2b4..bf3155cd2 100644 --- a/src/core/GameRunner.ts +++ b/src/core/GameRunner.ts @@ -4,8 +4,8 @@ import { getConfig } from "./configuration/Config"; import { EventBus } from "./EventBus"; import { Executor } from "./execution/ExecutionManager"; import { WinCheckExecution } from "./execution/WinCheckExecution"; -import { AllPlayers, Cell, Game, MessageType, Player, PlayerActions, PlayerID, PlayerProfile, PlayerType, UnitType } from "./game/Game"; -import { DisplayMessageUpdate } from "./game/GameUpdates"; +import { AllPlayers, Cell, Game, GameUpdates, MessageType, Player, PlayerActions, PlayerID, PlayerProfile, PlayerType, UnitType } from "./game/Game"; +import { DisplayMessageUpdate, ErrorUpdate } from "./game/GameUpdates"; import { NameViewData } from './game/Game'; import { GameUpdateType } from "./game/GameUpdates"; import { createGame } from "./game/GameImpl"; @@ -35,7 +35,7 @@ export class GameRunner { constructor( public game: Game, private execManager: Executor, - private callBack: (gu: GameUpdateViewData) => void + private callBack: (gu: GameUpdateViewData | ErrorUpdate) => void ) { } @@ -64,7 +64,22 @@ export class GameRunner { this.game.addExecution(...this.execManager.createExecs(this.turns[this.currTurn])) this.currTurn++ - const updates = this.game.executeNextTick() + + let updates: GameUpdates; + + try { + updates = this.game.executeNextTick(); + } catch (error: unknown) { + if (error instanceof Error) { + console.error('Game tick error:', error.message); + this.callBack({ + errMsg: error.message, + stack: error.stack + } as ErrorUpdate) + clearInterval(this.tickInterval) + return + } + } if (this.game.inSpawnPhase() && this.game.ticks() % 2 == 0) { this.game.players() diff --git a/src/core/game/GameUpdates.ts b/src/core/game/GameUpdates.ts index bcf5d7d35..97e30aba7 100644 --- a/src/core/game/GameUpdates.ts +++ b/src/core/game/GameUpdates.ts @@ -10,6 +10,11 @@ export interface GameUpdateViewData { playerNameViewData: Record; } +export interface ErrorUpdate { + errMsg: string + stack?: string +} + export enum GameUpdateType { Tile, Unit, diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts index 7d70fcee1..dfaf9a614 100644 --- a/src/core/worker/WorkerClient.ts +++ b/src/core/worker/WorkerClient.ts @@ -1,5 +1,5 @@ import { PlayerActions, PlayerID, PlayerInfo, PlayerProfile } from "../game/Game"; -import { GameUpdateViewData } from '../game/GameUpdates'; +import { ErrorUpdate, GameUpdateViewData } from '../game/GameUpdates'; import { GameConfig, GameID, Turn } from "../Schemas"; import { generateID } from "../Util"; import { WorkerMessage } from "./WorkerMessages"; @@ -8,7 +8,7 @@ export class WorkerClient { private worker: Worker; private isInitialized = false; private messageHandlers: Map void>; - private gameUpdateCallback?: (update: GameUpdateViewData) => void; + private gameUpdateCallback?: (update: GameUpdateViewData | ErrorUpdate) => void; constructor(private gameID: GameID, private gameConfig: GameConfig) { this.worker = new Worker(new URL('./Worker.worker.ts', import.meta.url)); @@ -67,7 +67,7 @@ export class WorkerClient { }); } - start(gameUpdate: (gu: GameUpdateViewData) => void) { + start(gameUpdate: (gu: GameUpdateViewData | ErrorUpdate) => void) { if (!this.isInitialized) { throw new Error('Failed to initialize pathfinder'); }