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
This commit is contained in:
APuddle210
2025-04-03 19:09:34 -04:00
committed by GitHub
parent 647805de70
commit f94da14d1f
2 changed files with 113 additions and 61 deletions
+107
View File
@@ -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;
}
}
+6 -61
View File
@@ -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<number> {
}
// 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<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}