diff --git a/Documentations/COMPLETE_GAME_WORKFLOW.md b/Documentations/COMPLETE_GAME_WORKFLOW.md index 12beb10b..eac61973 100644 --- a/Documentations/COMPLETE_GAME_WORKFLOW.md +++ b/Documentations/COMPLETE_GAME_WORKFLOW.md @@ -970,6 +970,8 @@ socket.on('game:ended', { message: string; finalPositions: PlayerPosition[]; timestamp: string; + reason?: string; // Optional: 'gamemaster_left' if GM disconnected + gamemasterName?: string; // Optional: GM name if GM left }); // Server → All Players: Cleanup complete @@ -980,6 +982,36 @@ socket.on('game:cleanup-complete', { }); ``` +**Game End Scenarios**: + +1. **Normal Win** (Player reaches position 100): + ```typescript + { + winner: "player-uuid", + winnerName: "Alice", + message: "🎉 Alice won the game! Congratulations!", + finalPositions: [...], + timestamp: "2025-11-06T..." + } + ``` + +2. **Gamemaster Disconnect** (Game cancelled): + ```typescript + { + reason: "gamemaster_left", + gamemasterName: "Bob", + message: "🎭 Gamemaster Bob left. Game has ended.", + timestamp: "2025-11-06T..." + } + ``` + + **Behavior**: + - Game immediately cancelled when gamemaster disconnects + - All players notified via `game:ended` event + - Database updated: `state = CANCELLED`, `enddate = now` + - All Redis data and socket connections cleaned up + - No winner recorded (game didn't complete normally) + --- #### Error Events diff --git a/Documentations/COMPLETE_GAME_WORKFLOW.pdf b/Documentations/COMPLETE_GAME_WORKFLOW.pdf index 7a10fb58..08e14ee5 100644 Binary files a/Documentations/COMPLETE_GAME_WORKFLOW.pdf and b/Documentations/COMPLETE_GAME_WORKFLOW.pdf differ diff --git a/SerpentRace_Backend/src/Application/Services/GameWebSocketService.ts b/SerpentRace_Backend/src/Application/Services/GameWebSocketService.ts index 1a44e9c2..297f3cc9 100644 --- a/SerpentRace_Backend/src/Application/Services/GameWebSocketService.ts +++ b/SerpentRace_Backend/src/Application/Services/GameWebSocketService.ts @@ -1139,6 +1139,36 @@ export class GameWebSocketService { // If the socket was in a game, handle cleanup if (socket.gameCode && socket.playerName) { try { + // Check if this player is the gamemaster + const game = await this.gameRepository.findByGameCode(socket.gameCode); + const isGamemaster = game && socket.userId && game.createdby === socket.userId; + + // If gamemaster leaves, end the game immediately + if (isGamemaster && game) { + logOther(`Gamemaster ${socket.playerName} left game ${socket.gameCode}, ending game`); + + const gameRoomName = `game_${socket.gameCode}`; + + // Notify all players + this.io.of('/game').to(gameRoomName).emit('game:ended', { + reason: 'gamemaster_left', + gamemasterName: socket.playerName, + message: `🎭 Gamemaster ${socket.playerName} left. Game has ended.`, + timestamp: new Date().toISOString() + }); + + // Update database + await this.gameRepository.update(game.id, { + state: GameState.CANCELLED, + enddate: new Date() + }); + + // Clean up all game data + await this.cleanupGameData(socket.gameCode, game.id); + + return; // Exit early, no need for further cleanup + } + // Clean up any pending card answer if (socket.userId) { const pendingCard = await this.getPendingCard(socket.gameCode, socket.userId); @@ -2236,7 +2266,7 @@ export class GameWebSocketService { if (game) { await this.gameRepository.update(game.id, { state: GameState.FINISHED, - winner: winnerId, + winnerId: winnerId, enddate: new Date() }); }