Files
MultiRoombaRover/server/src/services/roverManager/spectatorAccess.js
T
2026-04-28 20:39:45 -04:00

80 lines
2.2 KiB
JavaScript

// rover Manager spectator access
// Purpose: Manages spectator room joins and private auto-close behavior.
// Scope: Keeps runtime behavior unchanged while isolating spectator-specific orchestration.
function createSpectatorAccess(deps) {
const {
io,
logger,
rovers,
spectatorSockets,
privateNoUsersSince,
PRIVATE_AUTO_CLOSE_IDLE_MS,
isPrivateRecord,
isPrivateOpen,
isRoverVisibleToSocket,
setPrivateOpen,
} = deps;
function enableSpectator(socket) {
if (!socket?.id || spectatorSockets.has(socket.id)) return;
spectatorSockets.add(socket.id);
for (const record of rovers.values()) {
if (isRoverVisibleToSocket(record, socket)) {
socket.join(record.room);
} else {
socket.leave(record.room);
}
}
}
function disableSpectator(socket) {
if (!socket?.id || !spectatorSockets.has(socket.id)) return;
spectatorSockets.delete(socket.id);
for (const record of rovers.values()) {
socket.leave(record.room);
}
}
function tickPrivateAutoClose() {
const now = Date.now();
const onlineCount = io.sockets.sockets.size;
for (const record of rovers.values()) {
if (!isPrivateRecord(record) || !isPrivateOpen(record)) {
privateNoUsersSince.delete(record.id);
continue;
}
if (record.drivers && record.drivers.size > 0) {
privateNoUsersSince.delete(record.id);
continue;
}
if (onlineCount > 0) {
privateNoUsersSince.delete(record.id);
continue;
}
const since = privateNoUsersSince.get(record.id) || now;
if (!privateNoUsersSince.has(record.id)) {
privateNoUsersSince.set(record.id, since);
continue;
}
if (now - since >= PRIVATE_AUTO_CLOSE_IDLE_MS) {
try {
setPrivateOpen(record.id, false, { reason: 'auto_idle', tts: true });
} catch (err) {
logger.warn('Private auto-close failed', { roverId: record.id, error: err.message });
}
privateNoUsersSince.delete(record.id);
}
}
}
return {
enableSpectator,
disableSpectator,
tickPrivateAutoClose,
};
}
module.exports = {
createSpectatorAccess,
};