Files
OpenFrontIO/src/core/worker/WorkerClient.ts
T
2025-02-01 12:05:11 -08:00

120 lines
3.8 KiB
TypeScript

import { PlayerID, Tile } from "../game/Game";
import { GameUpdateViewData, PlayerActions, PlayerInteraction } from "../GameView";
import { GameConfig, GameID, Turn } from "../Schemas";
import { generateID } from "../Util";
import { WorkerMessage } from "./WorkerMessages";
export class WorkerClient {
private worker: Worker;
private isInitialized = false;
private messageHandlers: Map<string, (message: WorkerMessage) => void>;
private gameUpdateCallback?: (update: GameUpdateViewData) => void;
constructor(private gameID: GameID, private gameConfig: GameConfig) {
this.worker = new Worker(new URL('./Worker.worker.ts', import.meta.url));
this.messageHandlers = new Map();
// Set up global message handler
this.worker.addEventListener('message', this.handleWorkerMessage.bind(this));
}
private handleWorkerMessage(event: MessageEvent<WorkerMessage>) {
const message = event.data;
switch (message.type) {
case 'game_update':
if (this.gameUpdateCallback && message.gameUpdate) {
this.gameUpdateCallback(message.gameUpdate);
}
break;
case 'initialized':
case 'player_actions_result':
if (message.id && this.messageHandlers.has(message.id)) {
const handler = this.messageHandlers.get(message.id)!;
handler(message);
this.messageHandlers.delete(message.id);
}
break;
}
}
initialize(): Promise<void> {
return new Promise((resolve, reject) => {
const messageId = generateID()
this.messageHandlers.set(messageId, (message) => {
if (message.type === 'initialized') {
this.isInitialized = true;
resolve();
}
});
this.worker.postMessage({
type: 'init',
id: messageId,
gameID: this.gameID,
gameConfig: this.gameConfig
});
// Add timeout for initialization
setTimeout(() => {
if (!this.isInitialized) {
this.messageHandlers.delete(messageId);
reject(new Error('Worker initialization timeout'));
}
}, 5000); // 5 second timeout
});
}
start(gameUpdate: (gu: GameUpdateViewData) => void) {
if (!this.isInitialized) {
throw new Error('Failed to initialize pathfinder');
}
this.gameUpdateCallback = gameUpdate;
}
sendTurn(turn: Turn) {
if (!this.isInitialized) {
throw new Error('Worker not initialized');
}
this.worker.postMessage({
type: 'turn',
turn
});
}
playerInteraction(playerID: PlayerID, tile: Tile): Promise<PlayerActions> {
return new Promise((resolve, reject) => {
if (!this.isInitialized) {
reject(new Error('Worker not initialized'));
return;
}
const messageId = generateID()
this.messageHandlers.set(messageId, (message) => {
if (message.type === 'player_actions_result' && message.result !== undefined) {
resolve(message.result);
}
});
this.worker.postMessage({
type: 'player_actions',
id: messageId,
playerID: playerID,
x: tile.cell().x,
y: tile.cell().y
});
});
}
cleanup() {
this.worker.terminate();
this.messageHandlers.clear();
this.gameUpdateCallback = undefined;
}
}