diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index 3a9ab7a03..2adbdda3e 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -3,7 +3,6 @@ import { EventBus } from "../core/EventBus"; import { AllPlayersStats, ClientMessage, - ClientMessageSchema, ClientSendWinnerMessage, GameRecordSchema, Intent, @@ -91,22 +90,7 @@ export class LocalServer { this.paused = false; } - onMessage(message: string) { - let parsed; - try { - parsed = JSON.parse(message); - } catch (e) { - console.error("Failed to parse client message:", e); - return; - } - const result = ClientMessageSchema.safeParse(parsed); - if (!result.success) { - const error = z.prettifyError(result.error); - console.error("Error parsing client message", error); - return; - } - - const clientMsg: ClientMessage = result.data; + onMessage(clientMsg: ClientMessage) { if (clientMsg.type === "intent") { if (this.lobbyConfig.gameRecord) { // If we are replaying a game, we don't want to process intents diff --git a/src/client/Transport.ts b/src/client/Transport.ts index 70150fe12..515fab7b7 100644 --- a/src/client/Transport.ts +++ b/src/client/Transport.ts @@ -17,6 +17,7 @@ import { ClientHashMessage, ClientIntentMessage, ClientJoinMessage, + ClientMessage, ClientPingMessage, ClientSendWinnerMessage, Intent, @@ -241,11 +242,9 @@ export class Transport { if (this.pingInterval === null) { this.pingInterval = window.setInterval(() => { if (this.socket !== null && this.socket.readyState === WebSocket.OPEN) { - this.sendMsg( - JSON.stringify({ - type: "ping", - } satisfies ClientPingMessage), - ); + this.sendMsg({ + type: "ping", + } satisfies ClientPingMessage); } }, 5 * 1000); } @@ -299,6 +298,10 @@ export class Transport { this.onmessage = onmessage; this.socket.onopen = () => { console.log("Connected to game server!"); + if (this.socket === null) { + console.error("socket is null"); + return; + } while (this.buffer.length > 0) { console.log("sending dropped message"); const msg = this.buffer.pop(); @@ -306,25 +309,24 @@ export class Transport { console.warn("msg is undefined"); continue; } - this.sendMsg(msg); + this.socket.send(msg); } onconnect(); }; this.socket.onmessage = (event: MessageEvent) => { - let parsed; try { - parsed = JSON.parse(event.data); + const parsed = JSON.parse(event.data); + const result = ServerMessageSchema.safeParse(parsed); + if (!result.success) { + const error = z.prettifyError(result.error); + console.error("Error parsing server message", error); + return; + } + this.onmessage(result.data); } catch (e) { - console.error("Failed to parse server message:", e, event.data); + console.error("Error in onmessage handler:", e, event.data); return; } - const result = ServerMessageSchema.safeParse(parsed); - if (!result.success) { - const error = z.prettifyError(result.error); - console.error("Error parsing server message", error); - return; - } - this.onmessage(result.data); }; this.socket.onerror = (err) => { console.error("Socket encountered error: ", err, "Closing socket"); @@ -353,17 +355,15 @@ export class Transport { } joinGame(numTurns: number) { - this.sendMsg( - JSON.stringify({ - type: "join", - gameID: this.lobbyConfig.gameID, - clientID: this.lobbyConfig.clientID, - lastTurn: numTurns, - token: this.lobbyConfig.token, - username: this.lobbyConfig.playerName, - flag: this.lobbyConfig.flag, - } satisfies ClientJoinMessage), - ); + this.sendMsg({ + type: "join", + gameID: this.lobbyConfig.gameID, + clientID: this.lobbyConfig.clientID, + lastTurn: numTurns, + token: this.lobbyConfig.token, + username: this.lobbyConfig.playerName, + flag: this.lobbyConfig.flag, + } satisfies ClientJoinMessage); } leaveGame(saveFullGame: boolean = false) { @@ -539,12 +539,11 @@ export class Transport { private onSendWinnerEvent(event: SendWinnerEvent) { if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { - const msg = { + this.sendMsg({ type: "winner", winner: event.winner, allPlayersStats: event.allPlayersStats, - } satisfies ClientSendWinnerMessage; - this.sendMsg(JSON.stringify(msg, replacer)); + } satisfies ClientSendWinnerMessage); } else { console.log( "WebSocket is not open. Current state:", @@ -556,13 +555,11 @@ export class Transport { private onSendHashEvent(event: SendHashEvent) { if (this.isLocal || this.socket?.readyState === WebSocket.OPEN) { - this.sendMsg( - JSON.stringify({ - type: "hash", - turnNumber: event.tick, - hash: event.hash, - } satisfies ClientHashMessage), - ); + this.sendMsg({ + type: "hash", + turnNumber: event.tick, + hash: event.hash, + } satisfies ClientHashMessage); } else { console.log( "WebSocket is not open. Current state:", @@ -603,7 +600,7 @@ export class Transport { type: "intent", intent: intent, } satisfies ClientIntentMessage; - this.sendMsg(JSON.stringify(msg)); + this.sendMsg(msg); } else { console.log( "WebSocket is not open. Current state:", @@ -613,23 +610,26 @@ export class Transport { } } - private sendMsg(msg: string) { + private sendMsg(msg: ClientMessage) { if (this.isLocal) { + // Forward message to local server this.localServer.onMessage(msg); + return; + } else if (this.socket === null) { + // Socket missing, do nothing + return; + } + const str = JSON.stringify(msg, replacer); + if (this.socket.readyState === WebSocket.CLOSED) { + // Buffer message + console.warn("socket not ready, closing and trying later"); + this.socket.close(); + this.socket = null; + this.connectRemote(this.onconnect, this.onmessage); + this.buffer.push(str); } else { - if (this.socket === null) return; - if ( - this.socket.readyState === WebSocket.CLOSED || - this.socket.readyState === WebSocket.CLOSED - ) { - console.warn("socket not ready, closing and trying later"); - this.socket.close(); - this.socket = null; - this.connectRemote(this.onconnect, this.onmessage); - this.buffer.push(msg); - } else { - this.socket.send(msg); - } + // Send the message directly + this.socket.send(str); } }