From 87a72a4e93f87d71ff565b61650a1b1fb5072752 Mon Sep 17 00:00:00 2001
From: FloPinguin <25036848+FloPinguin@users.noreply.github.com>
Date: Thu, 16 Apr 2026 06:03:01 +0200
Subject: [PATCH] =?UTF-8?q?Forward=20game=20tick=20errors=20from=20worker?=
=?UTF-8?q?=20to=20client=20=F0=9F=90=9B=20(#3690)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Description:
Currently there is a "Game tick error: Invalid coordinates: NaN,NaN"
error which crashes the game rarely.
Maybe introduced by water nukes, I'm not sure.
To properly debug it when it happens again, lets print the stacktrace:
- Game tick errors (`ErrorUpdate`) were silently dropped in the web
worker's `drain()` function, so the client's error modal never displayed
- Added a `"game_error"` worker message type to forward `ErrorUpdate`
from the worker to the main thread
- The client now receives the full error message and stack trace via the
existing `showErrorModal` dialog
## 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
- [X] I confirm I have thoroughly tested these changes and take full
responsibility for any bugs introduced
## Please put your Discord username so you can be contacted if a bug or
regression is found:
FloPinguin
---
src/core/worker/Worker.worker.ts | 3 +++
src/core/worker/WorkerClient.ts | 5 +++++
src/core/worker/WorkerMessages.ts | 9 ++++++++-
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts
index 65e33c6de..1a41bfb3c 100644
--- a/src/core/worker/Worker.worker.ts
+++ b/src/core/worker/Worker.worker.ts
@@ -60,6 +60,9 @@ async function drain(): Promise {
const batch: GameUpdateViewData[] = [];
const onTickUpdate = (gu: GameUpdateViewData | ErrorUpdate) => {
if (!("updates" in gu)) {
+ if ("errMsg" in gu) {
+ sendMessage({ type: "game_error", error: gu } as WorkerMessage);
+ }
return;
}
batch.push(gu);
diff --git a/src/core/worker/WorkerClient.ts b/src/core/worker/WorkerClient.ts
index 3ceadf32e..459ada824 100644
--- a/src/core/worker/WorkerClient.ts
+++ b/src/core/worker/WorkerClient.ts
@@ -53,6 +53,11 @@ export class WorkerClient {
}
}
break;
+ case "game_error":
+ if (this.gameUpdateCallback && message.error) {
+ this.gameUpdateCallback(message.error);
+ }
+ break;
case "initialized":
default:
diff --git a/src/core/worker/WorkerMessages.ts b/src/core/worker/WorkerMessages.ts
index 861e31172..7f4158a46 100644
--- a/src/core/worker/WorkerMessages.ts
+++ b/src/core/worker/WorkerMessages.ts
@@ -7,7 +7,7 @@ import {
PlayerProfile,
} from "../game/Game";
import { TileRef } from "../game/GameMap";
-import { GameUpdateViewData } from "../game/GameUpdates";
+import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates";
import { ClientID, GameStartInfo, Turn } from "../Schemas";
export type WorkerMessageType =
@@ -16,6 +16,7 @@ export type WorkerMessageType =
| "turn"
| "game_update"
| "game_update_batch"
+ | "game_error"
| "player_actions"
| "player_actions_result"
| "player_buildables"
@@ -62,6 +63,11 @@ export interface GameUpdateBatchMessage extends BaseWorkerMessage {
gameUpdates: GameUpdateViewData[];
}
+export interface GameErrorMessage extends BaseWorkerMessage {
+ type: "game_error";
+ error: ErrorUpdate;
+}
+
export interface PlayerActionsMessage extends BaseWorkerMessage {
type: "player_actions";
playerID: PlayerID;
@@ -147,6 +153,7 @@ export type WorkerMessage =
| InitializedMessage
| GameUpdateMessage
| GameUpdateBatchMessage
+ | GameErrorMessage
| PlayerActionsResultMessage
| PlayerBuildablesResultMessage
| PlayerProfileResultMessage