diff --git a/backend/utils/player-count-checker.js b/backend/utils/player-count-checker.js index 93e743e..acbb4a3 100644 --- a/backend/utils/player-count-checker.js +++ b/backend/utils/player-count-checker.js @@ -67,6 +67,7 @@ async function watchGameAsAudience(sessionId, gameId, roomCode, maxPlayers) { let bestPlayerCount = null; let startPlayerCount = null; // Authoritative count from 'start' action let gameEnded = false; + let audienceJoined = false; // Track whether we've confirmed audience join let frameCount = 0; // Enable CDP and listen for WebSocket frames BEFORE navigating @@ -91,6 +92,22 @@ async function watchGameAsAudience(sessionId, gameId, roomCode, maxPlayers) { if (process.env.DEBUG) { console.log(`[Frame ${frameCount}] Found bc:room in client/welcome`); } + + // First client/welcome means Jackbox accepted our audience join + if (!audienceJoined) { + audienceJoined = true; + console.log(`[Audience] Successfully joined room ${roomCode} as audience`); + + // Broadcast audience.joined event via WebSocket + const wsManager = getWebSocketManager(); + if (wsManager) { + wsManager.broadcastEvent('audience.joined', { + sessionId, + gameId, + roomCode + }, parseInt(sessionId)); + } + } } if (data.opcode === 'object' && data.result?.key === 'bc:room') { @@ -285,6 +302,26 @@ async function watchGameAsAudience(sessionId, gameId, roomCode, maxPlayers) { } } +/** + * Broadcast game.started event when room becomes locked + */ +function broadcastGameStarted(sessionId, gameId, roomCode, maxPlayers) { + try { + const wsManager = getWebSocketManager(); + if (wsManager) { + wsManager.broadcastEvent('game.started', { + sessionId, + gameId, + roomCode, + maxPlayers + }, parseInt(sessionId)); + } + console.log(`[Player Count] Broadcasted game.started for room ${roomCode} (max: ${maxPlayers})`); + } catch (error) { + console.error('[Player Count] Failed to broadcast game.started:', error.message); + } +} + /** * Update player count in database */ @@ -315,12 +352,13 @@ function updatePlayerCount(sessionId, gameId, playerCount, status) { /** * Start checking player count for a game - * New strategy: - * 1. Wait 30 seconds - * 2. Check if game is locked - if not, wait another 30 seconds - * 3. Once locked, join audience and watch entire game - * 4. Update UI as we learn more - * 5. Finalize when game ends + * Strategy: + * 1. Wait 10 seconds for initial room setup + * 2. Poll every 10 seconds until game is locked (started) + * 3. Broadcast game.started event when locked detected + * 4. Join audience and watch entire game + * 5. Update UI as we learn more + * 6. Finalize when game ends */ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8) { const checkKey = `${sessionId}-${gameId}`; @@ -388,7 +426,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8 return null; }; - // Wait 30 seconds before first check + // Wait 10 seconds before first check const initialTimeout = setTimeout(async () => { try { // Update status to checking @@ -398,16 +436,17 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8 WHERE session_id = ? AND id = ? `).run(sessionId, gameId); - console.log(`[Player Count] Initial check after 30s for ${checkKey}`); + console.log(`[Player Count] Initial check after 10s for ${checkKey}`); const result = await waitForGameStart(); if (result && result.ready === true) { - // Game is locked, start watching with REAL maxPlayers from Jackbox + // Game is locked, broadcast game.started and start watching const realMaxPlayers = result.maxPlayers; + broadcastGameStarted(sessionId, gameId, roomCode, realMaxPlayers); console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`); await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers); } else if (result === null) { - // Not ready yet, check every 30 seconds + // Not ready yet, poll every 10 seconds const checkInterval = setInterval(async () => { // Check if we should stop const game = db.prepare(` @@ -424,11 +463,12 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8 const result = await waitForGameStart(); if (result && result.ready === true) { - // Game is now locked, stop interval and start watching with REAL maxPlayers + // Game is now locked, stop interval, broadcast game.started, and start watching clearInterval(checkInterval); const check = activeChecks.get(checkKey); if (check) check.interval = null; const realMaxPlayers = result.maxPlayers; + broadcastGameStarted(sessionId, gameId, roomCode, realMaxPlayers); console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`); await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers); } else if (result === false) { @@ -436,7 +476,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8 clearInterval(checkInterval); stopPlayerCountCheck(sessionId, gameId); } - }, 30000); // Check every 30 seconds + }, 10000); // Poll every 10 seconds // Store the interval const check = activeChecks.get(checkKey); @@ -449,7 +489,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8 updatePlayerCount(sessionId, gameId, null, 'failed'); stopPlayerCountCheck(sessionId, gameId); } - }, 30000); // Wait 30 seconds before first check + }, 10000); // Wait 10 seconds before first check // Store the check references activeChecks.set(checkKey, {