diff --git a/src/gameState.js b/src/gameState.js index cd5997c..d27491e 100644 --- a/src/gameState.js +++ b/src/gameState.js @@ -22,6 +22,9 @@ export function createInitialState() { visuForm: false, visuStriscia: true, timeout: false, + timeoutTeam: null, + timeoutVideo: null, + timeoutStartedAt: null, modalitaPartita: "3/5", sp: { striscia: [{ serv: 'h', ris: '', vinc: null }], @@ -165,8 +168,21 @@ export function applyAction(state, action) { break } - case "toggleTimeout": { - s.timeout = !s.timeout + case "startTimeout": { + const team = action.team + if (team !== 'home' && team !== 'guest') break + s.timeout = true + s.timeoutTeam = team + s.timeoutVideo = action.video ?? null + s.timeoutStartedAt = action.startedAt ?? null + break + } + + case "stopTimeout": { + s.timeout = false + s.timeoutTeam = null + s.timeoutVideo = null + s.timeoutStartedAt = null break } diff --git a/src/websocket-handler.js b/src/websocket-handler.js index faf7150..77e8eaa 100644 --- a/src/websocket-handler.js +++ b/src/websocket-handler.js @@ -5,6 +5,8 @@ import { createInitialState, applyAction } from './gameState.js' * @param {WebSocketServer} wss - Istanza del server WebSocket. * @returns {Object} Oggetto con metodi di gestione dello stato. */ +const DURATA_TIMEOUT_MS = 30000 + export function setupWebSocketHandler(wss, options = {}) { // Stato globale della partita. let gameState = options.initialState ?? createInitialState() @@ -12,6 +14,17 @@ export function setupWebSocketHandler(wss, options = {}) { // Mappa dei ruoli associati ai client connessi. const clients = new Map() // ws -> { role: 'display' | 'controller' } + // Timer che chiude automaticamente il time out dopo 30s, indipendentemente + // dal controller (sopravvive a refresh/disconnessione/standby del tablet). + let timeoutTimer = null + + function annullaTimerTimeout() { + if (timeoutTimer) { + clearTimeout(timeoutTimer) + timeoutTimer = null + } + } + /** * Gestisce i messaggi in arrivo dal client */ @@ -87,10 +100,16 @@ export function setupWebSocketHandler(wss, options = {}) { return } + // Il momento di inizio del time out lo decide il server (non il client), + // cosi' gameState.js resta puro/deterministico e testabile. + const action = msg.action.type === 'startTimeout' + ? { ...msg.action, startedAt: Date.now() } + : msg.action + // Applica l'azione allo stato della partita. const previousState = gameState try { - gameState = applyAction(gameState, msg.action) + gameState = applyAction(gameState, action) } catch (err) { console.error('Error applying action:', err) sendError(ws, 'Failed to apply action') @@ -98,6 +117,20 @@ export function setupWebSocketHandler(wss, options = {}) { return } + // Qualsiasi azione che tocca il time out invalida il timer di auto-chiusura + // pendente; se il time out e' (ri)partito, ne arma uno nuovo. + if (action.type === 'startTimeout' || action.type === 'stopTimeout') { + annullaTimerTimeout() + } + if (action.type === 'startTimeout' && gameState.timeout) { + timeoutTimer = setTimeout(() => { + timeoutTimer = null + gameState = applyAction(gameState, { type: 'stopTimeout' }) + broadcastState() + options.onStateChange?.(gameState) + }, DURATA_TIMEOUT_MS) + } + // Propaga il nuovo stato a tutti i client connessi. broadcastState() options.onStateChange?.(gameState)