mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-27 16:27:50 +01:00
end to end tests for the game page
This commit is contained in:
170
client/src/modules/timer/GameTimerManager.js
Normal file
170
client/src/modules/timer/GameTimerManager.js
Normal file
@@ -0,0 +1,170 @@
|
||||
import { globals } from '../../config/globals.js';
|
||||
|
||||
export class GameTimerManager {
|
||||
constructor (stateBucket, socket) {
|
||||
this.stateBucket = stateBucket;
|
||||
this.playListener = () => {
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.RESUME_TIMER, this.stateBucket.currentGameState.accessCode);
|
||||
};
|
||||
this.pauseListener = () => {
|
||||
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.PAUSE_TIMER, this.stateBucket.currentGameState.accessCode);
|
||||
};
|
||||
}
|
||||
|
||||
resumeGameTimer (totalTime, tickRate, soundManager, timerWorker) {
|
||||
if (window.Worker) {
|
||||
if (
|
||||
this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|
||||
|| this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
|
||||
) {
|
||||
this.swapToPauseButton();
|
||||
}
|
||||
const instance = this;
|
||||
const timer = document.getElementById('game-timer');
|
||||
timer.classList.remove('paused');
|
||||
timer.classList.remove('paused-low');
|
||||
timer.classList.remove('low');
|
||||
if (totalTime < 60000) {
|
||||
timer.classList.add('low');
|
||||
}
|
||||
timer.innerText = totalTime < 60000
|
||||
? returnHumanReadableTime(totalTime, true)
|
||||
: returnHumanReadableTime(totalTime);
|
||||
timerWorker.onmessage = function (e) {
|
||||
if (e.data.hasOwnProperty('timeRemainingInMilliseconds') && e.data.timeRemainingInMilliseconds >= 0) {
|
||||
if (e.data.timeRemainingInMilliseconds === 0) {
|
||||
instance.displayExpiredTime();
|
||||
} else if (e.data.timeRemainingInMilliseconds < 60000) {
|
||||
timer.classList.add('low');
|
||||
timer.innerText = e.data.displayTime;
|
||||
} else {
|
||||
timer.innerText = e.data.displayTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
timerWorker.postMessage({ totalTime: totalTime, tickInterval: tickRate });
|
||||
}
|
||||
}
|
||||
|
||||
pauseGameTimer (timerWorker, timeRemaining) {
|
||||
if (window.Worker) {
|
||||
if (
|
||||
this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|
||||
|| this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
|
||||
) {
|
||||
this.swapToPlayButton();
|
||||
}
|
||||
|
||||
timerWorker.postMessage('stop');
|
||||
const timer = document.getElementById('game-timer');
|
||||
if (timeRemaining < 60000) {
|
||||
timer.innerText = returnHumanReadableTime(timeRemaining, true);
|
||||
timer.classList.add('paused-low');
|
||||
timer.classList.add('low');
|
||||
} else {
|
||||
timer.innerText = returnHumanReadableTime(timeRemaining);
|
||||
timer.classList.add('paused');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
displayPausedTime (time) {
|
||||
if (
|
||||
this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR
|
||||
|| this.stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR
|
||||
) {
|
||||
this.swapToPlayButton();
|
||||
}
|
||||
|
||||
const timer = document.getElementById('game-timer');
|
||||
if (time < 60000) {
|
||||
timer.innerText = returnHumanReadableTime(time, true);
|
||||
timer.classList.add('paused-low');
|
||||
timer.classList.add('low');
|
||||
} else {
|
||||
timer.innerText = returnHumanReadableTime(time);
|
||||
timer.classList.add('paused');
|
||||
}
|
||||
}
|
||||
|
||||
displayExpiredTime () {
|
||||
const currentBtn = document.querySelector('#play-pause img');
|
||||
if (currentBtn) {
|
||||
currentBtn.removeEventListener('click', this.pauseListener);
|
||||
currentBtn.removeEventListener('click', this.playListener);
|
||||
currentBtn.remove();
|
||||
}
|
||||
|
||||
const timer = document.getElementById('game-timer');
|
||||
timer.innerText = returnHumanReadableTime(0, true);
|
||||
}
|
||||
|
||||
attachTimerSocketListeners (socket, timerWorker, gameStateRenderer) {
|
||||
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) => {
|
||||
if (paused) {
|
||||
this.displayPausedTime(timeRemaining);
|
||||
} else if (timeRemaining === 0) {
|
||||
this.displayExpiredTime();
|
||||
} else {
|
||||
this.resumeGameTimer(timeRemaining, globals.CLOCK_TICK_INTERVAL_MILLIS, null, timerWorker);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
swapToPlayButton () {
|
||||
const currentBtn = document.querySelector('#play-pause img');
|
||||
if (currentBtn) {
|
||||
currentBtn.removeEventListener('click', this.pauseListener);
|
||||
currentBtn.remove();
|
||||
}
|
||||
|
||||
const playBtn = document.createElement('img');
|
||||
playBtn.setAttribute('src', '../images/play-button.svg');
|
||||
playBtn.addEventListener('click', this.playListener);
|
||||
document.querySelector('#play-pause-placeholder')?.remove();
|
||||
document.getElementById('play-pause').appendChild(playBtn);
|
||||
}
|
||||
|
||||
swapToPauseButton () {
|
||||
const currentBtn = document.querySelector('#play-pause img');
|
||||
if (currentBtn) {
|
||||
currentBtn.removeEventListener('click', this.playListener);
|
||||
currentBtn.remove();
|
||||
}
|
||||
|
||||
const pauseBtn = document.createElement('img');
|
||||
pauseBtn.setAttribute('src', '../images/pause-button.svg');
|
||||
pauseBtn.addEventListener('click', this.pauseListener);
|
||||
document.querySelector('#play-pause-placeholder')?.remove();
|
||||
document.getElementById('play-pause').appendChild(pauseBtn);
|
||||
}
|
||||
}
|
||||
|
||||
function returnHumanReadableTime (milliseconds, tenthsOfSeconds = false) {
|
||||
const 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;
|
||||
}
|
||||
122
client/src/modules/timer/Timer.js
Normal file
122
client/src/modules/timer/Timer.js
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
A timer using setTimeout that compensates for drift. Drift can happen for several reasons:
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#reasons_for_delays
|
||||
|
||||
This means the timer may very well be late in executing the next call (but never early).
|
||||
This timer is accurate to within a few ms for any amount of time provided. This is the client-side version of this module,
|
||||
and is meant to be utilized as a Web Worker.
|
||||
See: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
|
||||
*/
|
||||
|
||||
const messageParameters = {
|
||||
STOP: 'stop',
|
||||
TICK_INTERVAL: 'tickInterval',
|
||||
TOTAL_TIME: 'totalTime'
|
||||
};
|
||||
|
||||
let timer;
|
||||
|
||||
onmessage = function (e) {
|
||||
if (typeof e.data === 'object'
|
||||
&& e.data.hasOwnProperty(messageParameters.TOTAL_TIME)
|
||||
&& e.data.hasOwnProperty(messageParameters.TICK_INTERVAL)
|
||||
) {
|
||||
timer = new Singleton(e.data.totalTime, e.data.tickInterval);
|
||||
timer.startTimer();
|
||||
} else if (e.data === 'stop') {
|
||||
timer.stopTimer();
|
||||
}
|
||||
};
|
||||
|
||||
function stepFn (expected, interval, start, totalTime) {
|
||||
const now = Date.now();
|
||||
if (now - start >= totalTime) {
|
||||
postMessage({
|
||||
timeRemainingInMilliseconds: 0,
|
||||
displayTime: returnHumanReadableTime(0, true)
|
||||
});
|
||||
return;
|
||||
}
|
||||
const delta = now - expected;
|
||||
expected += interval;
|
||||
const displayTime = (totalTime - (now - start)) < 60000
|
||||
? returnHumanReadableTime(totalTime - (now - start), true)
|
||||
: returnHumanReadableTime(totalTime - (now - start));
|
||||
postMessage({
|
||||
timeRemainingInMilliseconds: totalTime - (now - start),
|
||||
displayTime: displayTime
|
||||
});
|
||||
Singleton.setNewTimeoutReference(setTimeout(() => {
|
||||
stepFn(expected, interval, start, totalTime);
|
||||
}, Math.max(0, interval - delta)
|
||||
)); // take into account drift - also retain a reference to this clock tick so it can be cleared later
|
||||
}
|
||||
|
||||
class Timer {
|
||||
constructor (totalTime, tickInterval) {
|
||||
this.timeoutId = undefined;
|
||||
this.totalTime = totalTime;
|
||||
this.tickInterval = tickInterval;
|
||||
}
|
||||
|
||||
startTimer () {
|
||||
if (!isNaN(this.tickInterval)) {
|
||||
const interval = this.tickInterval;
|
||||
const start = Date.now();
|
||||
const expected = Date.now() + this.tickInterval;
|
||||
const totalTime = this.totalTime;
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
this.timeoutId = setTimeout(() => {
|
||||
stepFn(expected, interval, start, totalTime);
|
||||
}, this.tickInterval);
|
||||
}
|
||||
}
|
||||
|
||||
stopTimer () {
|
||||
if (this.timeoutId) {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Singleton {
|
||||
constructor (totalTime, tickInterval) {
|
||||
if (!Singleton.instance) {
|
||||
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(totalTime, tickInterval);
|
||||
}
|
||||
return Singleton.instance;
|
||||
}
|
||||
|
||||
static setNewTimerParameters (totalTime, tickInterval) {
|
||||
if (Singleton.instance) {
|
||||
Singleton.instance.totalTime = totalTime;
|
||||
Singleton.instance.tickInterval = tickInterval;
|
||||
}
|
||||
}
|
||||
|
||||
static setNewTimeoutReference (timeoutId) {
|
||||
if (Singleton.instance) {
|
||||
Singleton.instance.timeoutId = timeoutId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function returnHumanReadableTime (milliseconds, tenthsOfSeconds = false) {
|
||||
const 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;
|
||||
}
|
||||
Reference in New Issue
Block a user