Skip to content

Bug: disconnect handler always sets winner to undefined in simultaneous-disconnect path #3

@dennisthemenacing

Description

@dennisthemenacing

Bug

In src/sockets/chessSockets.ts, the disconnect event handler has a logic error that causes the game outcome to always have winner: undefined when both players are offline.

Root Cause

The sequence is:

const game = gameManager.disconnect(userId); // marks current player as connected: false

if (!game.players.white?.connected && !game.players.black?.connected) {
  game.outcome = {
    // Both are already false at this point — ternary always returns undefined
    winner: game.players.white?.connected ? 'white' : game.players.black?.connected ? 'black' : undefined,
    reason: 'resignation',
  };
}

After gameManager.disconnect(userId) runs, the disconnecting player is already marked connected: false. So when the both-offline check passes, both game.players.white?.connected and game.players.black?.connected are false. The winner ternary therefore always resolves to undefined.

Impact

When a player disconnects while their opponent is already disconnected (e.g. opponent dropped then this player drops before the 5-second timer fires), the game is erased with outcome = { winner: undefined, reason: 'resignation' }. No player is credited a win.

Expected Behavior

The winner should be the player who was still connected before this disconnect — i.e., the opponent of userId.

Suggested Fix

Capture the still-connected color before calling gameManager.disconnect, or derive the winner from userId directly:

const playerColor = /* derive from game before disconnect */;
const game = gameManager.disconnect(userId);
if (!game.players.white?.connected && !game.players.black?.connected) {
  const winnerColor = playerColor === 'white' ? 'black' : 'white';
  game.outcome = {
    winner: game.players[winnerColor] ? winnerColor : undefined,
    reason: 'resignation',
  };
}

Related

The existing getPlayerColor private method on GameManager could expose this info, but it's currently private. Alternatively, the socket layer could determine the disconnecting player's color before calling disconnect.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions