const globals = require('../config/globals'); const GameStateCurator = require('./GameStateCurator'); const EVENT_IDS = globals.EVENT_IDS; const Events = [ { id: EVENT_IDS.PLAYER_JOINED, stateChange: async (game, socketArgs, vars) => { const toBeAssignedIndex = game.people.findIndex( (person) => person.id === socketArgs.id && person.assigned === false ); if (toBeAssignedIndex >= 0) { game.people[toBeAssignedIndex] = socketArgs; game.isFull = vars.gameManager.isGameFull(game); } }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode).emit( globals.EVENTS.PLAYER_JOINED, GameStateCurator.mapPerson(socketArgs), game.isFull ); } }, { id: EVENT_IDS.ADD_SPECTATOR, stateChange: async (game, socketArgs, vars) => { game.people.push(socketArgs); }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode).emit( globals.EVENT_IDS.ADD_SPECTATOR, GameStateCurator.mapPerson(socketArgs) ); } }, { id: EVENT_IDS.FETCH_GAME_STATE, stateChange: async (game, socketArgs, vars) => { const matchingPerson = vars.gameManager.findPersonByField(game, 'cookie', socketArgs.personId); if (matchingPerson && matchingPerson.socketId !== vars.socketId) { matchingPerson.socketId = vars.socketId; vars.gameManager.namespace.sockets.get(vars.socketId)?.join(game.accessCode); } }, communicate: async (game, socketArgs, vars) => { if (!vars.ackFn) return; const matchingPerson = vars.gameManager.findPersonByField(game, 'cookie', socketArgs.personId); if (matchingPerson && vars.gameManager.namespace.sockets.get(matchingPerson.socketId)) { vars.ackFn(GameStateCurator.getGameStateFromPerspectiveOfPerson(game, matchingPerson)); } else { vars.ackFn(null); } } }, // { // id: EVENT_IDS.UPDATE_SOCKET, // stateChange: (game, socketArgs, vars) => { // const matchingPerson = vars.gameManager.findPersonByField(game, 'id', socketArgs.personId); // if (matchingPerson) { // matchingPerson.socketId = socketArgs.socketId; // } // } // } { id: EVENT_IDS.SYNC_GAME_STATE, stateChange: async (game, socketArgs, vars) => {}, communicate: async (game, socketArgs, vars) => { const matchingPerson = vars.gameManager.findPersonByField(game, 'id', socketArgs.personId); if (matchingPerson && vars.gameManager.namespace.sockets.get(matchingPerson.socketId)) { vars.gameManager.namespace.to(matchingPerson.socketId).emit(globals.EVENTS.SYNC_GAME_STATE); } } }, { id: EVENT_IDS.START_GAME, stateChange: async (game, socketArgs, vars) => { if (game.isFull) { game.status = globals.STATUS.IN_PROGRESS; if (game.hasTimer) { game.timerParams.paused = true; await vars.activeGameRunner.runGame(game, vars.gameManager.namespace, vars.socketManager, vars.gameManager); } } }, communicate: async (game, socketArgs, vars) => { if (vars.ackFn) { vars.ackFn(); } vars.gameManager.namespace.in(game.accessCode).emit(globals.EVENT_IDS.START_GAME); } }, { id: EVENT_IDS.KILL_PLAYER, stateChange: async (game, socketArgs, vars) => { const person = game.people.find((person) => person.id === socketArgs.personId); if (person && !person.out) { person.userType = globals.USER_TYPES.KILLED_PLAYER; person.out = true; person.killed = true; } }, communicate: async (game, socketArgs, vars) => { const person = game.people.find((person) => person.id === socketArgs.personId); if (person) { vars.gameManager.namespace.in(game.accessCode).emit(globals.EVENT_IDS.KILL_PLAYER, person.id); } } }, { id: EVENT_IDS.REVEAL_PLAYER, stateChange: async (game, socketArgs, vars) => { const person = game.people.find((person) => person.id === socketArgs.personId); if (person && !person.revealed) { person.revealed = true; } }, communicate: async (game, socketArgs, vars) => { const person = game.people.find((person) => person.id === socketArgs.personId); if (person) { vars.gameManager.namespace.in(game.accessCode).emit( globals.EVENT_IDS.REVEAL_PLAYER, { id: person.id, gameRole: person.gameRole, alignment: person.alignment } ); } } }, { id: EVENT_IDS.END_GAME, stateChange: async (game, socketArgs, vars) => { 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(); // } for (const person of game.people) { person.revealed = true; } }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode) .emit(globals.EVENT_IDS.END_GAME, GameStateCurator.mapPeopleForModerator(game.people)); if (vars.ackFn) { vars.ackFn(); } } }, { id: EVENT_IDS.TRANSFER_MODERATOR, stateChange: async (game, socketArgs, vars) => { const currentModerator = vars.gameManager.findPersonByField(game, 'id', game.currentModeratorId); const toTransferTo = vars.gameManager.findPersonByField(game, 'id', socketArgs.personId); if (currentModerator) { if (currentModerator.gameRole) { currentModerator.userType = globals.USER_TYPES.KILLED_PLAYER; } else { currentModerator.userType = globals.USER_TYPES.SPECTATOR; } game.previousModeratorId = currentModerator.id; } if (toTransferTo) { toTransferTo.userType = globals.USER_TYPES.MODERATOR; game.currentModeratorId = toTransferTo.id; } }, communicate: async (game, socketArgs, vars) => { if (vars.ackFn) { vars.ackFn(); } vars.gameManager.namespace.to(game.accessCode).emit(globals.EVENT_IDS.SYNC_GAME_STATE); } }, { id: EVENT_IDS.ASSIGN_DEDICATED_MOD, stateChange: async (game, socketArgs, vars) => { const currentModerator = vars.gameManager.findPersonByField(game, 'id', game.currentModeratorId); const toTransferTo = vars.gameManager.findPersonByField(game, 'id', socketArgs.personId); if (currentModerator && toTransferTo) { if (currentModerator.id !== toTransferTo.id) { currentModerator.userType = globals.USER_TYPES.PLAYER; } toTransferTo.userType = globals.USER_TYPES.MODERATOR; toTransferTo.out = true; toTransferTo.killed = true; game.previousModeratorId = currentModerator.id; game.currentModeratorId = toTransferTo.id; } }, communicate: async (game, socketArgs, vars) => { const moderator = vars.gameManager.findPersonByField(game, 'id', game.currentModeratorId); const moderatorSocket = vars.gameManager.namespace.sockets.get(moderator?.socketId); if (moderator && moderatorSocket) { vars.gameManager.namespace.to(moderator.socketId).emit(globals.EVENTS.SYNC_GAME_STATE); moderatorSocket.to(game.accessCode).emit(globals.EVENT_IDS.KILL_PLAYER, game.previousModeratorId); } else { vars.gameManager.namespace.in(game.accessCode).emit(globals.EVENT_IDS.KILL_PLAYER, game.currentModeratorId); } const previousModerator = vars.gameManager.findPersonByField(game, 'id', game.previousModeratorId); if (previousModerator && previousModerator.id !== moderator.id && vars.gameManager.namespace.sockets.get(previousModerator.socketId)) { vars.gameManager.namespace.to(previousModerator.socketId).emit(globals.EVENTS.SYNC_GAME_STATE); } } }, { id: EVENT_IDS.RESTART_GAME, stateChange: async (game, socketArgs, vars) => {}, communicate: async (game, socketArgs, vars) => { if (vars.ackFn) { vars.ackFn(); } vars.gameManager.namespace.in(game.accessCode).emit(globals.EVENT_IDS.RESTART_GAME); } }, { id: EVENT_IDS.GET_TIME_REMAINING, stateChange: async (game, socketArgs, vars) => {}, communicate: async (game, socketArgs, vars) => { const thread = vars.activeGameRunner.timerThreads[game.accessCode]; if (thread && (!thread.killed && thread.exitCode === null)) { thread.send({ command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, accessCode: game.accessCode, socketId: vars.socketId, logLevel: vars.logger.logLevel }); } else if (thread) { console.log(game.timerParams); if (game.timerParams && game.timerParams.timeRemaining === 0) { vars.gameManager.namespace.to(vars.socketId) .emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused); await vars.socketManager.publisher.publish( globals.REDIS_CHANNELS.ACTIVE_GAME_STREAM, game.accessCode + ';' + globals.EVENT_IDS.SHARE_TIME_REMAINING + ';' + JSON.stringify({ socketId: vars.socketId, timeRemaining: game.timerParams.timeRemaining, paused: game.timerParams.paused }) + ';' + vars.instanceId ); } } else { // we need to consult another container for the timer data await vars.socketManager.publisher?.publish( globals.REDIS_CHANNELS.ACTIVE_GAME_STREAM, game.accessCode + ';' + globals.EVENT_IDS.SOURCE_TIME_REMAINING + ';' + JSON.stringify({ socketId: vars.socketId }) + ';' + vars.instanceId ); } } }, { /* unlike the GET_TIME_REMAINING event, this event is a request from another instance for timer data. In response * to this event, this instance will check if it is home to a particular timer thread. */ id: EVENT_IDS.SOURCE_TIME_REMAINING, stateChange: async (game, socketArgs, vars) => {}, communicate: async (game, socketArgs, vars) => { const thread = vars.activeGameRunner.timerThreads[game.accessCode]; if (thread && (!thread.killed && thread.exitCode === null)) { thread.send({ command: globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, accessCode: game.accessCode, socketId: socketArgs.socketId, logLevel: vars.logger.logLevel }); } else if (thread) { if (game.timerParams && game.timerParams.timeRemaining === 0) { vars.gameManager.namespace.to(vars.socketId) .emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, game.timerParams.timeRemaining, game.timerParams.paused); } await vars.socketManager.publisher.publish( globals.REDIS_CHANNELS.ACTIVE_GAME_STREAM, game.accessCode + ';' + globals.EVENT_IDS.SHARE_TIME_REMAINING + ';' + JSON.stringify({ socketId: socketArgs.socketId, timeRemaining: game.timerParams.timeRemaining, paused: game.timerParams.paused }) + ';' + vars.instanceId ); } } }, { /* This is an event fired when an instance receives timer data from another instance. In this case, we should check if the socket id * given in the message is connected to this namespace. If it is, emit the time remaining to them. */ id: EVENT_IDS.SHARE_TIME_REMAINING, stateChange: async (game, socketArgs, vars) => {}, communicate: async (game, socketArgs, vars) => { const socket = vars.gameManager.namespace.sockets.get(socketArgs.socketId); if (socket) { vars.gameManager.namespace.to(socket.id) .emit(globals.GAME_PROCESS_COMMANDS.GET_TIME_REMAINING, socketArgs.timeRemaining, socketArgs.paused); } } }, { id: EVENT_IDS.END_TIMER, stateChange: async (game, socketArgs, vars) => { game.timerParams.paused = false; game.timerParams.timeRemaining = 0; }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.END_TIMER); } }, { id: EVENT_IDS.PAUSE_TIMER, stateChange: async (game, socketArgs, vars) => { game.timerParams.paused = true; game.timerParams.timeRemaining = socketArgs.timeRemaining; }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.PAUSE_TIMER, game.timerParams.timeRemaining); } }, { id: EVENT_IDS.RESUME_TIMER, stateChange: async (game, socketArgs, vars) => { game.timerParams.paused = false; game.timerParams.timeRemaining = socketArgs.timeRemaining; }, communicate: async (game, socketArgs, vars) => { vars.gameManager.namespace.in(game.accessCode).emit(globals.GAME_PROCESS_COMMANDS.RESUME_TIMER, game.timerParams.timeRemaining); } } ]; module.exports = Events;