diff --git a/client/src/config/globals.js b/client/src/config/globals.js index 5b374b8..6a46d43 100644 --- a/client/src/config/globals.js +++ b/client/src/config/globals.js @@ -9,7 +9,6 @@ export const globals = { ACCESS_CODE_CHAR_POOL: 'abcdefghijklmnopqrstuvwxyz0123456789', COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', - GET_ENVIRONMENT: 'getEnvironment', START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', RESUME_TIMER: 'resumeTimer', @@ -18,8 +17,7 @@ export const globals = { REVEAL_PLAYER: 'revealPlayer', TRANSFER_MODERATOR: 'transferModerator', CHANGE_NAME: 'changeName', - END_GAME: 'endGame', - FETCH_IN_PROGRESS_STATE: 'fetchInitialInProgressState' + END_GAME: 'endGame' }, STATUS: { LOBBY: 'lobby', diff --git a/client/src/modules/GameCreationStepManager.js b/client/src/modules/GameCreationStepManager.js index a425727..0e1c078 100644 --- a/client/src/modules/GameCreationStepManager.js +++ b/client/src/modules/GameCreationStepManager.js @@ -105,6 +105,10 @@ export class GameCreationStepManager { window.location = ('/game/' + res.content); } }).catch((e) => { + let button = document.getElementById("create-game"); + button.innerText = "Create Game"; + button.classList.remove('submitted'); + button.addEventListener('click', this.steps["4"].forwardHandler); if (e.status === 429) { toast('You\'ve sent this request too many times.', 'error', true, true, 6); } @@ -395,6 +399,9 @@ function showButtons (back, forward, forwardHandler, backHandler, builtGame = nu createButton.setAttribute('id', 'create-game'); createButton.classList.add('app-button'); createButton.addEventListener('click', () => { + createButton.removeEventListener('click', forwardHandler); + createButton.classList.add('submitted'); + createButton.innerText = 'Creating...' forwardHandler( builtGame.deck.filter((card) => card.quantity > 0), builtGame.hasTimer, diff --git a/client/src/modules/GameStateRenderer.js b/client/src/modules/GameStateRenderer.js index 188741a..474adfb 100644 --- a/client/src/modules/GameStateRenderer.js +++ b/client/src/modules/GameStateRenderer.js @@ -254,7 +254,7 @@ function renderPotentialMods (gameState, group, transferModHandlers, socket) { if ((member.out || member.userType === globals.USER_TYPES.SPECTATOR) && !(member.id === gameState.client.id)) { const container = document.createElement('div'); container.classList.add('potential-moderator'); - container.setAttribute("tabindex", "0"); + container.setAttribute('tabindex', '0'); container.dataset.pointer = member.id; container.innerText = member.name; transferModHandlers[member.id] = (e) => { diff --git a/client/src/scripts/game.js b/client/src/scripts/game.js index 9c1ac9a..1b9ce22 100644 --- a/client/src/scripts/game.js +++ b/client/src/scripts/game.js @@ -8,82 +8,105 @@ import { ModalManager } from '../modules/ModalManager.js'; import { stateBucket } from '../modules/StateBucket.js'; import { io } from 'socket.io-client'; import { injectNavbar } from '../modules/Navbar.js'; +import { XHRUtility } from '../modules/XHRUtility.js'; const game = () => { injectNavbar(); - const timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); - const socket = io('/in-game'); - socket.on('disconnect', () => { - toast('Disconnected. Attempting reconnect...', 'error', true, false); - }); - socket.on('connect', () => { - console.log('connect event fired'); - socket.emit(globals.COMMANDS.GET_ENVIRONMENT, function (returnedEnvironment) { - prepareGamePage(returnedEnvironment, socket, timerWorker); + XHRUtility.xhr( + '/api/games/environment', + 'GET', + null, + null + ) + .then((res) => { + joinGame(res); + }).catch((res) => { + toast(res.content, 'error', true); }); - }); }; -function prepareGamePage (environment, socket, timerWorker) { - let userId = UserUtility.validateAnonUserSignature(environment); +function joinGame (environmentResponse) { + let cookie = UserUtility.validateAnonUserSignature(environmentResponse.content); const splitUrl = window.location.href.split('/game/'); const accessCode = splitUrl[1]; if (/^[a-zA-Z0-9]+$/.test(accessCode) && accessCode.length === globals.ACCESS_CODE_LENGTH) { - socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, userId, function (gameState) { - stateBucket.currentGameState = gameState; - document.querySelector('.spinner-container')?.remove(); - document.querySelector('.spinner-background')?.remove(); - - if (gameState === null) { - window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); - } else { - document.getElementById('game-content').innerHTML = templates.INITIAL_GAME_DOM; - toast('You are connected.', 'success', true, true, 2); - userId = gameState.client.cookie; - UserUtility.setAnonymousUserId(userId, environment); + XHRUtility.xhr( + '/api/games/' + accessCode + '/players', + 'PATCH', + null, + JSON.stringify({ cookie: cookie }) + ) + .then((res) => { + cookie = res.content; + UserUtility.setAnonymousUserId(res.content, environmentResponse.content); + const timerWorker = new Worker(new URL('../modules/Timer.js', import.meta.url)); + const socket = io('/in-game'); + const gameTimerManager = new GameTimerManager(stateBucket, socket); const gameStateRenderer = new GameStateRenderer(stateBucket, socket); - let gameTimerManager; - if (stateBucket.currentGameState.timerParams) { - gameTimerManager = new GameTimerManager(stateBucket, socket); + socket.on('disconnect', () => { + toast('Disconnected. Attempting reconnect...', 'error', true, false); + }); + socket.on('connect', () => { + prepareGamePage( + environmentResponse.content, + socket, + timerWorker, + cookie, + accessCode, + gameStateRenderer, + gameTimerManager + ); + }); + setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager); + }).catch((res) => { + if (res.status === 404) { + window.location = '/not-found?reason=' + encodeURIComponent('game-not-found'); + } else if (res.status >= 500) { + toast( + 'The server is experiencing problems. Please try again later', + 'error', + true + ); } - initializeGame(stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager); - - if (!gameState.client.hasEnteredName) { - document.getElementById('prompt').innerHTML = templates.NAME_CHANGE_MODAL; - document.getElementById('change-name-form').onsubmit = (e) => { - e.preventDefault(); - const name = document.getElementById('player-new-name').value; - if (validateName(name)) { - socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { - name: name, - personId: gameState.client.id - }, (result) => { - switch (result) { - case 'taken': - toast('This name is already taken.', 'error', true, true, 8); - break; - case 'changed': - ModalManager.dispelModal('change-name-modal', 'change-name-modal-background'); - toast('Name set.', 'success', true, true, 5); - propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id); - processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); - } - }); - } else { - toast('Name must be between 1 and 30 characters.', 'error', true, true, 8); - } - }; - } - } - }); - } else { - window.location = '/not-found?reason=' + encodeURIComponent('invalid-access-code'); + }); } } -function initializeGame (stateBucket, socket, timerWorker, userId, gameStateRenderer, gameTimerManager) { - setClientSocketHandlers(stateBucket, gameStateRenderer, socket, timerWorker, gameTimerManager); - processGameState(stateBucket.currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker); +function prepareGamePage (environment, socket, timerWorker, cookie, accessCode, gameStateRenderer, gameTimerManager) { + socket.emit(globals.COMMANDS.FETCH_GAME_STATE, accessCode, cookie, function (gameState) { + stateBucket.currentGameState = gameState; + document.querySelector('.spinner-container')?.remove(); + document.querySelector('.spinner-background')?.remove(); + document.getElementById('game-content').innerHTML = templates.INITIAL_GAME_DOM; + toast('You are connected.', 'success', true, true, 2); + processGameState(stateBucket.currentGameState, cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); + if (!gameState.client.hasEnteredName) { + document.getElementById('prompt').innerHTML = templates.NAME_CHANGE_MODAL; + document.getElementById('change-name-form').onsubmit = (e) => { + e.preventDefault(); + const name = document.getElementById('player-new-name').value; + if (validateName(name)) { + socket.emit(globals.COMMANDS.CHANGE_NAME, gameState.accessCode, { + name: name, + personId: gameState.client.id + }, (result) => { + switch (result) { + case 'taken': + toast('This name is already taken.', 'error', true, true, 8); + break; + case 'changed': + ModalManager.dispelModal('change-name-modal', 'change-name-modal-background'); + toast('Name set.', 'success', true, true, 5); + propagateNameChange(stateBucket.currentGameState, name, stateBucket.currentGameState.client.id); + processGameState(stateBucket.currentGameState, cookie, socket, gameStateRenderer, gameTimerManager, timerWorker); + } + }); + } else { + toast('Name must be between 1 and 30 characters.', 'error', true, true, 8); + } + }; + } + }); } function processGameState (currentGameState, userId, socket, gameStateRenderer, gameTimerManager, timerWorker, refreshPrompt = true) { @@ -163,79 +186,72 @@ function displayClientInfo (name, userType) { } function setClientSocketHandlers (stateBucket, 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, true, 3); - stateBucket.currentGameState.people.push(player); - stateBucket.currentGameState.isFull = gameIsFull; + socket.on(globals.EVENTS.PLAYER_JOINED, (player, gameIsFull) => { + toast(player.name + ' joined!', 'success', false, true, 3); + stateBucket.currentGameState.people.push(player); + stateBucket.currentGameState.isFull = gameIsFull; + gameStateRenderer.renderLobbyPlayers(); + if ( + gameIsFull + && ( + stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR + || stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR + ) + ) { + displayStartGamePromptForModerators(stateBucket.currentGameState, gameStateRenderer); + } + }); + + socket.on(globals.EVENTS.PLAYER_LEFT, (player) => { + removeStartGameFunctionalityIfPresent(gameStateRenderer); + toast(player.name + ' has left!', 'error', false, true, 3); + const index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id); + if (index >= 0) { + stateBucket.currentGameState.people.splice( + index, + 1 + ); gameStateRenderer.renderLobbyPlayers(); - if ( - gameIsFull - && ( - stateBucket.currentGameState.client.userType === globals.USER_TYPES.MODERATOR - || stateBucket.currentGameState.client.userType === globals.USER_TYPES.TEMPORARY_MODERATOR - ) - ) { - displayStartGamePromptForModerators(stateBucket.currentGameState, gameStateRenderer); - } - }); - } + } + }); - if (!socket.hasListeners(globals.EVENTS.PLAYER_LEFT)) { - socket.on(globals.EVENTS.PLAYER_LEFT, (player) => { - removeStartGameFunctionalityIfPresent(gameStateRenderer); - toast(player.name + ' has left!', 'error', false, true, 3); - const index = stateBucket.currentGameState.people.findIndex(person => person.id === player.id); - if (index >= 0) { - stateBucket.currentGameState.people.splice( - index, - 1 + socket.on(globals.EVENTS.START_GAME, () => { + socket.emit( + globals.COMMANDS.FETCH_GAME_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker ); - gameStateRenderer.renderLobbyPlayers(); } - }); - } + ); + }); - if (!socket.hasListeners(globals.EVENTS.START_GAME)) { - socket.on(globals.EVENTS.START_GAME, () => { - socket.emit( - globals.COMMANDS.FETCH_IN_PROGRESS_STATE, - stateBucket.currentGameState.accessCode, - stateBucket.currentGameState.client.cookie, - function (gameState) { - stateBucket.currentGameState = gameState; - processGameState( - stateBucket.currentGameState, - gameState.client.cookie, - socket, - gameStateRenderer, - gameTimerManager, - timerWorker - ); - } - ); - }); - } - if (!socket.hasListeners(globals.EVENTS.SYNC_GAME_STATE)) { - socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { - socket.emit( - globals.COMMANDS.FETCH_IN_PROGRESS_STATE, - stateBucket.currentGameState.accessCode, - stateBucket.currentGameState.client.cookie, - function (gameState) { - stateBucket.currentGameState = gameState; - processGameState( - stateBucket.currentGameState, - gameState.client.cookie, - socket, - gameStateRenderer, - gameTimerManager, - timerWorker - ); - } - ); - }); - } + socket.on(globals.EVENTS.SYNC_GAME_STATE, () => { + socket.emit( + globals.COMMANDS.FETCH_GAME_STATE, + stateBucket.currentGameState.accessCode, + stateBucket.currentGameState.client.cookie, + function (gameState) { + stateBucket.currentGameState = gameState; + processGameState( + stateBucket.currentGameState, + gameState.client.cookie, + socket, + gameStateRenderer, + gameTimerManager, + timerWorker + ); + } + ); + }); if (timerWorker && gameTimerManager) { gameTimerManager.attachTimerSocketListeners(socket, timerWorker, gameStateRenderer); diff --git a/client/src/scripts/home.js b/client/src/scripts/home.js index 15cf119..8e2733c 100644 --- a/client/src/scripts/home.js +++ b/client/src/scripts/home.js @@ -21,7 +21,7 @@ function roomCodeIsValid (code) { function attemptToJoinGame (code) { XHRUtility.xhr( - '/api/games/availability/' + code.toLowerCase().trim(), + '/api/games/' + code.toLowerCase().trim() + 'availability', 'GET', null, null diff --git a/client/src/styles/GLOBAL.css b/client/src/styles/GLOBAL.css index 052a35c..cd568a3 100644 --- a/client/src/styles/GLOBAL.css +++ b/client/src/styles/GLOBAL.css @@ -11,10 +11,6 @@ th, thead, tr, tt, u, ul, var { border: 0; background: transparent; } -@font-face { - font-family: 'signika-negative'; - src: url("../webfonts/Diavlo_LIGHT_II_37.woff2") format("woff2"); -} @font-face { font-family: 'signika-negative'; @@ -164,6 +160,11 @@ button { border: 2px solid #8a1c1c !important; } +.submitted { + filter: opacity(0.5); + pointer-events: none; +} + .container { padding: 5px; border-radius: 3px; diff --git a/client/src/views/404.html b/client/src/views/404.html index 5e96291..358c0ce 100644 --- a/client/src/views/404.html +++ b/client/src/views/404.html @@ -1,68 +1,68 @@ - - - - Create A Game - - - - - - - - - - - - - - - -
- - -

404

-

The game or other resource that you are looking for could not be found, or you don't have permission to access it. - If this error is unexpected, the application may have restarted.

-
- - - + @media(min-width: 701px) { + h1 { + font-size: 75px; + } + } + @media(max-width: 700px) { + h1 { + font-size: 40px; + } + } + + + +
+ +
+

404

+

The game or other resource that you are looking for could not be found, or you don't have permission to access it. + If this error is unexpected, the application may have restarted.

+
+ + diff --git a/client/src/views/game.html b/client/src/views/game.html index 89ab19a..8f2304c 100644 --- a/client/src/views/game.html +++ b/client/src/views/game.html @@ -16,8 +16,7 @@ - - +
diff --git a/package.json b/package.json index 1235b00..3a84453 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "bundle": "webpack --config client/webpack/webpack-prod.config.js", "prestart": "npm run bundle", + "gcp-build": "webpack --config client/webpack/webpack-prod.config.js", "build:dev": "webpack --watch --config client/webpack/webpack-dev.config.js --mode=development", "start:dev": "NODE_ENV=development nodemon server/main.js", "start:dev:no-hot-reload": "NODE_ENV=development && node server/main.js", diff --git a/server/api/GamesAPI.js b/server/api/GamesAPI.js index 206f745..d76789d 100644 --- a/server/api/GamesAPI.js +++ b/server/api/GamesAPI.js @@ -5,6 +5,7 @@ const logger = require('../modules/Logger')(debugMode); const GameManager = require('../modules/GameManager.js'); const rateLimit = require('express-rate-limit').default; const globals = require('../config/globals'); +const cors = require('cors'); const gameManager = new GameManager().getInstance(); @@ -15,6 +16,20 @@ const apiLimiter = rateLimit({ legacyHeaders: false }); +const corsOptions = process.env.NODE_ENV.trim() === 'development' + ? { + origin: '*', + optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204 + } + : { + origin: 'https://playwerewolf.uk.r.appspot.com', + optionsSuccessStatus: 200 + } + +router.use(cors(corsOptions)); +router.options('/:code/players', cors(corsOptions)) // enable pre-flight request for DELETE + + if (process.env.NODE_ENV.trim() === 'production') { // in prod, limit clients to creating 5 games per 10 minutes. router.use('/create', apiLimiter); } @@ -35,9 +50,9 @@ router.post('/create', function (req, res) { }); }); -router.get('/availability/:code', function (req, res) { - const joinGamePromise = gameManager.joinGame(req.params.code); - joinGamePromise.then((result) => { +router.get('/:code/availability', function (req, res) { + const availabilityPromise = gameManager.checkAvailability(req.params.code); + availabilityPromise.then((result) => { if (result === 404) { res.status(404).send(); } else if (result instanceof Error) { @@ -51,4 +66,24 @@ router.get('/availability/:code', function (req, res) { }); }); +router.patch('/:code/players', function (req, res) { + if ( + req.body === null + || req.body.cookie === null + || (typeof req.body.cookie !== 'string' && req.body.cookie !== false) + || (req.body.cookie.length !== globals.USER_SIGNATURE_LENGTH && req.body.cookie !== false) + ) { + res.status(400).send(); + } + gameManager.joinGame(req.body.cookie, req.params.code).then((data) => { + res.status(200).send(data); + }).catch((code) => { + res.status(code).send(); + }); +}); + +router.get('/environment', function (req, res) { + res.status(200).send(gameManager.environment); +}); + module.exports = router; diff --git a/server/config/globals.js b/server/config/globals.js index 59c26d0..bfcdf3b 100644 --- a/server/config/globals.js +++ b/server/config/globals.js @@ -5,7 +5,6 @@ const globals = { STALE_GAME_HOURS: 12, CLIENT_COMMANDS: { FETCH_GAME_STATE: 'fetchGameState', - GET_ENVIRONMENT: 'getEnvironment', START_GAME: 'startGame', PAUSE_TIMER: 'pauseTimer', RESUME_TIMER: 'resumeTimer', @@ -14,8 +13,7 @@ const globals = { REVEAL_PLAYER: 'revealPlayer', TRANSFER_MODERATOR: 'transferModerator', CHANGE_NAME: 'changeName', - END_GAME: 'endGame', - FETCH_IN_PROGRESS_STATE: 'fetchInitialInProgressState' + END_GAME: 'endGame' }, MESSAGES: { ENTER_NAME: 'Client must enter name.' diff --git a/server/main.js b/server/main.js index 8d6284c..7b067e8 100644 --- a/server/main.js +++ b/server/main.js @@ -24,14 +24,6 @@ app.set('port', args.port); const inGameSocketServer = ServerBootstrapper.createSocketServer(main, app, args.port); -inGameSocketServer.on('connection', function (socket) { - socket.on('disconnecting', (reason) => { - logger.trace('client socket disconnecting because: ' + reason); - gameManager.removeClientFromLobbyIfApplicable(socket); - }); - gameManager.addGameSocketHandlers(inGameSocketServer, socket); -}); - let gameManager; /* Instantiate the singleton game manager */ @@ -41,6 +33,16 @@ if (process.env.NODE_ENV.trim() === 'development') { gameManager = new GameManager(logger, globals.ENVIRONMENT.PRODUCTION).getInstance(); } +gameManager.namespace = inGameSocketServer; + +inGameSocketServer.on('connection', function (socket) { + socket.on('disconnecting', (reason) => { + logger.trace('client socket disconnecting because: ' + reason); + gameManager.removeClientFromLobbyIfApplicable(socket); + }); + gameManager.addGameSocketHandlers(inGameSocketServer, socket); +}); + /* api endpoints */ const games = require('./api/GamesAPI'); app.use('/api/games', games); diff --git a/server/modules/GameManager.js b/server/modules/GameManager.js index 931a8d7..0e1b745 100644 --- a/server/modules/GameManager.js +++ b/server/modules/GameManager.js @@ -14,7 +14,6 @@ class GameManager { } addGameSocketHandlers = (namespace, socket) => { - this.namespace = namespace; socket.on(globals.CLIENT_COMMANDS.FETCH_GAME_STATE, (accessCode, personId, ackFn) => { this.logger.trace('request for game state for accessCode: ' + accessCode + ' from socket: ' + socket.id + ' with cookie: ' + personId); this.handleRequestForGameState( @@ -28,27 +27,6 @@ class GameManager { ); }); - /* this event handler will call handleRequestForGameState() with the 'handleNoMatch' arg as false - only - connections that match a participant in the game at that time will have the game state sent to them. - */ - socket.on(globals.CLIENT_COMMANDS.FETCH_IN_PROGRESS_STATE, (accessCode, personId, ackFn) => { - this.logger.trace('request for game state for accessCode ' + accessCode + ', person ' + personId); - this.handleRequestForGameState( - this.namespace, - this.logger, - this.activeGameRunner, - accessCode, - personId, - ackFn, - socket, - false - ); - }); - - socket.on(globals.CLIENT_COMMANDS.GET_ENVIRONMENT, (ackFn) => { - ackFn(this.environment); - }); - socket.on(globals.CLIENT_COMMANDS.START_GAME, (accessCode) => { const game = this.activeGameRunner.activeGames[accessCode]; if (game && game.isFull) { @@ -152,7 +130,7 @@ class GameManager { socket.on(globals.CLIENT_COMMANDS.CHANGE_NAME, (accessCode, data, ackFn) => { const game = this.activeGameRunner.activeGames[accessCode]; if (game) { - const person = findPersonById(game, data.personId); + const person = findPersonByField(game, 'id', data.personId); if (person) { if (!isNameTaken(game, data.name)) { ackFn('changed'); @@ -207,7 +185,7 @@ class GameManager { } }; - joinGame = (code) => { + checkAvailability = (code) => { const game = this.activeGameRunner.activeGames[code]; if (game) { const unassignedPerson = game.people.find((person) => person.assigned === false); @@ -275,16 +253,47 @@ class GameManager { } }; - handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket, handleNoMatch = true) => { + joinGame = (cookie, accessCode) => { + const game = this.activeGameRunner.activeGames[accessCode]; + if (game) { + const person = findPersonByField(game, 'cookie', cookie); + if (person) { + return Promise.resolve(person.cookie); + } else { + const unassignedPerson = game.moderator.assigned === false + ? game.moderator + : game.people.find((person) => person.assigned === false); + if (unassignedPerson) { + this.logger.trace('request from client to join game. Assigning: ' + unassignedPerson.name); + unassignedPerson.assigned = true; + game.isFull = isGameFull(game); + this.namespace.in(game.accessCode).emit( + globals.EVENTS.PLAYER_JOINED, + GameStateCurator.mapPerson(unassignedPerson), + game.isFull + ); + return Promise.resolve(unassignedPerson.cookie); + } else { // if the game is full, make them a spectator. + const spectator = new Person( + createRandomId(), + createRandomId(), + UsernameGenerator.generate(), + globals.USER_TYPES.SPECTATOR + ); + this.logger.trace('new spectator: ' + spectator.name); + game.spectators.push(spectator); + return Promise.resolve(spectator.cookie); + } + } + } else { + return Promise.reject(404); + } + }; + + handleRequestForGameState = (namespace, logger, gameRunner, accessCode, personCookie, ackFn, socket) => { const game = gameRunner.activeGames[accessCode]; if (game) { - let matchingPerson = game.people.find((person) => person.cookie === personCookie); - if (!matchingPerson) { - matchingPerson = game.spectators.find((spectator) => spectator.cookie === personCookie); - } - if (!matchingPerson && game.moderator.cookie === personCookie) { - matchingPerson = game.moderator; - } + const matchingPerson = findPersonByField(game, 'cookie', personCookie); if (matchingPerson) { if (matchingPerson.socketId === socket.id) { logger.trace('matching person found with an established connection to the room: ' + matchingPerson.name); @@ -295,8 +304,8 @@ class GameManager { matchingPerson.socketId = socket.id; ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson, gameRunner, socket, logger)); } - } else if (handleNoMatch) { - this.handleRequestFromNonMatchingPerson(game, socket, gameRunner, ackFn, logger); + } else { + rejectClientRequestForGameState(ackFn); } } else { rejectClientRequestForGameState(ackFn); @@ -304,43 +313,6 @@ class GameManager { } }; - handleRequestFromNonMatchingPerson = (game, socket, gameRunner, ackFn, logger) => { - const personWithMatchingSocketId = findPersonWithMatchingSocketId(game, 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, gameRunner, socket, logger)); - } else { - const unassignedPerson = game.moderator.assigned === false - ? game.moderator - : game.people.find((person) => person.assigned === false); - if (unassignedPerson) { - logger.trace('completely new person with a first connection to the room: ' + unassignedPerson.name); - unassignedPerson.assigned = true; - unassignedPerson.socketId = socket.id; - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, unassignedPerson, gameRunner, socket, logger)); - const isFull = isGameFull(game); - game.isFull = isFull; - socket.to(game.accessCode).emit( - globals.EVENTS.PLAYER_JOINED, - GameStateCurator.mapPerson(unassignedPerson), - isFull - ); - } else { // if the game is full, make them a spectator. - const spectator = new Person( - createRandomId(), - createRandomId(), - UsernameGenerator.generate(), - globals.USER_TYPES.SPECTATOR - ); - spectator.socketId = socket.id; - logger.trace('new spectator: ' + spectator.name); - game.spectators.push(spectator); - ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, spectator, gameRunner, socket, logger)); - } - socket.join(game.accessCode); - } - }; - removeClientFromLobbyIfApplicable (socket) { socket.rooms.forEach((room) => { if (this.activeGameRunner.activeGames[room]) { @@ -442,17 +414,6 @@ function rejectClientRequestForGameState (acknowledgementFunction) { return acknowledgementFunction(null); } -function findPersonWithMatchingSocketId (game, socketId) { - let person = game.people.find((person) => person.socketId === socketId); - if (!person) { - person = game.spectators.find((spectator) => spectator.socketId === socketId); - } - if (!person && game.moderator.socketId === socketId) { - person = game.moderator; - } - return person; -} - function findPlayerBySocketId (people, socketId) { return people.find((person) => person.socketId === socketId && person.userType === globals.USER_TYPES.PLAYER); } @@ -461,16 +422,16 @@ function isGameFull (game) { return game.moderator.assigned === true && !game.people.find((person) => person.assigned === false); } -function findPersonById (game, id) { +function findPersonByField (game, fieldName, value) { let person; - if (id === game.moderator.id) { + if (value === game.moderator[fieldName]) { person = game.moderator; } if (!person) { - person = game.people.find((person) => person.id === id); + person = game.people.find((person) => person[fieldName] === value); } if (!person) { - person = game.spectators.find((spectator) => spectator.id === id); + person = game.spectators.find((spectator) => spectator[fieldName] === value); } return person; } diff --git a/server/modules/ServerBootstrapper.js b/server/modules/ServerBootstrapper.js index e492ca4..fdaaec6 100644 --- a/server/modules/ServerBootstrapper.js +++ b/server/modules/ServerBootstrapper.js @@ -65,15 +65,11 @@ const ServerBootstrapper = { let io; if (process.env.NODE_ENV.trim() === 'development') { io = require('socket.io')(main, { - cors: { origin: 'http://localhost:' + port }, - pingTimeout: 5000, - pingInterval: 5000 + cors: { origin: 'http://localhost:' + port } }); } else { io = require('socket.io')(main, { - cors: { origin: 'https://playwerewolf.uk.r.appspot.com' }, - pingTimeout: 5000, - pingInterval: 5000 + cors: { origin: 'https://playwerewolf.uk.r.appspot.com' } }); }