Add audience.joined and game.started WebSocket events, reduce poll interval

Broadcast audience.joined when the audience client receives its first
client/welcome frame. Broadcast game.started when the room lock is
detected. Reduced initial wait and poll interval from 30s to 10s for
faster feedback.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
cottongin
2026-02-07 14:09:37 -05:00
parent 84398ebdd0
commit 52e00e56f6

View File

@@ -67,6 +67,7 @@ async function watchGameAsAudience(sessionId, gameId, roomCode, maxPlayers) {
let bestPlayerCount = null; let bestPlayerCount = null;
let startPlayerCount = null; // Authoritative count from 'start' action let startPlayerCount = null; // Authoritative count from 'start' action
let gameEnded = false; let gameEnded = false;
let audienceJoined = false; // Track whether we've confirmed audience join
let frameCount = 0; let frameCount = 0;
// Enable CDP and listen for WebSocket frames BEFORE navigating // Enable CDP and listen for WebSocket frames BEFORE navigating
@@ -91,6 +92,22 @@ async function watchGameAsAudience(sessionId, gameId, roomCode, maxPlayers) {
if (process.env.DEBUG) { if (process.env.DEBUG) {
console.log(`[Frame ${frameCount}] Found bc:room in client/welcome`); 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') { 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 * Update player count in database
*/ */
@@ -315,12 +352,13 @@ function updatePlayerCount(sessionId, gameId, playerCount, status) {
/** /**
* Start checking player count for a game * Start checking player count for a game
* New strategy: * Strategy:
* 1. Wait 30 seconds * 1. Wait 10 seconds for initial room setup
* 2. Check if game is locked - if not, wait another 30 seconds * 2. Poll every 10 seconds until game is locked (started)
* 3. Once locked, join audience and watch entire game * 3. Broadcast game.started event when locked detected
* 4. Update UI as we learn more * 4. Join audience and watch entire game
* 5. Finalize when game ends * 5. Update UI as we learn more
* 6. Finalize when game ends
*/ */
async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8) { async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8) {
const checkKey = `${sessionId}-${gameId}`; const checkKey = `${sessionId}-${gameId}`;
@@ -388,7 +426,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8
return null; return null;
}; };
// Wait 30 seconds before first check // Wait 10 seconds before first check
const initialTimeout = setTimeout(async () => { const initialTimeout = setTimeout(async () => {
try { try {
// Update status to checking // Update status to checking
@@ -398,16 +436,17 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8
WHERE session_id = ? AND id = ? WHERE session_id = ? AND id = ?
`).run(sessionId, gameId); `).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(); const result = await waitForGameStart();
if (result && result.ready === true) { 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; const realMaxPlayers = result.maxPlayers;
broadcastGameStarted(sessionId, gameId, roomCode, realMaxPlayers);
console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`); console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`);
await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers); await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers);
} else if (result === null) { } else if (result === null) {
// Not ready yet, check every 30 seconds // Not ready yet, poll every 10 seconds
const checkInterval = setInterval(async () => { const checkInterval = setInterval(async () => {
// Check if we should stop // Check if we should stop
const game = db.prepare(` const game = db.prepare(`
@@ -424,11 +463,12 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8
const result = await waitForGameStart(); const result = await waitForGameStart();
if (result && result.ready === true) { 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); clearInterval(checkInterval);
const check = activeChecks.get(checkKey); const check = activeChecks.get(checkKey);
if (check) check.interval = null; if (check) check.interval = null;
const realMaxPlayers = result.maxPlayers; const realMaxPlayers = result.maxPlayers;
broadcastGameStarted(sessionId, gameId, roomCode, realMaxPlayers);
console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`); console.log(`[Player Count] Using real maxPlayers from Jackbox: ${realMaxPlayers} (database had: ${maxPlayers})`);
await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers); await watchGameAsAudience(sessionId, gameId, roomCode, realMaxPlayers);
} else if (result === false) { } else if (result === false) {
@@ -436,7 +476,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8
clearInterval(checkInterval); clearInterval(checkInterval);
stopPlayerCountCheck(sessionId, gameId); stopPlayerCountCheck(sessionId, gameId);
} }
}, 30000); // Check every 30 seconds }, 10000); // Poll every 10 seconds
// Store the interval // Store the interval
const check = activeChecks.get(checkKey); const check = activeChecks.get(checkKey);
@@ -449,7 +489,7 @@ async function startPlayerCountCheck(sessionId, gameId, roomCode, maxPlayers = 8
updatePlayerCount(sessionId, gameId, null, 'failed'); updatePlayerCount(sessionId, gameId, null, 'failed');
stopPlayerCountCheck(sessionId, gameId); stopPlayerCountCheck(sessionId, gameId);
} }
}, 30000); // Wait 30 seconds before first check }, 10000); // Wait 10 seconds before first check
// Store the check references // Store the check references
activeChecks.set(checkKey, { activeChecks.set(checkKey, {