diff --git a/src/server/Client.ts b/src/server/Client.ts index 9fda7317d..07b918db8 100644 --- a/src/server/Client.ts +++ b/src/server/Client.ts @@ -17,7 +17,7 @@ export class Client { public readonly roles: string[] | undefined, public readonly flares: string[] | undefined, public readonly ip: string, - public readonly username: string, + public username: string, public readonly uncensoredUsername: string, public ws: WebSocket, public readonly cosmetics: PlayerCosmetics | undefined, diff --git a/src/server/GameManager.ts b/src/server/GameManager.ts index 1be464dfa..c512896ee 100644 --- a/src/server/GameManager.ts +++ b/src/server/GameManager.ts @@ -46,10 +46,11 @@ export class GameManager { persistentID: string, gameID: GameID, lastTurn: number = 0, + newUsername?: string, ): boolean { const game = this.games.get(gameID); if (!game) return false; - return game.rejoinClient(ws, persistentID, lastTurn); + return game.rejoinClient(ws, persistentID, lastTurn, newUsername); } createGame( diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index 42b46a68d..bb55c02c5 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -257,10 +257,13 @@ export class GameServer { // Attempt to reconnect a client by persistentID. Returns true if successful. // Only the WebSocket is updated — username, cosmetics, etc. are preserved // from the original join to maintain consistency throughout the game session. + // Exception: in the pre-game lobby, the username is updated so players can + // rename between leaving and rejoining. public rejoinClient( ws: WebSocket, persistentID: string, lastTurn: number = 0, + newUsername?: string, ): boolean { const clientID = this.getClientIdForPersistentId(persistentID); if (!clientID) return false; @@ -283,6 +286,11 @@ export class GameServer { client.lastPing = Date.now(); this.markClientDisconnected(client.clientID, false); + // Allow username updates in the pre-game lobby + if (!this._hasStarted && newUsername !== undefined) { + client.username = newUsername; + } + client.ws = ws; this.addListeners(client); this.startLobbyInfoBroadcast(); diff --git a/src/server/Worker.ts b/src/server/Worker.ts index a5b2ecdf1..2a2a0e46b 100644 --- a/src/server/Worker.ts +++ b/src/server/Worker.ts @@ -356,8 +356,20 @@ export async function startWorker() { } // Try to reconnect an existing client (e.g., page refresh) - // If successful, skip all authorization - if (gm.rejoinClient(ws, persistentId, clientMsg.gameID)) { + // If successful, skip all authorization (but pass updated username + // so players can rename in the pre-game lobby) + const censoredUsername = privilegeRefresher + .get() + .censorUsername(clientMsg.username); + if ( + gm.rejoinClient( + ws, + persistentId, + clientMsg.gameID, + 0, + censoredUsername, + ) + ) { return; } @@ -439,11 +451,6 @@ export async function startWorker() { } } - // Censor profane usernames server-side (don't reject, just rename) - const censoredUsername = privilegeRefresher - .get() - .censorUsername(clientMsg.username); - // Create client and add to game const client = new Client( generateID(),