/** * GameWebSocketService Usage Examples * * This file demonstrates how to use the GameWebSocketService with the new * game token authentication system and private game approval workflow. * * BOARD STRUCTURE: * - Starting position: 0 (before the board) * - Gameplay board: positions 1-100 * - Winning position: 101 (finish line) * - Field types: 'regular', 'positive', 'negative', 'luck' (special effects to be implemented later) */ import { gameWebSocketService } from './src/Api/index'; // Example 1: Frontend WebSocket Connection with Game Tokens /* const gameSocket = io('/game'); // Step 1: Join game via REST API to get game token const joinResponse = await fetch('/api/games/join', { method: 'POST', headers: { 'Content-Type': 'application/json', // Include authorization header if user is authenticated 'Authorization': 'Bearer jwt-token-here' // Optional for public games }, body: JSON.stringify({ gameCode: 'ABC123', playerName: 'Player1' // Required for public games, optional for authenticated users }) }); const gameData = await joinResponse.json(); const gameToken = gameData.gameToken; // Game session token from REST API // Step 2: Join WebSocket room using the game token gameSocket.emit('game:join', { gameToken: gameToken // Single token contains all game session info }); // Listen for game events gameSocket.on('game:joined', (data) => { console.log('Successfully joined game:', data); // { gameCode: 'ABC123', playerName: 'Player1', isAuthenticated: false, gameId: 'uuid', isGamemaster: false, timestamp: '...' } }); // PRIVATE GAME APPROVAL WORKFLOW: gameSocket.on('game:pending-approval', (data) => { console.log('Waiting for gamemaster approval:', data); // Show waiting message to player }); gameSocket.on('game:approval-granted', (data) => { console.log('Approved! Now joining game rooms:', data); // Re-emit with special approved join event gameSocket.emit('game:join-approved', { gameToken: gameToken }); }); gameSocket.on('game:approval-denied', (data) => { console.log('Join request denied:', data); // Show rejection message and reason }); // Gamemaster events (private games only) gameSocket.on('game:player-requesting-join', (data) => { console.log('Player requesting to join:', data); // Show approval/reject buttons to gamemaster }); gameSocket.on('game:state-update', (gameState) => { console.log('Game state updated:', gameState); // gameState.pendingPlayers array available for private games }); gameSocket.on('game:player-specific-event', (data) => { console.log('Event sent specifically to me:', data); }); */ // Example 1.5: Gamemaster Controls (Private Games Only) /* // Approve a pending player function approvePlayer(gameCode: string, playerName: string) { gameSocket.emit('game:approve-player', { gameCode: gameCode, playerName: playerName }); } // Reject a pending player function rejectPlayer(gameCode: string, playerName: string, reason?: string) { gameSocket.emit('game:reject-player', { gameCode: gameCode, playerName: playerName, reason: reason || 'Request denied by gamemaster' }); } // Example UI for gamemaster approval gameSocket.on('game:state', (gameState) => { if (gameState.pendingPlayers && gameState.pendingPlayers.length > 0) { console.log('Pending players awaiting approval:', gameState.pendingPlayers); // Display approval UI for each pending player: // [Approve] [Reject] PlayerName } }); */ // Example 2: Backend Broadcasting (from game logic services) export class GameLogicExample { // Broadcast to all players in a game async notifyAllPlayers(gameCode: string, message: string): Promise { await gameWebSocketService.broadcastGameEvent(gameCode, 'game:notification', { message, timestamp: new Date().toISOString() }); } // Send event to specific player async notifyPlayer(gameCode: string, playerName: string, action: string, data: any): Promise { await gameWebSocketService.sendToPlayer(gameCode, playerName, 'game:player-action', { action, data, timestamp: new Date().toISOString() }); } // Handle dice roll - broadcast to all, send specific result to player async handleDiceRoll(gameCode: string, playerName: string, diceResult: number): Promise { // Broadcast that a player rolled dice await gameWebSocketService.broadcastGameEvent(gameCode, 'game:dice-rolled', { playerName, timestamp: new Date().toISOString() }); // Send specific dice result to the player await gameWebSocketService.sendToPlayer(gameCode, playerName, 'game:dice-result', { result: diceResult, canMove: true, timestamp: new Date().toISOString() }); } // Handle turn change - notify all players and give specific instructions to current player async handleTurnChange(gameCode: string, currentPlayer: string, nextPlayer: string): Promise { // Broadcast turn change to all players await gameWebSocketService.broadcastGameEvent(gameCode, 'game:turn-changed', { previousPlayer: currentPlayer, currentPlayer: nextPlayer, timestamp: new Date().toISOString() }); // Send specific "your turn" message to next player await gameWebSocketService.sendToPlayer(gameCode, nextPlayer, 'game:your-turn', { message: "It's your turn! Roll the dice when ready.", actions: ['roll-dice'], timestamp: new Date().toISOString() }); // Send "waiting" message to other players const connectedPlayers = await gameWebSocketService.getConnectedPlayers(gameCode); const waitingPlayers = connectedPlayers.filter((player: string) => player !== nextPlayer); await gameWebSocketService.sendToPlayers(gameCode, waitingPlayers, 'game:waiting-turn', { message: `Waiting for ${nextPlayer} to play...`, currentPlayer: nextPlayer, timestamp: new Date().toISOString() }); } // Handle field effects - different messages for different players async handleFieldEffect(gameCode: string, playerName: string, fieldType: string, effect: any): Promise { // Broadcast the field activation to all players await gameWebSocketService.broadcastGameEvent(gameCode, 'game:field-activated', { playerName, fieldType, timestamp: new Date().toISOString() }); // Send specific effect to the player who landed on the field await gameWebSocketService.sendToPlayer(gameCode, playerName, 'game:field-effect', { fieldType, effect, message: `You landed on a ${fieldType} field!`, timestamp: new Date().toISOString() }); } // Handle game state monitoring async checkGameStatus(gameCode: string): Promise { const connectedPlayers = await gameWebSocketService.getConnectedPlayers(gameCode); const readyPlayers = await gameWebSocketService.getReadyPlayers(gameCode); console.log(`Game ${gameCode} status:`); console.log(`- Connected players: ${connectedPlayers.join(', ')}`); console.log(`- Ready players: ${readyPlayers.join(', ')}`); if (connectedPlayers.length === 0) { console.log('- Game is empty'); } else if (readyPlayers.length === connectedPlayers.length) { console.log('- All players are ready!'); await this.startGame(gameCode); } } // Start game when all players are ready async startGame(gameCode: string): Promise { await gameWebSocketService.broadcastGameEvent(gameCode, 'game:started', { message: 'Game is starting! Get ready to play!', timestamp: new Date().toISOString() }); // Send game board and initial state to all players const gameState = { status: 'active', currentPlayer: 'Player1', // Determine first player board: {}, // Board data players: await gameWebSocketService.getConnectedPlayers(gameCode) }; await gameWebSocketService.broadcastGameStateUpdate(gameCode, gameState); } } // Example 3: Room Structure /* Dynamic Room Names: - game_ABC123 // All players in game ABC123 - game_ABC123:Player1 // Specific to Player1 in game ABC123 - game_ABC123:Player2 // Specific to Player2 in game ABC123 - game_XYZ789 // All players in game XYZ789 - game_XYZ789:PublicPlayer // Specific to PublicPlayer in game XYZ789 Usage: - Broadcast events: Send to game_ABC123 (all players receive) - Player-specific events: Send to game_ABC123:Player1 (only Player1 receives) */ // Example 4: Game Lifecycle Events /* // Game start event (broadcasted when gamemaster starts the game) gameSocket.on('game:start', (data) => { console.log('Game has started!', data); // data includes: // - gameCode: string // - gameId: string // - boardData: { fields: GameField[] } - Complete board layout (100 gameplay fields, positions 1-100) // - playerOrder: string[] - Turn sequence (player IDs in order) // - currentPlayer: string - First player to move // - currentTurn: number - Current turn index (starts at 0) // - players: string[] - All players in game // - startedAt: string - ISO timestamp // - message: 'Game has started! Good luck to all players!' // Initialize game board UI renderGameBoard(data.boardData.fields); // Set up turn indicator showCurrentPlayer(data.currentPlayer, data.playerOrder); // Show start message displayGameMessage(data.message); }); // Turn notification for current player gameSocket.on('game:your-turn', (data) => { console.log('It\'s your turn!', data); // data: { message: 'It\'s your turn! Roll the dice!', canRoll: true, timestamp: '...' } // Enable dice roll button for current player enableDiceRoll(); showTurnMessage(data.message); }); // Turn change notification for all players gameSocket.on('game:turn-changed', (data) => { console.log('Turn changed:', data); // data: { currentPlayer: 'id', currentPlayerName: 'Name', turnNumber: 2, message: '...', timestamp: '...' } // Update UI to show whose turn it is updateCurrentPlayerIndicator(data.currentPlayerName); showTurnMessage(data.message); }); // Player movement notification gameSocket.on('game:player-moved', (data) => { console.log('Player moved:', data); // data: { playerId: 'id', playerName: 'Name', diceValue: 4, oldPosition: 15, newPosition: 19, hasWon: false, timestamp: '...' } // Note: positions 0 (start) → 1-100 (gameplay board) → 101 (finish/win) // Animate player movement on board animatePlayerMovement(data.playerName, data.oldPosition, data.newPosition); // Show dice result showDiceResult(data.playerName, data.diceValue); if (data.hasWon) { showWinnerAnimation(data.playerName); } }); // Game end notification gameSocket.on('game:ended', (data) => { console.log('Game ended:', data); // data: { winner: 'id', winnerName: 'Name', message: '🎉 Name won!', finalPositions: [...], timestamp: '...' } // Show game over screen showGameOverScreen(data.winnerName, data.finalPositions); disableAllGameActions(); }); // Frontend dice roll (when it's your turn) function rollDice() { const diceValue = Math.floor(Math.random() * 6) + 1; // Generate 1-6 // Send dice value to server gameSocket.emit('game:dice-roll', { gameCode: currentGameCode, diceValue: diceValue }); // Disable dice roll button until turn changes disableDiceRoll(); showDiceAnimation(diceValue); } // Other game events gameSocket.on('game:state-update', (gameState) => { console.log('Game state updated:', gameState); }); gameSocket.on('game:action-result', (data) => { console.log('Player action result:', data); // { action: 'roll-dice', playerName: 'Player1', result: { dice: 4 }, timestamp: '...' } }); */ // Example 5: REST API Integration (Game Token Flow + Game Start) /* // Step 1: REST API handles game joining and returns game token POST /api/games/join { "gameCode": "ABC123", "playerName": "NewPlayer" } // Response includes game data + game token { "id": "game-uuid", "gamecode": "ABC123", "players": ["player1", "player2", "NewPlayer"], ...otherGameData, "gameToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." // Game session token } // Step 2: Player connects to WebSocket using the game token const gameSocket = io('/game'); gameSocket.emit('game:join', { gameToken: gameTokenFromRestAPI // Contains gameId, gameCode, playerName, auth status }); // Step 3: Gamemaster starts the game via REST API POST /api/games/{gameId}/start // Authorization: Bearer {gamemaster-jwt-token} // Response includes game and board data { "message": "Game started successfully", "gameId": "game-uuid", "playerCount": 3, "game": { ...gameData }, "boardData": { "fields": [ { "position": 1, "type": "regular" }, { "position": 2, "type": "positive", "stepValue": 3 }, { "position": 3, "type": "negative", "stepValue": -2 }, { "position": 4, "type": "luck" }, { "position": 5, "type": "regular" }, // ... continues to position 100 (100 gameplay fields) { "position": 100, "type": "regular" } ] // Note: Players start at 0, play on 1-100, win by reaching 101 } } // Step 4: All players automatically receive game:start WebSocket event // (No additional frontend action needed - happens automatically when gamemaster calls start endpoint) */