From 78d1d979de90d11be1cf91dfd02d10b3aad8df0c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 13:58:17 -0700 Subject: [PATCH 01/15] Change to a strict type check --- server/src/events/JoinGame.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 7a274f4..3936ce1 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -94,8 +94,8 @@ export default (io: Server, socket: Socket, data: JoinGame) => { 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) { + let sameName = game.players.find(x => x.name === data.name); + if (sameName) { if (!sameName.socket?.connected) { sameName.socket = socket; From daa18d6a9a3e74ad71b9458ded87759da7fc737f Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 14:13:58 -0700 Subject: [PATCH 02/15] Fix: Character cases causing interface not recognize the game end. (closes #47) --- web/src/components/GameBoard.vue | 12 ++++++++++-- web/src/components/Hand.vue | 9 +++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/web/src/components/GameBoard.vue b/web/src/components/GameBoard.vue index 8db1d70..3780643 100644 --- a/web/src/components/GameBoard.vue +++ b/web/src/components/GameBoard.vue @@ -13,7 +13,8 @@ v-for="answerIndex in 8" :class="[ `answer`, - answers[`team_${3 - $store.state.team}`][answerIndex-1].toLowerCase() == $store.state.chosen_object+`.` ? `correct`: `` + answers[`team_${3 - $store.state.team}`][answerIndex-1].toLowerCase() === getObject + ? `correct`: `` ]" :key="`${otherTeamID}-answer-container-${answerIndex}`" > @@ -57,7 +58,8 @@ v-for="answerIndex in 8" :class="[ `answer`, - answers[`team_${$store.state.team}`][answerIndex-1].toLowerCase() == $store.state.chosen_object+`.` ? `correct`: `` + answers[`team_${$store.state.team}`][answerIndex-1].toLowerCase() === getObject + ? `correct`: `` ]" :key="`${teamID}-answer-container-${answerIndex}`" > @@ -122,6 +124,12 @@ export default { answers() { return this.$store.state.answers; }, + getObject() { + if (!this.$store.state.chosen_object) { + return ``; + }; + return this.$store.state.chosen_object.toLowerCase() + `.`; + }, }, methods: { isCorrect(team, answerIndex) { diff --git a/web/src/components/Hand.vue b/web/src/components/Hand.vue index ba6b307..f07d3b9 100644 --- a/web/src/components/Hand.vue +++ b/web/src/components/Hand.vue @@ -74,8 +74,13 @@ export default { gameOver() { if (this.$store.state.chosen_object) { let targetAnswer = this.$store.state.chosen_object.toLowerCase()+`.`; - return this.$store.state.answers.team_1.includes(targetAnswer) - || this.$store.state.answers.team_2.includes(targetAnswer); + for (var team in this.$store.state.answers) { + for (var answer of this.$store.state.answers[team]) { + if (answer.toLowerCase() === targetAnswer) { + return true; + }; + }; + }; }; return false; }, From 6bdf0ee17cc8c88d9b4834c8bbf6a0e631dfc224 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 14:15:18 -0700 Subject: [PATCH 03/15] Fix spelling error in conditional (closes #46) --- web/src/components/Hand.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/components/Hand.vue b/web/src/components/Hand.vue index f07d3b9..b68e3d5 100644 --- a/web/src/components/Hand.vue +++ b/web/src/components/Hand.vue @@ -5,7 +5,7 @@
Date: Sun, 10 Jan 2021 14:15:48 -0700 Subject: [PATCH 04/15] Add bash scripts to the gitignore. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6f52af8..cbbc451 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ server/built/* server/dist/* server/resources/* *.log +*.sh #=============================================================================# # The files that were auto-generated into a .gitignore by Vue-cli From e0f76432bf48aea33976914e522b94bb159f6a37 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 14:21:42 -0700 Subject: [PATCH 05/15] Update error response message to not give more info away than needed. --- server/src/events/JoinGame.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 3936ce1..aa202b0 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -132,7 +132,7 @@ export default (io: Server, socket: Socket, data: JoinGame) => { game.log.debug(`${socket.id} attempted to join with a name already in use ${data.name}`); socket.emit(`GameJoined`, { status: 403, - message: `A player already has that name in the game.`, + message: `Cannot connect to a game that's in progress.`, source: `JoinGame` }); return; From 9a398e41a83528b745856a148a368da46bcce8ea Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 14:38:44 -0700 Subject: [PATCH 06/15] Log that the game is being reset and mark game as in lobby --- server/src/events/ResetGame.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/events/ResetGame.ts b/server/src/events/ResetGame.ts index 8c2b093..388d5a7 100644 --- a/server/src/events/ResetGame.ts +++ b/server/src/events/ResetGame.ts @@ -13,9 +13,11 @@ export default (io: Server, socket: Socket, data: ResetGame) => { return; }; let game = games[data.game_code]; + game.log.info(`Resetting game`) game.questions.reset(); game.resetObject(); + game.ingame = false; io.to(game.id).emit(`GameReset`, { status: 200 }); } catch (err) { From fd1129362353e589d2e1f3c97a654527c61b64c9 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 14:39:23 -0700 Subject: [PATCH 07/15] Set object to a falsey value when resetting --- server/src/objects/Game.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index c3e2e99..77bf99c 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -119,6 +119,7 @@ export class Game { if (this._objectCard) { this._objects.discard(this._objectCard); this._objectCard = null; + this.object = ``; }; }; From d66f538f3a00a6626c8885ece56be9d666c4681e Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 15:41:11 -0700 Subject: [PATCH 08/15] Move the playerData check around --- server/src/events/JoinGame.ts | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index aa202b0..3509e39 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -16,22 +16,29 @@ export default (io: Server, socket: Socket, data: JoinGame) => { `${conf.datastores.directory}/${data.game_code}.${conf.datastores.filetype}`, `utf-8` )) as datastoreGame; + + let playerData = datastore.players.find(p => p.name === data.name); + + // Assert that the name matches someone in the hibernated game + if (!playerData) { + log.info(`[${data.game_code}] User attempted unhibernate game with an invalid name`); + socket.emit(`GameJoined`, { + status: 403, + message: `Game with code "${data.game_code}" could not be found`, + source: `JoinGame` + }); + return; + } + + // Instantiate the host's player object let host = new Player(data.name, socket, true); let game = Game.fromJSON(host, datastore); game.log = log.getChildLogger({ displayLoggerName: true, 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) { - host.role = playerData.role; - host.team = playerData.team; - }; - let hand: string[] = []; if (host.team) { let team = game.teams[host.team - 1]; From 0f74a60cb3a8167e0db13566a4089fd964cd9e71 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 15:41:48 -0700 Subject: [PATCH 09/15] Adjust logs and comments & a couple assignments --- server/src/events/JoinGame.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index 3509e39..c6c745b 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -10,8 +10,9 @@ export default (io: Server, socket: Socket, data: JoinGame) => { // 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.`); + log.info(`Attempting to recreate game from datastore.`); + // Reinstantiate the game using the data from the disk let datastore = JSON.parse(readFileSync( `${conf.datastores.directory}/${data.game_code}.${conf.datastores.filetype}`, `utf-8` @@ -32,6 +33,10 @@ export default (io: Server, socket: Socket, data: JoinGame) => { // Instantiate the host's player object let host = new Player(data.name, socket, true); + host.role = playerData.role; + host.team = playerData.team; + + // Re-instantiate the game object let game = Game.fromJSON(host, datastore); game.log = log.getChildLogger({ displayLoggerName: true, @@ -57,7 +62,7 @@ export default (io: Server, socket: Socket, data: JoinGame) => { `${game.id}:*:${host.role}`, `${game.id}:${host.team}:${host.role}` ]); - game.log.debug(`Host assigned to team`); + game.log.debug(`Host assigned to team object`); }; hibernatedGames.splice(hibernatedIndex, 1); From bd70fa5f99fd086e333f268ab25e6fe855b75941 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 15:42:14 -0700 Subject: [PATCH 10/15] Change how the status check is working --- web/src/views/CreateJoin.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/views/CreateJoin.vue b/web/src/views/CreateJoin.vue index 39db72d..2861f53 100644 --- a/web/src/views/CreateJoin.vue +++ b/web/src/views/CreateJoin.vue @@ -114,7 +114,7 @@ export default { * }, */ console.log(data) - if (!(200 <= data.status && data.status < 300)) { + if (data.status < 200 || 300 <= data.status) { this.$emit(`error`, data); return; }; From d4356348dc5849b63579e9a4a58e91c4bcf04723 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 16:14:35 -0700 Subject: [PATCH 11/15] Delete datastore when it's reloaded --- server/src/events/JoinGame.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/server/src/events/JoinGame.ts b/server/src/events/JoinGame.ts index c6c745b..b37c02b 100644 --- a/server/src/events/JoinGame.ts +++ b/server/src/events/JoinGame.ts @@ -1,7 +1,7 @@ -import { readFileSync } from 'fs'; import { Game } from '../objects/Game'; import { Player } from '../objects/Player'; import { Server, Socket } from 'socket.io'; +import { readFileSync, unlinkSync } from 'fs'; import { games, hibernatedGames, log, conf } from '../main'; export default (io: Server, socket: Socket, data: JoinGame) => { @@ -68,6 +68,14 @@ export default (io: Server, socket: Socket, data: JoinGame) => { hibernatedGames.splice(hibernatedIndex, 1); games[game.id] = game; + // Try removing the file from the directory + try { + unlinkSync(`${conf.datastores.directory}/${game.id}.${conf.datastores.filetype}`); + game.log.info(`Game datastore deleted`); + } catch (err) { + game.log.prettyError(err); + }; + game.log.info(`Successfully unhibernated`); socket.join(game.id); socket.emit(`GameRejoined`, { From 9afec3f372fa417b620488ba47cafa23eb29cc2f Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Sun, 10 Jan 2021 16:31:25 -0700 Subject: [PATCH 12/15] Move guesser limit to config file. --- server/src/events/UpdatePlayer.ts | 18 +++++++++--------- server/src/types/config.d.ts | 1 + server/template.toml | 2 ++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/server/src/events/UpdatePlayer.ts b/server/src/events/UpdatePlayer.ts index f2dff42..9cc3799 100644 --- a/server/src/events/UpdatePlayer.ts +++ b/server/src/events/UpdatePlayer.ts @@ -85,11 +85,11 @@ const modifyPlayer = (io: Server, socket: Socket, data: UpdatePlayer): void => { switch (data.to.role) { case "guesser": - if (team.guessers.length >= 7) { - game.log.debug(`Game cannot have more than 7 guessers`); + if (team.guessers.length >= conf.game.guesser_limit) { + game.log.debug(`Game cannot have more than ${conf.game.guesser_limit} guessers`); socket.emit(`PlayerUpdate`, { status: 403, - message: `A team can't have 8 or more ${conf.game.guesser_name}`, + message: `That team already has the maximum number of ${conf.game.guesser_name}s`, source: `UpdatePlayer.Modify` }); return; @@ -128,11 +128,11 @@ const modifyPlayer = (io: Server, socket: Socket, data: UpdatePlayer): void => { let team = game.teams[data.to.team - 1]; switch (data.to.role) { case "guesser": - if (team.guessers.length >= 7) { - game.log.debug(`Game cannot have more than 7 guessers`); + if (team.guessers.length >= conf.game.guesser_limit) { + game.log.debug(`Game cannot have more than ${conf.game.guesser_limit} guessers`); socket.emit(`PlayerUpdate`, { status: 403, - message: `A team can't have 8 or more ${conf.game.guesser_name}`, + message: `That team already has the maximum number of ${conf.game.guesser_name}s`, source: `UpdatePlayer.Modify` }); return; @@ -181,11 +181,11 @@ const modifyPlayer = (io: Server, socket: Socket, data: UpdatePlayer): void => { switch (data.to.role) { case "guesser": // Ensure we don't get 8 guessers - if (newTeam.guessers.length >= 7) { - game.log.debug(`Game cannot have 8 or more guessers on a team.`); + if (newTeam.guessers.length >= conf.game.guesser_limit) { + game.log.debug(`That team already`); socket.emit(`PlayerUpdate`, { status: 403, - message: `Cannot have 8 players as ${conf.game.guesser_name}s on a single team.`, + message: `That team already has the maximum number of ${conf.game.guesser_name}s`, source: `UpdatePlayer.Modify` }); return; diff --git a/server/src/types/config.d.ts b/server/src/types/config.d.ts index dfda422..4e59563 100644 --- a/server/src/types/config.d.ts +++ b/server/src/types/config.d.ts @@ -17,6 +17,7 @@ interface config { code_length: number; writer_name: string; guesser_name: string; + guesser_limit: number; cards: { type: `csv` | `sheets`; key?: string; diff --git a/server/template.toml b/server/template.toml index 2d85d90..ca45d4b 100644 --- a/server/template.toml +++ b/server/template.toml @@ -11,6 +11,8 @@ code_length = 6 writer_name = "Spirit" guesser_name = "Medium" +# The limit of players that are allowed to be guessers on each team. +guesser_limit = 7 [game.cards] From f5a2de29de8a5dc407b4e5b5eb927b29b2ba78cb Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 11 Jan 2021 16:33:58 -0700 Subject: [PATCH 13/15] Implement method to download the CSV data from Google Sheets --- server/src/objects/Game.ts | 68 ++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/server/src/objects/Game.ts b/server/src/objects/Game.ts index 77bf99c..f02db0e 100644 --- a/server/src/objects/Game.ts +++ b/server/src/objects/Game.ts @@ -1,10 +1,11 @@ +import axios from "axios"; import { Team } from "./Team"; import { Deck } from "./Deck"; import { readFile } from "fs"; import neatCSV from "neat-csv"; import { Logger } from "tslog"; import { Player } from "./Player"; -import { games, hibernatedGames, conf } from "../main"; +import { games, hibernatedGames, conf, log } from "../main"; export class Game { readonly id: string; @@ -32,10 +33,10 @@ export class Game { // Get the decks based on what type of data they are. switch (conf.game.cards.type) { case "csv": - this.parseDeckCSV(conf); + this.parseDeckCSV(); break; case "sheets": - this.parseDeckGoogleSheets(conf); + this.parseDeckGoogleSheets(); break; }; // Instantiate everything for the teams @@ -68,11 +69,10 @@ export class Game { } - private parseDeckCSV(conf: config) { + private parseDeckCSV() { /** - * Parses out the CSV files and creates the decks for the game to run on - * - * @param path -> The filepath of the CSV file + * Parses out the CSV files and creates the decks for the game to run + * on. */ // parse the questions from the CSV @@ -102,13 +102,61 @@ export class Game { }); }; - private parseDeckGoogleSheets(conf: config) { + private parseDeckGoogleSheets() { /** * Fetches and parses the CSV data from Google Sheets instead of local * CSV files. - * - * @param conf -> The config object */ + let key = conf.game.cards.key as string; + let questions_id = conf.game.cards.questions.fingerprint; + let objects_id = conf.game.cards.objects.fingerprint; + + // Get the questions deck + axios.get(`https://docs.google.com/spreadsheets/d/e/${key}/pub?gid=${questions_id}&single=true&output=csv`) + .then(response => { + // Ensure not errored + if (response.status !== 200) { + log.warn(`Error Downloading CSV: ${response.statusText}`); + return; + }; + + // Parse the loaded CSV + neatCSV(response.data) + .then((data) => { + let questions: question_deck[] = []; + for (var entry of data) { + questions.push(Object.values(entry)[conf.game.cards.questions.column]); + }; + this._questions = new Deck(questions); + }); + }) + .catch(err => { + log.prettyError(err); + }); + + + // Get the objects deck + axios.get(`https://docs.google.com/spreadsheets/d/e/${key}/pub?gid=${objects_id}&single=true&output=csv`) + .then(response => { + // Ensure not errored + if (response.status !== 200) { + log.warn(`Error Downloading CSV: ${response.statusText}`); + return; + }; + + // Parse the downloaded CSV + neatCSV(response.data) + .then((data) => { + let objects: object_deck[] = []; + for (var line of data) { + objects.push(Object.values(line)); + }; + this._objects = new Deck(objects); + }); + }) + .catch(err => { + log.prettyError(err); + }); }; From 6f3301e1c9739db6170a3badea26ec73388cdd1c Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 11 Jan 2021 16:34:17 -0700 Subject: [PATCH 14/15] Allow "sheets" to be used in the config --- 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 62ffe6a..b628dec 100644 --- a/server/src/utils/validate.ts +++ b/server/src/utils/validate.ts @@ -12,7 +12,7 @@ export class Validate { }; // Assert data in the game object - if (![`csv`].includes(conf.game.cards.type)) { + if (![`csv`, `sheets`].includes(conf.game.cards.type)) { log.error(`Unsupported cards type: ${conf.game.cards.type}`); valid = false; }; From 62e42b20e3f4eab876939a0f6fd472ec72a54ca1 Mon Sep 17 00:00:00 2001 From: Oliver-Akins Date: Mon, 11 Jan 2021 16:35:23 -0700 Subject: [PATCH 15/15] Add Axios as a dependency --- server/package.json | 1 + server/pnpm-lock.yaml | 19 +++++++++++++++++++ web/src/components/Attributions.vue | 1 + 3 files changed, 21 insertions(+) diff --git a/server/package.json b/server/package.json index b21b2d4..d2bee85 100644 --- a/server/package.json +++ b/server/package.json @@ -15,6 +15,7 @@ "@types/engine.io": "^3.1.4", "@types/node": "^14.14.14", "@types/socket.io": "^2.1.12", + "axios": "^0.21.1", "fs": "^0.0.1-security", "neat-csv": "^6.0.0", "socket.io": "^3.0.4", diff --git a/server/pnpm-lock.yaml b/server/pnpm-lock.yaml index e1a6db5..1525650 100644 --- a/server/pnpm-lock.yaml +++ b/server/pnpm-lock.yaml @@ -2,6 +2,7 @@ dependencies: '@types/engine.io': 3.1.4 '@types/node': 14.14.14 '@types/socket.io': 2.1.12 + axios: 0.21.1 fs: 0.0.1-security neat-csv: 6.0.0 socket.io: 3.0.4 @@ -54,6 +55,12 @@ packages: node: '>= 0.6' resolution: integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + /axios/0.21.1: + dependencies: + follow-redirects: 1.13.1 + dev: false + resolution: + integrity: sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== /base64-arraybuffer/0.1.4: dev: false engines: @@ -127,6 +134,17 @@ packages: node: '>=10.0.0' resolution: integrity: sha512-Ri+whTNr2PKklxQkfbGjwEo+kCBUM4Qxk4wtLqLrhH+b1up2NFL9g9pjYWiCV/oazwB0rArnvF/ZmZN2ab5Hpg== + /follow-redirects/1.13.1: + dev: false + engines: + node: '>=4.0' + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + resolution: + integrity: sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== /fs/0.0.1-security: dev: false resolution: @@ -266,6 +284,7 @@ specifiers: '@types/engine.io': ^3.1.4 '@types/node': ^14.14.14 '@types/socket.io': ^2.1.12 + axios: ^0.21.1 fs: ^0.0.1-security neat-csv: ^6.0.0 socket.io: ^3.0.4 diff --git a/web/src/components/Attributions.vue b/web/src/components/Attributions.vue index 7d8a53d..d6b93c0 100644 --- a/web/src/components/Attributions.vue +++ b/web/src/components/Attributions.vue @@ -61,6 +61,7 @@ export default { "Toml": "https://www.npmjs.com/package/toml", "tslog": "https://www.npmjs.com/package/tslog", "Socket.io": "https://socket.io", + "Axios": "https://www.npmjs.com/package/axios", "neat-csv": "https://github.com/sindresorhus/neat-csv", } }},