mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 12:00:44 +00:00
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:
@@ -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
@@ -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));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user