From 643b6e84794eb3eabb1f6a0b55b5a4d16238992d Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 13:44:27 -0700 Subject: [PATCH 01/67] Add eye counts --- web/src/store/index.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/src/store/index.js b/web/src/store/index.js index 0177e06..b883433 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -11,9 +11,9 @@ export default new Vuex.Store({ icon: `sun.svg`, eyes: { 1: 0, 2: 0, - 3: 0, 4: 0, - 5: 0, 6: 0, - 7: 0, 8: 0, + 3: 0, 4: 1, + 5: 0, 6: 1, + 7: 1, 8: 0, }, }, team_2: { @@ -21,8 +21,8 @@ export default new Vuex.Store({ icon: `moon.svg`, eyes: { 1: 0, 2: 0, - 3: 0, 4: 0, - 5: 0, 6: 0, + 3: 1, 4: 0, + 5: 1, 6: 1, 7: 0, 8: 0, }, }, From cb88f7e74191286d54253adeec615f4faedf9f4c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 13:44:52 -0700 Subject: [PATCH 02/67] Remove VueX-persist --- web/package.json | 3 +-- web/pnpm-lock.yaml | 22 ---------------------- web/src/components/Attributions.vue | 1 - web/src/store/index.js | 5 ----- 4 files changed, 1 insertion(+), 30 deletions(-) diff --git a/web/package.json b/web/package.json index 99c7754..2dc1b33 100644 --- a/web/package.json +++ b/web/package.json @@ -13,8 +13,7 @@ "vue": "^2.6.11", "vue-clipboard2": "^0.3.1", "vue-socket.io-extended": "^4.0.5", - "vuex": "^3.4.0", - "vuex-persist": "^3.1.3" + "vuex": "^3.4.0" }, "devDependencies": { "@vue/cli-plugin-babel": "~4.5.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 8882ded..7ed15c1 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -5,7 +5,6 @@ dependencies: vue-clipboard2: 0.3.1 vue-socket.io-extended: 4.0.5 vuex: 3.6.0_vue@2.6.12 - vuex-persist: 3.1.3_vuex@3.6.0 devDependencies: '@vue/cli-plugin-babel': 4.5.9_8ae91920fb9b3c76895c2e8acb765728 '@vue/cli-plugin-eslint': 4.5.9_6778c0324b153720448c6ab0d5359212 @@ -3243,12 +3242,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - /deepmerge/4.2.2: - dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== /default-gateway/4.2.0: dependencies: execa: 1.0.0 @@ -4195,10 +4188,6 @@ packages: dev: true resolution: integrity: sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - /flatted/3.1.0: - dev: false - resolution: - integrity: sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA== /flush-write-stream/1.1.1: dependencies: inherits: 2.0.4 @@ -8744,16 +8733,6 @@ packages: dev: false resolution: integrity: sha512-uhmLFETqPPNyuLLbsKz6ioJ4q7AZHzD8ZVFNATNyICSZouqP2Sz0rotWQC8UNBF6VGSCs5abnKJoStA6JbCbfg== - /vuex-persist/3.1.3_vuex@3.6.0: - dependencies: - deepmerge: 4.2.2 - flatted: 3.1.0 - vuex: 3.6.0_vue@2.6.12 - dev: false - peerDependencies: - vuex: '>=2.5' - resolution: - integrity: sha512-QWOpP4SxmJDC5Y1+0+Yl/F4n7z27syd1St/oP+IYCGe0X0GFio0Zan6kngZFufdIhJm+5dFGDo3VG5kdkCGeRQ== /vuex/3.6.0_vue@2.6.12: dependencies: vue: 2.6.12 @@ -9161,4 +9140,3 @@ specifiers: vue-socket.io-extended: ^4.0.5 vue-template-compiler: ^2.6.11 vuex: ^3.4.0 - vuex-persist: ^3.1.3 diff --git a/web/src/components/Attributions.vue b/web/src/components/Attributions.vue index 930d964..221277b 100644 --- a/web/src/components/Attributions.vue +++ b/web/src/components/Attributions.vue @@ -42,7 +42,6 @@ export default { modal: false, tooling: { "Vue.JS (With VueX)": "https://vuejs.org", - "VueX-Persist": "https://www.npmjs.com/package/vuex-persist", "Vue-Socket.io": "https://github.com/MetinSeylan/Vue-Socket.io", "Vue-Clipboard2": "https://www.npmjs.com/package/vue-clipboard2", "Toml": "https://www.npmjs.com/package/toml", diff --git a/web/src/store/index.js b/web/src/store/index.js index b883433..419d32d 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -1,6 +1,5 @@ import Vue from 'vue'; import Vuex from 'vuex'; -import VuexPersistence from 'vuex-persist'; Vue.use(Vuex); @@ -118,8 +117,4 @@ export default new Vuex.Store({ }, modules: { }, - plugins: - process.env.NODE_ENV === `production` - ? [new VuexPersistence({ key: `ghost-writer-save` }).plugin] - : [] }); \ No newline at end of file From 2c6d22e92247bf753a24d7c1d3247727b0b1a55c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 13:45:22 -0700 Subject: [PATCH 03/67] Use a different websocket URI for dev versus prod --- web/src/main.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/src/main.js b/web/src/main.js index c048d8b..b9102f4 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -7,8 +7,14 @@ import VueSocketIOExt from 'vue-socket.io-extended'; Vue.config.productionTip = false; +// Get the URI for +let websocket_uri = `/`; +if (process.env.NODE_ENV === `development`) { + websocket_uri = `http:${window.location.hostname}:8081`; +}; + Vue.use(clipboard); -Vue.use(VueSocketIOExt, io(`http://${window.location.hostname}:8081`)); +Vue.use(VueSocketIOExt, io(websocket_uri)); new Vue({ store, From d428d1160bf42c3ebd6eb7639b5ad59ff478a3c9 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 14:12:30 -0700 Subject: [PATCH 04/67] Tranform text to all uppercase in the writing boxes. --- web/src/components/GameBoard.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index 435d58d..bc8820e 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -220,6 +220,7 @@ input[type="text"] { font-family: var(--fonts); background-color: var(--board-background-alt); color: var(--board-background-alt-text); + text-transform: uppercase; border-color: transparent; border-style: solid; border-radius: 7px; From ee16218c18ba4b76ec4c04d75f8eb4e64cf4456b Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 14:20:54 -0700 Subject: [PATCH 05/67] Draw new cards for a team when the spirit selects a card to answer rather than right when the medium sends the card to the spirit. --- server/src/events/SendCard.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/src/events/SendCard.ts b/server/src/events/SendCard.ts index 6947d5d..ddda9ad 100644 --- a/server/src/events/SendCard.ts +++ b/server/src/events/SendCard.ts @@ -20,8 +20,11 @@ export default (io: Server, socket: Socket, data: SendCard) => { // The writer is answering if (data.from === "writer") { - game.log.debug(` Writer selected question to answer.`); + game.log.debug(`Writer selected question to answer.`); + + // Draw new cards for team deck.discard(data.text); + team.addCardsToHand(game.questions.draw(conf.game.hand_size - team.hand.length)); team.selectQuestion(data.text); socket.emit(`UpdateHand`, { @@ -29,6 +32,11 @@ export default (io: Server, socket: Socket, data: SendCard) => { mode: "replace", questions: [] }); + io.to(`${game.id}:${team.id}:guesser`).emit(`UpdateHand`, { + status: 200, + mode: "replace", + questions: team.hand + }); return; } @@ -38,7 +46,6 @@ export default (io: Server, socket: Socket, data: SendCard) => { // Update the team's hand team.removeCard(data.text); - team.addCardsToHand(game.questions.draw(conf.game.hand_size - team.hand.length)); // send the question text to the writer player io.to(`${game.id}:${team.id}:writer`).emit(`UpdateHand`, { From 5ce4f45c89d19206cda2aafc84668ed3f358f8e3 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 14:21:10 -0700 Subject: [PATCH 06/67] Update error message. --- server/src/events/SendCard.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/events/SendCard.ts b/server/src/events/SendCard.ts index ddda9ad..97b5aa8 100644 --- a/server/src/events/SendCard.ts +++ b/server/src/events/SendCard.ts @@ -76,7 +76,7 @@ export default (io: Server, socket: Socket, data: SendCard) => { catch (err) { socket.emit(`UpdateHand`, { status: 500, - message: `${err.name}: ${err.message}`, + message: err.message, source: `SendCard`, }); } From bf62907c7b531ba75c6496fb22a1824b03048ee1 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 14:51:18 -0700 Subject: [PATCH 07/67] Finish writing the uri string --- web/src/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/main.js b/web/src/main.js index b9102f4..2f38b4d 100644 --- a/web/src/main.js +++ b/web/src/main.js @@ -7,10 +7,10 @@ import VueSocketIOExt from 'vue-socket.io-extended'; Vue.config.productionTip = false; -// Get the URI for +// Get the URI for dev enfironments let websocket_uri = `/`; if (process.env.NODE_ENV === `development`) { - websocket_uri = `http:${window.location.hostname}:8081`; + websocket_uri = `http://${window.location.hostname}:8081`; }; Vue.use(clipboard); From 0968181e060234ec26356754dbfe25ef07fe97a2 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 15:15:46 -0700 Subject: [PATCH 08/67] Add serif'd font to the text inputs --- web/src/components/GameBoard.vue | 2 +- web/src/css/theme.css | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index bc8820e..a812c5a 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -217,9 +217,9 @@ h2 { } input[type="text"] { - font-family: var(--fonts); background-color: var(--board-background-alt); color: var(--board-background-alt-text); + font-family: var(--input-fonts); text-transform: uppercase; border-color: transparent; border-style: solid; diff --git a/web/src/css/theme.css b/web/src/css/theme.css index bbcb24e..7c60758 100644 --- a/web/src/css/theme.css +++ b/web/src/css/theme.css @@ -1,9 +1,12 @@ +@import url('https://fonts.googleapis.com/css2?family=Roboto+Slab&display=swap'); + :root { /* The fonts and font colours the site will use */ --fonts: "Roboto", "Open Sans", sans-serif; + --input-fonts: "Roboto Slab", var(--fonts); --light-font-colour: #ECE3BB; --dark-font-colour: #000F3D; From 5786ad2742a5763d1fa0f3c2cf88cdd726fbb5a0 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:51:37 -0700 Subject: [PATCH 09/67] begin working on the data types needed for the datastore --- server/src/types/datastore.d.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 server/src/types/datastore.d.ts diff --git a/server/src/types/datastore.d.ts b/server/src/types/datastore.d.ts new file mode 100644 index 0000000..cc24554 --- /dev/null +++ b/server/src/types/datastore.d.ts @@ -0,0 +1,30 @@ +interface datastorePlayer { + team: team | null; + role: role | null; + host: boolean; + name: string; +} + +type datastoreQuestionCard = string; +type datastoreObjectCard = string[]; + +interface datastoreTeam { + questions: datastoreQuestionCard[]; + hand: datastoreQuestionCard[]; + id: team; +} + +interface datastoreDeck { + discard: T[]; + unknown: T[]; + deck: T[]; +} + +interface datastore { + questions: datastoreDeck; + objects: datastoreDeck; + objectCard: datastoreObjectCard; + players: datastorePlayer[]; + ingame: boolean; + object: string; +} \ No newline at end of file From a5c96318194bbdb3716b0873c809e998ff47d0da Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:51:54 -0700 Subject: [PATCH 10/67] Add cleanup functions for the games. --- server/src/utils/cleanup.ts | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 server/src/utils/cleanup.ts diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts new file mode 100644 index 0000000..06d4515 --- /dev/null +++ b/server/src/utils/cleanup.ts @@ -0,0 +1,58 @@ +import { writeFileSync } from "fs"; +import { Game } from "../objects/Game"; +import { games, conf, log } from "../main"; + +export function processExit() { + /** + * This is the cleanup code that runs when the server has been exited for + * any reason. We check all games if they have any active connection(s), + * then if they do, we save the game to disk, if not we just delete it + * completely from the system. + */ + log.info(`Cleaning up games`) + for (var gc in games) { + let game = games[gc]; + if (activeGame(game)) { + game.log.debug(`Saving to datastore`); + writeFileSync( + `${conf.datastores.directory}/${game.id}.game`, + JSON.stringify(game.toJSON()) + ); + } else { + game.log.debug(`Deleting game`); + delete games[gc]; + }; + }; + log.info(`Done cleaning up games`); +}; + + +export async function checkSocketConnections() { + /** + * This is the cleanup that occurs whenever a new game has been started + */ + log.info(`[checkSocketConnections] Checking for games to clean up`) + for (var gc in games) { + let game = games[gc]; + if (!activeGame(game)) { + game.log.debug(`[checkSocketConnections] Deleting game`); + delete games[gc]; + }; + }; + log.info(`[checkSocketConnections] Done cleaning up games`); +}; + + +function activeGame(game: Game): boolean { + /** + * This checks if a game is still active by checking that is at least one + * socket client still connected. If not then the game is considered + * stagnant and will be deleted by the cleanup code. + */ + for (var player of game.players) { + if (player.socket.connected) { + return true; + }; + }; + return false; +}; \ No newline at end of file From 4e73b1ede51142cbab5e78c5ddafaa1cc168fc9c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:52:18 -0700 Subject: [PATCH 11/67] Adjust GameDeleted response to include message. --- server/src/events/DeleteGame.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/events/DeleteGame.ts b/server/src/events/DeleteGame.ts index 846195a..87bd33a 100644 --- a/server/src/events/DeleteGame.ts +++ b/server/src/events/DeleteGame.ts @@ -33,7 +33,10 @@ export default (io: Server, socket: Socket, data: DeleteGame) => { // Delete game game.log.debug(`Game deleted.`) delete games[data.game_code]; - io.to(game.id).emit(`GameDeleted`, { status: 200 }); + io.to(game.id).emit(`GameDeleted`, { + status: 200, + message: `Game deleted by the host.` + }); } catch (err) { socket.emit(`GameDeleted`, { From 483e37df7353765f19d618052ebf1e1a7b49d8a6 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:52:43 -0700 Subject: [PATCH 12/67] Update config type to reflect changes for datastores. --- server/src/types/config.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/types/config.d.ts b/server/src/types/config.d.ts index 3cc0527..aabf326 100644 --- a/server/src/types/config.d.ts +++ b/server/src/types/config.d.ts @@ -6,9 +6,9 @@ interface config { port: number; permitted_hosts: string | string[]; }; - webserver: { + datastores: { enabled: boolean; - port: number; + directory: string; }; game: { hand_size: number; From 3a9045ef87299286155566dcde415b0f72959894 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:53:35 -0700 Subject: [PATCH 13/67] Update GameDeleted type to add message. --- server/src/types/data.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/types/data.d.ts b/server/src/types/data.d.ts index c381569..82dc1e3 100644 --- a/server/src/types/data.d.ts +++ b/server/src/types/data.d.ts @@ -29,7 +29,9 @@ interface GameCreated extends response { interface DeleteGame { game_code: string; } -interface GameDeleted extends response {} +interface GameDeleted extends response { + message?: string +} interface LeaveGame { From 7914ff7e4541e8de4a5a94dedb7b24d3ece77c8c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:54:26 -0700 Subject: [PATCH 14/67] Update config validation to check datastores instead of webserver --- server/src/utils/validate.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/server/src/utils/validate.ts b/server/src/utils/validate.ts index 467d5cb..89ab38a 100644 --- a/server/src/utils/validate.ts +++ b/server/src/utils/validate.ts @@ -27,16 +27,24 @@ export class Validate { valid = false; } - // Assert data in the web server object - if (conf.webserver.enabled) { - if (!conf.webserver.port) { - log.error(`Invalid webserver port value: ${conf.webserver.port}`); + if (!conf.websocket.permitted_hosts) { + log.error(`Can't have a blank or null websocket.permitted_hosts`); + valid = false; + }; + + if (!conf.datastores) { + log.error(`Datastores object must be defined`); + valid = false; + } else { + if (conf.datastores.enabled == null) { + log.error(`datastores.enabled must be defined`); + valid = false; + }; + + if (conf.datastores.enabled && conf.datastores.directory?.length > 0) { + log.error(`datastores.directory must be a filepath if datastores.enabled is set to true`); valid = false; }; - }; - if (!conf.websocket.permitted_hosts) { - log.error(`Can't have a blank or null webserver.hostname`); - valid = false; }; // Config is valid From f0f303246590d802109a12b2b968bd1622c95866 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:54:45 -0700 Subject: [PATCH 15/67] Add cleanup listeners if they are enabled. --- server/src/main.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/main.ts b/server/src/main.ts index 7b38cfd..e0147df 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -4,6 +4,7 @@ import { readFileSync } from "fs"; import { Game } from "./objects/Game"; import startWebsocket from "./websocket"; import { Validate } from "./utils/validate"; +import { processExit } from "./utils/cleanup"; export const conf: config = toml.parse(readFileSync(`server.toml`, `utf-8`)); @@ -18,6 +19,17 @@ export const log: Logger = new Logger({ name: `GLOBAL`, }); +// Ensure the config valid if (Validate.config(conf)) { + + // Add event listeners if we want to use the datastore saving game system + if (conf.datastores.enabled) { + + // Get games from datastore + + process.on(`uncaughtException`, processExit); + process.on(`SIGINT`, processExit); + }; + startWebsocket(conf); } \ No newline at end of file From 0a24c009ada68473d111a4007b117be01079f722 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 16:59:52 -0700 Subject: [PATCH 16/67] Use the provided message and not a hardcoded value --- web/src/App.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/App.vue b/web/src/App.vue index 4aa0092..e7a1d48 100644 --- a/web/src/App.vue +++ b/web/src/App.vue @@ -95,7 +95,7 @@ export default { this.handleError(data); } else { this.alert = { - message: `The game has been ended by the host.`, + message: data.message, type: `info`, }; this.$store.commit(`resetState`); From 4b5a05f57bb02b4d8479637428405879ecc242a8 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 17:00:38 -0700 Subject: [PATCH 17/67] implement toJSON and fromJSON methods --- server/src/objects/Player.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/server/src/objects/Player.ts b/server/src/objects/Player.ts index 2d85416..f446e2a 100644 --- a/server/src/objects/Player.ts +++ b/server/src/objects/Player.ts @@ -12,4 +12,20 @@ export class Player { this.socket = socket; this.isHost = isHost; }; + + public toJSON(): datastorePlayer { + return { + name: this.name, + host: this.isHost, + team: this.team, + role: this.role, + }; + }; + + public static fromJSON(data: datastorePlayer, socket: Socket): Player { + let player = new this(data.name, socket, data.host); + player.role = data.role; + player.team = data.team; + return player; + }; }; \ No newline at end of file From 48c0f995d1e0409f7670353f45bf9c6a98b4b641 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Tue, 5 Jan 2021 17:03:10 -0700 Subject: [PATCH 18/67] Begin implementing toJSON and fromJSON --- server/src/objects/Game.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 1552455..f0ba119 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -107,6 +107,20 @@ export class Game { }; + public toJSON() { + /** + * Returns a JSON representation of the game. + */ + let players = this.players.map(p => p.toJSON()); + }; + + public static fromJSON() { + /** + * Converts a JSON object into a Game object + */ + }; + + public static generateID(length: number): string { /** * Generates a game code with the given length From b49662f00f4de10fb0bca0f38b39f52b77fae8c8 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 16:24:35 -0700 Subject: [PATCH 19/67] Add the team's answers into the datastore team object. --- server/src/types/datastore.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/types/datastore.d.ts b/server/src/types/datastore.d.ts index cc24554..b1797b1 100644 --- a/server/src/types/datastore.d.ts +++ b/server/src/types/datastore.d.ts @@ -11,6 +11,7 @@ type datastoreObjectCard = string[]; interface datastoreTeam { questions: datastoreQuestionCard[]; hand: datastoreQuestionCard[]; + answers: string[]; id: team; } From 2e8497c77df892380a14d8bd2e2a8baddab8a41a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 16:24:52 -0700 Subject: [PATCH 20/67] Implement toJSON and fromJSON methods. --- server/src/objects/Team.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/server/src/objects/Team.ts b/server/src/objects/Team.ts index acbeb6f..bf7cb21 100644 --- a/server/src/objects/Team.ts +++ b/server/src/objects/Team.ts @@ -69,4 +69,28 @@ export class Team { }; this._answers[answerIndex - 1] = answer; }; + + + public toJSON(): datastoreTeam { + /** + * Converts the given object into a JSON representation of the data + */ + return { + questions: this._questions, + answers: this._answers, + hand: this._hand, + id: this.id, + }; + }; + + public static fromJSON(data: datastoreTeam): Team { + /** + * Converts a team JSON object back into a Team object. + */ + let t = new Team(data.id); + t._questions = data.questions; + t._answers = data.answers; + t._hand = data.hand; + return t; + }; }; \ No newline at end of file From 4195159003c40912ec8f5dd10e880b617233af03 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:16:51 -0700 Subject: [PATCH 21/67] Rename function to routineCheck --- server/src/utils/cleanup.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts index 06d4515..e9b387f 100644 --- a/server/src/utils/cleanup.ts +++ b/server/src/utils/cleanup.ts @@ -27,19 +27,19 @@ export function processExit() { }; -export async function checkSocketConnections() { +export async function routineCheck() { /** * This is the cleanup that occurs whenever a new game has been started */ - log.info(`[checkSocketConnections] Checking for games to clean up`) + log.info(`[routineCheck] Checking for games to clean up`) for (var gc in games) { let game = games[gc]; if (!activeGame(game)) { - game.log.debug(`[checkSocketConnections] Deleting game`); + game.log.debug(`[routineCheck] Deleting game`); delete games[gc]; }; }; - log.info(`[checkSocketConnections] Done cleaning up games`); + log.info(`[routineCheck] Done cleaning up games`); }; From cc10c145b53b3e4106db6ba77f92a9c303ec9547 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:17:11 -0700 Subject: [PATCH 22/67] Add filetype to the config. --- server/src/types/config.d.ts | 1 + server/src/utils/cleanup.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/types/config.d.ts b/server/src/types/config.d.ts index aabf326..f114c07 100644 --- a/server/src/types/config.d.ts +++ b/server/src/types/config.d.ts @@ -8,6 +8,7 @@ interface config { }; datastores: { enabled: boolean; + filetype: string; directory: string; }; game: { diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts index e9b387f..6951df2 100644 --- a/server/src/utils/cleanup.ts +++ b/server/src/utils/cleanup.ts @@ -15,7 +15,7 @@ export function processExit() { if (activeGame(game)) { game.log.debug(`Saving to datastore`); writeFileSync( - `${conf.datastores.directory}/${game.id}.game`, + `${conf.datastores.directory}/${game.id}.${conf.datastores.filetype}`, JSON.stringify(game.toJSON()) ); } else { From 95c298002f3bbe4f01bf9ee245e3b827da598cbd Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:17:34 -0700 Subject: [PATCH 23/67] Implement a toJSON function. --- server/src/objects/Deck.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/objects/Deck.ts b/server/src/objects/Deck.ts index 1d04a7b..78ae90c 100644 --- a/server/src/objects/Deck.ts +++ b/server/src/objects/Deck.ts @@ -56,4 +56,16 @@ export class Deck { this._unknown = this._unknown.filter(x => x != card); this._discard.push(card); }; + + + public toJSON(): datastoreDeck { + /** + * Converts this Deck into a JSON-compatible object + */ + return { + deck: this._deck, + unknown: this._unknown, + discard: this._discard, + }; + }; }; \ No newline at end of file From 28a1f1ee147daed5a3bdb93085890a85da8b3114 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:18:07 -0700 Subject: [PATCH 24/67] Change structure of the decks for the datastore. --- server/src/types/datastore.d.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/server/src/types/datastore.d.ts b/server/src/types/datastore.d.ts index b1797b1..1dc27c2 100644 --- a/server/src/types/datastore.d.ts +++ b/server/src/types/datastore.d.ts @@ -21,11 +21,14 @@ interface datastoreDeck { deck: T[]; } -interface datastore { - questions: datastoreDeck; - objects: datastoreDeck; +interface datastoreGame { + decks: { + questions: datastoreDeck; + objects: datastoreDeck; + }; objectCard: datastoreObjectCard; players: datastorePlayer[]; + teams: datastoreTeam[]; ingame: boolean; object: string; } \ No newline at end of file From ac050fc50579ea6d8c3333559d2644d15742d815 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:18:43 -0700 Subject: [PATCH 25/67] Begin work on implementing game loading from the datastore. --- server/src/main.ts | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/server/src/main.ts b/server/src/main.ts index e0147df..6294c24 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -1,13 +1,27 @@ import * as toml from "toml"; import { Logger } from "tslog"; -import { readFileSync } from "fs"; import { Game } from "./objects/Game"; import startWebsocket from "./websocket"; import { Validate } from "./utils/validate"; import { processExit } from "./utils/cleanup"; +import { readdirSync, readFileSync } from "fs"; export const conf: config = toml.parse(readFileSync(`server.toml`, `utf-8`)); +/* + * These are the objects that we use to keep track of the different games, + * there are two different types of game, hibernated, and active. Hibernated + * games are games which have data associated with them in the datastore, but + * have not had any requests associated with them since they were hibernated. + * Active games are games that have active socket connections associated with + * them or have not been cleaned up by the game creation process yet. Active + * games become hibernated once the server has been closed while they are in + * the middle of a game, if they are in the lobby, the cleanup systems will + * just delete the game outright instead of saving it to disk. These methods + * allow us to keep games that are in the process of being played if/when the + * server crashes/restarts from having to completely start the game over again. + */ +export var hibernatedGames: string[] = []; export var games: {[index: string]: Game} = {}; export const log: Logger = new Logger({ @@ -25,7 +39,9 @@ if (Validate.config(conf)) { // Add event listeners if we want to use the datastore saving game system if (conf.datastores.enabled) { - // Get games from datastore + // Get game IDs from datastore + hibernatedGames = readdirSync(conf.datastores.directory) + .filter(g => g.endsWith(conf.datastores.filetype)); process.on(`uncaughtException`, processExit); process.on(`SIGINT`, processExit); From 6a0536761cd0f813f00a7f7b5b62d128a1e6da50 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Wed, 6 Jan 2021 17:30:48 -0700 Subject: [PATCH 26/67] Make sure newly generated IDs don't interefere with the hibernated games. --- server/src/objects/Game.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index f0ba119..20f7ba5 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -1,10 +1,10 @@ import { Team } from "./Team"; import { Deck } from "./Deck"; +import { readFile } from "fs"; import neatCSV from "neat-csv"; import { Logger } from "tslog"; -import { games } from "../main"; import { Player } from "./Player"; -import { readFile } from "fs"; +import { games, hibernatedGames } from "../main"; export class Game { readonly id: string; @@ -135,7 +135,7 @@ export class Game { for (var i = 0; i < length; i++) { code += `${Math.floor(Math.random() * 9)}`; }; - } while (games[code]); + } while (games[code] || hibernatedGames.includes(code)); return code; }; From 633267316026bda883472bee1c998014e34e15f3 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 11:22:32 -0700 Subject: [PATCH 27/67] Close #32 -> Hand Discarding Size Increase --- server/src/events/NewHand.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/server/src/events/NewHand.ts b/server/src/events/NewHand.ts index 710abf4..cd41e3d 100644 --- a/server/src/events/NewHand.ts +++ b/server/src/events/NewHand.ts @@ -1,4 +1,4 @@ -import { conf, games, log } from '../main'; +import { games, log } from '../main'; import { Server, Socket } from 'socket.io'; export default (io: Server, socket: Socket, data: NewHand) => { @@ -18,6 +18,13 @@ export default (io: Server, socket: Socket, data: NewHand) => { let team = game.teams[data.team - 1]; let deck = game.questions; + /** + * The amount of cards that the team has in their hand prior to + * discarding all of their hand, this is used to make sure that they + * get back the same number of cards that they had in their hand. + */ + let handSize = team.hand.length; + // Empty the medium's hand to the discard pile so we know where the // cards are. for (var card of team.hand) { @@ -27,7 +34,7 @@ export default (io: Server, socket: Socket, data: NewHand) => { }; // Add the questions and then alert the game clients about the changes - team.addCardsToHand(deck.draw(conf.game.hand_size)); + team.addCardsToHand(deck.draw(handSize)); game.log.silly(`Drew a new hand of cards for team ${data.team}.`); io.to(game.id).emit(`UpdateHand`, { status: 200, From c79d8f970ab4364eb105a5c3b7444ed17d9915bc Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:32:31 -0700 Subject: [PATCH 28/67] Prevent host player duplication. --- server/src/events/CreateGame.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/server/src/events/CreateGame.ts b/server/src/events/CreateGame.ts index c0b309c..91e9adb 100644 --- a/server/src/events/CreateGame.ts +++ b/server/src/events/CreateGame.ts @@ -8,13 +8,12 @@ export default (io: Server, socket: Socket, data: CreateGame) => { let host = new Player(data.name, socket, true); // Create the game object to save - let game = new Game(conf, host); + let game = new Game(host); games[game.id] = game; - game.players.push(host); game.log = log.getChildLogger({ displayLoggerName: true, name: game.id, - }) + }); game.log.info(`New game created (host=${host.name})`); socket.join(game.id); From ce252d471a3307b9ca7fd0b52c4da11661a9118e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:32:47 -0700 Subject: [PATCH 29/67] Remove unused conf import --- server/src/events/CreateGame.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/events/CreateGame.ts b/server/src/events/CreateGame.ts index 91e9adb..38bf2da 100644 --- a/server/src/events/CreateGame.ts +++ b/server/src/events/CreateGame.ts @@ -1,7 +1,7 @@ +import { games, log } from '../main'; import { Game } from '../objects/Game'; import { Player } from '../objects/Player'; import { Server, Socket } from 'socket.io'; -import { conf, games, log } from '../main'; export default (io: Server, socket: Socket, data: CreateGame) => { try { From b155791f264a3ab70060c4109e584947bacd172b Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:33:10 -0700 Subject: [PATCH 30/67] Implement fromJSON method --- server/src/objects/Deck.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/server/src/objects/Deck.ts b/server/src/objects/Deck.ts index 78ae90c..64404ec 100644 --- a/server/src/objects/Deck.ts +++ b/server/src/objects/Deck.ts @@ -68,4 +68,14 @@ export class Deck { discard: this._discard, }; }; + + public static fromJSON(data: datastoreDeck): Deck { + /** + * Converts the JSON representation of a deck into a Deck + */ + let d = new Deck(data.deck); + d._discard = data.discard; + d._unknown = data.unknown; + return d; + }; }; \ No newline at end of file From d385f8830e749a3baeae2a411c85f0fadffbefe5 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:33:36 -0700 Subject: [PATCH 31/67] Implement toJSON method. --- server/src/objects/Game.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 20f7ba5..97947db 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -107,11 +107,22 @@ export class Game { }; - public toJSON() { + public toJSON(): datastoreGame { /** * Returns a JSON representation of the game. */ - let players = this.players.map(p => p.toJSON()); + return { + players: this.players.map(p => p.toJSON()), + teams: this.teams.map(t => t.toJSON()), + decks: { + questions: this._questions.toJSON(), + objects: this._objects.toJSON(), + }, + objectCard: this._objectCard, + object: this.object, + ingame: this.ingame, + id: this.id, + }; }; public static fromJSON() { From 348b0453f7ce6d0067bdceea33169bae947717e0 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:34:17 -0700 Subject: [PATCH 32/67] Change constructor to work with the fromJSON method --- server/src/objects/Game.ts | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 97947db..413b3b2 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -4,14 +4,14 @@ import { readFile } from "fs"; import neatCSV from "neat-csv"; import { Logger } from "tslog"; import { Player } from "./Player"; -import { games, hibernatedGames } from "../main"; +import { games, hibernatedGames, conf } from "../main"; export class Game { readonly id: string; readonly host: Player; public log: Logger; public ingame: boolean; - public teams: [Team, Team]; + public teams: Team[]; public players: Player[]; private _questions: Deck; private _objects: Deck; @@ -19,23 +19,28 @@ export class Game { public object: string; - constructor(conf: config, host: Player) { - this.id = Game.generateID(conf.game.code_length); + constructor(host: Player, options:any=null) { this.host = host; this.ingame = false; - this.players = []; + this.players = [host]; + this.id = options.id || Game.generateID(conf.game.code_length); - // Get the decks based on what type of data they are. - switch (conf.game.cards.type) { - case "csv": - this.parseDeckCSV(conf); - break; - case "sheets": - this.parseDeckGoogleSheets(conf); - break; + // If the object is being instantiated from JSON we don't want to do + // any of the stuff that requires weird per-game stuff + if (!options) { + + // Get the decks based on what type of data they are. + switch (conf.game.cards.type) { + case "csv": + this.parseDeckCSV(conf); + break; + case "sheets": + this.parseDeckGoogleSheets(conf); + break; + }; + // Instantiate everything for the teams + this.teams = [ new Team(1), new Team(2) ]; }; - // Instantiate everything for the teams - this.teams = [ new Team(1), new Team(2) ]; }; get questions() { return this._questions; }; From a982ccd7043005e22a85b4b49cdb9b9ca2f4804b Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:34:34 -0700 Subject: [PATCH 33/67] Implement fromJSON method --- server/src/objects/Game.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 413b3b2..0ff0ed5 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -130,10 +130,31 @@ export class Game { }; }; - public static fromJSON() { + public static fromJSON(host: Player, data: datastoreGame): Game { /** - * Converts a JSON object into a Game object + * Converts a JSON representation into a Game object */ + let game = new this(host, { id: data.id }); + + // Re-create the deck objects + game._questions = Deck.fromJSON(data.decks.questions); + game._objects = Deck.fromJSON(data.decks.objects); + + game.teams = data.teams.map(t => Team.fromJSON(t)); + + // Re-instantiate all the players from the game. + game.players.push(host) + for (var player of data.players) { + if (player.name !== host.name) { + player.host = false; + game.players.push(Player.fromJSON(player)); + }; + }; + + game._objectCard = data.objectCard; + game.object = data.object; + + return game; }; From 00d22626635a6bbeab0b1cda83da1e0d60a0154e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:34:56 -0700 Subject: [PATCH 34/67] Allow player socket to be null --- server/src/objects/Player.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/objects/Player.ts b/server/src/objects/Player.ts index f446e2a..02934c7 100644 --- a/server/src/objects/Player.ts +++ b/server/src/objects/Player.ts @@ -4,10 +4,10 @@ export class Player { readonly name: string; public team: team|null = null; public role: role|null = null; - public socket: Socket; + public socket: Socket|null; readonly isHost: boolean; - constructor(name: string, socket: Socket, isHost=false) { + constructor(name: string, socket: Socket|null=null, isHost=false) { this.name = name; this.socket = socket; this.isHost = isHost; From 48840ffdecca5ec0af96b3605ac9af623109b100 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:35:36 -0700 Subject: [PATCH 35/67] Use null socket when creating player data from JSON. --- server/src/objects/Player.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/objects/Player.ts b/server/src/objects/Player.ts index 02934c7..2d279ba 100644 --- a/server/src/objects/Player.ts +++ b/server/src/objects/Player.ts @@ -22,8 +22,8 @@ export class Player { }; }; - public static fromJSON(data: datastorePlayer, socket: Socket): Player { - let player = new this(data.name, socket, data.host); + public static fromJSON(data: datastorePlayer): Player { + let player = new this(data.name, null, data.host); player.role = data.role; player.team = data.team; return player; From ffc4524127f1eb73c128375d2c5736f13bc79b31 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:35:53 -0700 Subject: [PATCH 36/67] Add the game's ID to the datastore. --- server/src/types/datastore.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/types/datastore.d.ts b/server/src/types/datastore.d.ts index 1dc27c2..3571e09 100644 --- a/server/src/types/datastore.d.ts +++ b/server/src/types/datastore.d.ts @@ -31,4 +31,5 @@ interface datastoreGame { teams: datastoreTeam[]; ingame: boolean; object: string; + id: string; } \ No newline at end of file From 325a2cb423e94286358b0455a4fc463c6076a780 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:36:13 -0700 Subject: [PATCH 37/67] Update log messages --- server/src/utils/cleanup.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts index 6951df2..ee4f8f3 100644 --- a/server/src/utils/cleanup.ts +++ b/server/src/utils/cleanup.ts @@ -9,18 +9,17 @@ export function processExit() { * then if they do, we save the game to disk, if not we just delete it * completely from the system. */ - log.info(`Cleaning up games`) + log.info(`Cleaning up games`); for (var gc in games) { let game = games[gc]; - if (activeGame(game)) { + if (game.ingame && activeGame(game)) { game.log.debug(`Saving to datastore`); writeFileSync( `${conf.datastores.directory}/${game.id}.${conf.datastores.filetype}`, JSON.stringify(game.toJSON()) ); } else { - game.log.debug(`Deleting game`); - delete games[gc]; + game.log.debug(`Not saving to datastore`); }; }; log.info(`Done cleaning up games`); From 4d63e78d2c2dbf7c1936ecd189003a8b53823ae5 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:36:33 -0700 Subject: [PATCH 38/67] Actually exit the process when done cleaning up --- server/src/utils/cleanup.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts index ee4f8f3..74b44f8 100644 --- a/server/src/utils/cleanup.ts +++ b/server/src/utils/cleanup.ts @@ -23,6 +23,7 @@ export function processExit() { }; }; log.info(`Done cleaning up games`); + process.exit(); }; From e5a14322c056cdf677a9ea28003e17e44370ff51 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:36:47 -0700 Subject: [PATCH 39/67] Ensure player's socket object exists. --- server/src/utils/cleanup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/utils/cleanup.ts b/server/src/utils/cleanup.ts index 74b44f8..9ff7ca4 100644 --- a/server/src/utils/cleanup.ts +++ b/server/src/utils/cleanup.ts @@ -50,7 +50,7 @@ function activeGame(game: Game): boolean { * stagnant and will be deleted by the cleanup code. */ for (var player of game.players) { - if (player.socket.connected) { + if (player.socket?.connected) { return true; }; }; From 5666a507c87752577c5a69011e3dd7cc0dcfbf5a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:37:20 -0700 Subject: [PATCH 40/67] Correct config validation on the datastore filepath --- server/src/utils/validate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/utils/validate.ts b/server/src/utils/validate.ts index 89ab38a..62ffe6a 100644 --- a/server/src/utils/validate.ts +++ b/server/src/utils/validate.ts @@ -41,7 +41,7 @@ export class Validate { valid = false; }; - if (conf.datastores.enabled && conf.datastores.directory?.length > 0) { + if (conf.datastores.enabled && conf.datastores.directory?.length == 0) { log.error(`datastores.directory must be a filepath if datastores.enabled is set to true`); valid = false; }; From 9268958a2fc9a39c33d81bf84e56768a35be0be1 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:38:08 -0700 Subject: [PATCH 41/67] Remove filetype from the names of the hibernated games. --- server/src/main.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/src/main.ts b/server/src/main.ts index 6294c24..3f0aad2 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -38,10 +38,12 @@ if (Validate.config(conf)) { // Add event listeners if we want to use the datastore saving game system if (conf.datastores.enabled) { + log.info(`Loading list of hibernated games`); // Get game IDs from datastore hibernatedGames = readdirSync(conf.datastores.directory) - .filter(g => g.endsWith(conf.datastores.filetype)); + .filter(g => g.endsWith(conf.datastores.filetype)) + .map(f => f.replace(`\.${conf.datastores.filetype}`, ``)); process.on(`uncaughtException`, processExit); process.on(`SIGINT`, processExit); From aee33fd63a8f6fcb86267a73a8c66fa8cddb94dc Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:40:10 -0700 Subject: [PATCH 42/67] If the game the player is trying to join is hibernated, make it active. --- server/src/events/JoinGame.ts | 83 +++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 18 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index a04e9ea..84a0d95 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -1,9 +1,58 @@ -import { games, log } from '../main'; +import { readFileSync } from 'fs'; +import { Game } from '../objects/Game'; import { Player } from '../objects/Player'; import { Server, Socket } from 'socket.io'; +import { games, hibernatedGames, log, conf } from '../main'; export default (io: Server, socket: Socket, data: JoinGame) => { try { + // Check if the game is hibernated so that we can re-instantiate the + // Game object and bring it back to being alive + let hibernatedIndex = hibernatedGames.indexOf(data.game_code) + if (hibernatedIndex >= 0) { + log.info(`Recreating game from datastore.`); + + let datastore = JSON.parse(readFileSync( + `${conf.datastores.directory}/${data.game_code}.${conf.datastores.filetype}`, + `utf-8` + )) as datastoreGame; + let host = new Player(data.name, socket, true); + let game = Game.fromJSON(host, datastore); + + game.log = log.getChildLogger({ + displayLoggerName: true, + name: game.id, + }); + + if (host.team) { + let team = game.teams[host.team - 1]; + switch (host.role) { + case "guesser": + game.log.silly(`${host.name} is one of the team's guessers`); + team.guessers.push(host); + break; + case "writer": + game.log.silly(`${host.name} is the team's writer`); + team.writer = host; + break; + }; + game.log.debug(`Host assigned to team object.`); + }; + + hibernatedGames.splice(hibernatedIndex, 1); + games[game.id] = game; + + game.log.info(`Successfully unhibernated`); + + socket.emit(`GameRejoined`, { + status: 200, + data: { + players: game.players.map(p => p.toJSON()), + + }, + }); + return; + }; // Assert game exists if (!games[data.game_code]) { @@ -18,30 +67,28 @@ export default (io: Server, socket: Socket, data: JoinGame) => { let game = games[data.game_code]; - // Ensure no one has the same name as the player that is joining + /* + Ensure that if the socket is attempting to reconnect to the game, that + the player they are connecting to does not have actively connected + socket. This will also function as the main game joining for hibernated + games that were reloaded from disk. + */ let sameName = game.players.find(x => x.name == data.name); if (sameName != null) { - if (!game.ingame) { - game.log.info(`Client attempted to connect using name already in use.`); - socket.emit(`GameJoined`, { - status: 400, - message: `A player already has that name in the game.`, - source: `JoinGame` + + if (!sameName.socket?.connected) { + sameName.socket = socket; + game.log.info(`Player Reconnected to the game (name=${data.name})`); + socket.emit(`GameRejoined`, { + status: 200, + data: {}, }); return; - }; - - // Player has the same name but is allowed to rejoin if they - // disconnect in the middle of the game - if (!sameName.socket.connected) { - game.log.info(`Player Reconnected to the game (name=${data.name})`); - socket.emit(`GameRejoined`, { status: 200 }); - return; } else { - game.log.debug(`${socket.id} attempted to claim ${sameName.socket.id}'s game spot.`); + game.log.debug(`${socket.id} attempted to join with a name already in use ${data.name}`); socket.emit(`GameJoined`, { status: 403, - message: `Can't connect to an already connected client`, + message: `A player already has that name in the game.`, source: `JoinGame` }); return; From c34fec10adca979a5cffcb3a9cde51aa9feca8d7 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:43:13 -0700 Subject: [PATCH 43/67] Run a check of all active games for ones with no socket connections. --- server/src/events/CreateGame.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/src/events/CreateGame.ts b/server/src/events/CreateGame.ts index 38bf2da..3a2ee3d 100644 --- a/server/src/events/CreateGame.ts +++ b/server/src/events/CreateGame.ts @@ -2,6 +2,7 @@ import { games, log } from '../main'; import { Game } from '../objects/Game'; import { Player } from '../objects/Player'; import { Server, Socket } from 'socket.io'; +import { routineCheck } from '../utils/cleanup'; export default (io: Server, socket: Socket, data: CreateGame) => { try { @@ -22,6 +23,9 @@ export default (io: Server, socket: Socket, data: CreateGame) => { game_code: game.id, players: game.playerData, }); + + // Check for any inactive games that are still marked as active + routineCheck(); } catch (err) { socket.emit(`GameCreated`, { From 5c55f73f8520b7a7b917c442a2c7c50cffb4c358 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 12:44:10 -0700 Subject: [PATCH 44/67] Add datastores to the gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5a4c89c..2e1c742 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ node_modules server/server.toml server/built/* server/dist/* +server/resources/games #=============================================================================# # The files that were auto-generated into a .gitignore by Vue-cli From 81d810e95187400a54bea2bb0959e7261d6ae9cd Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 13:07:52 -0700 Subject: [PATCH 45/67] Update to use answers from VueX and not the data object. --- web/src/components/GameBoard.vue | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index a812c5a..34347f3 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -105,10 +105,6 @@ export default { }, data() {return { visible: false, - answers: { - team_1: [ ``, ``, ``, ``, ``, ``, ``, `` ], - team_2: [ ``, ``, ``, ``, ``, ``, ``, `` ], - }, }}, computed: { teamID() { @@ -117,6 +113,9 @@ export default { otherTeamID() { return this.$store.getters.otherTeamName.replace(/\s/g, `-`).toLowerCase(); }, + answers() { + return this.$store.state.answer; + }, }, methods: { answerInputHandler(answerIndex) { @@ -147,7 +146,7 @@ export default { * value: string * } */ - this.answers[`team_${data.team}`].splice(data.answer - 1, 1, data.value); + this.$store.commit(`UpdateAnswer`, data); }, }, } From 07ad42280dbeb7a8bac87513224f8aa3d1abf56a Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 13:22:53 -0700 Subject: [PATCH 46/67] Add answers and mutations for answers --- web/src/store/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/web/src/store/index.js b/web/src/store/index.js index 419d32d..1212916 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -46,6 +46,10 @@ export default new Vuex.Store({ questions: [], game_code: null, players: [], + answers: { + team_1: [ ``, ``, ``, ``, ``, ``, ``, `` ], + team_2: [ ``, ``, ``, ``, ``, ``, ``, `` ], + } }, getters: { teamName(state) { @@ -112,6 +116,12 @@ export default new Vuex.Store({ appendToHand(state, questions) { state.questions.push(...questions); }, + updateAnswer(state, data) { + state.answers[`team_${data.team}`].splice(data.answer - 1, 1, data.value) + }, + setAnswers(state, data) { + state.answers = Vue.reactive(data); + }, }, actions: { }, From 31d19bfe2d31420ddce165246c180049cc4db58d Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 13:23:55 -0700 Subject: [PATCH 47/67] Add log stating how many hibernated games were found --- server/src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main.ts b/server/src/main.ts index 3f0aad2..22eb409 100644 --- a/server/src/main.ts +++ b/server/src/main.ts @@ -45,6 +45,7 @@ if (Validate.config(conf)) { .filter(g => g.endsWith(conf.datastores.filetype)) .map(f => f.replace(`\.${conf.datastores.filetype}`, ``)); + log.info(`Found ${hibernatedGames.length} hibernated games`); process.on(`uncaughtException`, processExit); process.on(`SIGINT`, processExit); }; From cecfddfdeea46541456fea4e9cd19ec49bc87b62 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 13:33:55 -0700 Subject: [PATCH 48/67] Change "game_code" commit to "gameCode" for consistency --- web/src/store/index.js | 2 +- web/src/views/CreateJoin.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/store/index.js b/web/src/store/index.js index 1212916..b6a80c3 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -87,7 +87,7 @@ export default new Vuex.Store({ if (data.host) state.is_host = data.host }, - game_code(state, game_code) { + gameCode(state, game_code) { state.game_code = game_code; }, view(state, target) { diff --git a/web/src/views/CreateJoin.vue b/web/src/views/CreateJoin.vue index fd92990..92d4ea3 100644 --- a/web/src/views/CreateJoin.vue +++ b/web/src/views/CreateJoin.vue @@ -71,7 +71,7 @@ export default { // Save the data in the store this.$store.commit(`playerList`, data.players); - this.$store.commit(`game_code`, this.game_code); + this.$store.commit(`gameCode`, this.game_code); this.$store.commit(`player`, { name: this.name, host: false, @@ -89,7 +89,7 @@ export default { // Update storage this.$store.commit(`playerList`, data.players); - this.$store.commit(`game_code`, data.game_code); + this.$store.commit(`gameCode`, data.game_code); this.$store.commit(`player`, { name: this.name, host: true, From 8fcc85dfeb70b5d372cfd86bc93e2b5aba1a6ba0 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 13:41:08 -0700 Subject: [PATCH 49/67] Add answers object to reset commit. --- web/src/store/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web/src/store/index.js b/web/src/store/index.js index b6a80c3..455e571 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -76,6 +76,10 @@ export default new Vuex.Store({ state.questions = []; state.game_code = null; state.players = []; + state.answers = { + team_1: new Array(8).fill(``), + team_2: new Array(8).fill(``), + }; }, player(state, data) { if (data.name) From ee1a19ed2bd123ca3bf25c15f570ad33efd6479b Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 15:05:02 -0700 Subject: [PATCH 50/67] Fix host duplication --- server/src/objects/Game.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 0ff0ed5..0087324 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -143,7 +143,6 @@ export class Game { game.teams = data.teams.map(t => Team.fromJSON(t)); // Re-instantiate all the players from the game. - game.players.push(host) for (var player of data.players) { if (player.name !== host.name) { player.host = false; From 9689b230076a70d3842da820e9b1548eeb6bef43 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 15:33:18 -0700 Subject: [PATCH 51/67] Add data needed for the GameRejoined event --- server/src/events/JoinGame.ts | 54 ++++++++++++++++++++++++++++++----- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 84a0d95..9388c33 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -18,37 +18,60 @@ export default (io: Server, socket: Socket, data: JoinGame) => { )) as datastoreGame; let host = new Player(data.name, socket, true); let game = Game.fromJSON(host, datastore); - game.log = log.getChildLogger({ displayLoggerName: true, name: game.id, }); + // Get the specific information for team + let playerData = datastore.players.find(p => p.name === data.name); + if (playerData) { + host.role = playerData.role; + host.team = playerData.team; + }; + + let hand: string[] = []; if (host.team) { let team = game.teams[host.team - 1]; + hand = team.hand; switch (host.role) { case "guesser": game.log.silly(`${host.name} is one of the team's guessers`); team.guessers.push(host); + socket.join([ + `${game.id}:*:guesser`, + `${game.id}:${team.id}:guesser` + ]); break; case "writer": game.log.silly(`${host.name} is the team's writer`); team.writer = host; + socket.join([ + `${game.id}:*:writer`, + `${game.id}:${team.id}:writer` + ]); break; }; - game.log.debug(`Host assigned to team object.`); + game.log.debug(`Host assigned to team`); }; hibernatedGames.splice(hibernatedIndex, 1); games[game.id] = game; game.log.info(`Successfully unhibernated`); - + socket.join(game.id); socket.emit(`GameRejoined`, { status: 200, - data: { - players: game.players.map(p => p.toJSON()), - + ingame: game.ingame, + role: host.role, + team: host.team, + is_host: true, + players: game.playerData, + chosen_object: game.object, + hand: hand, + answers: { + team_1: game.teams[0].answers, + team_2: game.teams[1].answers, }, }); return; @@ -79,9 +102,26 @@ export default (io: Server, socket: Socket, data: JoinGame) => { if (!sameName.socket?.connected) { sameName.socket = socket; game.log.info(`Player Reconnected to the game (name=${data.name})`); + + // Get the hand of the player's team + let hand: string[] = []; + if (sameName.team) { + hand = game.teams[sameName.team - 1].hand; + }; + socket.emit(`GameRejoined`, { status: 200, - data: {}, + ingame: game.ingame, + role: sameName.role, + team: sameName.team, + is_host: sameName.isHost, + players: game.playerData, + chosen_object: game.object, + answers: { + team_1: game.teams[0].answers, + team_2: game.teams[1].answers, + }, + hand: hand, }); return; } else { From 76e667142f2ed6173a38cb5263ce4f9ed49607a0 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 15:33:37 -0700 Subject: [PATCH 52/67] Prevent error if options is not defined. --- server/src/objects/Game.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 0087324..d26f042 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -23,7 +23,7 @@ export class Game { this.host = host; this.ingame = false; this.players = [host]; - this.id = options.id || Game.generateID(conf.game.code_length); + this.id = options?.id || Game.generateID(conf.game.code_length); // If the object is being instantiated from JSON we don't want to do // any of the stuff that requires weird per-game stuff From 80209790a848a4cc8e554ef0e015e86c6e118c78 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 15:33:53 -0700 Subject: [PATCH 53/67] Sync state upon game rejoin --- web/src/views/CreateJoin.vue | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/web/src/views/CreateJoin.vue b/web/src/views/CreateJoin.vue index 92d4ea3..39db72d 100644 --- a/web/src/views/CreateJoin.vue +++ b/web/src/views/CreateJoin.vue @@ -97,11 +97,42 @@ export default { this.$store.commit(`view`, `lobby`); }, GameRejoined(data) { + /** + * data = { + * status: integer, + * ingame: boolean, + * role: role, + * team: team, + * is_host: boolean, + * players: Player[], + * chosen_object: string, + * hand: string[], + * answers: { + * team_1: string[], + * team_2: string[], + * }, + * }, + */ + console.log(data) if (!(200 <= data.status && data.status < 300)) { this.$emit(`error`, data); return; }; - // TODO -> Update all data that is received from the server + this.$store.commit(`resetState`); + this.$store.commit(`player`, { + name: this.name, + host: data.is_host, + role: data.role, + team: data.team, + }); + this.$store.commit(`setObject`, data.chosen_object); + this.$store.commit(`view`, data.ingame ? `in-game` : `lobby`); + this.$store.commit(`setAnswers`, data.answers); + this.$store.commit(`playerList`, data.players); + this.$store.commit(`replaceHand`, data.hand); + + history.replaceState(null, ``, `?game=${this.game_code}`); + this.$store.commit(`gameCode`, this.game_code); }, }, mounted() {}, From 65776efd06549b9d1cb63824f4c8a2bb29304848 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:11:48 -0700 Subject: [PATCH 54/67] Fix error that prevents rendering of the entire board --- web/src/components/GameBoard.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index 34347f3..30c263c 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -114,7 +114,7 @@ export default { return this.$store.getters.otherTeamName.replace(/\s/g, `-`).toLowerCase(); }, answers() { - return this.$store.state.answer; + return this.$store.state.answers; }, }, methods: { @@ -146,7 +146,7 @@ export default { * value: string * } */ - this.$store.commit(`UpdateAnswer`, data); + this.$store.commit(`updateAnswer`, data); }, }, } From 7993c0257fb70105b28df752b0e322fae40fe7df Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:12:15 -0700 Subject: [PATCH 55/67] Fix data not being populated properly. --- server/src/events/JoinGame.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 9388c33..80fd70d 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -23,6 +23,8 @@ export default (io: Server, socket: Socket, data: JoinGame) => { name: game.id, }); + game.ingame = datastore.ingame; + // Get the specific information for team let playerData = datastore.players.find(p => p.name === data.name); if (playerData) { @@ -33,10 +35,10 @@ export default (io: Server, socket: Socket, data: JoinGame) => { let hand: string[] = []; if (host.team) { let team = game.teams[host.team - 1]; - hand = team.hand; switch (host.role) { case "guesser": game.log.silly(`${host.name} is one of the team's guessers`); + hand = team.hand; team.guessers.push(host); socket.join([ `${game.id}:*:guesser`, @@ -105,7 +107,7 @@ export default (io: Server, socket: Socket, data: JoinGame) => { // Get the hand of the player's team let hand: string[] = []; - if (sameName.team) { + if (sameName.team && sameName.role == `guesser`) { hand = game.teams[sameName.team - 1].hand; }; From 93c0ee591d6993855c7ab9ed7f55f395f109c5c7 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:12:48 -0700 Subject: [PATCH 56/67] Fix setAnswers mutation. --- web/src/store/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/store/index.js b/web/src/store/index.js index 455e571..7ef1b22 100644 --- a/web/src/store/index.js +++ b/web/src/store/index.js @@ -124,7 +124,8 @@ export default new Vuex.Store({ state.answers[`team_${data.team}`].splice(data.answer - 1, 1, data.value) }, setAnswers(state, data) { - state.answers = Vue.reactive(data); + state.answers.team_1 = data.team_1; + state.answers.team_2 = data.team_2; }, }, actions: { From 2d59397c4a1323131913747bc78f09c207a0e7f6 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:13:37 -0700 Subject: [PATCH 57/67] Update event template. --- server/src/events/_template.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/src/events/_template.ts b/server/src/events/_template.ts index c70c896..40d11d2 100644 --- a/server/src/events/_template.ts +++ b/server/src/events/_template.ts @@ -1,14 +1,16 @@ +import { log } from '../main'; import { Server, Socket } from 'socket.io'; export default (io: Server, socket: Socket, data: any) => { try { - socket.emit(`Error`, { + socket.emit(``, { status: 501, message: `: Not Implemented Yet`, source: ``, }); } catch (err) { - socket.emit(`Error`, { + log.prettyError(err); + socket.emit(``, { status: 500, message: `${err.name}: ${err.message}`, source: ``, From eb2a85399b3fcdb7282cdece6becc231667583d5 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:15:43 -0700 Subject: [PATCH 58/67] Update logs / add an error log on 500 errors. --- server/src/events/CreateGame.ts | 1 + server/src/events/DeleteGame.ts | 1 + server/src/events/GetHand.ts | 1 + server/src/events/GetPastQuestions.ts | 1 + server/src/events/JoinGame.ts | 1 + server/src/events/LeaveGame.ts | 1 + server/src/events/NewHand.ts | 1 + server/src/events/ObjectList.ts | 5 +++-- server/src/events/SelectObject.ts | 1 + server/src/events/SendCard.ts | 3 ++- server/src/events/StartGame.ts | 3 ++- server/src/events/UpdateAnswer.ts | 1 + server/src/events/UpdatePlayer.ts | 1 + 13 files changed, 17 insertions(+), 4 deletions(-) diff --git a/server/src/events/CreateGame.ts b/server/src/events/CreateGame.ts index 3a2ee3d..bd52466 100644 --- a/server/src/events/CreateGame.ts +++ b/server/src/events/CreateGame.ts @@ -28,6 +28,7 @@ export default (io: Server, socket: Socket, data: CreateGame) => { routineCheck(); } catch (err) { + log.prettyError(err); socket.emit(`GameCreated`, { status: 500, message: err.message, diff --git a/server/src/events/DeleteGame.ts b/server/src/events/DeleteGame.ts index 87bd33a..00b1624 100644 --- a/server/src/events/DeleteGame.ts +++ b/server/src/events/DeleteGame.ts @@ -39,6 +39,7 @@ export default (io: Server, socket: Socket, data: DeleteGame) => { }); } catch (err) { + log.prettyError(err); socket.emit(`GameDeleted`, { status: 500, message: err.message, diff --git a/server/src/events/GetHand.ts b/server/src/events/GetHand.ts index 4eb1ca7..9e39a8e 100644 --- a/server/src/events/GetHand.ts +++ b/server/src/events/GetHand.ts @@ -23,6 +23,7 @@ export default (io: Server, socket: Socket, data: GetHand) => { }); } catch (err) { + log.prettyError(err); socket.emit(`QuestionList`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/GetPastQuestions.ts b/server/src/events/GetPastQuestions.ts index 8a7100d..c9d7f60 100644 --- a/server/src/events/GetPastQuestions.ts +++ b/server/src/events/GetPastQuestions.ts @@ -24,6 +24,7 @@ export default (io: Server, socket: Socket, data: GetPastQuestions) => { }); } catch (err) { + log.prettyError(err); socket.emit(`PastQuestions`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 80fd70d..27a76be 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -166,6 +166,7 @@ export default (io: Server, socket: Socket, data: JoinGame) => { }); } catch (err) { + log.prettyError(err); socket.emit(`GameJoined`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/LeaveGame.ts b/server/src/events/LeaveGame.ts index 43725e6..e01a0f2 100644 --- a/server/src/events/LeaveGame.ts +++ b/server/src/events/LeaveGame.ts @@ -64,6 +64,7 @@ export default (io: Server, socket: Socket, data: LeaveGame) => { }; } catch (err) { + log.prettyError(err); socket.emit(`GameLeft`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/NewHand.ts b/server/src/events/NewHand.ts index cd41e3d..7acb880 100644 --- a/server/src/events/NewHand.ts +++ b/server/src/events/NewHand.ts @@ -43,6 +43,7 @@ export default (io: Server, socket: Socket, data: NewHand) => { }); } catch (err) { + log.prettyError(err); socket.emit(`UpdateHand`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/ObjectList.ts b/server/src/events/ObjectList.ts index 04f6cfe..11a08e6 100644 --- a/server/src/events/ObjectList.ts +++ b/server/src/events/ObjectList.ts @@ -7,7 +7,7 @@ export default (io: Server, socket: Socket, data: ObjectList) => { // Assert game exists if (!games[data.game_code]) { log.debug(`Can't get objects for game that doesn't exist: ${data.game_code}`); - socket.emit(`Error`, { + socket.emit(`ObjectList`, { status: 404, message: `Game with code ${data.game_code} could not be found`, source: `ObjectList` @@ -22,7 +22,8 @@ export default (io: Server, socket: Socket, data: ObjectList) => { }); } catch (err) { - socket.emit(`Error`, { + log.prettyError(err); + socket.emit(`ObjectList`, { status: 500, message: `${err.name}: ${err.message}`, source: `ObjectList`, diff --git a/server/src/events/SelectObject.ts b/server/src/events/SelectObject.ts index 521522d..d50faf2 100644 --- a/server/src/events/SelectObject.ts +++ b/server/src/events/SelectObject.ts @@ -35,6 +35,7 @@ export default (io: Server, socket: Socket, data: SelectObject) => { }); } catch (err) { + log.prettyError(err); socket.emit(`ChosenObject`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/SendCard.ts b/server/src/events/SendCard.ts index 97b5aa8..67aae24 100644 --- a/server/src/events/SendCard.ts +++ b/server/src/events/SendCard.ts @@ -42,7 +42,7 @@ export default (io: Server, socket: Socket, data: SendCard) => { // The writer is sending the card to the writer else if (data.from === "guesser") { - game.log.debug(`Guesser is sending the card to the writer.`); + game.log.debug(`Guesser is sending a card to the writer.`); // Update the team's hand team.removeCard(data.text); @@ -74,6 +74,7 @@ export default (io: Server, socket: Socket, data: SendCard) => { }; } catch (err) { + log.prettyError(err); socket.emit(`UpdateHand`, { status: 500, message: err.message, diff --git a/server/src/events/StartGame.ts b/server/src/events/StartGame.ts index 719037b..14580e9 100644 --- a/server/src/events/StartGame.ts +++ b/server/src/events/StartGame.ts @@ -7,7 +7,7 @@ export default (io: Server, socket: Socket, data: StartGame) => { // Assert game exists if (!games[data.game_code]) { log.debug(`Could not find a game with ID ${data.game_code} to start`); - socket.emit(`GameJoined`, { + socket.emit(`GameStarted`, { status: 404, message: `Game with code "${data.game_code}" could not be found`, source: `StartGame`, @@ -65,6 +65,7 @@ export default (io: Server, socket: Socket, data: StartGame) => { io.to(game.id).emit(`GameStarted`, { status: 200 }); } catch (err) { + log.prettyError(err); socket.emit(`GameStarted`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/UpdateAnswer.ts b/server/src/events/UpdateAnswer.ts index 0821170..ee7c2c8 100644 --- a/server/src/events/UpdateAnswer.ts +++ b/server/src/events/UpdateAnswer.ts @@ -26,6 +26,7 @@ export default (io: Server, socket: Socket, data: UpdateAnswer) => { }); } catch (err) { + log.prettyError(err); socket.emit(`UpdateAnswer`, { status: 500, message: `${err.name}: ${err.message}`, diff --git a/server/src/events/UpdatePlayer.ts b/server/src/events/UpdatePlayer.ts index af1de08..f2dff42 100644 --- a/server/src/events/UpdatePlayer.ts +++ b/server/src/events/UpdatePlayer.ts @@ -32,6 +32,7 @@ export default (io: Server, socket: Socket, data: UpdatePlayer) => { }; } catch (err) { + log.prettyError(err); socket.emit(`PlayerUpdate`, { status: 500, message: `${err.name}: ${err.message}`, From c0a2c22691a03809d6fcdf52710b935b94a2708c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 16:29:08 -0700 Subject: [PATCH 59/67] Add missing punctuation --- web/src/components/Hand.vue | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/web/src/components/Hand.vue b/web/src/components/Hand.vue index 54e972f..26edebc 100644 --- a/web/src/components/Hand.vue +++ b/web/src/components/Hand.vue @@ -3,6 +3,9 @@
{{ mostRecentQuestion }}
+
+ +
Date: Thu, 7 Jan 2021 18:14:17 -0700 Subject: [PATCH 60/67] Add ResetGame functionality. --- server/src/events/ResetGame.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 server/src/events/ResetGame.ts diff --git a/server/src/events/ResetGame.ts b/server/src/events/ResetGame.ts new file mode 100644 index 0000000..07e403d --- /dev/null +++ b/server/src/events/ResetGame.ts @@ -0,0 +1,29 @@ +import { games, log } from '../main'; +import { Server, Socket } from 'socket.io'; + +export default (io: Server, socket: Socket, data: any) => { + try { + if (!games[data.game_code]) { + log.debug(`Can't find game with code: ${data.game_code}`); + socket.emit(`GameReset`, { + status: 404, + message: `Can't find game with code: ${data.game_code}`, + source: `ResetGame` + }); + return; + }; + let game = games[data.game_code]; + + game.questions.reset(); + game.resetObject(); + + io.to(game.id).emit(`GameReset`, { status: 200 }); + } catch (err) { + log.prettyError(err); + socket.emit(`GameReset`, { + status: 500, + message: `${err.name}: ${err.message}`, + source: `ResetGame`, + }); + } +}; \ No newline at end of file From aa5159d10f1896cf5107cc430a1b179fb4aa8db3 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:14:39 -0700 Subject: [PATCH 61/67] Register ResetGame listener --- server/src/websocket.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/websocket.ts b/server/src/websocket.ts index 5a7bfdd..f3e6ac5 100644 --- a/server/src/websocket.ts +++ b/server/src/websocket.ts @@ -5,6 +5,7 @@ import GetHand from "./events/GetHand"; import NewHand from "./events/NewHand"; import JoinGame from "./events/JoinGame"; import SendCard from "./events/SendCard"; +import ResetGame from "./events/ResetGame"; import LeaveGame from "./events/LeaveGame"; import StartGame from "./events/StartGame"; import CreateGame from "./events/CreateGame"; @@ -33,6 +34,7 @@ export default async (conf: config) => { // Game Management socket.on(`CreateGame`, (data: CreateGame) => CreateGame(io, socket, data)); socket.on(`StartGame`, (data: StartGame) => StartGame(io, socket, data)); + socket.on(`ResetGame`, (data: ResetGame) => ResetGame(io, socket, data)); socket.on(`DeleteGame`, (data: DeleteGame) => DeleteGame(io, socket, data)); From 3441e5acd2c3cb8e546d8dc4456e3e89fa32538f Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:15:32 -0700 Subject: [PATCH 62/67] Add ResetGame types --- server/src/events/ResetGame.ts | 2 +- server/src/types/data.d.ts | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/events/ResetGame.ts b/server/src/events/ResetGame.ts index 07e403d..8c2b093 100644 --- a/server/src/events/ResetGame.ts +++ b/server/src/events/ResetGame.ts @@ -1,7 +1,7 @@ import { games, log } from '../main'; import { Server, Socket } from 'socket.io'; -export default (io: Server, socket: Socket, data: any) => { +export default (io: Server, socket: Socket, data: ResetGame) => { try { if (!games[data.game_code]) { log.debug(`Can't find game with code: ${data.game_code}`); diff --git a/server/src/types/data.d.ts b/server/src/types/data.d.ts index 82dc1e3..07b35b8 100644 --- a/server/src/types/data.d.ts +++ b/server/src/types/data.d.ts @@ -45,6 +45,11 @@ interface StartGame { } interface GameStarted extends response {} +interface ResetGame { + game_code: string; +} +interface GameReset extends response {} + interface GetPastQuestions { game_code: string; From 8e5cc3a6122cbdd72ca87a01b3621c4807222c42 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:15:41 -0700 Subject: [PATCH 63/67] Add reset method. --- server/src/objects/Deck.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/server/src/objects/Deck.ts b/server/src/objects/Deck.ts index 64404ec..a65e249 100644 --- a/server/src/objects/Deck.ts +++ b/server/src/objects/Deck.ts @@ -58,6 +58,13 @@ export class Deck { }; + public reset() { + this._deck.push(...this._discard, ...this._unknown); + this._discard = []; + this._unknown = []; + }; + + public toJSON(): datastoreDeck { /** * Converts this Deck into a JSON-compatible object From d2760c6a265ff7913ad1b451b39e194efd8e8bef Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:15:57 -0700 Subject: [PATCH 64/67] Add reset method. --- server/src/objects/Game.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index d26f042..c3e2e99 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -15,7 +15,7 @@ export class Game { public players: Player[]; private _questions: Deck; private _objects: Deck; - private _objectCard: string[]; + private _objectCard: string[]|null; public object: string; @@ -68,7 +68,7 @@ export class Game { } - private parseDeckCSV(conf: config): any { + private parseDeckCSV(conf: config) { /** * Parses out the CSV files and creates the decks for the game to run on * @@ -102,7 +102,7 @@ export class Game { }); }; - private parseDeckGoogleSheets(conf: config): void { + private parseDeckGoogleSheets(conf: config) { /** * Fetches and parses the CSV data from Google Sheets instead of local * CSV files. @@ -112,6 +112,17 @@ export class Game { }; + public resetObject() { + /** + * Resets the objects card, for restarting the game + */ + if (this._objectCard) { + this._objects.discard(this._objectCard); + this._objectCard = null; + }; + }; + + public toJSON(): datastoreGame { /** * Returns a JSON representation of the game. From 2f190cafd8181448b8be93a2f93971be3d1c54cb Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:16:27 -0700 Subject: [PATCH 65/67] Allow null object cards. --- server/src/types/datastore.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/types/datastore.d.ts b/server/src/types/datastore.d.ts index 3571e09..e819395 100644 --- a/server/src/types/datastore.d.ts +++ b/server/src/types/datastore.d.ts @@ -26,7 +26,7 @@ interface datastoreGame { questions: datastoreDeck; objects: datastoreDeck; }; - objectCard: datastoreObjectCard; + objectCard: datastoreObjectCard|null; players: datastorePlayer[]; teams: datastoreTeam[]; ingame: boolean; From c1226a81c518b56127a9fc2070cf1594b987b1c8 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:16:54 -0700 Subject: [PATCH 66/67] Add styling for when an answer is correct. --- web/src/components/GameBoard.vue | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index 30c263c..81d94c6 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -10,8 +10,11 @@ a model attribute to keep them synced correctly. -->
input { + border-color: green !important; + border-width: 3px; +} .eye-container { position: absolute; From 21bca5f468570ef24e113faa34891a0bd6fb7882 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Thu, 7 Jan 2021 18:17:37 -0700 Subject: [PATCH 67/67] Add ResetGame button when someone has the right answer. --- web/src/components/Hand.vue | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/web/src/components/Hand.vue b/web/src/components/Hand.vue index 26edebc..43a215d 100644 --- a/web/src/components/Hand.vue +++ b/web/src/components/Hand.vue @@ -1,10 +1,15 @@