From 1838dfa74b20700ee0afbd68ffa655854425c115 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Fri, 1 Dec 2023 14:27:44 +0000 Subject: [PATCH 01/24] Fix king of the hill center star calculation and add 48p support --- server/config/game/settings/options.json | 18 +++++++++++++++++- server/db/models/schemas/game.ts | 4 ++-- server/services/gameCreate.ts | 4 ++++ server/services/star.ts | 14 +++++++++++++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/server/config/game/settings/options.json b/server/config/game/settings/options.json index 9cc29abc9..c8f2a1b4c 100644 --- a/server/config/game/settings/options.json +++ b/server/config/game/settings/options.json @@ -45,7 +45,23 @@ 29, 30, 31, - 32 + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48 ], "playerType":[ { diff --git a/server/db/models/schemas/game.ts b/server/db/models/schemas/game.ts index 908016e27..b812b6bb6 100644 --- a/server/db/models/schemas/game.ts +++ b/server/db/models/schemas/game.ts @@ -43,7 +43,7 @@ const schema = new Schema({ featured: { type: Types.Boolean, required: false, default: false }, password: { type: Types.String, required: false, default: null }, passwordRequired: { type: Types.Boolean, required: false, default: false }, - playerLimit: { type: Types.Number, required: true, default: 8, min: 2, max: 32 }, + playerLimit: { type: Types.Number, required: true, default: 8, min: 2, max: 48 }, playerType: { type: Types.String, required: true, enum: ['all', 'establishedPlayers'], default: 'all' }, anonymity: { type: Types.String, required: true, enum: ['normal', 'extra'], default: 'normal' }, playerOnlineStatus: { type: Types.String, required: true, enum: ['hidden', 'visible'], default: 'hidden' }, @@ -127,7 +127,7 @@ const schema = new Schema({ diplomacy: { enabled: { type: Types.String, required: true, enum: ['enabled', 'disabled'], default: 'disabled' }, tradeRestricted: { type: Types.String, required: true, enum: ['enabled', 'disabled'], default: 'disabled' }, - maxAlliances: { type: Types.Number, required: true, min: 1, max: 31, default: 31 }, + maxAlliances: { type: Types.Number, required: true, min: 1, max: 47, default: 47 }, upkeepCost: { type: Types.String, required: true, enum: ['none', 'cheap', 'standard', 'expensive', 'crazyExpensive'], default: 'none' }, globalEvents: { type: Types.String, required: true, enum: ['enabled', 'disabled'], default: 'disabled' } }, diff --git a/server/services/gameCreate.ts b/server/services/gameCreate.ts index 68eee882b..ca19e2d68 100644 --- a/server/services/gameCreate.ts +++ b/server/services/gameCreate.ts @@ -135,6 +135,10 @@ export default class GameCreateService { throw new ValidationError(`Cannot create a galaxy of ${desiredStarCount} stars with ${game.settings.player.startingStars} stars per player.`); } + if (desiredStarCount > 1000) { + throw new ValidationError(`Galaxy size cannot exceed 1000 stars.`); + } + // Ensure that c2c combat is disabled for orbital games. if (game.settings.orbitalMechanics.enabled === 'enabled' && game.settings.specialGalaxy.carrierToCarrierCombat === 'enabled') { game.settings.specialGalaxy.carrierToCarrierCombat = 'disabled'; diff --git a/server/services/star.ts b/server/services/star.ts index eafaaa974..05d3c3c81 100644 --- a/server/services/star.ts +++ b/server/services/star.ts @@ -792,7 +792,19 @@ export default class StarService extends EventEmitter { getKingOfTheHillStar(game: Game) { const center = this.starDistanceService.getGalacticCenter(); - return game.galaxy.stars.find(s => s.location.x === center.x && s.location.y === center.y)!; + // Note: We have to get the closest one to the center as its possible + // to move the center star by using a stellar engine so we can't assume + // the center star will always be at 0,0 + const closestToCenter = game.galaxy.stars.map(star => { + const distance = this.distanceService.getDistanceBetweenLocations(center, star.location); + + return { + star, + distance + } + }).sort((a, b) => a.distance - b.distance)[0].star; + + return closestToCenter; } isKingOfTheHillStar(star: Star) { From 2fe273eb5dffceb15297ce0b1958fcd6abaef0ea Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Tue, 5 Dec 2023 13:55:12 +0000 Subject: [PATCH 02/24] Disable abandoning stars in standard games --- server/config/game/settings/official/1v1.json | 2 +- server/config/game/settings/official/1v1turnBased.json | 2 +- server/config/game/settings/official/32player.json | 2 +- server/config/game/settings/official/special_anonymous.json | 2 +- server/config/game/settings/official/special_arcade.json | 2 +- server/config/game/settings/official/special_battleRoyale.json | 2 +- server/config/game/settings/official/special_dark.json | 2 +- server/config/game/settings/official/special_fog.json | 2 +- server/config/game/settings/official/special_freeForAll.json | 2 +- server/config/game/settings/official/special_homeStar.json | 2 +- .../game/settings/official/special_homeStarElimination.json | 2 +- server/config/game/settings/official/special_kingOfTheHill.json | 2 +- server/config/game/settings/official/special_orbital.json | 2 +- server/config/game/settings/official/special_tinyGalaxy.json | 2 +- server/config/game/settings/official/special_ultraDark.json | 2 +- server/config/game/settings/official/standard.json | 2 +- server/config/game/settings/official/turnBased.json | 2 +- server/config/game/settings/user/standard.json | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/server/config/game/settings/official/1v1.json b/server/config/game/settings/official/1v1.json index 16437571c..5466d209a 100644 --- a/server/config/game/settings/official/1v1.json +++ b/server/config/game/settings/official/1v1.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "disabled", diff --git a/server/config/game/settings/official/1v1turnBased.json b/server/config/game/settings/official/1v1turnBased.json index ac28ccb65..48ac16a2c 100644 --- a/server/config/game/settings/official/1v1turnBased.json +++ b/server/config/game/settings/official/1v1turnBased.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "disabled", diff --git a/server/config/game/settings/official/32player.json b/server/config/game/settings/official/32player.json index a3d96b807..05ed59054 100644 --- a/server/config/game/settings/official/32player.json +++ b/server/config/game/settings/official/32player.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_anonymous.json b/server/config/game/settings/official/special_anonymous.json index 63d0dd4c0..2d1462f25 100644 --- a/server/config/game/settings/official/special_anonymous.json +++ b/server/config/game/settings/official/special_anonymous.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_arcade.json b/server/config/game/settings/official/special_arcade.json index 21df37257..4c5f880d7 100644 --- a/server/config/game/settings/official/special_arcade.json +++ b/server/config/game/settings/official/special_arcade.json @@ -82,7 +82,7 @@ "enabled": "enabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_battleRoyale.json b/server/config/game/settings/official/special_battleRoyale.json index 5a085f5cf..7e7177979 100644 --- a/server/config/game/settings/official/special_battleRoyale.json +++ b/server/config/game/settings/official/special_battleRoyale.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_dark.json b/server/config/game/settings/official/special_dark.json index d62e6bafd..4b37ae652 100644 --- a/server/config/game/settings/official/special_dark.json +++ b/server/config/game/settings/official/special_dark.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_fog.json b/server/config/game/settings/official/special_fog.json index 31ba36afa..30ee125f6 100644 --- a/server/config/game/settings/official/special_fog.json +++ b/server/config/game/settings/official/special_fog.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_freeForAll.json b/server/config/game/settings/official/special_freeForAll.json index 318ca8a67..7f7feb137 100644 --- a/server/config/game/settings/official/special_freeForAll.json +++ b/server/config/game/settings/official/special_freeForAll.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "disabled", diff --git a/server/config/game/settings/official/special_homeStar.json b/server/config/game/settings/official/special_homeStar.json index 326ac5c2f..49c6c7f27 100644 --- a/server/config/game/settings/official/special_homeStar.json +++ b/server/config/game/settings/official/special_homeStar.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_homeStarElimination.json b/server/config/game/settings/official/special_homeStarElimination.json index 849a184b9..6fbab4793 100644 --- a/server/config/game/settings/official/special_homeStarElimination.json +++ b/server/config/game/settings/official/special_homeStarElimination.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_kingOfTheHill.json b/server/config/game/settings/official/special_kingOfTheHill.json index 39f03be71..22608b47f 100644 --- a/server/config/game/settings/official/special_kingOfTheHill.json +++ b/server/config/game/settings/official/special_kingOfTheHill.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_orbital.json b/server/config/game/settings/official/special_orbital.json index f913dc759..630212311 100644 --- a/server/config/game/settings/official/special_orbital.json +++ b/server/config/game/settings/official/special_orbital.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_tinyGalaxy.json b/server/config/game/settings/official/special_tinyGalaxy.json index 5db53ea9a..cfc27ad44 100644 --- a/server/config/game/settings/official/special_tinyGalaxy.json +++ b/server/config/game/settings/official/special_tinyGalaxy.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/special_ultraDark.json b/server/config/game/settings/official/special_ultraDark.json index 66b0f36ce..20c657b31 100644 --- a/server/config/game/settings/official/special_ultraDark.json +++ b/server/config/game/settings/official/special_ultraDark.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/standard.json b/server/config/game/settings/official/standard.json index f4bdabb6d..68a4642ad 100644 --- a/server/config/game/settings/official/standard.json +++ b/server/config/game/settings/official/standard.json @@ -82,7 +82,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/official/turnBased.json b/server/config/game/settings/official/turnBased.json index c6e680747..96a37c1dc 100644 --- a/server/config/game/settings/official/turnBased.json +++ b/server/config/game/settings/official/turnBased.json @@ -79,7 +79,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", diff --git a/server/config/game/settings/user/standard.json b/server/config/game/settings/user/standard.json index 6fb653204..21426db9e 100644 --- a/server/config/game/settings/user/standard.json +++ b/server/config/game/settings/user/standard.json @@ -82,7 +82,7 @@ "enabled": "disabled", "shipsPerStar": 100 }, - "allowAbandonStars": "enabled" + "allowAbandonStars": "disabled" }, "diplomacy": { "enabled": "enabled", From 64ee6dee9705d7a117425876208244756c197493 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Fri, 15 Dec 2023 16:16:18 +0000 Subject: [PATCH 03/24] Fix territory drawing --- client/src/game/territories.js | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/client/src/game/territories.js b/client/src/game/territories.js index 7b8f8d515..6c44e3f22 100644 --- a/client/src/game/territories.js +++ b/client/src/game/territories.js @@ -318,22 +318,24 @@ class Territories { // let sanitizedPoints = points let sanitizedPoints = points.map(getPoint) - // Draw the graphic - let territoryGraphic = new PIXI.Graphics() - territoryGraphic.lineStyle(borderWidth, colour, 1) - territoryGraphic.beginFill(colour, 0.3) - territoryGraphic.moveTo(sanitizedPoints[0].x, sanitizedPoints[0].y) - - for (let point of sanitizedPoints) { - territoryGraphic.lineTo(point.x, point.y) - } + if (sanitizedPoints.length) { + // Draw the graphic + let territoryGraphic = new PIXI.Graphics() + territoryGraphic.lineStyle(borderWidth, colour, 1) + territoryGraphic.beginFill(colour, 0.3) + territoryGraphic.moveTo(sanitizedPoints[0].x, sanitizedPoints[0].y) + + for (let point of sanitizedPoints) { + territoryGraphic.lineTo(point.x, point.y) + } - // Draw another line back to the origin. - territoryGraphic.lineTo(sanitizedPoints[0].x, sanitizedPoints[0].y) + // Draw another line back to the origin. + territoryGraphic.lineTo(sanitizedPoints[0].x, sanitizedPoints[0].y) - territoryGraphic.endFill() + territoryGraphic.endFill() - this.container.addChild(territoryGraphic) + this.container.addChild(territoryGraphic) + } } // ---------- From 623d4bc7ecc598a0cd5a19a2fe67f8ee0d527e01 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Tue, 26 Dec 2023 20:43:05 +0000 Subject: [PATCH 04/24] Upgrade mongo version to 5 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 01cd0b776..e633eb1c5 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ The client uses the following tech: ## Development Environment Setup 1. Install the prerequisites. - [Node.js](https://nodejs.org/en/) v14 - - [MongoDB](https://www.mongodb.com/) v4.4 + - [MongoDB](https://www.mongodb.com/) v5.0 2. Clone the repository. 3. Checkout `master`. 4. `npm install` in both `client/` and `server/` directories. From 2dae6e45b9de06438db61cd3ba17f2225de215a0 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Tue, 26 Dec 2023 20:47:50 +0000 Subject: [PATCH 05/24] Remove outdated google analytics --- client/.env.example | 1 - client/package-lock.json | 11 ----------- client/package.json | 1 - client/src/main.js | 9 --------- 4 files changed, 22 deletions(-) diff --git a/client/.env.example b/client/.env.example index e1d36370e..2ae2180db 100644 --- a/client/.env.example +++ b/client/.env.example @@ -1,6 +1,5 @@ VUE_APP_API_HOST=http://localhost:3000 VUE_APP_SOCKETS_HOST=localhost:3000 -VUE_APP_GOOGLE_ANALYTICS_TRACKING_CODE=ABC-123-1 VUE_APP_DOCUMENTATION_URL=https://solaris-games.github.io/solaris-docs VUE_APP_GOOGLE_RECAPTCHA_ENABLED=false VUE_APP_GOOGLE_RECAPTCHA_SITE_KEY=ABC_123 diff --git a/client/package-lock.json b/client/package-lock.json index 69a8886ed..d7144121a 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -21,7 +21,6 @@ "voronoi": "^1.0.0", "vue": "^2.6.12", "vue-chartjs": "^3.5.1", - "vue-gtag": "^1.14.0", "vue-recaptcha": "^1.3.0", "vue-router": "^3.5.1", "vue-socket.io": "^3.0.10", @@ -15980,11 +15979,6 @@ "node": ">=4.0.0" } }, - "node_modules/vue-gtag": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/vue-gtag/-/vue-gtag-1.14.0.tgz", - "integrity": "sha512-jutSxDedzckzPxOV1dOfV3rWqlfw5uzXOGGxDiOiq5o1UiGerpCOP4uWlO7CfwmujJotMaxgo0xR/TXsc2TLqA==" - }, "node_modules/vue-hot-reload-api": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", @@ -30429,11 +30423,6 @@ } } }, - "vue-gtag": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/vue-gtag/-/vue-gtag-1.14.0.tgz", - "integrity": "sha512-jutSxDedzckzPxOV1dOfV3rWqlfw5uzXOGGxDiOiq5o1UiGerpCOP4uWlO7CfwmujJotMaxgo0xR/TXsc2TLqA==" - }, "vue-hot-reload-api": { "version": "2.3.4", "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz", diff --git a/client/package.json b/client/package.json index c2a33057f..8529bfbb4 100644 --- a/client/package.json +++ b/client/package.json @@ -21,7 +21,6 @@ "voronoi": "^1.0.0", "vue": "^2.6.12", "vue-chartjs": "^3.5.1", - "vue-gtag": "^1.14.0", "vue-recaptcha": "^1.3.0", "vue-router": "^3.5.1", "vue-socket.io": "^3.0.10", diff --git a/client/src/main.js b/client/src/main.js index 4f695b89d..5b8e8e789 100644 --- a/client/src/main.js +++ b/client/src/main.js @@ -1,6 +1,5 @@ import Vue from 'vue' import VueSocketio from 'vue-socket.io' // NOTE: There is an issue with >3.0.7 so forced to use 3.0.7, see here: https://stackoverflow.com/questions/61769716/vue-socket-connection-not-triggered -import VueGtag from 'vue-gtag' import Toasted from 'vue-toasted' import App from './App.vue' import router from './router' @@ -30,14 +29,6 @@ Vue.use(new VueSocketio({ } })) -let trackingCode = process.env.VUE_APP_GOOGLE_ANALYTICS_TRACKING_CODE - -if (trackingCode) { - Vue.use(VueGtag, { - config: { id: trackingCode } - }, router) -} - Vue.use(Toasted, { position: 'bottom-right', duration: 2500 From b7aa8dc86babce7847b6ce887d3c474087ed7b87 Mon Sep 17 00:00:00 2001 From: Metritutus <324345+Metritutus@users.noreply.github.com> Date: Wed, 15 Nov 2023 19:09:38 +0000 Subject: [PATCH 06/24] Update SelectAlias.vue Fixed incorrect spelling of the word "villains". --- client/src/views/game/components/welcome/SelectAlias.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/views/game/components/welcome/SelectAlias.vue b/client/src/views/game/components/welcome/SelectAlias.vue index a43f18004..9fca5b7ca 100644 --- a/client/src/views/game/components/welcome/SelectAlias.vue +++ b/client/src/views/game/components/welcome/SelectAlias.vue @@ -14,7 +14,7 @@
-

Every great story needs both heroes and villians. Which will you be?

+

Every great story needs both heroes and villains. Which will you be?

{{avatar.name}}

{{avatar.description}}

From 5a2e8d0f87051be1a26e452f9d972a7798855853 Mon Sep 17 00:00:00 2001 From: IHateAttackMaps <116509588+IHateAttackMaps@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:44:58 +0200 Subject: [PATCH 07/24] Fixed typo in irregular.ts STAR_COUNT_MULTIPLYER -> STAR_COUNT_MULTIPLIER https://dictionary.cambridge.org/dictionary/english/multiplier --- server/services/maps/irregular.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/services/maps/irregular.ts b/server/services/maps/irregular.ts index 84c1ba3a9..d45cc98bf 100644 --- a/server/services/maps/irregular.ts +++ b/server/services/maps/irregular.ts @@ -302,8 +302,8 @@ export default class IrregularMapService { const NOISE_SPREAD = NOISE_BASE_SPREAD * ( (STARS_PER_PLAYER+20)/9.0 ) //the amount of rings must produce about 30% more stars then requested. this way they can be pruned latter with noise to produce nice gap - const STAR_COUNT_MULTIPLYER = 1.3; - const RING_COUNT = this._getRingCount(STARS_PER_PLAYER, (STARS_PER_PLAYER*STAR_COUNT_MULTIPLYER)); + const STAR_COUNT_MULTIPLIER = 1.3; + const RING_COUNT = this._getRingCount(STARS_PER_PLAYER, (STARS_PER_PLAYER*STAR_COUNT_MULTIPLIER)); const STAR_DISTANCE = MINIMUM_STAR_DISTANCE*SPREAD; const STAR_DISLOCATION_THRESHOLD = MINIMUM_STAR_DISTANCE*((SPREAD-1.0)/2.0); const PIVOT_DISTANCE = RING_COUNT*STAR_DISTANCE; From 3efc11848ea08d68c3c706500b5fc0e34c1dc448 Mon Sep 17 00:00:00 2001 From: IHateAttackMaps <116509588+IHateAttackMaps@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:03:57 +0200 Subject: [PATCH 08/24] Fixed carrierSpeed units in GameSettings.vue y/tick, not /ly tick --- client/src/views/game/components/settings/GameSettings.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/views/game/components/settings/GameSettings.vue b/client/src/views/game/components/settings/GameSettings.vue index 531cc959a..292933daf 100644 --- a/client/src/views/game/components/settings/GameSettings.vue +++ b/client/src/views/game/components/settings/GameSettings.vue @@ -288,7 +288,7 @@ v-if="game.settings.galaxy.galaxyType !== 'custom'"/> Date: Thu, 28 Sep 2023 00:25:17 +0200 Subject: [PATCH 09/24] Add buttons for pausing and resuming games --- client/src/views/game/Detail.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index b9a9866a5..e543a75ce 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -18,6 +18,8 @@
+ + Open Game
@@ -39,7 +41,6 @@ import ViewTitle from '../components/ViewTitle' import ViewSubtitle from '../components/ViewSubtitle' import ViewContainer from '../components/ViewContainer' import GameSettings from './components/settings/GameSettings' -import FluxBar from './components/menu/FluxBar' import gameService from '../../services/api/game' import router from '../../router' import GameHelper from '../../services/gameHelper' @@ -83,6 +84,12 @@ export default { this.isLoadingGame = false }, methods: { + async pauseGame () { + + }, + async resumeGame () { + + }, async deleteGame () { if (await this.$confirm('Delete game', 'Are you sure you want to delete this game?')) { this.isDeletingGame = true From a0ab5903e69bfb3666fdb6d3ac84a11dddbe086f Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Thu, 28 Sep 2023 00:31:53 +0200 Subject: [PATCH 10/24] Have one loading spinner for game details page --- client/src/views/game/Detail.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index e543a75ce..baccc08b3 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -2,9 +2,9 @@ - + -
+

{{game.settings.general.description}}

@@ -55,7 +55,7 @@ export default { }, data () { return { - isLoadingGame: true, + isLoading: true, game: { _id: null, settings: { @@ -71,7 +71,7 @@ export default { this.game._id = this.$route.query.id }, async mounted () { - this.isLoadingGame = true + this.isLoading = true try { let response = await gameService.getGameInfo(this.game._id) @@ -81,7 +81,7 @@ export default { console.error(err) } - this.isLoadingGame = false + this.isLoading = false }, methods: { async pauseGame () { @@ -92,7 +92,7 @@ export default { }, async deleteGame () { if (await this.$confirm('Delete game', 'Are you sure you want to delete this game?')) { - this.isDeletingGame = true + this.isLoading = true try { let response = await gameService.delete(this.game._id) @@ -104,7 +104,7 @@ export default { console.error(err) } - this.isDeletingGame = false + this.isLoading = false } } }, From 4e229dd8f3a1b308c8aed89f1e0b13a2241ab6b9 Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Thu, 28 Sep 2023 00:54:11 +0200 Subject: [PATCH 11/24] Make requests to backend when pausing/resuming --- client/src/services/api/game.js | 10 +++++++ client/src/views/game/Detail.vue | 47 ++++++++++++++++++++++++-------- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/client/src/services/api/game.js b/client/src/services/api/game.js index fdd620132..7b8554fb7 100644 --- a/client/src/services/api/game.js +++ b/client/src/services/api/game.js @@ -117,6 +117,16 @@ class GameService extends BaseApiService { { withCredentials: true }) } + pause (gameId) { + return axios.put(this.BASE_URL + 'game/' + gameId + '/pause', null, + { withCredentials: true }) + } + + resume (gameId) { + return axios.put(this.BASE_URL + 'game/' + gameId + '/resume', null, + { withCredentials: true }) + } + confirmReady (gameId) { return axios.put(this.BASE_URL + 'game/' + gameId + '/ready', null, { withCredentials: true }) diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index baccc08b3..d86eb0609 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -71,24 +71,49 @@ export default { this.game._id = this.$route.query.id }, async mounted () { - this.isLoading = true + await this.loadGame() + }, + methods: { + async loadGame () { + this.isLoading = true - try { - let response = await gameService.getGameInfo(this.game._id) + try { + let response = await gameService.getGameInfo(this.game._id) - this.game = response.data - } catch (err) { - console.error(err) - } + this.game = response.data + } catch (err) { + console.error(err) + } - this.isLoading = false - }, - methods: { + this.isLoading = false + }, async pauseGame () { + if (await this.$confirm('Pause game', 'Are you sure you want to pause this game?')) { + this.isLoading = true + try { + await gameService.pause(this.game._id) + await this.loadGame() + } catch (err) { + console.error(err) + } + + this.isLoading = false + } }, async resumeGame () { + if (await this.$confirm('Resume game', 'Are you sure you want to resume this game?')) { + this.isLoading = true + try { + await gameService.resume(this.game._id) + await this.loadGame() + } catch (err) { + console.error(err) + } + + this.isLoading = false + } }, async deleteGame () { if (await this.$confirm('Delete game', 'Are you sure you want to delete this game?')) { @@ -98,7 +123,7 @@ export default { let response = await gameService.delete(this.game._id) if (response.status === 200) { - router.push({ name: 'main-menu' }) + router.push({name: 'main-menu'}) } } catch (err) { console.error(err) From ecd4b47c5063b5ae4e2a66a608bb83e88fb4d09e Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Thu, 28 Sep 2023 23:53:20 +0200 Subject: [PATCH 12/24] Implement endpoint for game pause/resume --- client/src/services/api/game.js | 12 ++++++++---- server/api/controllers/game.ts | 9 +++++++++ server/api/routes/games.ts | 14 ++++++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/client/src/services/api/game.js b/client/src/services/api/game.js index 7b8554fb7..0cfb973e6 100644 --- a/client/src/services/api/game.js +++ b/client/src/services/api/game.js @@ -118,13 +118,17 @@ class GameService extends BaseApiService { } pause (gameId) { - return axios.put(this.BASE_URL + 'game/' + gameId + '/pause', null, - { withCredentials: true }) + return axios.put(this.BASE_URL + 'game/' + gameId + '/pause', { + pause: true + }, + { withCredentials: true }) } resume (gameId) { - return axios.put(this.BASE_URL + 'game/' + gameId + '/resume', null, - { withCredentials: true }) + return axios.put(this.BASE_URL + 'game/' + gameId + '/pause', { + pause: false + }, + { withCredentials: true }) } confirmReady (gameId) { diff --git a/server/api/controllers/game.ts b/server/api/controllers/game.ts index 523e9ed8f..987f82ed4 100644 --- a/server/api/controllers/game.ts +++ b/server/api/controllers/game.ts @@ -325,6 +325,15 @@ export default (container: DependencyContainer) => { return next(err); } }, + pause: async (req, res, next) => { + const doPause = req.body?.pause; + + if (doPause === null || doPause === undefined) { + return next(new ValidationError('Pause parameter is required.')); + } + + return res.sendStatus(200) + }, getPlayerUser: async (req, res, next) => { try { let user = await container.gameService.getPlayerUser( diff --git a/server/api/routes/games.ts b/server/api/routes/games.ts index 2c9cf8d37..692a706da 100644 --- a/server/api/routes/games.ts +++ b/server/api/routes/games.ts @@ -291,6 +291,20 @@ export default (router: Router, mw: MiddlewareContainer, validator: ExpressJoiIn controller.delete, mw.core.handleError); + router.put('/api/game/:gameId/pause', + mw.auth.authenticate(), + mw.game.loadGame({ + lean: true, + settings: true, + state: true, + }), + mw.game.validateGameState({ + isUnlocked: true, + isInProgress: true + }), + controller.pause, + mw.core.handleError); + router.get('/api/game/:gameId/player/:playerId', mw.game.loadGame({ lean: true, From f515da9c770ad7e274f7ebe2cc7336a6ae60b382 Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Fri, 29 Sep 2023 00:48:47 +0200 Subject: [PATCH 13/24] Implement game pausing --- server/api/controllers/game.ts | 20 +++++++++++++++----- server/services/game.ts | 25 +++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/server/api/controllers/game.ts b/server/api/controllers/game.ts index 987f82ed4..0bf1ad0f5 100644 --- a/server/api/controllers/game.ts +++ b/server/api/controllers/game.ts @@ -326,13 +326,23 @@ export default (container: DependencyContainer) => { } }, pause: async (req, res, next) => { - const doPause = req.body?.pause; + try { + + const doPause = req.body?.pause; + + if (doPause === null || doPause === undefined) { + throw new ValidationError('Pause parameter is required.'); + } + + await container.gameService.setPauseState( + req.game, + doPause, + req.session.userId); - if (doPause === null || doPause === undefined) { - return next(new ValidationError('Pause parameter is required.')); + return res.sendStatus(200); + } catch (err) { + return next(err); } - - return res.sendStatus(200) }, getPlayerUser: async (req, res, next) => { try { diff --git a/server/services/game.ts b/server/services/game.ts index 885f9e7b2..5f0a5b130 100644 --- a/server/services/game.ts +++ b/server/services/game.ts @@ -226,6 +226,31 @@ export default class GameService extends EventEmitter { this.emit(GameServiceEvents.onPlayerDefeated, e); } + async setPauseState(game: Game, pauseState: boolean, pausingUser: DBObjectId) { + const gameCreator = game.settings.general.createdByUserId; + + if (!gameCreator || gameCreator.toString() !== pausingUser.toString()) { + throw new ValidationError('Only the game creator can pause the game.'); + } + + if (game.state.endDate) { + throw new ValidationError('Cannot pause a game that has finished.'); + } + + if (game.state.startDate == null) { + throw new ValidationError('Cannot pause a game that has not yet started.'); + } + + await this.gameRepo.updateOne({ + _id: game._id + }, { + $set: { + 'state.paused': pauseState + } + }); + } + + async delete(game: Game, deletedByUserId?: DBObjectId) { // If being deleted by a legit user then do some validation. if (deletedByUserId && game.state.startDate) { From 981ca7db1dbf9a11dde98524c8b56a01eb706d35 Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Sun, 1 Oct 2023 18:23:09 +0200 Subject: [PATCH 14/24] Show resume button as warning --- client/src/views/game/Detail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index d86eb0609..af77016dc 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -19,7 +19,7 @@
- + Open Game
From 850b6dc909c222ea053ea9680a425e9188818c28 Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Sun, 1 Oct 2023 18:36:35 +0200 Subject: [PATCH 15/24] Improve pausing logic --- client/src/views/game/Detail.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index af77016dc..51a4dd49a 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -18,8 +18,8 @@
- - + + Open Game
@@ -74,6 +74,9 @@ export default { await this.loadGame() }, methods: { + canModifyPauseState () { + return this.game.state.startDate && this.game.settings.general.isGameAdmin && !GameHelper.isGamePendingStart(this.game); + }, async loadGame () { this.isLoading = true From e81c42e78d4ab9d1b0d8fc65af62b6a50d1a12a9 Mon Sep 17 00:00:00 2001 From: SpacialCircumstances Date: Sun, 31 Dec 2023 13:09:15 +0100 Subject: [PATCH 16/24] Reset afk timers after unpausing game --- server/services/game.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/server/services/game.ts b/server/services/game.ts index 5f0a5b130..4109ff645 100644 --- a/server/services/game.ts +++ b/server/services/game.ts @@ -1,3 +1,5 @@ +import moment from "moment/moment"; + const EventEmitter = require('events'); import { DBObjectId } from './types/DBObjectId'; import ValidationError from '../errors/validation'; @@ -240,6 +242,29 @@ export default class GameService extends EventEmitter { if (game.state.startDate == null) { throw new ValidationError('Cannot pause a game that has not yet started.'); } + + if (!pauseState) { + // Reset afk timers + for (const player of game.galaxy.players) { + if (player.afk) { + continue; + } + + const userId = player.userId; + + if (!userId) { + continue; + } + + await this.userRepo.updateOne({ + _id: userId + }, { + $set: { + 'lastSeen': moment().utc(), + } + }); + } + } await this.gameRepo.updateOne({ _id: game._id From a3c3226035031e8f1b6fc7148d0d57b64244bed0 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Tue, 9 Jan 2024 20:27:46 +0000 Subject: [PATCH 17/24] Cleanup --- client/src/services/gameHelper.js | 4 +++ client/src/views/game/Detail.vue | 11 +++++++- server/api/controllers/game.ts | 3 +-- server/api/routes/games.ts | 2 +- server/services/game.ts | 45 +++++++++++-------------------- server/services/gameList.ts | 3 +-- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/client/src/services/gameHelper.js b/client/src/services/gameHelper.js index 40a5ace2d..e2254671a 100644 --- a/client/src/services/gameHelper.js +++ b/client/src/services/gameHelper.js @@ -508,6 +508,10 @@ class GameHelper { return !game.state.startDate } + isGameStarted (game) { + return game.state.startDate != null + } + isGameInProgress (game) { return !this.isGameWaitingForPlayers(game) && !this.isGamePaused(game) && game.state.startDate != null && moment().utc().diff(game.state.startDate) >= 0 && !game.state.endDate } diff --git a/client/src/views/game/Detail.vue b/client/src/views/game/Detail.vue index 51a4dd49a..9dc88e089 100644 --- a/client/src/views/game/Detail.vue +++ b/client/src/views/game/Detail.vue @@ -75,7 +75,10 @@ export default { }, methods: { canModifyPauseState () { - return this.game.state.startDate && this.game.settings.general.isGameAdmin && !GameHelper.isGamePendingStart(this.game); + return this.game.settings.general.isGameAdmin + && GameHelper.isGameStarted(this.game) + && !GameHelper.isGamePendingStart(this.game) + && !GameHelper.isGameFinished(this.game); }, async loadGame () { this.isLoading = true @@ -96,6 +99,9 @@ export default { try { await gameService.pause(this.game._id) + + this.$toasted.show(`The game has been paused. Please notify the players.`, { type: 'success' }) + await this.loadGame() } catch (err) { console.error(err) @@ -110,6 +116,9 @@ export default { try { await gameService.resume(this.game._id) + + this.$toasted.show(`The game has been resumed. Please notify the players.`, { type: 'success' }) + await this.loadGame() } catch (err) { console.error(err) diff --git a/server/api/controllers/game.ts b/server/api/controllers/game.ts index 0bf1ad0f5..71bafc1ae 100644 --- a/server/api/controllers/game.ts +++ b/server/api/controllers/game.ts @@ -325,9 +325,8 @@ export default (container: DependencyContainer) => { return next(err); } }, - pause: async (req, res, next) => { + togglePaused: async (req, res, next) => { try { - const doPause = req.body?.pause; if (doPause === null || doPause === undefined) { diff --git a/server/api/routes/games.ts b/server/api/routes/games.ts index 692a706da..8ffe203ef 100644 --- a/server/api/routes/games.ts +++ b/server/api/routes/games.ts @@ -302,7 +302,7 @@ export default (router: Router, mw: MiddlewareContainer, validator: ExpressJoiIn isUnlocked: true, isInProgress: true }), - controller.pause, + controller.togglePaused, mw.core.handleError); router.get('/api/game/:gameId/player/:playerId', diff --git a/server/services/game.ts b/server/services/game.ts index 4109ff645..d98673dec 100644 --- a/server/services/game.ts +++ b/server/services/game.ts @@ -228,42 +228,30 @@ export default class GameService extends EventEmitter { this.emit(GameServiceEvents.onPlayerDefeated, e); } - async setPauseState(game: Game, pauseState: boolean, pausingUser: DBObjectId) { - const gameCreator = game.settings.general.createdByUserId; + async setPauseState(game: Game, pauseState: boolean, pausingUserId: DBObjectId) { + const gameCreatorId = game.settings.general.createdByUserId; - if (!gameCreator || gameCreator.toString() !== pausingUser.toString()) { + if (!gameCreatorId || gameCreatorId.toString() !== pausingUserId.toString()) { throw new ValidationError('Only the game creator can pause the game.'); } - if (game.state.endDate) { - throw new ValidationError('Cannot pause a game that has finished.'); - } - - if (game.state.startDate == null) { - throw new ValidationError('Cannot pause a game that has not yet started.'); + if (!this.gameStateService.isInProgress(game)) { + throw new ValidationError('Cannot pause a game that is not in progress.'); } if (!pauseState) { - // Reset afk timers - for (const player of game.galaxy.players) { - if (player.afk) { - continue; + // Reset afk timers of the players + // Note: We do not want to update last seen of users as those + // are separate from the game afk logic. + await this.gameRepo.updateOne({ + _id: game._id, + 'galaxy.players.$.afk': false, + 'galaxy.players.$.defeated': false + }, { + $set: { + 'galaxy.players.$.lastSeen': moment().utc(), } - - const userId = player.userId; - - if (!userId) { - continue; - } - - await this.userRepo.updateOne({ - _id: userId - }, { - $set: { - 'lastSeen': moment().utc(), - } - }); - } + }); } await this.gameRepo.updateOne({ @@ -275,7 +263,6 @@ export default class GameService extends EventEmitter { }); } - async delete(game: Game, deletedByUserId?: DBObjectId) { // If being deleted by a legit user then do some validation. if (deletedByUserId && game.state.startDate) { diff --git a/server/services/gameList.ts b/server/services/gameList.ts index 261fcabec..bb4a8b0a5 100644 --- a/server/services/gameList.ts +++ b/server/services/gameList.ts @@ -291,8 +291,7 @@ export default class GameListService { let games = await this.gameRepo.find({ 'settings.general.type': { $nin: ['tutorial'] }, 'state.startDate': { $ne: null }, - 'state.endDate': { $eq: null }, - 'state.paused': { $eq: false } + 'state.endDate': { $eq: null } }, { 'settings.general.name': 1, 'settings.general.type': 1, From c7a63ecd932d2f7d4df3c726288e60b4cf4f2d7b Mon Sep 17 00:00:00 2001 From: IHateAttackMaps <116509588+IHateAttackMaps@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:27:38 +0200 Subject: [PATCH 18/24] Fixed issue in extra dark games Remove state.leaderboard in extra dark games, as that information should not be accessible in this game type. --- server/services/gameGalaxy.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/services/gameGalaxy.ts b/server/services/gameGalaxy.ts index 29b1502b5..5204727a8 100644 --- a/server/services/gameGalaxy.ts +++ b/server/services/gameGalaxy.ts @@ -198,8 +198,11 @@ export default class GameGalaxyService { // can't see and therefore global stats should display what the current player can see // instead of their actual values. // TODO: Better to not overwrite, but just not do it above in the first place? + // Also, remove the leaderboard, since it should not be visible to players in extra + // dark mode games. if (this.gameTypeService.isDarkModeExtra(game)) { this._setPlayerStats(game); + delete game.state.leaderboard; } // If any kind of dark mode, remove the galaxy center from the constants. From ea8918f8f39dd35f4c212914410804f9d8c662d9 Mon Sep 17 00:00:00 2001 From: IHateAttackMaps <116509588+IHateAttackMaps@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:39:58 +0200 Subject: [PATCH 19/24] Fix the fix Just setting leaderboard to null works too. --- server/services/gameGalaxy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/services/gameGalaxy.ts b/server/services/gameGalaxy.ts index 5204727a8..eb688804f 100644 --- a/server/services/gameGalaxy.ts +++ b/server/services/gameGalaxy.ts @@ -202,7 +202,7 @@ export default class GameGalaxyService { // dark mode games. if (this.gameTypeService.isDarkModeExtra(game)) { this._setPlayerStats(game); - delete game.state.leaderboard; + game.state.leaderboard = null; } // If any kind of dark mode, remove the galaxy center from the constants. From 2b04cc2a2fe7044336f54aef6e06aec92ac0f820 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Fri, 12 Jan 2024 20:14:48 +0000 Subject: [PATCH 20/24] Fix get conversation exploit --- server/services/conversation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/services/conversation.ts b/server/services/conversation.ts index 644b2bee9..fd04fa9c1 100644 --- a/server/services/conversation.ts +++ b/server/services/conversation.ts @@ -193,7 +193,7 @@ export default class ConversationService extends EventEmitter { throw new ValidationError(`The conversation requested does not exist.`); } - if (convo.participants.find(p => p.toString() === playerId.toString() == null)) { + if (convo.participants.find(p => p.toString() === playerId.toString()) == null) { throw new ValidationError(`You are not participating in this conversation.`); } From 6aeec0d828abdbdf50884dd14a5f93255730819c Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Sat, 13 Jan 2024 16:28:39 +0000 Subject: [PATCH 21/24] Fixed convo exploit part 2 --- server/services/conversation.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/services/conversation.ts b/server/services/conversation.ts index fd04fa9c1..9c6417d3f 100644 --- a/server/services/conversation.ts +++ b/server/services/conversation.ts @@ -247,7 +247,7 @@ export default class ConversationService extends EventEmitter { throw new ValidationError(`The conversation requested does not exist.`); } - if (convo.participants.find(p => p.toString() === player._id.toString() == null)) { + if (convo.participants.find(p => p.toString() === player._id.toString()) == null) { throw new ValidationError(`You are not participating in this conversation.`); } From 6ec69086329a4e6f6398cab4ea7efcc5a36612a5 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Sun, 14 Jan 2024 20:09:00 +0000 Subject: [PATCH 22/24] Fix impersonation of web sockets exploit --- client/src/views/game/Game.vue | 3 +- server/api/express.ts | 11 +++--- server/api/index.ts | 8 +++-- server/api/sockets.ts | 55 +++++++++++++++++++++++++---- server/package-lock.json | 63 ++++++++++++++++++++++++++++++---- server/package.json | 2 ++ server/services/broadcast.ts | 4 +-- server/services/index.ts | 4 +-- 8 files changed, 125 insertions(+), 25 deletions(-) diff --git a/client/src/views/game/Game.vue b/client/src/views/game/Game.vue index 596848183..5e8c1a6be 100644 --- a/client/src/views/game/Game.vue +++ b/client/src/views/game/Game.vue @@ -111,8 +111,7 @@ export default { this.unsubscribeToSockets() let socketData = { - gameId: this.$store.state.game._id, - userId: this.$store.state.userId + gameId: this.$store.state.game._id } let player = GameHelper.getUserPlayer(this.$store.state.game) diff --git a/server/api/express.ts b/server/api/express.ts index dc5180a06..74937c4a8 100644 --- a/server/api/express.ts +++ b/server/api/express.ts @@ -17,13 +17,13 @@ export default async (config: Config, app, container: DependencyContainer) => { // --------------- // Set up MongoDB session store - let store = new MongoDBStore({ + let sessionStorage = new MongoDBStore({ uri: config.connectionString, collection: 'sessions' }); // Catch session store errors - store.on('error', function(err) { + sessionStorage.on('error', function(err) { console.error(err); }); @@ -37,7 +37,7 @@ export default async (config: Config, app, container: DependencyContainer) => { secure: config.sessionSecureCookies, // Requires HTTPS maxAge: 1000 * 60 * 60 * 24 * 365 // 1 Year }, - store + store: sessionStorage })); // --------------- @@ -86,5 +86,8 @@ export default async (config: Config, app, container: DependencyContainer) => { console.log('Express intialized.'); - return app; + return { + app, + sessionStorage + }; }; diff --git a/server/api/index.ts b/server/api/index.ts index e5963b5fe..cd4e50753 100644 --- a/server/api/index.ts +++ b/server/api/index.ts @@ -16,10 +16,12 @@ async function startServer() { const app = express(); const server = http.createServer(app); - const io = socketLoader(server); - const container = containerLoader(config, io); + const container = containerLoader(config); - await expressLoader(config, app, container); + const { sessionStorage } = await expressLoader(config, app, container); + + const io = socketLoader(config, server, sessionStorage); + container.broadcastService.setIOController(io); server.listen(config.port, (err) => { if (err) { diff --git a/server/api/sockets.ts b/server/api/sockets.ts index 3a3722cdd..018a18890 100644 --- a/server/api/sockets.ts +++ b/server/api/sockets.ts @@ -1,15 +1,53 @@ +import { Config } from "../config/types/Config"; + const socketio = require('socket.io'); +const cookieParser = require('cookie-parser'); +const cookie = require('cookie'); -export default (server) => { +export default (config: Config, server, sessionStore) => { const io = socketio(server); - io.on('connection', (socket) => { + const getUserId = async (socket, data) => { + return new Promise((resolve, reject) => { + const cookieString = socket.request.headers.cookie; + + if (cookieString) { + const cookieParsed = cookie.parse(cookieString); + const sid = cookieParsed['connect.sid']; + + if (sid) { + const sidParsed = cookieParser.signedCookie(sid, config.sessionSecret); + + sessionStore.get(sidParsed, (err, session) => { + if (err) return reject(err); + + if (session.userId) { + resolve(session.userId.toString()) + } else { + resolve(null); + } + }); + } + } else { + resolve(null); + } + }) + } + + + io.on('connection', async (socket) => { + // When the user opens a game, they will be put // into that room to receive web sockets scoped to the game room. - socket.on('gameRoomJoined', (data) => { + socket.on('gameRoomJoined', async (data) => { + const userId = await getUserId(socket, data); + + if (userId) { + socket.join(userId); // Join a private room to receive user/player specific messages. + } + socket.join(data.gameId); // Join the game room to receive game-wide messages. - socket.join(data.userId); // Join a private room to receive user/player specific messages. if (data.playerId) { socket.join(data.playerId); @@ -21,9 +59,14 @@ export default (server) => { } }); - socket.on('gameRoomLeft', (data) => { + socket.on('gameRoomLeft', async (data) => { + const userId = await getUserId(socket, data); + + if (userId) { + socket.leave(userId) + } + socket.leave(data.gameId) - socket.leave(data.userId) if (data.playerId) { socket.leave(data.playerId) diff --git a/server/package-lock.json b/server/package-lock.json index 67bddc6db..88e2ebe22 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,6 +15,8 @@ "body-parser": "^1.19.0", "compression": "^1.7.4", "connect-mongodb-session": "^2.4.1", + "cookie": "^0.6.0", + "cookie-parser": "^1.4.6", "discord.js": "^12.5.3", "dotenv": "^8.2.0", "elo-rating": "^1.0.1", @@ -785,9 +787,29 @@ } }, "node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", "engines": { "node": ">= 0.6" } @@ -1209,6 +1231,14 @@ } ] }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3766,9 +3796,25 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==" + }, + "cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + } + } }, "cookie-signature": { "version": "1.0.6", @@ -4040,6 +4086,11 @@ "vary": "~1.1.2" }, "dependencies": { + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", diff --git a/server/package.json b/server/package.json index e1f60d9a6..d1fadbf28 100644 --- a/server/package.json +++ b/server/package.json @@ -27,6 +27,8 @@ "body-parser": "^1.19.0", "compression": "^1.7.4", "connect-mongodb-session": "^2.4.1", + "cookie": "^0.6.0", + "cookie-parser": "^1.4.6", "discord.js": "^12.5.3", "dotenv": "^8.2.0", "elo-rating": "^1.0.1", diff --git a/server/services/broadcast.ts b/server/services/broadcast.ts index bcc63d540..a3eb023ac 100644 --- a/server/services/broadcast.ts +++ b/server/services/broadcast.ts @@ -9,9 +9,9 @@ import { LedgerType } from "./ledger"; export default class BroadcastService { - io; + io: any; - constructor(io) { + setIOController(io) { this.io = io; } diff --git a/server/services/index.ts b/server/services/index.ts index 2c6cfb0a7..bb087cfee 100644 --- a/server/services/index.ts +++ b/server/services/index.ts @@ -107,7 +107,7 @@ const guildRepository = new Repository(GuildModel); const paymentRepository = new Repository(PaymentModel); const reportRepository = new Repository(ReportModel); -export default (config, io): DependencyContainer => { +export default (config): DependencyContainer => { // Poor man's dependency injection. @@ -124,7 +124,7 @@ export default (config, io): DependencyContainer => { const guildService = new GuildService(GuildModel, guildRepository, userRepository, userService); const guildUserService = new GuildUserService(userRepository, guildService); - const broadcastService = new BroadcastService(io); + const broadcastService = new BroadcastService(); const distanceService = new DistanceService(); const randomService = new RandomService(); const cacheService = new CacheService(config); From 11462466206d507d559617c1ef6d57dfe389b600 Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Sun, 14 Jan 2024 20:13:46 +0000 Subject: [PATCH 23/24] Fix build errors --- server/db/recalculateRankings.ts | 2 +- server/jobs/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/db/recalculateRankings.ts b/server/db/recalculateRankings.ts index 12817d3ce..abef5f526 100644 --- a/server/db/recalculateRankings.ts +++ b/server/db/recalculateRankings.ts @@ -38,7 +38,7 @@ async function startup() { poolSize: 1 }); - container = containerLoader(config, null); + container = containerLoader(config); console.log('Recalculating all player ranks...'); diff --git a/server/jobs/index.ts b/server/jobs/index.ts index 566df9dab..75e749d0b 100644 --- a/server/jobs/index.ts +++ b/server/jobs/index.ts @@ -13,7 +13,7 @@ import SendReviewRemindersJob from './sendReviewReminders'; let mongo; async function startup() { - const container = containerLoader(config, null); + const container = containerLoader(config); mongo = await mongooseLoader(config, { unlockJobs: true, From d353cc71f9f86a24a56e7f14f2eb456ce959097f Mon Sep 17 00:00:00 2001 From: Mike Eason Date: Sun, 14 Jan 2024 21:33:34 +0000 Subject: [PATCH 24/24] Actually fixed message spoofing exploit --- client/src/views/game/Game.vue | 3 +-- server/services/broadcast.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/client/src/views/game/Game.vue b/client/src/views/game/Game.vue index 5e8c1a6be..d4a460cdc 100644 --- a/client/src/views/game/Game.vue +++ b/client/src/views/game/Game.vue @@ -70,8 +70,7 @@ export default { let player = GameHelper.getUserPlayer(this.$store.state.game) let socketData = { - gameId: this.$store.state.game._id, - userId: this.$store.state.userId + gameId: this.$store.state.game._id } if (player) { diff --git a/server/services/broadcast.ts b/server/services/broadcast.ts index a3eb023ac..aab12a57f 100644 --- a/server/services/broadcast.ts +++ b/server/services/broadcast.ts @@ -72,7 +72,14 @@ export default class BroadcastService { } gameMessageSent(game: Game, message: ConversationMessageSentResult) { - message.toPlayerIds.forEach(p => this.io.to(p).emit('gameMessageSent', message)); + // Note: We need to ensure we send to the users' socket, not the players as the player one + // can be spoofed. + const toUserIds = game.galaxy.players + .filter(p => message.toPlayerIds.find(m => m.toString() === p._id.toString()) != null) + .filter(p => p.userId != null) + .map(p => p.userId!); + + toUserIds.forEach(p => this.io.to(p.toString()).emit('gameMessageSent', message)); } gameConversationRead(game: Game, conversation: Conversation, readByPlayerId: DBObjectId) {