From 6b9c2309c30bdd34b2837ec51b91ab26eb478cf9 Mon Sep 17 00:00:00 2001 From: AlecM33 Date: Thu, 21 Jul 2022 21:33:42 -0400 Subject: [PATCH] change active games to map --- server/api/GamesAPI.js | 4 +- server/model/Game.js | 1 + server/modules/ActiveGameRunner.js | 2 +- server/modules/GameManager.js | 19 +-- server/modules/SocketManager.js | 2 +- spec/unit/server/modules/GameManager_Spec.js | 122 ++++++++++--------- 6 files changed, 81 insertions(+), 69 deletions(-) diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 0ca2764..8c1344b 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -85,7 +85,7 @@ router.patch('/:code/players', function (req, res) { ) { res.status(400).send(); } else { - const game = gameManager.activeGameRunner.activeGames[req.body.accessCode]; + const game = gameManager.activeGameRunner.activeGames.get(req.body.accessCode); if (game) { const inUseCookie = gameManager.environment === globals.ENVIRONMENT.PRODUCTION ? req.body.localCookie : req.body.sessionCookie; gameManager.joinGame(game, req.body.playerName, inUseCookie).then((data) => { @@ -109,7 +109,7 @@ router.patch('/:code/restart', function (req, res) { ) { res.status(400).send(); } else { - const game = gameManager.activeGameRunner.activeGames[req.body.accessCode]; + const game = gameManager.activeGameRunner.activeGames.get(req.body.accessCode); if (game) { gameManager.restartGame(game, gameManager.namespace).then((data) => { res.status(200).send(); diff --git a/server/model/Game.js b/server/model/Game.js index 3756190..a772f27 100644 --- a/server/model/Game.js +++ b/server/model/Game.js @@ -8,6 +8,7 @@ class Game { moderator, hasDedicatedModerator, originalModeratorId, + createTime, timerParams = null ) { this.accessCode = accessCode; diff --git a/server/modules/ActiveGameRunner.js b/server/modules/ActiveGameRunner.js index 66b79f8..0a2a533 100644 --- a/server/modules/ActiveGameRunner.js +++ b/server/modules/ActiveGameRunner.js @@ -4,7 +4,7 @@ const globals = require('../config/globals'); class ActiveGameRunner { constructor (logger) { - this.activeGames = {}; + this.activeGames = new Map(); this.timerThreads = {}; this.logger = logger; } diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index b17a6c9..543a95c 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -36,7 +36,7 @@ class GameManager { if (gameParams.timerParams !== null) { gameParams.timerParams.paused = false; } - this.activeGameRunner.activeGames[newAccessCode] = new Game( + const newGame = new Game( newAccessCode, globals.STATUS.LOBBY, initializePeopleForGame(gameParams.deck, moderator, this.shuffle), @@ -45,9 +45,12 @@ class GameManager { moderator, gameParams.hasDedicatedModerator, moderator.id, + new Date().toJSON(), gameParams.timerParams ); - this.activeGameRunner.activeGames[newAccessCode].createTime = new Date().toJSON(); + + this.activeGameRunner.activeGames.set(newAccessCode, newGame); + return Promise.resolve({ accessCode: newAccessCode, cookie: moderator.cookie, environment: this.environment }); } }; @@ -146,7 +149,7 @@ class GameManager { }; checkAvailability = (code) => { - const game = this.activeGameRunner.activeGames[code.toUpperCase()]; + const game = this.activeGameRunner.activeGames.get(code.toUpperCase().trim()); if (game) { return Promise.resolve({ accessCode: code, playerCount: getGameSize(game.deck), timerParams: game.timerParams }); } else { @@ -158,7 +161,7 @@ class GameManager { const charCount = charPool.length; let codeDigits, accessCode; let attempts = 0; - while (!accessCode || (this.activeGameRunner.activeGames[accessCode] && attempts < globals.ACCESS_CODE_GENERATION_ATTEMPTS)) { + while (!accessCode || (this.activeGameRunner.activeGames.get(accessCode) && attempts < globals.ACCESS_CODE_GENERATION_ATTEMPTS)) { codeDigits = []; let iterations = globals.ACCESS_CODE_LENGTH; while (iterations > 0) { @@ -168,7 +171,7 @@ class GameManager { accessCode = codeDigits.join(''); attempts ++; } - return this.activeGameRunner.activeGames[accessCode] + return this.activeGameRunner.activeGames.get(accessCode) ? null : accessCode; }; @@ -316,7 +319,7 @@ class GameManager { }; handleRequestForGameState = async (namespace, logger, gameRunner, accessCode, personCookie, ackFn, clientSocket) => { - const game = gameRunner.activeGames[accessCode]; + const game = gameRunner.activeGames.get(accessCode); if (game) { const matchingPerson = findPersonByField(game, 'cookie', personCookie); if (matchingPerson) { @@ -340,9 +343,9 @@ class GameManager { removeClientFromLobbyIfApplicable (socket) { socket.rooms.forEach((room) => { - if (this.activeGameRunner.activeGames[room]) { + if (this.activeGameRunner.activeGames.get(room)) { this.logger.trace('disconnected socket is in a game'); - const game = this.activeGameRunner.activeGames[room]; + const game = this.activeGameRunner.activeGames.get(room); if (game.status === globals.STATUS.LOBBY) { const matchingPlayer = findPlayerBySocketId(game.people, socket.id); if (matchingPlayer) { diff --git a/server/modules/SocketManager.js b/server/modules/SocketManager.js index 0b25bb5..91d9f30 100644 --- a/server/modules/SocketManager.js +++ b/server/modules/SocketManager.js @@ -46,7 +46,7 @@ class SocketManager { registerHandlers = (namespace, socket, gameManager) => { socket.on(globals.SOCKET_EVENTS.IN_GAME_MESSAGE, async (eventId, accessCode, args, ackFn) => { - const game = gameManager.activeGameRunner.activeGames[accessCode]; + const game = gameManager.activeGameRunner.activeGames.get(accessCode); if (game) { switch (eventId) { case EVENT_IDS.FETCH_GAME_STATE: diff --git a/spec/unit/server/modules/GameManager_Spec.js b/spec/unit/server/modules/GameManager_Spec.js index 11ae7fc..58af434 100644 --- a/spec/unit/server/modules/GameManager_Spec.js +++ b/spec/unit/server/modules/GameManager_Spec.js @@ -35,7 +35,10 @@ describe('GameManager', () => { [personToTransferTo, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)], [], false, - moderator + moderator, + true, + moderator.id, + new Date().toJSON() ); gameManager.transferModeratorPowers(game, personToTransferTo, logger); @@ -53,7 +56,10 @@ describe('GameManager', () => { [new Person('2', '456', 'Jane', USER_TYPES.PLAYER)], [], false, - moderator + moderator, + true, + moderator.id, + new Date().toJSON() ); game.spectators.push(personToTransferTo); gameManager.transferModeratorPowers(game, personToTransferTo, logger); @@ -73,7 +79,10 @@ describe('GameManager', () => { [personToTransferTo, tempMod, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)], [], false, - tempMod + tempMod, + false, + tempMod.id, + new Date().toJSON() ); gameManager.transferModeratorPowers(game, personToTransferTo, logger); @@ -92,7 +101,10 @@ describe('GameManager', () => { [personToTransferTo, tempMod, new Person('2', '456', 'Jane', USER_TYPES.PLAYER)], [], false, - tempMod + tempMod, + true, + tempMod.id, + new Date().toJSON() ); gameManager.transferModeratorPowers(game, personToTransferTo, logger); @@ -107,13 +119,17 @@ describe('GameManager', () => { spyOn(namespace.in(), 'emit'); spyOn(gameManager, 'transferModeratorPowers'); const player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER); + const mod = new Person('2', '456', 'Jane', USER_TYPES.MODERATOR); const game = new Game( 'abc', globals.STATUS.IN_PROGRESS, [player], [], false, - new Person('2', '456', 'Jane', USER_TYPES.MODERATOR) + mod, + true, + mod.id, + new Date().toJSON() ); gameManager.killPlayer(game, player, namespace, logger); @@ -133,7 +149,10 @@ describe('GameManager', () => { [tempMod], [], false, - tempMod + tempMod, + true, + tempMod.id, + new Date().toJSON() ); gameManager.killPlayer(game, tempMod, namespace, logger); @@ -145,23 +164,32 @@ describe('GameManager', () => { }); describe('#handleRequestForGameState', () => { - it('should send the game state to a matching person with an active connection to the room', () => { - const player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER); - const socket = { id: 'socket1' }; - spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); - player.socketId = 'socket1'; - const gameRunner = { - activeGames: { - abc: new Game( + let gameRunner, mod, player; + + beforeEach(() => { + mod = new Person('2', '456', 'Jane', USER_TYPES.MODERATOR); + player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER); + gameRunner = { + activeGames: new Map([ + ['abc', new Game( 'abc', globals.STATUS.IN_PROGRESS, [player], [], false, - new Person('2', '456', 'Jane', USER_TYPES.MODERATOR) - ) - } + mod, + true, + mod.id, + new Date().toJSON()) + ] + ]) }; + }); + + it('should send the game state to a matching person with an active connection to the room', () => { + const socket = { id: 'socket1' }; + spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); + player.socketId = 'socket1'; spyOn(namespace.in(), 'emit'); gameManager.handleRequestForGameState( namespace, @@ -174,27 +202,14 @@ describe('GameManager', () => { ); expect(GameStateCurator.getGameStateFromPerspectiveOfPerson) - .toHaveBeenCalledWith(gameRunner.activeGames.abc, player, gameRunner, socket, logger); + .toHaveBeenCalledWith(gameRunner.activeGames.get('abc'), player, gameRunner, socket, logger); }); it('should send the game state to a matching person who reset their connection', () => { - const player = new Person('1', '123', 'Joe', USER_TYPES.PLAYER); const socket = { id: 'socket_222222', join: () => {} }; spyOn(socket, 'join'); spyOn(GameStateCurator, 'getGameStateFromPerspectiveOfPerson'); player.socketId = 'socket_111111'; - const gameRunner = { - activeGames: { - abc: new Game( - 'abc', - globals.STATUS.IN_PROGRESS, - [player], - [], - false, - new Person('2', '456', 'Jane', USER_TYPES.MODERATOR) - ) - } - }; spyOn(namespace.in(), 'emit'); gameManager.handleRequestForGameState( namespace, @@ -207,25 +222,33 @@ describe('GameManager', () => { ); expect(GameStateCurator.getGameStateFromPerspectiveOfPerson) - .toHaveBeenCalledWith(gameRunner.activeGames.abc, player, gameRunner, socket, logger); + .toHaveBeenCalledWith(gameRunner.activeGames.get('abc'), player, gameRunner, socket, logger); expect(player.socketId).toEqual(socket.id); expect(socket.join).toHaveBeenCalled(); }); }); describe('#joinGame', () => { - it('should mark the game as full when all players have been assigned', () => { - const person = new Person('1', '123', 'Placeholder', USER_TYPES.KILLED_PLAYER); - const moderator = new Person('3', '789', 'Jack', USER_TYPES.MODERATOR); - moderator.assigned = true; - const game = new Game( + let game, person, moderator; + + beforeEach(() => { + person = new Person('1', '123', 'Placeholder', USER_TYPES.KILLED_PLAYER); + moderator = new Person('3', '789', 'Jack', USER_TYPES.MODERATOR); + game = new Game( 'abc', globals.STATUS.IN_PROGRESS, [person], [], false, - moderator + moderator, + true, + moderator.id, + new Date().toJSON() ); + }); + + it('should mark the game as full when all players have been assigned', () => { + moderator.assigned = true; gameManager.joinGame(game, 'Jill', 'x'); @@ -235,27 +258,16 @@ describe('GameManager', () => { }); it('should create a spectator if the game is already full and broadcast it to the room', () => { - const person = new Person('1', '123', 'AlreadyJoined', USER_TYPES.KILLED_PLAYER); - const moderator = new Person('3', '789', 'AlreadyTheModerator', USER_TYPES.MODERATOR); moderator.assigned = true; person.assigned = true; - const game = new Game( - 'abc', - globals.STATUS.IN_PROGRESS, - [person], - [], - false, - moderator - ); game.isFull = true; - spyOn(gameManager.namespace.in(), 'emit'); gameManager.joinGame(game, 'Jane', 'x'); expect(game.isFull).toEqual(true); - expect(game.people[0].name).toEqual('AlreadyJoined'); - expect(game.moderator.name).toEqual('AlreadyTheModerator'); + expect(game.people[0].name).toEqual('Placeholder'); + expect(game.moderator.name).toEqual('Jack'); expect(game.spectators.length).toEqual(1); expect(game.spectators[0].name).toEqual('Jane'); expect(game.spectators[0].userType).toEqual(USER_TYPES.SPECTATOR); @@ -265,18 +277,14 @@ describe('GameManager', () => { describe('#generateAccessCode', () => { it('should continue to generate access codes up to the max attempts when the generated code is already in use by another game', () => { - gameManager.activeGameRunner.activeGames = { - AAAA: {} - }; + gameManager.activeGameRunner.activeGames = new Map([['AAAA', {}]]); const accessCode = gameManager.generateAccessCode(['A']); expect(accessCode).toEqual(null); // we might the max generation attempts of 50. }); it('should generate and return a unique access code', () => { - gameManager.activeGameRunner.activeGames = { - AAAA: {} - }; + gameManager.activeGameRunner.activeGames = new Map([['AAAA', {}]]); const accessCode = gameManager.generateAccessCode(['B']); expect(accessCode).toEqual('BBBB');