From f94da14d1f3ffaec3002973f88d3ac4f97ba564a Mon Sep 17 00:00:00 2001 From: APuddle210 Date: Thu, 3 Apr 2025 19:09:34 -0400 Subject: [PATCH] Created MapPlaylist class (#405) ## Description: Creates new MapPlaylist class, which contains the expanded may playlist logic. Currently implements two distinct playlists, one for big maps, and another for small. Designed with future need for more types of playlist in mind. It pulls from the big playlist two for every one time it pulls from the small playlist. Moved out of the Master.ts as requested. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors ## Please put your Discord username so you can be contacted if a bug or regression is found: aPuddle --- src/server/MapPlaylist.ts | 107 ++++++++++++++++++++++++++++++++++++++ src/server/Master.ts | 67 +++--------------------- 2 files changed, 113 insertions(+), 61 deletions(-) create mode 100644 src/server/MapPlaylist.ts diff --git a/src/server/MapPlaylist.ts b/src/server/MapPlaylist.ts new file mode 100644 index 000000000..488d48aea --- /dev/null +++ b/src/server/MapPlaylist.ts @@ -0,0 +1,107 @@ +import { GameMapType } from "../core/game/Game"; +import { PseudoRandom } from "../core/PseudoRandom"; + +enum PlaylistType { + BigMaps, + SmallMaps, +} + +const random = new PseudoRandom(123); + +export class MapPlaylist { + private mapsPlaylistBig: GameMapType[] = []; + private mapsPlaylistSmall: GameMapType[] = []; + private currentPlaylistCounter = 0; + + // Get the next map in rotation + public getNextMap(): GameMapType { + const playlistType: PlaylistType = this.getNextPlaylistType(); + const mapsPlaylist: GameMapType[] = this.getNextMapsPlayList(playlistType); + return mapsPlaylist.shift()!; + } + + private getNextMapsPlayList(playlistType: PlaylistType): GameMapType[] { + switch (playlistType) { + case PlaylistType.BigMaps: + if (!(this.mapsPlaylistBig.length > 0)) { + this.fillMapsPlaylist(playlistType, this.mapsPlaylistBig); + } + return this.mapsPlaylistBig; + + case PlaylistType.SmallMaps: + if (!(this.mapsPlaylistSmall.length > 0)) { + this.fillMapsPlaylist(playlistType, this.mapsPlaylistSmall); + } + return this.mapsPlaylistSmall; + } + } + + private fillMapsPlaylist( + playlistType: PlaylistType, + mapsPlaylist: GameMapType[], + ): void { + const frequency = this.getFrequency(playlistType); + Object.keys(GameMapType).forEach((key) => { + let count = parseInt(frequency[key]); + while (count > 0) { + mapsPlaylist.push(GameMapType[key]); + count--; + } + }); + while (!this.allNonConsecutive(mapsPlaylist)) { + random.shuffleArray(mapsPlaylist); + } + } + + // Specifically controls how the playlists rotate. + private getNextPlaylistType(): PlaylistType { + switch (this.currentPlaylistCounter) { + case 0: + case 1: + this.currentPlaylistCounter++; + return PlaylistType.BigMaps; + case 2: + this.currentPlaylistCounter = 0; + return PlaylistType.SmallMaps; + } + } + + private getFrequency(playlistType: PlaylistType) { + switch (playlistType) { + // Big Maps are those larger than ~2.5 mil pixels + case PlaylistType.BigMaps: + return { + Europe: 3, + NorthAmerica: 2, + Africa: 2, + Britannia: 1, + GatewayToTheAtlantic: 2, + Australia: 1, + Iceland: 1, + SouthAmerica: 3, + KnownWorld: 2, + }; + case PlaylistType.SmallMaps: + return { + World: 1, + Mena: 2, + Pangaea: 1, + Asia: 1, + Mars: 1, + TwoSeas: 3, + Japan: 3, + BlackSea: 1, + }; + } + } + + // Check for consecutive duplicates in the maps array + private allNonConsecutive(maps: GameMapType[]): boolean { + for (let i = 0; i < maps.length - 1; i++) { + if (maps[i] === maps[i + 1]) { + return false; + } + } + return true; + } +} diff --git a/src/server/Master.ts b/src/server/Master.ts index c2a00d42b..a371bfb9c 100644 --- a/src/server/Master.ts +++ b/src/server/Master.ts @@ -5,15 +5,16 @@ import http from "http"; import path from "path"; import { fileURLToPath } from "url"; import { getServerConfigFromServer } from "../core/configuration/ConfigLoader"; -import { Difficulty, GameMapType, GameMode, GameType } from "../core/game/Game"; -import { PseudoRandom } from "../core/PseudoRandom"; +import { Difficulty, GameMode, GameType } from "../core/game/Game"; import { GameConfig, GameInfo } from "../core/Schemas"; import { generateID } from "../core/Util"; import { gatekeeper, LimiterType } from "./Gatekeeper"; import { logger } from "./Logger"; +import { MapPlaylist } from "./MapPlaylist"; import { setupMetricsServer } from "./MasterMetrics"; const config = getServerConfigFromServer(); +const playlist = new MapPlaylist(); const readyWorkers = new Set(); const app = express(); @@ -100,7 +101,7 @@ export async function startMaster() { log.info("All workers ready, starting game scheduling"); const scheduleLobbies = () => { - schedulePublicGame().catch((error) => { + schedulePublicGame(playlist).catch((error) => { log.error("Error scheduling public game:", error); }); }; @@ -222,9 +223,9 @@ async function fetchLobbies(): Promise { } // Function to schedule a new public game -async function schedulePublicGame() { +async function schedulePublicGame(playlist: MapPlaylist) { const gameID = generateID(); - const map = getNextMap(); + const map = playlist.getNextMap(); publicLobbyIDs.add(gameID); // Create the default public game config (from your GameManager) const defaultGameConfig = { @@ -270,62 +271,6 @@ async function schedulePublicGame() { } } -// Map rotation management (moved from GameManager) -const mapsPlaylist: GameMapType[] = []; -const random = new PseudoRandom(123); - -// Get the next map in rotation -function getNextMap(): GameMapType { - if (mapsPlaylist.length > 0) { - return mapsPlaylist.shift()!; - } - - const frequency = { - World: 1, - Europe: 3, - Mena: 2, - NorthAmerica: 2, - BlackSea: 1, - Pangaea: 1, - Africa: 2, - Asia: 1, - Mars: 1, - Britannia: 2, - GatewayToTheAtlantic: 2, - Australia: 2, - Iceland: 2, - SouthAmerica: 3, - Japan: 3, - TwoSeas: 3, - }; - - Object.keys(GameMapType).forEach((key) => { - let count = parseInt(frequency[key]); - - while (count > 0) { - mapsPlaylist.push(GameMapType[key]); - count--; - } - }); - - while (true) { - random.shuffleArray(mapsPlaylist); - if (allNonConsecutive(mapsPlaylist)) { - return mapsPlaylist.shift()!; - } - } -} - -// Check for consecutive duplicates in the maps array -function allNonConsecutive(maps: GameMapType[]): boolean { - for (let i = 0; i < maps.length - 1; i++) { - if (maps[i] === maps[i + 1]) { - return false; - } - } - return true; -} - function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); }