Simplify ClientMessage handling (#1235)

## Description:

- Expand the try/catch block in socket message handlers to encapsulate
all possible throwers.
- Remove unnecessary zod schema parsing of outgoing messages.
- Avoid unnecessary serialization and deserialization in singleplayer.

## 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
- [x] I understand that submitting code with bugs that could have been
caught through manual testing blocks releases and new features for all
contributors

---------

Co-authored-by: Scott Anderson <662325+scottanderson@users.noreply.github.com>
This commit is contained in:
Scott Anderson
2025-06-21 22:34:45 -04:00
committed by GitHub
parent ce991f97a7
commit 0f2008a68d
2 changed files with 53 additions and 69 deletions
+1 -17
View File
@@ -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
+52 -52
View File
@@ -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);
}
}