mirror of
https://github.com/AlecM33/Werewolf.git
synced 2025-12-26 07:47:50 +01:00
more refactoring
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
"no-void": ["error", { "allowAsStatement": true }],
|
"no-void": ["error", { "allowAsStatement": true }],
|
||||||
"no-prototype-builtins": "off",
|
"no-prototype-builtins": "off",
|
||||||
"no-undef": "off",
|
"no-undef": "off",
|
||||||
|
"no-case-declarations": "off",
|
||||||
"no-return-assign": "warn",
|
"no-return-assign": "warn",
|
||||||
"prefer-promise-reject-errors": "warn",
|
"prefer-promise-reject-errors": "warn",
|
||||||
"no-trailing-spaces": "off",
|
"no-trailing-spaces": "off",
|
||||||
|
|||||||
@@ -42,6 +42,21 @@ export const globals = {
|
|||||||
PLAYER_LEFT: 'playerLeft',
|
PLAYER_LEFT: 'playerLeft',
|
||||||
NEW_SPECTATOR: 'newSpectator'
|
NEW_SPECTATOR: 'newSpectator'
|
||||||
},
|
},
|
||||||
|
SOCKET_EVENTS: {
|
||||||
|
IN_GAME_MESSAGE: 'inGameMessage'
|
||||||
|
},
|
||||||
|
EVENT_IDS: {
|
||||||
|
FETCH_GAME_STATE: 'fetchGameState',
|
||||||
|
START_GAME: 'startGame',
|
||||||
|
PAUSE_TIMER: 'pauseTimer',
|
||||||
|
RESUME_TIMER: 'resumeTimer',
|
||||||
|
GET_TIME_REMAINING: 'getTimeRemaining',
|
||||||
|
KILL_PLAYER: 'killPlayer',
|
||||||
|
REVEAL_PLAYER: 'revealPlayer',
|
||||||
|
TRANSFER_MODERATOR: 'transferModerator',
|
||||||
|
CHANGE_NAME: 'changeName',
|
||||||
|
END_GAME: 'endGame'
|
||||||
|
},
|
||||||
USER_TYPES: {
|
USER_TYPES: {
|
||||||
MODERATOR: 'moderator',
|
MODERATOR: 'moderator',
|
||||||
PLAYER: 'player',
|
PLAYER: 'player',
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 775 KiB After Width: | Height: | Size: 927 KiB |
@@ -305,7 +305,7 @@ function renderModerationTypeStep (game, containerId, stepNumber) {
|
|||||||
|
|
||||||
stepContainer.innerHTML =
|
stepContainer.innerHTML =
|
||||||
"<div tabindex=\"0\" id='moderation-dedicated'>I will be the <strong>dedicated mod.</strong> Don't deal me a card.</div>" +
|
"<div tabindex=\"0\" id='moderation-dedicated'>I will be the <strong>dedicated mod.</strong> Don't deal me a card.</div>" +
|
||||||
"<div tabindex=\"0\" id='moderation-self'>The <strong>first person out</strong> will mod. Deal me into the game.</div>";
|
"<div tabindex=\"0\" id='moderation-self'>I will be the <strong>temporary mod</strong>. Deal me into the game.</div>";
|
||||||
|
|
||||||
const dedicatedOption = stepContainer.querySelector('#moderation-dedicated');
|
const dedicatedOption = stepContainer.querySelector('#moderation-dedicated');
|
||||||
if (game.hasDedicatedModerator) {
|
if (game.hasDedicatedModerator) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export class GameStateRenderer {
|
|||||||
this.startGameHandler = (e) => { // TODO: prevent multiple emissions of this event (recommend converting to XHR)
|
this.startGameHandler = (e) => { // TODO: prevent multiple emissions of this event (recommend converting to XHR)
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (confirm('Start the game and deal roles?')) {
|
if (confirm('Start the game and deal roles?')) {
|
||||||
socket.emit(globals.COMMANDS.START_GAME, this.stateBucket.currentGameState.accessCode);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.START_GAME, stateBucket.currentGameState.accessCode);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.restartGameHandler = (e) => {
|
this.restartGameHandler = (e) => {
|
||||||
@@ -110,7 +110,6 @@ export class GameStateRenderer {
|
|||||||
|
|
||||||
QRCode.toCanvas(document.getElementById('canvas'), link, { scale: 2 }, function (error) {
|
QRCode.toCanvas(document.getElementById('canvas'), link, { scale: 2 }, function (error) {
|
||||||
if (error) console.error(error);
|
if (error) console.error(error);
|
||||||
console.log('success!');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const linkCopyHandler = (e) => {
|
const linkCopyHandler = (e) => {
|
||||||
@@ -358,7 +357,7 @@ export class GameStateRenderer {
|
|||||||
} else if (!player.out && moderatorType) {
|
} else if (!player.out && moderatorType) {
|
||||||
killPlayerHandlers[player.id] = () => {
|
killPlayerHandlers[player.id] = () => {
|
||||||
if (confirm('KILL ' + player.name + '?')) {
|
if (confirm('KILL ' + player.name + '?')) {
|
||||||
socket.emit(globals.COMMANDS.KILL_PLAYER, accessCode, player.id);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.KILL_PLAYER, accessCode, { personId: player.id });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
playerEl.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]);
|
playerEl.querySelector('.kill-player-button').addEventListener('click', killPlayerHandlers[player.id]);
|
||||||
@@ -373,7 +372,7 @@ export class GameStateRenderer {
|
|||||||
} else if (!player.revealed && moderatorType) {
|
} else if (!player.revealed && moderatorType) {
|
||||||
revealRoleHandlers[player.id] = () => {
|
revealRoleHandlers[player.id] = () => {
|
||||||
if (confirm('REVEAL ' + player.name + '?')) {
|
if (confirm('REVEAL ' + player.name + '?')) {
|
||||||
socket.emit(globals.COMMANDS.REVEAL_PLAYER, accessCode, player.id);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.REVEAL_PLAYER, accessCode, { personId: player.id });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
playerEl.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]);
|
playerEl.querySelector('.reveal-role-button').addEventListener('click', revealRoleHandlers[player.id]);
|
||||||
@@ -404,7 +403,7 @@ function renderPotentialMods (gameState, group, transferModHandlers, socket) {
|
|||||||
if (transferPrompt !== null) {
|
if (transferPrompt !== null) {
|
||||||
transferPrompt.innerHTML = '';
|
transferPrompt.innerHTML = '';
|
||||||
}
|
}
|
||||||
socket.emit(globals.COMMANDS.TRANSFER_MODERATOR, gameState.accessCode, member.id);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.TRANSFER_MODERATOR, gameState.accessCode, { personId: member.id });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -535,7 +534,8 @@ function createEndGamePromptComponent (socket, stateBucket) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (confirm('End the game?')) {
|
if (confirm('End the game?')) {
|
||||||
socket.emit(
|
socket.emit(
|
||||||
globals.COMMANDS.END_GAME,
|
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
|
||||||
|
globals.EVENT_IDS.END_GAME,
|
||||||
stateBucket.currentGameState.accessCode
|
stateBucket.currentGameState.accessCode
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ export class GameTimerManager {
|
|||||||
constructor (stateBucket, socket) {
|
constructor (stateBucket, socket) {
|
||||||
this.stateBucket = stateBucket;
|
this.stateBucket = stateBucket;
|
||||||
this.playListener = () => {
|
this.playListener = () => {
|
||||||
socket.emit(globals.COMMANDS.RESUME_TIMER, this.stateBucket.currentGameState.accessCode);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.RESUME_TIMER, this.stateBucket.currentGameState.accessCode);
|
||||||
};
|
};
|
||||||
this.pauseListener = () => {
|
this.pauseListener = () => {
|
||||||
socket.emit(globals.COMMANDS.PAUSE_TIMER, this.stateBucket.currentGameState.accessCode);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.PAUSE_TIMER, this.stateBucket.currentGameState.accessCode);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ function getNavbarLinks (page = null, device) {
|
|||||||
'<a class="' + linkClass + '" href="/create">Create</a>' +
|
'<a class="' + linkClass + '" href="/create">Create</a>' +
|
||||||
'<a class="' + linkClass + '" href="/how-to-use">How to Use</a>' +
|
'<a class="' + linkClass + '" href="/how-to-use">How to Use</a>' +
|
||||||
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Contact</a>' +
|
'<a class="' + linkClass + ' "href="mailto:play.werewolf.contact@gmail.com?Subject=Werewolf App" target="_top">Contact</a>' +
|
||||||
|
'<a class="' + linkClass + ' "href="https://github.com/alecm33/Werewolf" target="_top">Github</a>' +
|
||||||
'<a class="' + linkClass + '" href="https://www.buymeacoffee.com/alecm33">Support the App</a>';
|
'<a class="' + linkClass + '" href="https://www.buymeacoffee.com/alecm33">Support the App</a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ function syncWithGame (stateBucket, gameTimerManager, gameStateRenderer, timerWo
|
|||||||
const splitUrl = window.location.href.split('/game/');
|
const splitUrl = window.location.href.split('/game/');
|
||||||
const accessCode = splitUrl[1];
|
const accessCode = splitUrl[1];
|
||||||
if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) {
|
if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) {
|
||||||
socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, cookie, function (gameState) {
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.FETCH_GAME_STATE, accessCode, { personId: cookie }, function (gameState) {
|
||||||
if (gameState === null) {
|
if (gameState === null) {
|
||||||
window.location = '/not-found?reason=' + encodeURIComponent('game-not-found');
|
window.location = '/not-found?reason=' + encodeURIComponent('game-not-found');
|
||||||
} else {
|
} else {
|
||||||
@@ -138,7 +138,7 @@ function processGameState (
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (currentGameState.timerParams) {
|
if (currentGameState.timerParams) {
|
||||||
socket.emit(globals.COMMANDS.GET_TIME_REMAINING, currentGameState.accessCode);
|
socket.emit(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, globals.EVENT_IDS.GET_TIME_REMAINING, currentGameState.accessCode);
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('#game-timer')?.remove();
|
document.querySelector('#game-timer')?.remove();
|
||||||
document.querySelector('#timer-container-moderator')?.remove();
|
document.querySelector('#timer-container-moderator')?.remove();
|
||||||
@@ -198,9 +198,10 @@ function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerW
|
|||||||
|
|
||||||
socket.on(globals.EVENTS.START_GAME, () => {
|
socket.on(globals.EVENTS.START_GAME, () => {
|
||||||
socket.emit(
|
socket.emit(
|
||||||
globals.COMMANDS.FETCH_GAME_STATE,
|
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
|
||||||
|
globals.EVENT_IDS.FETCH_GAME_STATE,
|
||||||
stateBucket.currentGameState.accessCode,
|
stateBucket.currentGameState.accessCode,
|
||||||
stateBucket.currentGameState.client.cookie,
|
{ personId: stateBucket.currentGameState.client.cookie },
|
||||||
function (gameState) {
|
function (gameState) {
|
||||||
stateBucket.currentGameState = gameState;
|
stateBucket.currentGameState = gameState;
|
||||||
processGameState(
|
processGameState(
|
||||||
@@ -219,9 +220,10 @@ function setClientSocketHandlers (stateBucket, gameStateRenderer, socket, timerW
|
|||||||
|
|
||||||
socket.on(globals.EVENTS.SYNC_GAME_STATE, () => {
|
socket.on(globals.EVENTS.SYNC_GAME_STATE, () => {
|
||||||
socket.emit(
|
socket.emit(
|
||||||
globals.COMMANDS.FETCH_GAME_STATE,
|
globals.SOCKET_EVENTS.IN_GAME_MESSAGE,
|
||||||
|
globals.EVENT_IDS.FETCH_GAME_STATE,
|
||||||
stateBucket.currentGameState.accessCode,
|
stateBucket.currentGameState.accessCode,
|
||||||
stateBucket.currentGameState.client.cookie,
|
{ personId: stateBucket.currentGameState.client.cookie },
|
||||||
function (gameState) {
|
function (gameState) {
|
||||||
stateBucket.currentGameState = gameState;
|
stateBucket.currentGameState = gameState;
|
||||||
processGameState(
|
processGameState(
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.toast-bottom {
|
.toast-bottom {
|
||||||
bottom: 90px;
|
bottom: 140px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.toast-success {
|
.toast-success {
|
||||||
@@ -309,6 +309,10 @@ button {
|
|||||||
#how-to-use-container h1 {
|
#how-to-use-container h1 {
|
||||||
color: #4b6bfa;
|
color: #4b6bfa;
|
||||||
font-family: signika-negative, sans-serif;
|
font-family: signika-negative, sans-serif;
|
||||||
|
background-color: #1e1b26;
|
||||||
|
width: fit-content;
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@@ -355,8 +359,8 @@ input {
|
|||||||
color: #d7d7d7;
|
color: #d7d7d7;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
margin: 0 auto;
|
margin: 1em auto 0 auto;
|
||||||
width: 95%;
|
width: 90%;
|
||||||
max-width: 64em;
|
max-width: 64em;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
@@ -782,7 +786,7 @@ input {
|
|||||||
|
|
||||||
@media(min-width: 551px) {
|
@media(min-width: 551px) {
|
||||||
.how-to-use-header {
|
.how-to-use-header {
|
||||||
font-size: 40px;
|
font-size: 35px;
|
||||||
}
|
}
|
||||||
#how-to-use-container h3 {
|
#how-to-use-container h3 {
|
||||||
font-size: 25px;
|
font-size: 25px;
|
||||||
|
|||||||
67
index.js
67
index.js
@@ -1,11 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
const path = require('path');
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const GameManager = require('./server/modules/GameManager.js');
|
|
||||||
const SocketManager = require('./server/modules/SocketManager.js');
|
|
||||||
const globals = require('./server/config/globals');
|
|
||||||
const ServerBootstrapper = require('./server/modules/ServerBootstrapper');
|
const ServerBootstrapper = require('./server/modules/ServerBootstrapper');
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
@@ -15,64 +11,17 @@ const args = ServerBootstrapper.processCLIArgs();
|
|||||||
const logger = require('./server/modules/Logger')(args.logLevel);
|
const logger = require('./server/modules/Logger')(args.logLevel);
|
||||||
logger.info('LOG LEVEL IS: ' + args.logLevel);
|
logger.info('LOG LEVEL IS: ' + args.logLevel);
|
||||||
|
|
||||||
const index = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger);
|
const port = parseInt(process.env.PORT) || args.port || 8080;
|
||||||
|
|
||||||
app.set('port', parseInt(process.env.PORT) || args.port || 8080);
|
const webServer = ServerBootstrapper.createServerWithCorrectHTTPProtocol(app, args.useHttps, args.port, logger);
|
||||||
|
const singletons = ServerBootstrapper.singletons(logger);
|
||||||
|
|
||||||
const inGameSocketServer = ServerBootstrapper.createSocketServer(index, app, args.port, logger);
|
const socketServer = singletons.socketManager.createSocketServer(webServer, app, port);
|
||||||
const gameNamespace = ServerBootstrapper.createGameSocketNamespace(inGameSocketServer, logger);
|
singletons.gameManager.setGameSocketNamespace(singletons.socketManager.createGameSocketNamespace(socketServer, logger, singletons.gameManager));
|
||||||
|
ServerBootstrapper.establishRouting(app, express);
|
||||||
|
|
||||||
let gameManager;
|
app.set('port', port);
|
||||||
|
|
||||||
/* Instantiate the singleton game manager */
|
webServer.listen(app.get('port'), function () {
|
||||||
if (process.env.NODE_ENV.trim() === 'development') {
|
|
||||||
gameManager = new GameManager(logger, globals.ENVIRONMENT.LOCAL, gameNamespace).getInstance();
|
|
||||||
} else {
|
|
||||||
gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, gameNamespace).getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Instantiate the singleton socket manager */
|
|
||||||
const socketManager = new SocketManager(logger, inGameSocketServer).getInstance();
|
|
||||||
|
|
||||||
gameNamespace.on('connection', function (socket) {
|
|
||||||
socket.on('disconnecting', (reason) => {
|
|
||||||
logger.trace('client socket disconnecting because: ' + reason);
|
|
||||||
});
|
|
||||||
gameManager.addGameSocketHandlers(gameNamespace, socket);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* api endpoints */
|
|
||||||
const games = require('./server/api/GamesAPI');
|
|
||||||
const admin = require('./server/api/AdminAPI');
|
|
||||||
app.use('/api/games', games);
|
|
||||||
app.use('/api/admin', admin);
|
|
||||||
|
|
||||||
/* serve all the app's pages */
|
|
||||||
app.use('/manifest.json', (req, res) => {
|
|
||||||
res.sendFile(path.join(__dirname, './manifest.json'));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use('/favicon.ico', (req, res) => {
|
|
||||||
res.sendFile(path.join(__dirname, './client/favicon_package/favicon.ico'));
|
|
||||||
});
|
|
||||||
|
|
||||||
const router = require('./server/routes/router');
|
|
||||||
app.use('', router);
|
|
||||||
|
|
||||||
app.use('/dist', express.static(path.join(__dirname, './client/dist')));
|
|
||||||
|
|
||||||
// set up routing for static content that isn't being bundled.
|
|
||||||
app.use('/images', express.static(path.join(__dirname, './client/src/images')));
|
|
||||||
app.use('/styles', express.static(path.join(__dirname, './client/src/styles')));
|
|
||||||
app.use('/webfonts', express.static(path.join(__dirname, './client/src/webfonts')));
|
|
||||||
app.use('/robots.txt', (req, res) => {
|
|
||||||
res.sendFile(path.join(__dirname, './client/robots.txt'));
|
|
||||||
});
|
|
||||||
|
|
||||||
app.use(function (req, res) {
|
|
||||||
res.sendFile(path.join(__dirname, './client/src/views/404.html'));
|
|
||||||
});
|
|
||||||
|
|
||||||
index.listen(app.get('port'), function () {
|
|
||||||
logger.info(`Starting server on port ${app.get('port')}`);
|
logger.info(`Starting server on port ${app.get('port')}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ const globals = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
STALE_GAME_HOURS: 24,
|
STALE_GAME_HOURS: 24,
|
||||||
CLIENT_COMMANDS: {
|
SOCKET_EVENTS: {
|
||||||
|
IN_GAME_MESSAGE: 'inGameMessage'
|
||||||
|
},
|
||||||
|
EVENT_IDS: {
|
||||||
FETCH_GAME_STATE: 'fetchGameState',
|
FETCH_GAME_STATE: 'fetchGameState',
|
||||||
START_GAME: 'startGame',
|
START_GAME: 'startGame',
|
||||||
PAUSE_TIMER: 'pauseTimer',
|
PAUSE_TIMER: 'pauseTimer',
|
||||||
|
|||||||
@@ -6,159 +6,15 @@ const GameStateCurator = require('./GameStateCurator');
|
|||||||
const UsernameGenerator = require('./UsernameGenerator');
|
const UsernameGenerator = require('./UsernameGenerator');
|
||||||
|
|
||||||
class GameManager {
|
class GameManager {
|
||||||
constructor (logger, environment, namespace) {
|
constructor (logger, environment) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.environment = environment;
|
this.environment = environment;
|
||||||
this.activeGameRunner = new ActiveGameRunner(logger).getInstance();
|
this.activeGameRunner = new ActiveGameRunner(logger).getInstance();
|
||||||
this.namespace = namespace;
|
this.namespace = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
addGameSocketHandlers = (namespace, socket) => {
|
setGameSocketNamespace = (namespace) => {
|
||||||
socket.on(globals.CLIENT_COMMANDS.FETCH_GAME_STATE, async (accessCode, personId, ackFn) => {
|
this.namespace = namespace;
|
||||||
this.logger.trace('request for game state for accessCode: ' + accessCode + ' from socket: ' + socket.id + ' with cookie: ' + personId);
|
|
||||||
await this.handleRequestForGameState(
|
|
||||||
this.namespace,
|
|
||||||
this.logger,
|
|
||||||
this.activeGameRunner,
|
|
||||||
accessCode,
|
|
||||||
personId,
|
|
||||||
ackFn,
|
|
||||||
socket
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game && game.isFull) {
|
|
||||||
game.status = globals.STATUS.IN_PROGRESS;
|
|
||||||
if (game.hasTimer) {
|
|
||||||
game.timerParams.paused = true;
|
|
||||||
this.activeGameRunner.runGame(game, namespace);
|
|
||||||
}
|
|
||||||
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.START_GAME);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.PAUSE_TIMER, (accessCode) => {
|
|
||||||
this.logger.trace(accessCode);
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const thread = this.activeGameRunner.timerThreads[accessCode];
|
|
||||||
if (thread) {
|
|
||||||
this.logger.debug('Timer thread found for game ' + accessCode);
|
|
||||||
thread.send({
|
|
||||||
command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER,
|
|
||||||
accessCode: game.accessCode,
|
|
||||||
logLevel: this.logger.logLevel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.RESUME_TIMER, (accessCode) => {
|
|
||||||
this.logger.trace(accessCode);
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const thread = this.activeGameRunner.timerThreads[accessCode];
|
|
||||||
if (thread) {
|
|
||||||
this.logger.debug('Timer thread found for game ' + accessCode);
|
|
||||||
thread.send({
|
|
||||||
command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER,
|
|
||||||
accessCode: game.accessCode,
|
|
||||||
logLevel: this.logger.logLevel
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.GET_TIME_REMAINING, (accessCode) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const thread = this.activeGameRunner.timerThreads[accessCode];
|
|
||||||
if (thread) {
|
|
||||||
thread.send({
|
|
||||||
command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING,
|
|
||||||
accessCode: accessCode,
|
|
||||||
socketId: socket.id,
|
|
||||||
logLevel: this.logger.logLevel
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (game.timerParams && game.timerParams.timeRemaining === 0) {
|
|
||||||
this.namespace.to(socket.id).emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.KILL_PLAYER, (accessCode, personId) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const person = game.people.find((person) => person.id === personId);
|
|
||||||
this.killPlayer(game, person, namespace, this.logger);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.REVEAL_PLAYER, (accessCode, personId) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const person = game.people.find((person) => person.id === personId);
|
|
||||||
if (person && !person.revealed) {
|
|
||||||
this.logger.debug('game ' + accessCode + ': revealing player ' + person.name);
|
|
||||||
person.revealed = true;
|
|
||||||
namespace.in(accessCode).emit(
|
|
||||||
globals.CLIENT_COMMANDS.REVEAL_PLAYER,
|
|
||||||
{
|
|
||||||
id: person.id,
|
|
||||||
gameRole: person.gameRole,
|
|
||||||
alignment: person.alignment
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.TRANSFER_MODERATOR, (accessCode, personId) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
let person = game.people.find((person) => person.id === personId);
|
|
||||||
if (!person) {
|
|
||||||
person = game.spectators.find((spectator) => spectator.id === personId);
|
|
||||||
}
|
|
||||||
this.transferModeratorPowers(game, person, namespace, this.logger);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.CHANGE_NAME, (accessCode, data, ackFn) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
const person = findPersonByField(game, 'id', data.personId);
|
|
||||||
if (person) {
|
|
||||||
if (!isNameTaken(game, data.name)) {
|
|
||||||
ackFn('changed');
|
|
||||||
person.name = data.name.trim();
|
|
||||||
person.hasEnteredName = true;
|
|
||||||
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.CHANGE_NAME, person.id, person.name);
|
|
||||||
} else {
|
|
||||||
ackFn('taken');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on(globals.CLIENT_COMMANDS.END_GAME, (accessCode) => {
|
|
||||||
const game = this.activeGameRunner.activeGames[accessCode];
|
|
||||||
if (game) {
|
|
||||||
game.status = globals.STATUS.ENDED;
|
|
||||||
if (this.activeGameRunner.timerThreads[accessCode]) {
|
|
||||||
this.logger.trace('KILLING TIMER PROCESS FOR ENDED GAME ' + accessCode);
|
|
||||||
this.activeGameRunner.timerThreads[accessCode].kill();
|
|
||||||
delete this.activeGameRunner.timerThreads[accessCode];
|
|
||||||
}
|
|
||||||
for (const person of game.people) {
|
|
||||||
person.revealed = true;
|
|
||||||
}
|
|
||||||
namespace.in(accessCode).emit(globals.CLIENT_COMMANDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
createGame = (gameParams) => {
|
createGame = (gameParams) => {
|
||||||
@@ -196,6 +52,99 @@ class GameManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
startGame = (game, namespace) => {
|
||||||
|
if (game.isFull) {
|
||||||
|
game.status = globals.STATUS.IN_PROGRESS;
|
||||||
|
if (game.hasTimer) {
|
||||||
|
game.timerParams.paused = true;
|
||||||
|
this.activeGameRunner.runGame(game, namespace);
|
||||||
|
}
|
||||||
|
namespace.in(game.accessCode).emit(globals.EVENT_IDS.START_GAME);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pauseTimer = (game, logger) => {
|
||||||
|
const thread = this.activeGameRunner.timerThreads[game.accessCode];
|
||||||
|
if (thread) {
|
||||||
|
this.logger.debug('Timer thread found for game ' + game.accessCode);
|
||||||
|
thread.send({
|
||||||
|
command: globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER,
|
||||||
|
accessCode: game.accessCode,
|
||||||
|
logLevel: this.logger.logLevel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
resumeTimer = (game, logger) => {
|
||||||
|
const thread = this.activeGameRunner.timerThreads[game.accessCode];
|
||||||
|
if (thread) {
|
||||||
|
this.logger.debug('Timer thread found for game ' + game.accessCode);
|
||||||
|
thread.send({
|
||||||
|
command: globals.GAME_PROCESS_COMMANDS.RESUME_TIMER,
|
||||||
|
accessCode: game.accessCode,
|
||||||
|
logLevel: this.logger.logLevel
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
getTimeRemaining = (game, socket) => {
|
||||||
|
const thread = this.activeGameRunner.timerThreads[game.accessCode];
|
||||||
|
if (thread) {
|
||||||
|
thread.send({
|
||||||
|
command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING,
|
||||||
|
accessCode: game.accessCode,
|
||||||
|
socketId: socket.id,
|
||||||
|
logLevel: this.logger.logLevel
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (game.timerParams && game.timerParams.timeRemaining === 0) {
|
||||||
|
this.namespace.to(socket.id).emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
revealPlayer = (game, personId) => {
|
||||||
|
const person = game.people.find((person) => person.id === personId);
|
||||||
|
if (person && !person.revealed) {
|
||||||
|
this.logger.debug('game ' + game.accessCode + ': revealing player ' + person.name);
|
||||||
|
person.revealed = true;
|
||||||
|
this.namespace.in(game.accessCode).emit(
|
||||||
|
globals.EVENT_IDS.REVEAL_PLAYER,
|
||||||
|
{
|
||||||
|
id: person.id,
|
||||||
|
gameRole: person.gameRole,
|
||||||
|
alignment: person.alignment
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
changeName = (game, data, ackFn) => {
|
||||||
|
const person = findPersonByField(game, 'id', data.personId);
|
||||||
|
if (person) {
|
||||||
|
if (!isNameTaken(game, data.name)) {
|
||||||
|
ackFn('changed');
|
||||||
|
person.name = data.name.trim();
|
||||||
|
person.hasEnteredName = true;
|
||||||
|
this.namespace.in(game.accessCode).emit(globals.EVENT_IDS.CHANGE_NAME, person.id, person.name);
|
||||||
|
} else {
|
||||||
|
ackFn('taken');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
endGame = (game) => {
|
||||||
|
game.status = globals.STATUS.ENDED;
|
||||||
|
if (this.activeGameRunner.timerThreads[game.accessCode]) {
|
||||||
|
this.logger.trace('KILLING TIMER PROCESS FOR ENDED GAME ' + game.accessCode);
|
||||||
|
this.activeGameRunner.timerThreads[game.accessCode].kill();
|
||||||
|
delete this.activeGameRunner.timerThreads[game.accessCode];
|
||||||
|
}
|
||||||
|
for (const person of game.people) {
|
||||||
|
person.revealed = true;
|
||||||
|
}
|
||||||
|
this.namespace.in(game.accessCode).emit(globals.EVENT_IDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people));
|
||||||
|
};
|
||||||
|
|
||||||
checkAvailability = (code) => {
|
checkAvailability = (code) => {
|
||||||
const game = this.activeGameRunner.activeGames[code.toUpperCase()];
|
const game = this.activeGameRunner.activeGames[code.toUpperCase()];
|
||||||
if (game) {
|
if (game) {
|
||||||
@@ -224,7 +173,7 @@ class GameManager {
|
|||||||
: accessCode;
|
: accessCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
transferModeratorPowers = (game, person, namespace, logger) => {
|
transferModeratorPowers = (game, person, logger) => {
|
||||||
if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) {
|
if (person && (person.out || person.userType === globals.USER_TYPES.SPECTATOR)) {
|
||||||
logger.debug('game ' + game.accessCode + ': transferring mod powers to ' + person.name);
|
logger.debug('game ' + game.accessCode + ': transferring mod powers to ' + person.name);
|
||||||
if (game.moderator === person) {
|
if (game.moderator === person) {
|
||||||
@@ -247,7 +196,7 @@ class GameManager {
|
|||||||
game.moderator = person;
|
game.moderator = person;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE);
|
this.namespace.in(game.accessCode).emit(globals.EVENTS.SYNC_GAME_STATE);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,10 +207,10 @@ class GameManager {
|
|||||||
person.userType = globals.USER_TYPES.KILLED_PLAYER;
|
person.userType = globals.USER_TYPES.KILLED_PLAYER;
|
||||||
}
|
}
|
||||||
person.out = true;
|
person.out = true;
|
||||||
namespace.in(game.accessCode).emit(globals.CLIENT_COMMANDS.KILL_PLAYER, person.id);
|
namespace.in(game.accessCode).emit(globals.EVENT_IDS.KILL_PLAYER, person.id);
|
||||||
// temporary moderators will transfer their powers automatically to the first person they kill.
|
// temporary moderators will transfer their powers automatically to the first person they kill.
|
||||||
if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
|
if (game.moderator.userType === globals.USER_TYPES.TEMPORARY_MODERATOR) {
|
||||||
this.transferModeratorPowers(game, person, namespace, logger);
|
this.transferModeratorPowers(game, person, logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -363,7 +312,7 @@ class GameManager {
|
|||||||
this.activeGameRunner.runGame(game, namespace);
|
this.activeGameRunner.runGame(game, namespace);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace.in(game.accessCode).emit(globals.CLIENT_COMMANDS.START_GAME);
|
namespace.in(game.accessCode).emit(globals.EVENT_IDS.START_GAME);
|
||||||
};
|
};
|
||||||
|
|
||||||
handleRequestForGameState = async (namespace, logger, gameRunner, accessCode, personCookie, ackFn, clientSocket) => {
|
handleRequestForGameState = async (namespace, logger, gameRunner, accessCode, personCookie, ackFn, clientSocket) => {
|
||||||
@@ -553,10 +502,10 @@ function getGameSize (cards) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Singleton {
|
class Singleton {
|
||||||
constructor (logger, environment, namespace) {
|
constructor (logger, environment) {
|
||||||
if (!Singleton.instance) {
|
if (!Singleton.instance) {
|
||||||
logger.info('CREATING SINGLETON GAME MANAGER');
|
logger.info('CREATING SINGLETON GAME MANAGER');
|
||||||
Singleton.instance = new GameManager(logger, environment, namespace);
|
Singleton.instance = new GameManager(logger, environment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,21 @@ const https = require('https');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const { RateLimiterMemory } = require('rate-limiter-flexible');
|
const SocketManager = require('./SocketManager.js');
|
||||||
|
const GameManager = require('./GameManager.js');
|
||||||
|
const { ENVIRONMENT } = require('../config/globals.js');
|
||||||
|
|
||||||
const ServerBootstrapper = {
|
const ServerBootstrapper = {
|
||||||
|
|
||||||
|
singletons: (logger) => {
|
||||||
|
return {
|
||||||
|
socketManager: new SocketManager(logger).getInstance(),
|
||||||
|
gameManager: process.env.NODE_ENV.trim() === 'development'
|
||||||
|
? new GameManager(logger, ENVIRONMENT.LOCAL).getInstance()
|
||||||
|
: new GameManager(logger, ENVIRONMENT.PRODUCTION).getInstance()
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
processCLIArgs: () => {
|
processCLIArgs: () => {
|
||||||
try {
|
try {
|
||||||
const args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase()));
|
const args = Array.from(process.argv.map((arg) => arg.trim().toLowerCase()));
|
||||||
@@ -80,46 +92,43 @@ const ServerBootstrapper = {
|
|||||||
return main;
|
return main;
|
||||||
},
|
},
|
||||||
|
|
||||||
createSocketServer: (main, app, port, logger) => {
|
establishRouting: (app, express) => {
|
||||||
let io;
|
/* api endpoints */
|
||||||
if (process.env.NODE_ENV.trim() === 'development') {
|
const games = require('../api/GamesAPI');
|
||||||
io = require('socket.io')(main, {
|
const admin = require('../api/AdminAPI');
|
||||||
cors: { origin: 'http://localhost:' + port }
|
app.use('/api/games', games);
|
||||||
});
|
app.use('/api/admin', admin);
|
||||||
} else {
|
|
||||||
io = require('socket.io')(main, {
|
|
||||||
cors: { origin: 'https://playwerewolf.uk.r.appspot.com' }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
registerRateLimiter(io, logger);
|
/* serve all the app's pages */
|
||||||
|
app.use('/manifest.json', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, '../../manifest.json'));
|
||||||
|
});
|
||||||
|
|
||||||
return io;
|
app.use('/favicon.ico', (req, res) => {
|
||||||
},
|
res.sendFile(path.join(__dirname, '../../client/favicon_package/favicon.ico'));
|
||||||
|
});
|
||||||
|
|
||||||
createGameSocketNamespace (server, logger) {
|
app.use('/apple-touch-icon.png', (req, res) => {
|
||||||
const namespace = server.of('/in-game');
|
res.sendFile(path.join(__dirname, '../../client/favicon_package/apple-touch-icon.png'));
|
||||||
registerRateLimiter(namespace, logger);
|
});
|
||||||
return server.of('/in-game');
|
|
||||||
|
const router = require('../routes/router');
|
||||||
|
app.use('', router);
|
||||||
|
|
||||||
|
app.use('/dist', express.static(path.join(__dirname, '../../client/dist')));
|
||||||
|
|
||||||
|
// set up routing for static content that isn't being bundled.
|
||||||
|
app.use('/images', express.static(path.join(__dirname, '../../client/src/images')));
|
||||||
|
app.use('/styles', express.static(path.join(__dirname, '../../client/src/styles')));
|
||||||
|
app.use('/webfonts', express.static(path.join(__dirname, '../../client/src/webfonts')));
|
||||||
|
app.use('/robots.txt', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, '../../client/robots.txt'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(function (req, res) {
|
||||||
|
res.sendFile(path.join(__dirname, '../../client/src/views/404.html'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function registerRateLimiter (server, logger) {
|
|
||||||
const rateLimiter = new RateLimiterMemory(
|
|
||||||
{
|
|
||||||
points: 10,
|
|
||||||
duration: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
server.use(async (socket, next) => {
|
|
||||||
try {
|
|
||||||
await rateLimiter.consume(socket.handshake.address);
|
|
||||||
logger.trace('consumed point from ' + socket.handshake.address);
|
|
||||||
next();
|
|
||||||
} catch (rejection) {
|
|
||||||
next(new Error('Your connection has been blocked.'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ServerBootstrapper;
|
module.exports = ServerBootstrapper;
|
||||||
|
|||||||
@@ -1,21 +1,127 @@
|
|||||||
const globals = require('../config/globals.js');
|
const globals = require('../config/globals');
|
||||||
|
const EVENT_IDS = globals.EVENT_IDS;
|
||||||
|
const { RateLimiterMemory } = require('rate-limiter-flexible');
|
||||||
|
|
||||||
class SocketManager {
|
class SocketManager {
|
||||||
constructor (logger, io) {
|
constructor (logger) {
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.io = io;
|
this.io = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
broadcast = (message) => {
|
broadcast = (message) => {
|
||||||
this.io.emit(globals.EVENTS.BROADCAST, message);
|
this.io?.emit(globals.EVENTS.BROADCAST, message);
|
||||||
|
};
|
||||||
|
|
||||||
|
createSocketServer = (main, app, port, logger) => {
|
||||||
|
let io;
|
||||||
|
if (process.env.NODE_ENV.trim() === 'development') {
|
||||||
|
io = require('socket.io')(main, {
|
||||||
|
cors: { origin: 'http://localhost:' + port }
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
io = require('socket.io')(main, {
|
||||||
|
cors: { origin: 'https://play-werewolf.app' }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
registerRateLimiter(io, logger);
|
||||||
|
this.io = io;
|
||||||
|
|
||||||
|
return io;
|
||||||
|
};
|
||||||
|
|
||||||
|
createGameSocketNamespace = (server, logger, gameManager) => {
|
||||||
|
const namespace = server.of('/in-game');
|
||||||
|
const registerHandlers = this.registerHandlers;
|
||||||
|
registerRateLimiter(namespace, logger);
|
||||||
|
namespace.on('connection', function (socket) {
|
||||||
|
socket.on('disconnecting', (reason) => {
|
||||||
|
logger.trace('client socket disconnecting because: ' + reason);
|
||||||
|
});
|
||||||
|
|
||||||
|
registerHandlers(namespace, socket, gameManager);
|
||||||
|
});
|
||||||
|
return server.of('/in-game');
|
||||||
|
};
|
||||||
|
|
||||||
|
registerHandlers = (namespace, socket, gameManager) => {
|
||||||
|
socket.on(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, async (eventId, accessCode, args, ackFn) => {
|
||||||
|
const game = gameManager.activeGameRunner.activeGames[accessCode];
|
||||||
|
if (game) {
|
||||||
|
switch (eventId) {
|
||||||
|
case EVENT_IDS.FETCH_GAME_STATE:
|
||||||
|
await gameManager.handleRequestForGameState(
|
||||||
|
this.namespace,
|
||||||
|
this.logger,
|
||||||
|
gameManager.activeGameRunner,
|
||||||
|
accessCode,
|
||||||
|
args.personId,
|
||||||
|
ackFn,
|
||||||
|
socket
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.START_GAME:
|
||||||
|
gameManager.startGame(game, namespace);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.PAUSE_TIMER:
|
||||||
|
gameManager.pauseTimer(game, this.logger);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.RESUME_TIMER:
|
||||||
|
gameManager.resumeTimer(game, this.logger);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.GET_TIME_REMAINING:
|
||||||
|
gameManager.getTimeRemaining(game, socket);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.KILL_PLAYER:
|
||||||
|
gameManager.killPlayer(game, game.people.find((person) => person.id === args.personId), namespace, this.logger);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.REVEAL_PLAYER:
|
||||||
|
gameManager.revealPlayer(game, args.personId);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.TRANSFER_MODERATOR:
|
||||||
|
let person = game.people.find((person) => person.id === args.personId);
|
||||||
|
if (!person) {
|
||||||
|
person = game.spectators.find((spectator) => spectator.id === args.personId);
|
||||||
|
}
|
||||||
|
gameManager.transferModeratorPowers(game, person, this.logger);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.CHANGE_NAME:
|
||||||
|
gameManager.changeName(game, args.data, ackFn);
|
||||||
|
break;
|
||||||
|
case EVENT_IDS.END_GAME:
|
||||||
|
gameManager.endGame(game);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function registerRateLimiter (server, logger) {
|
||||||
|
const rateLimiter = new RateLimiterMemory(
|
||||||
|
{
|
||||||
|
points: 10,
|
||||||
|
duration: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
server.use(async (socket, next) => {
|
||||||
|
try {
|
||||||
|
await rateLimiter.consume(socket.handshake.address);
|
||||||
|
logger.trace('consumed point from ' + socket.handshake.address);
|
||||||
|
next();
|
||||||
|
} catch (rejection) {
|
||||||
|
next(new Error('Your connection has been blocked.'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class Singleton {
|
class Singleton {
|
||||||
constructor (logger, io) {
|
constructor (logger) {
|
||||||
if (!Singleton.instance) {
|
if (!Singleton.instance) {
|
||||||
logger.info('CREATING SINGLETON SOCKET MANAGER');
|
logger.info('CREATING SINGLETON SOCKET MANAGER');
|
||||||
Singleton.instance = new SocketManager(logger, io);
|
Singleton.instance = new SocketManager(logger);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ describe('GameManager', () => {
|
|||||||
|
|
||||||
const inObj = { emit: () => {} };
|
const inObj = { emit: () => {} };
|
||||||
namespace = { in: () => { return inObj; } };
|
namespace = { in: () => { return inObj; } };
|
||||||
gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION, namespace).getInstance();
|
gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION).getInstance();
|
||||||
|
gameManager.setGameSocketNamespace(namespace);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -36,7 +37,7 @@ describe('GameManager', () => {
|
|||||||
false,
|
false,
|
||||||
moderator
|
moderator
|
||||||
);
|
);
|
||||||
gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger);
|
gameManager.transferModeratorPowers(game, personToTransferTo, logger);
|
||||||
|
|
||||||
expect(game.moderator).toEqual(personToTransferTo);
|
expect(game.moderator).toEqual(personToTransferTo);
|
||||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||||
@@ -55,7 +56,7 @@ describe('GameManager', () => {
|
|||||||
moderator
|
moderator
|
||||||
);
|
);
|
||||||
game.spectators.push(personToTransferTo);
|
game.spectators.push(personToTransferTo);
|
||||||
gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger);
|
gameManager.transferModeratorPowers(game, personToTransferTo, logger);
|
||||||
|
|
||||||
expect(game.moderator).toEqual(personToTransferTo);
|
expect(game.moderator).toEqual(personToTransferTo);
|
||||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||||
@@ -74,7 +75,7 @@ describe('GameManager', () => {
|
|||||||
false,
|
false,
|
||||||
tempMod
|
tempMod
|
||||||
);
|
);
|
||||||
gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger);
|
gameManager.transferModeratorPowers(game, personToTransferTo, logger);
|
||||||
|
|
||||||
expect(game.moderator).toEqual(personToTransferTo);
|
expect(game.moderator).toEqual(personToTransferTo);
|
||||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||||
@@ -93,7 +94,7 @@ describe('GameManager', () => {
|
|||||||
false,
|
false,
|
||||||
tempMod
|
tempMod
|
||||||
);
|
);
|
||||||
gameManager.transferModeratorPowers(game, personToTransferTo, namespace, logger);
|
gameManager.transferModeratorPowers(game, personToTransferTo, logger);
|
||||||
|
|
||||||
expect(game.moderator).toEqual(personToTransferTo);
|
expect(game.moderator).toEqual(personToTransferTo);
|
||||||
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
expect(personToTransferTo.userType).toEqual(USER_TYPES.MODERATOR);
|
||||||
@@ -330,7 +331,7 @@ describe('GameManager', () => {
|
|||||||
expect(person.gameRole).toBeDefined();
|
expect(person.gameRole).toBeDefined();
|
||||||
}
|
}
|
||||||
expect(shuffleSpy).toHaveBeenCalled();
|
expect(shuffleSpy).toHaveBeenCalled();
|
||||||
expect(emitSpy).toHaveBeenCalledWith(globals.CLIENT_COMMANDS.START_GAME);
|
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.START_GAME);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset all relevant game parameters, including when the game has a timer', async () => {
|
it('should reset all relevant game parameters, including when the game has a timer', async () => {
|
||||||
@@ -357,7 +358,7 @@ describe('GameManager', () => {
|
|||||||
expect(runGameSpy).toHaveBeenCalled();
|
expect(runGameSpy).toHaveBeenCalled();
|
||||||
expect(Object.keys(gameManager.activeGameRunner.timerThreads).length).toEqual(0);
|
expect(Object.keys(gameManager.activeGameRunner.timerThreads).length).toEqual(0);
|
||||||
expect(shuffleSpy).toHaveBeenCalled();
|
expect(shuffleSpy).toHaveBeenCalled();
|
||||||
expect(emitSpy).toHaveBeenCalledWith(globals.CLIENT_COMMANDS.START_GAME);
|
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.START_GAME);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset all relevant game parameters and preserve temporary moderator', async () => {
|
it('should reset all relevant game parameters and preserve temporary moderator', async () => {
|
||||||
@@ -378,7 +379,7 @@ describe('GameManager', () => {
|
|||||||
expect(person.gameRole).toBeDefined();
|
expect(person.gameRole).toBeDefined();
|
||||||
}
|
}
|
||||||
expect(shuffleSpy).toHaveBeenCalled();
|
expect(shuffleSpy).toHaveBeenCalled();
|
||||||
expect(emitSpy).toHaveBeenCalledWith(globals.CLIENT_COMMANDS.START_GAME);
|
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.START_GAME);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset all relevant game parameters and restore a temporary moderator from a dedicated moderator', async () => {
|
it('should reset all relevant game parameters and restore a temporary moderator from a dedicated moderator', async () => {
|
||||||
@@ -399,7 +400,7 @@ describe('GameManager', () => {
|
|||||||
expect(person.gameRole).toBeDefined();
|
expect(person.gameRole).toBeDefined();
|
||||||
}
|
}
|
||||||
expect(shuffleSpy).toHaveBeenCalled();
|
expect(shuffleSpy).toHaveBeenCalled();
|
||||||
expect(emitSpy).toHaveBeenCalledWith(globals.CLIENT_COMMANDS.START_GAME);
|
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.START_GAME);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reset all relevant game parameters and create a temporary mod if a dedicated mod transferred to a killed player', async () => {
|
it('should reset all relevant game parameters and create a temporary mod if a dedicated mod transferred to a killed player', async () => {
|
||||||
@@ -420,7 +421,7 @@ describe('GameManager', () => {
|
|||||||
expect(person.gameRole).toBeDefined();
|
expect(person.gameRole).toBeDefined();
|
||||||
}
|
}
|
||||||
expect(shuffleSpy).toHaveBeenCalled();
|
expect(shuffleSpy).toHaveBeenCalled();
|
||||||
expect(emitSpy).toHaveBeenCalledWith(globals.CLIENT_COMMANDS.START_GAME);
|
expect(emitSpy).toHaveBeenCalledWith(globals.EVENT_IDS.START_GAME);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user