136 lines
4.0 KiB
JavaScript
136 lines
4.0 KiB
JavaScript
|
|
const db = require('../database');
|
||
|
|
const { getWebSocketManager } = require('./websocket-manager');
|
||
|
|
const { checkRoomStatus } = require('./jackbox-api');
|
||
|
|
|
||
|
|
const POLL_INTERVAL_MS = 10000;
|
||
|
|
|
||
|
|
// Active room monitors, keyed by "{sessionId}-{gameId}"
|
||
|
|
const activeMonitors = new Map();
|
||
|
|
|
||
|
|
/**
|
||
|
|
* 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(`[Room Monitor] Broadcasted game.started for room ${roomCode} (max: ${maxPlayers})`);
|
||
|
|
} catch (error) {
|
||
|
|
console.error('[Room Monitor] Failed to broadcast game.started:', error.message);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Start monitoring a Jackbox room for game start (locked state).
|
||
|
|
*
|
||
|
|
* Polls the Jackbox REST API every 10 seconds. When the room becomes
|
||
|
|
* locked, broadcasts a game.started WebSocket event and then hands off
|
||
|
|
* to the player-count-checker to join as audience.
|
||
|
|
*/
|
||
|
|
async function startRoomMonitor(sessionId, gameId, roomCode, maxPlayers = 8) {
|
||
|
|
const monitorKey = `${sessionId}-${gameId}`;
|
||
|
|
|
||
|
|
if (activeMonitors.has(monitorKey)) {
|
||
|
|
console.log(`[Room Monitor] Already monitoring ${monitorKey}`);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log(`[Room Monitor] Starting monitor for room ${roomCode} (${monitorKey})`);
|
||
|
|
|
||
|
|
const onGameStarted = (realMaxPlayers) => {
|
||
|
|
broadcastGameStarted(sessionId, gameId, roomCode, realMaxPlayers);
|
||
|
|
// Lazy require breaks the circular dependency with player-count-checker
|
||
|
|
const { startPlayerCountCheck } = require('./player-count-checker');
|
||
|
|
console.log(`[Room Monitor] Room ${roomCode} locked — handing off to player count checker`);
|
||
|
|
startPlayerCountCheck(sessionId, gameId, roomCode, realMaxPlayers);
|
||
|
|
};
|
||
|
|
|
||
|
|
const pollRoom = async () => {
|
||
|
|
const game = db.prepare(`
|
||
|
|
SELECT status FROM session_games
|
||
|
|
WHERE session_id = ? AND id = ?
|
||
|
|
`).get(sessionId, gameId);
|
||
|
|
|
||
|
|
if (!game || game.status === 'skipped' || game.status === 'played') {
|
||
|
|
console.log(`[Room Monitor] Stopping — game status changed for ${monitorKey}`);
|
||
|
|
stopRoomMonitor(sessionId, gameId);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const roomStatus = await checkRoomStatus(roomCode);
|
||
|
|
|
||
|
|
if (!roomStatus.exists) {
|
||
|
|
console.log(`[Room Monitor] Room ${roomCode} does not exist — stopping`);
|
||
|
|
stopRoomMonitor(sessionId, gameId);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roomStatus.locked) {
|
||
|
|
stopRoomMonitor(sessionId, gameId);
|
||
|
|
onGameStarted(roomStatus.maxPlayers);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (roomStatus.full) {
|
||
|
|
console.log(`[Room Monitor] Room ${roomCode} is full but not locked yet — waiting`);
|
||
|
|
} else {
|
||
|
|
console.log(`[Room Monitor] Room ${roomCode} lobby still open — waiting`);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Poll immediately, then every POLL_INTERVAL_MS
|
||
|
|
activeMonitors.set(monitorKey, {
|
||
|
|
sessionId,
|
||
|
|
gameId,
|
||
|
|
roomCode,
|
||
|
|
interval: null
|
||
|
|
});
|
||
|
|
|
||
|
|
await pollRoom();
|
||
|
|
|
||
|
|
// If the monitor was already stopped (room locked or gone on first check), bail
|
||
|
|
if (!activeMonitors.has(monitorKey)) return;
|
||
|
|
|
||
|
|
const interval = setInterval(() => pollRoom(), POLL_INTERVAL_MS);
|
||
|
|
const monitor = activeMonitors.get(monitorKey);
|
||
|
|
if (monitor) monitor.interval = interval;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stop monitoring a room
|
||
|
|
*/
|
||
|
|
function stopRoomMonitor(sessionId, gameId) {
|
||
|
|
const monitorKey = `${sessionId}-${gameId}`;
|
||
|
|
const monitor = activeMonitors.get(monitorKey);
|
||
|
|
|
||
|
|
if (monitor) {
|
||
|
|
if (monitor.interval) clearInterval(monitor.interval);
|
||
|
|
activeMonitors.delete(monitorKey);
|
||
|
|
console.log(`[Room Monitor] Stopped monitor for ${monitorKey}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Clean up all active monitors (for graceful shutdown)
|
||
|
|
*/
|
||
|
|
function cleanupAllMonitors() {
|
||
|
|
for (const [, monitor] of activeMonitors.entries()) {
|
||
|
|
if (monitor.interval) clearInterval(monitor.interval);
|
||
|
|
}
|
||
|
|
activeMonitors.clear();
|
||
|
|
console.log('[Room Monitor] Cleaned up all active monitors');
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
startRoomMonitor,
|
||
|
|
stopRoomMonitor,
|
||
|
|
cleanupAllMonitors
|
||
|
|
};
|