feat: add startMonitor, stopMonitor, cleanupAllShards module exports
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
const WebSocket = require('ws');
|
const WebSocket = require('ws');
|
||||||
|
|
||||||
|
const db = require('../database');
|
||||||
|
const { getWebSocketManager } = require('./websocket-manager');
|
||||||
const { getRoomInfo } = require('./jackbox-api');
|
const { getRoomInfo } = require('./jackbox-api');
|
||||||
|
|
||||||
class EcastShardClient {
|
class EcastShardClient {
|
||||||
@@ -420,4 +422,149 @@ class EcastShardClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { EcastShardClient };
|
const activeShards = new Map();
|
||||||
|
|
||||||
|
function broadcastAndPersist(sessionId, gameId) {
|
||||||
|
return (eventType, eventData) => {
|
||||||
|
const wsManager = getWebSocketManager();
|
||||||
|
if (wsManager) {
|
||||||
|
wsManager.broadcastEvent(eventType, eventData, parseInt(sessionId, 10));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (['room.connected', 'lobby.player-joined', 'game.started', 'game.ended'].includes(eventType)) {
|
||||||
|
const status = eventType === 'game.ended' ? 'completed' : 'monitoring';
|
||||||
|
try {
|
||||||
|
db.prepare(
|
||||||
|
'UPDATE session_games SET player_count = ?, player_count_check_status = ? WHERE session_id = ? AND id = ?'
|
||||||
|
).run(eventData.playerCount ?? null, status, sessionId, gameId);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Shard Monitor] DB update failed:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'room.disconnected') {
|
||||||
|
const reason = eventData.reason;
|
||||||
|
const status =
|
||||||
|
reason === 'room_closed' ? 'completed' : reason === 'manually_stopped' ? 'stopped' : 'failed';
|
||||||
|
try {
|
||||||
|
const game = db
|
||||||
|
.prepare('SELECT player_count_check_status FROM session_games WHERE session_id = ? AND id = ?')
|
||||||
|
.get(sessionId, gameId);
|
||||||
|
if (game && game.player_count_check_status !== 'completed') {
|
||||||
|
db.prepare('UPDATE session_games SET player_count_check_status = ? WHERE session_id = ? AND id = ?').run(
|
||||||
|
status,
|
||||||
|
sessionId,
|
||||||
|
gameId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Shard Monitor] DB update failed:', e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startMonitor(sessionId, gameId, roomCode, maxPlayers = 8) {
|
||||||
|
const monitorKey = `${sessionId}-${gameId}`;
|
||||||
|
|
||||||
|
if (activeShards.has(monitorKey)) {
|
||||||
|
console.log(`[Shard Monitor] Already monitoring ${monitorKey}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`[Shard Monitor] Starting monitor for room ${roomCode} (${monitorKey})`);
|
||||||
|
|
||||||
|
const roomInfo = await getRoomInfo(roomCode);
|
||||||
|
if (!roomInfo.exists) {
|
||||||
|
console.log(`[Shard Monitor] Room ${roomCode} not found`);
|
||||||
|
const onEvent = broadcastAndPersist(sessionId, gameId);
|
||||||
|
onEvent('room.disconnected', {
|
||||||
|
sessionId,
|
||||||
|
gameId,
|
||||||
|
roomCode,
|
||||||
|
reason: 'room_not_found',
|
||||||
|
finalPlayerCount: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEvent = broadcastAndPersist(sessionId, gameId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
db.prepare('UPDATE session_games SET player_count_check_status = ? WHERE session_id = ? AND id = ?').run(
|
||||||
|
'monitoring',
|
||||||
|
sessionId,
|
||||||
|
gameId
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[Shard Monitor] DB update failed:', e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new EcastShardClient({
|
||||||
|
sessionId,
|
||||||
|
gameId,
|
||||||
|
roomCode,
|
||||||
|
maxPlayers: roomInfo.maxPlayers || maxPlayers,
|
||||||
|
onEvent,
|
||||||
|
});
|
||||||
|
|
||||||
|
activeShards.set(monitorKey, client);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.connect(roomInfo);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[Shard Monitor] Failed to connect to room ${roomCode}:`, e.message);
|
||||||
|
activeShards.delete(monitorKey);
|
||||||
|
onEvent('room.disconnected', {
|
||||||
|
sessionId,
|
||||||
|
gameId,
|
||||||
|
roomCode,
|
||||||
|
reason: 'connection_failed',
|
||||||
|
finalPlayerCount: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopMonitor(sessionId, gameId) {
|
||||||
|
const monitorKey = `${sessionId}-${gameId}`;
|
||||||
|
const client = activeShards.get(monitorKey);
|
||||||
|
|
||||||
|
if (client) {
|
||||||
|
client.manuallyStopped = true;
|
||||||
|
client.disconnect();
|
||||||
|
activeShards.delete(monitorKey);
|
||||||
|
|
||||||
|
const game = db
|
||||||
|
.prepare('SELECT player_count_check_status FROM session_games WHERE session_id = ? AND id = ?')
|
||||||
|
.get(sessionId, gameId);
|
||||||
|
|
||||||
|
if (game && game.player_count_check_status !== 'completed' && game.player_count_check_status !== 'failed') {
|
||||||
|
db.prepare('UPDATE session_games SET player_count_check_status = ? WHERE session_id = ? AND id = ?').run(
|
||||||
|
'stopped',
|
||||||
|
sessionId,
|
||||||
|
gameId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
client.onEvent('room.disconnected', {
|
||||||
|
sessionId,
|
||||||
|
gameId,
|
||||||
|
roomCode: client.roomCode,
|
||||||
|
reason: 'manually_stopped',
|
||||||
|
finalPlayerCount: client.playerCount,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`[Shard Monitor] Stopped monitor for ${monitorKey}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cleanupAllShards() {
|
||||||
|
for (const [, client] of activeShards) {
|
||||||
|
client.manuallyStopped = true;
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
activeShards.clear();
|
||||||
|
console.log('[Shard Monitor] Cleaned up all active shards');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { EcastShardClient, startMonitor, stopMonitor, cleanupAllShards };
|
||||||
|
|||||||
@@ -376,6 +376,22 @@ describe('EcastShardClient', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('module exports', () => {
|
||||||
|
const { startMonitor, stopMonitor, cleanupAllShards } = require('../../backend/utils/ecast-shard-client');
|
||||||
|
|
||||||
|
test('startMonitor is exported', () => {
|
||||||
|
expect(typeof startMonitor).toBe('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('stopMonitor is exported', () => {
|
||||||
|
expect(typeof stopMonitor).toBe('function');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('cleanupAllShards is exported', () => {
|
||||||
|
expect(typeof cleanupAllShards).toBe('function');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('handleError with code 2027', () => {
|
describe('handleError with code 2027', () => {
|
||||||
test('marks game as finished and emits events on room-closed error', () => {
|
test('marks game as finished and emits events on room-closed error', () => {
|
||||||
const events = [];
|
const events = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user