improve game websockt (re)connection (#2584)

Previously, the connection and reconnection logic were identical in
Worker.ts, so clients would need to be re-authorized for cosmetics etc
even when reconnecting. Now, on reconnect, Worker.ts only does
authentication - verifying the jwt is valid.

This will allow clients to require a valid turnstile token when first
connecting, and not when reconnecting after a broken ws connection.

## 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:

evan
This commit is contained in:
Evan
2025-12-08 14:07:07 -08:00
committed by GitHub
parent cb4cf091ff
commit 075c232d8a
7 changed files with 158 additions and 56 deletions
+16 -11
View File
@@ -79,9 +79,17 @@ export function joinLobby(
const transport = new Transport(lobbyConfig, eventBus);
let hasJoined = false;
const onconnect = () => {
console.log(`Joined game lobby ${lobbyConfig.gameID}`);
transport.joinGame(0);
if (hasJoined) {
console.log("rejoining game");
transport.rejoinGame(0);
} else {
hasJoined = true;
console.log(`Joining game lobby ${lobbyConfig.gameID}`);
transport.joinGame();
}
};
let terrainLoad: Promise<TerrainMapData> | null = null;
@@ -198,7 +206,6 @@ export class ClientGameRunner {
private isActive = false;
private turnsSeen = 0;
private hasJoined = false;
private lastMousePosition: { x: number; y: number } | null = null;
private lastMessageTime: number = 0;
@@ -322,13 +329,12 @@ export class ClientGameRunner {
const onconnect = () => {
console.log("Connected to game server!");
this.transport.joinGame(this.turnsSeen);
this.transport.rejoinGame(this.turnsSeen);
};
const onmessage = (message: ServerMessage) => {
this.lastMessageTime = Date.now();
if (message.type === "start") {
this.hasJoined = true;
console.log("starting game!");
console.log("starting game! in client game runner");
if (this.gameView.config().isRandomSpawn()) {
const goToPlayer = () => {
@@ -403,10 +409,6 @@ export class ClientGameRunner {
);
}
if (message.type === "turn") {
if (!this.hasJoined) {
this.transport.joinGame(0);
return;
}
// Track when we receive the turn to calculate delay
const now = Date.now();
if (this.lastTickReceiveTime > 0) {
@@ -425,7 +427,10 @@ export class ClientGameRunner {
}
}
};
this.transport.connect(onconnect, onmessage);
this.transport.updateCallback(onconnect, onmessage);
console.log("sending join game");
// Rejoin game from the start so we don't miss any turns.
this.transport.rejoinGame(0);
}
public stop() {
+20 -2
View File
@@ -17,6 +17,7 @@ import {
ClientJoinMessage,
ClientMessage,
ClientPingMessage,
ClientRejoinMessage,
ClientSendWinnerMessage,
Intent,
ServerMessage,
@@ -287,6 +288,14 @@ export class Transport {
}
}
public updateCallback(
onconnect: () => void,
onmessage: (message: ServerMessage) => void,
) {
this.onconnect = onconnect;
this.onmessage = onmessage;
}
private connectLocal(
onconnect: () => void,
onmessage: (message: ServerMessage) => void,
@@ -376,18 +385,27 @@ export class Transport {
}
}
joinGame(numTurns: number) {
joinGame() {
this.sendMsg({
type: "join",
gameID: this.lobbyConfig.gameID,
clientID: this.lobbyConfig.clientID,
lastTurn: numTurns,
token: this.lobbyConfig.token,
username: this.lobbyConfig.playerName,
cosmetics: this.lobbyConfig.cosmetics,
} satisfies ClientJoinMessage);
}
rejoinGame(lastTurn: number) {
this.sendMsg({
type: "rejoin",
gameID: this.lobbyConfig.gameID,
clientID: this.lobbyConfig.clientID,
lastTurn: lastTurn,
token: this.lobbyConfig.token,
} satisfies ClientRejoinMessage);
}
leaveGame() {
if (this.isLocal) {
this.localServer.endGame();