From e21ad8493f65aea02e79838127905c412bce9276 Mon Sep 17 00:00:00 2001 From: Alec Date: Tue, 30 Nov 2021 19:24:08 -0500 Subject: [PATCH] get timer on connect, patch play/pause functionality --- client/config/globals.js | 5 +- client/modules/GameTimerManager.js | 116 +++++++++++++++++++++++++++++ client/modules/Timer.js | 58 ++++++++------- client/scripts/create.js | 5 +- client/scripts/game.js | 44 ++++------- client/styles/game.css | 12 +++ client/views/404.html | 2 +- server/config/globals.js | 5 +- server/modules/ActiveGameRunner.js | 18 +++-- server/modules/GameManager.js | 8 +- server/modules/GameProcess.js | 10 +++ server/modules/GameStateCurator.js | 21 +++++- server/modules/ServerTimer.js | 4 +- 13 files changed, 230 insertions(+), 78 deletions(-) create mode 100644 client/modules/GameTimerManager.js diff --git a/client/config/globals.js b/client/config/globals.js index 9133683..db15ade 100644 --- a/client/config/globals.js +++ b/client/config/globals.js @@ -1,6 +1,6 @@ export const globals = { USER_SIGNATURE_LENGTH: 25, - CLOCK_TICK_INTERVAL_MILLIS: 100, + CLOCK_TICK_INTERVAL_MILLIS: 10, ACCESS_CODE_LENGTH: 6, PLAYER_ID_COOKIE_KEY: 'play-werewolf-anon-id', ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', @@ -9,7 +9,8 @@ export const globals = { GET_ENVIRONMENT: 'getEnvironment', START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', - RESUME_TIMER: 'resumeTimer' + RESUME_TIMER: 'resumeTimer', + GET_TIME_REMAINING: 'getTimeRemaining' }, STATUS: { LOBBY: "lobby", diff --git a/client/modules/GameTimerManager.js b/client/modules/GameTimerManager.js new file mode 100644 index 0000000..9fac66f --- /dev/null +++ b/client/modules/GameTimerManager.js @@ -0,0 +1,116 @@ +import {globals} from "../config/globals.js"; + +export class GameTimerManager { + constructor() { + + } + + startGameTimer (hours, minutes, tickRate, soundManager, timerWorker) { + if (window.Worker) { + timerWorker.onmessage = function (e) { + if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { + document.getElementById('game-timer').innerText = e.data.displayTime; + } + }; + const totalTime = convertFromHoursToMilliseconds(hours) + convertFromMinutesToMilliseconds(minutes); + timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate }); + } + } + + resumeGameTimer(totalTime, tickRate, soundManager, timerWorker) { + if (window.Worker) { + let timer = document.getElementById('game-timer'); + timer.classList.remove('paused'); + timer.innerText = totalTime < 60000 + ? returnHumanReadableTime(totalTime, true) + : returnHumanReadableTime(totalTime); + timerWorker.onmessage = function (e) { + if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds > 0) { + timer.innerText = e.data.displayTime; + } + }; + timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate }); + } + } + + pauseGameTimer(timerWorker, timeRemaining) { + if (window.Worker) { + timerWorker.postMessage('stop'); + let timer = document.getElementById('game-timer'); + timer.innerText = timeRemaining < 60000 + ? returnHumanReadableTime(timeRemaining, true) + : returnHumanReadableTime(timeRemaining); + timer.classList.add('paused'); + } + } + + displayPausedTime(time) { + let timer = document.getElementById('game-timer'); + timer.innerText = time < 60000 + ? returnHumanReadableTime(time, true) + : returnHumanReadableTime(time); + timer.classList.add('paused'); + } + + attachTimerSocketListeners(socket, timerWorker, gameStateRenderer) { + if (!socket.hasListeners(globals.EVENTS.START_TIMER)) { + socket.on(globals.EVENTS.START_TIMER, () => { + this.startGameTimer( + gameStateRenderer.gameState.timerParams.hours, + gameStateRenderer.gameState.timerParams.minutes, + globals.CLOCK_TICK_INTERVAL_MILLIS, + null, + timerWorker + ) + }); + } + + if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { + socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { + this.pauseGameTimer(timerWorker, timeRemaining) + }); + } + + if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { + socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { + this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); + }); + } + + if(!socket.hasListeners(globals.COMMANDS.GET_TIME_REMAINING)) { + socket.on(globals.COMMANDS.GET_TIME_REMAINING, (timeRemaining, paused) => { + console.log('received time remaining from server'); + if (paused) { + this.displayPausedTime(timeRemaining); + } else { + this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker); + } + }); + } + } +} + + +function convertFromMinutesToMilliseconds(minutes) { + return minutes * 60 * 1000; +} + +function convertFromHoursToMilliseconds(hours) { + return hours * 60 * 60 * 1000; +} + +function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { + + let tenths = Math.floor((milliseconds / 100) % 10); + let seconds = Math.floor((milliseconds / 1000) % 60); + let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); + let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); + + hours = hours < 10 ? "0" + hours : hours; + minutes = minutes < 10 ? "0" + minutes : minutes; + seconds = seconds < 10 ? "0" + seconds : seconds; + + return tenthsOfSeconds + ? hours + ":" + minutes + ":" + seconds + '.' + tenths + : hours + ":" + minutes + ":" + seconds; +} diff --git a/client/modules/Timer.js b/client/modules/Timer.js index d2fe55d..67926be 100644 --- a/client/modules/Timer.js +++ b/client/modules/Timer.js @@ -10,18 +10,20 @@ See: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API const messageParameters = { STOP: 'stop', TICK_INTERVAL: 'tickInterval', - HOURS: 'hours', - MINUTES: 'minutes' + TOTAL_TIME: 'totalTime' }; +let timer; + onmessage = function (e) { if (typeof e.data === 'object' - && e.data.hasOwnProperty(messageParameters.HOURS) - && e.data.hasOwnProperty(messageParameters.MINUTES) + && e.data.hasOwnProperty(messageParameters.TOTAL_TIME) && e.data.hasOwnProperty(messageParameters.TICK_INTERVAL) ) { - const timer = new Singleton(e.data.hours, e.data.minutes, e.data.tickInterval); + timer = new Singleton(e.data.totalTime, e.data.tickInterval); timer.startTimer(); + } else if (e.data === 'stop') { + timer.stopTimer(); } }; @@ -32,9 +34,12 @@ function stepFn (expected, interval, start, totalTime) { } const delta = now - expected; expected += interval; + let displayTime = (totalTime - (expected - start)) < 60000 + ? returnHumanReadableTime(totalTime - (expected - start), true) + : returnHumanReadableTime(totalTime - (expected - start)); postMessage({ timeRemainingInMilliseconds: totalTime - (expected - start), - displayTime: returnHumanReadableTime(totalTime - (expected - start)) + displayTime: displayTime }); Singleton.setNewTimeoutReference(setTimeout(() => { stepFn(expected, interval, start, totalTime); @@ -43,19 +48,18 @@ function stepFn (expected, interval, start, totalTime) { } class Timer { - constructor (hours, minutes, tickInterval) { + constructor (totalTime, tickInterval) { this.timeoutId = undefined; - this.hours = hours; - this.minutes = minutes; + this.totalTime = totalTime; this.tickInterval = tickInterval; } startTimer () { - if (!isNaN(this.hours) && !isNaN(this.minutes) && !isNaN(this.tickInterval)) { + if (!isNaN(this.tickInterval)) { const interval = this.tickInterval; - const totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); const start = Date.now(); const expected = Date.now() + this.tickInterval; + const totalTime = this.totalTime; if (this.timeoutId) { clearTimeout(this.timeoutId); } @@ -64,23 +68,28 @@ class Timer { }, this.tickInterval); } } + + stopTimer() { + if (this.timeoutId) { + clearTimeout(this.timeoutId); + } + } } class Singleton { - constructor (hours, minutes, tickInterval) { + constructor (totalTime, tickInterval) { if (!Singleton.instance) { - Singleton.instance = new Timer(hours, minutes, tickInterval); + Singleton.instance = new Timer(totalTime, tickInterval); } else { // This allows the same timer to be configured to run for different intervals / at a different granularity. - Singleton.setNewTimerParameters(hours, minutes, tickInterval); + Singleton.setNewTimerParameters(totalTime, tickInterval); } return Singleton.instance; } - static setNewTimerParameters (hours, minutes, tickInterval) { + static setNewTimerParameters (totalTime, tickInterval) { if (Singleton.instance) { - Singleton.instance.hours = hours; - Singleton.instance.minutes = minutes; + Singleton.instance.totalTime = totalTime; Singleton.instance.tickInterval = tickInterval; } } @@ -92,16 +101,9 @@ class Singleton { } } -function convertFromMinutesToMilliseconds(minutes) { - return minutes * 60 * 1000; -} - -function convertFromHoursToMilliseconds(hours) { - return hours * 60 * 60 * 1000; -} - -function returnHumanReadableTime(milliseconds) { +function returnHumanReadableTime(milliseconds, tenthsOfSeconds=false) { + let tenths = Math.floor((milliseconds / 100) % 10); let seconds = Math.floor((milliseconds / 1000) % 60); let minutes = Math.floor((milliseconds / (1000 * 60)) % 60); let hours = Math.floor((milliseconds / (1000 * 60 * 60)) % 24); @@ -110,5 +112,7 @@ function returnHumanReadableTime(milliseconds) { minutes = minutes < 10 ? "0" + minutes : minutes; seconds = seconds < 10 ? "0" + seconds : seconds; - return hours + ":" + minutes + ":" + seconds; + return tenthsOfSeconds + ? hours + ":" + minutes + ":" + seconds + '.' + tenths + : hours + ":" + minutes + ":" + seconds; } diff --git a/client/scripts/create.js b/client/scripts/create.js index 519f899..10992ca 100644 --- a/client/scripts/create.js +++ b/client/scripts/create.js @@ -13,13 +13,12 @@ export const create = () => { gameCreationStepManager.renderStep("creation-step-container", 1); } -// Display a widget for each default card that allows copies of it to be added/removed. Set initial deck state. function loadDefaultCards(deckManager) { defaultCards.sort((a, b) => { return a.role.localeCompare(b.role); }); let deck = []; - for (let i = 0; i < defaultCards.length; i ++) { // each dropdown should include every + for (let i = 0; i < defaultCards.length; i ++) { let card = defaultCards[i]; card.quantity = 0; deck.push(card); @@ -27,8 +26,6 @@ function loadDefaultCards(deckManager) { deckManager.deck = deck; } -/* Display a dropdown containing all the custom roles. Adding one will add it to the game deck and -create a widget for it */ function loadCustomRoles(deckManager) { customCards.sort((a, b) => { return a.role.localeCompare(b.role); diff --git a/client/scripts/game.js b/client/scripts/game.js index a739c5b..4d9ecad 100644 --- a/client/scripts/game.js +++ b/client/scripts/game.js @@ -3,20 +3,23 @@ import { globals } from "../config/globals.js"; import {templates} from "../modules/Templates.js"; import {GameStateRenderer} from "../modules/GameStateRenderer.js"; import {cancelCurrentToast, toast} from "../modules/Toast.js"; +import {GameTimerManager} from "../modules/GameTimerManager.js"; export const game = () => { + let timerWorker = new Worker('../modules/Timer.js'); const socket = io('/in-game'); socket.on('disconnect', () => { + timerWorker.terminate(); toast('You are disconnected.', 'error', true); }); socket.on('connect', () => { socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function(returnedEnvironment) { - prepareGamePage(returnedEnvironment, socket); + prepareGamePage(returnedEnvironment, socket, timerWorker); }); }) }; -function prepareGamePage(environment, socket, reconnect=false) { +function prepareGamePage(environment, socket, timerWorker) { let userId = UserUtility.validateAnonUserSignature(environment); const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; @@ -30,9 +33,12 @@ function prepareGamePage(environment, socket, reconnect=false) { userId = gameState.client.id; UserUtility.setAnonymousUserId(userId, environment); let gameStateRenderer = new GameStateRenderer(gameState); - const timerWorker = new Worker('../modules/Timer.js'); - setClientSocketHandlers(gameStateRenderer, socket, timerWorker); - processGameState(gameState, userId, socket, gameStateRenderer, timerWorker); + let gameTimerManager; + if (gameState.timerParams) { + gameTimerManager = new GameTimerManager(); + } + setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager); + processGameState(gameState, userId, socket, gameStateRenderer); } }); } else { @@ -40,7 +46,7 @@ function prepareGamePage(environment, socket, reconnect=false) { } } -function processGameState (gameState, userId, socket, gameStateRenderer, timerWorker) { +function processGameState (gameState, userId, socket, gameStateRenderer) { switch (gameState.status) { case globals.STATUS.LOBBY: document.getElementById("game-state-container").innerHTML = templates.LOBBY; @@ -81,7 +87,7 @@ function processGameState (gameState, userId, socket, gameStateRenderer, timerWo } } -function setClientSocketHandlers(gameStateRenderer, socket, timerWorker) { +function setClientSocketHandlers(gameStateRenderer, socket, timerWorker, gameTimerManager) { if (!socket.hasListeners(globals.EVENTS.PLAYER_JOINED)) { socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { toast(player.name + " joined!", "success", false); @@ -111,28 +117,8 @@ function setClientSocketHandlers(gameStateRenderer, socket, timerWorker) { }); } - if (!socket.hasListeners(globals.EVENTS.START_TIMER)) { - socket.on(globals.EVENTS.START_TIMER, () => { - runGameTimer( - gameStateRenderer.gameState.timerParams.hours, - gameStateRenderer.gameState.timerParams.minutes, - globals.CLOCK_TICK_INTERVAL_MILLIS, - null, - timerWorker - ) - }); - } - - if(!socket.hasListeners(globals.COMMANDS.PAUSE_TIMER)) { - socket.on(globals.COMMANDS.PAUSE_TIMER, (timeRemaining) => { - console.log(timeRemaining); - }); - } - - if(!socket.hasListeners(globals.COMMANDS.RESUME_TIMER)) { - socket.on(globals.COMMANDS.RESUME_TIMER, (timeRemaining) => { - console.log(timeRemaining); - }); + if (timerWorker && gameTimerManager) { + gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer); } } diff --git a/client/styles/game.css b/client/styles/game.css index a101038..b92f85c 100644 --- a/client/styles/game.css +++ b/client/styles/game.css @@ -237,6 +237,18 @@ label[for='lobby-players'] { border: 2px solid #1c8a36; } +.paused { + animation: pulse 0.75s linear infinite alternate; +} + +@keyframes pulse { + from { + color: rgba(255, 255, 255, 0.1); + } to { + color: rgba(255, 255, 255, 1); + } +} + @keyframes fade-in-slide-up { 0% { opacity: 0; diff --git a/client/views/404.html b/client/views/404.html index 4a212d6..210c111 100644 --- a/client/views/404.html +++ b/client/views/404.html @@ -71,7 +71,7 @@ } @media(max-width: 700px) { h1 { - font-size: 44vw; + font-size: 35vw; } } diff --git a/server/config/globals.js b/server/config/globals.js index 30d8721..638df4d 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -1,7 +1,7 @@ const globals = { ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', ACCESS_CODE_LENGTH: 6, - CLOCK_TICK_INTERVAL_MILLIS: 100, + CLOCK_TICK_INTERVAL_MILLIS: 10, CLIENT_COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', GET_ENVIRONMENT: 'getEnvironment', @@ -43,7 +43,8 @@ const globals = { START_GAME: "startGame", START_TIMER: "startTimer", PAUSE_TIMER: "pauseTimer", - RESUME_TIMER: "resumeTimer" + RESUME_TIMER: "resumeTimer", + GET_TIME_REMAINING: "getTimeRemaining" } }; diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index 23c2d91..742e12b 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -19,23 +19,31 @@ class ActiveGameRunner { gameProcess.on('message', (msg) => { switch (msg.command) { case globals.GAME_PROCESS_COMMANDS.END_GAME: - game.status = globals.STATUS.ENDED; + //game.status = globals.STATUS.ENDED; + game.timerParams.paused = false; + game.timerParams.timeRemaining = 0; this.logger.debug('PARENT: END GAME'); namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.END_GAME, game.accessCode); break; case globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER: game.timerParams.paused = true; this.logger.trace(msg); - game.timeRemaining = msg.timeRemaining; + game.timerParams.timeRemaining = msg.timeRemaining; this.logger.debug('PARENT: PAUSE TIMER'); - namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timeRemaining); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timerParams.timeRemaining); break; case globals.GAME_PROCESS_COMMANDS.RESUME_TIMER: game.timerParams.paused = false; this.logger.trace(msg); - game.timeRemaining = msg.timeRemaining; + game.timerParams.timeRemaining = msg.timeRemaining; this.logger.debug('PARENT: RESUME TIMER'); - namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timeRemaining); + namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timerParams.timeRemaining); + break; + case globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING: + this.logger.trace(msg); + game.timerParams.timeRemaining = msg.timeRemaining; + this.logger.debug('PARENT: GET TIME REMAINING'); + namespace.to(msg.socketId).emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused); break; } }); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 190b8e3..c90be74 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -220,13 +220,13 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe if (matchingPerson) { if (matchingPerson.socketId === socket.id) { logger.trace("matching person found with an established connection to the room: " + matchingPerson.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { if (!roomContainsSocketOfMatchingPerson(namespace, matchingPerson, logger, accessCode)) { logger.trace("matching person found with a new connection to the room: " + matchingPerson.name); socket.join(accessCode); matchingPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } else { rejectClientRequestForGameState(ackFn); } @@ -235,7 +235,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe let personWithMatchingSocketId = findPersonWithMatchingSocketId(game.people, socket.id); if (personWithMatchingSocketId) { logger.trace("matching person found whose cookie got cleared after establishing a connection to the room: " + personWithMatchingSocketId.name); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, personWithMatchingSocketId, gameRunner, socket, logger)); } else { let unassignedPerson = game.moderator.assigned === false ? game.moderator @@ -245,7 +245,7 @@ function handleRequestForGameState(namespace, logger, gameRunner, accessCode, pe socket.join(accessCode); unassignedPerson.assigned = true; unassignedPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson)); + ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); let isFull = isGameFull(game); game.isFull = isFull; socket.to(accessCode).emit( diff --git a/server/modules/GameProcess.js b/server/modules/GameProcess.js index e4cb8dd..60b3fca 100644 --- a/server/modules/GameProcess.js +++ b/server/modules/GameProcess.js @@ -37,6 +37,16 @@ process.on('message', (msg) => { logger.debug('CHILD PROCESS ' + msg.accessCode + ': RESUME TIMER'); process.send({ command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, timeRemaining: timer.currentTimeInMillis}); + break; + + case globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING: + logger.debug('CHILD PROCESS ' + msg.accessCode + ': GET TIME REMAINING'); + process.send({ + command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, + timeRemaining: timer.currentTimeInMillis, + socketId: msg.socketId + }); + break; } }); diff --git a/server/modules/GameStateCurator.js b/server/modules/GameStateCurator.js index 9680be0..47b402d 100644 --- a/server/modules/GameStateCurator.js +++ b/server/modules/GameStateCurator.js @@ -1,12 +1,15 @@ const globals = require("../config/globals") const GameStateCurator = { - getGameStateFromPerspectiveOfPerson: (game, person) => { - return getGameStateBasedOnPermissions(game, person); + getGameStateFromPerspectiveOfPerson: (game, person, gameRunner, socket, logger) => { + if (game.timerParams && game.status === globals.STATUS.IN_PROGRESS) { + getTimeRemaining(game.accessCode, gameRunner, socket, logger) + } + return getGameStateBasedOnPermissions(game, person, gameRunner); } } -function getGameStateBasedOnPermissions(game, person) { +function getGameStateBasedOnPermissions(game, person, gameRunner) { let client = game.status === globals.STATUS.LOBBY // people won't be able to know their role until past the lobby stage. ? { name: person.name, id: person.id } : { @@ -89,4 +92,16 @@ function mapPerson(person) { return { name: person.name }; } +function getTimeRemaining(accessCode, gameRunner, socket, logger) { + let thread = gameRunner.timerThreads[accessCode]; + if (thread) { + thread.send({ + command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, + accessCode: accessCode, + socketId: socket.id, + logLevel: logger.logLevel + }); + } +} + module.exports = GameStateCurator; diff --git a/server/modules/ServerTimer.js b/server/modules/ServerTimer.js index 48379c7..e17f801 100644 --- a/server/modules/ServerTimer.js +++ b/server/modules/ServerTimer.js @@ -37,7 +37,9 @@ class ServerTimer { } runTimer () { - this.totalTime = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + let total = convertFromHoursToMilliseconds(this.hours) + convertFromMinutesToMilliseconds(this.minutes); + this.totalTime = total; + this.currentTimeInMillis = total; this.logger.debug('STARTING TIMER FOR ' + this.totalTime + 'ms'); this.start = Date.now(); const expected = Date.now() + this.tickInterval;