Auto-derive server.game and server.map from the server name

Server folders encode the game and map (e.g. t5_ascension_1 = game t5, map
alias ascension, lobby 1). A T5_MAP_ALIASES table translates the friendly
alias to the canonical mapname RCON reports (ascension → zombie_cosmodrome),
covering every base + Rezurrection map. seedServerRegistry now populates
game and map at insert time and backfills existing NULL rows on every boot;
unknown aliases log a warning so we know when to add them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-09-12 13:35:43 -04:00
co-authored by Claude Opus 4.7
parent 1a0f06403d
commit 590f803d93
3 changed files with 149 additions and 3 deletions
+61
View File
@@ -0,0 +1,61 @@
// T5 (Black Ops 1) zombies maps: friendly aliases → canonical mapname the game
// actually reports via RCON. Server names in the fleet encode the friendly
// alias (t5_ascension_1 → 'ascension') so we can derive game + map from the
// folder name instead of asking the operator to fill it in.
//
// Add rows here when new servers land with unrecognized aliases; the parser
// logs unknowns so they're easy to spot in worker startup.
export const T5_MAP_ALIASES: Readonly<Record<string, string>> = Object.freeze({
kino: 'zombie_theater', // Kino Der Toten
five: 'zombie_pentagon', // Five
pentagon: 'zombie_pentagon',
doa: 'zombietron', // Dead Ops Arcade
deadops: 'zombietron',
ascension: 'zombie_cosmodrome',
cotd: 'zombie_coast', // Call Of The Dead
coast: 'zombie_coast',
shangri: 'zombie_temple', // Shangri-La
shangrila: 'zombie_temple',
temple: 'zombie_temple',
moon: 'zombie_moon',
nacht: 'zombie_cod5_prototype', // Nacht Der Untoten (WaW map, playable in T5 via Rezurrection)
verruckt: 'zombie_cod5_asylum',
asylum: 'zombie_cod5_asylum',
shi: 'zombie_cod5_sumpf', // Shi No Numa
shinonuma: 'zombie_cod5_sumpf',
sumpf: 'zombie_cod5_sumpf',
riese: 'zombie_cod5_factory', // Der Riese
derriese: 'zombie_cod5_factory',
factory: 'zombie_cod5_factory'
});
export interface ParsedServerName {
game: string;
mapAlias: string;
mapCanonical: string | null; // null when the alias isn't in T5_MAP_ALIASES
lobby: number | null;
}
/**
* `t5_ascension_1` → { game:'t5', mapAlias:'ascension', mapCanonical:'zombie_cosmodrome', lobby:1 }
*
* Rules:
* - First segment = game (e.g. 't5')
* - Trailing all-digits segment = lobby number
* - Everything in between = map alias (joined without separators)
*
* Multi-word aliases work because we strip underscores when looking up
* (e.g. `t5_shi_no_numa_1` → alias 'shinonuma').
*/
export function parseServerName(name: string): ParsedServerName | null {
const parts = name.split('_').filter(Boolean);
if (parts.length < 2) return null;
const game = parts[0];
const lastIsLobby = /^\d+$/.test(parts[parts.length - 1]);
const lobby = lastIsLobby ? Number.parseInt(parts[parts.length - 1], 10) : null;
const mapParts = lastIsLobby ? parts.slice(1, -1) : parts.slice(1);
if (mapParts.length === 0) return null;
const mapAlias = mapParts.join('').toLowerCase();
const mapCanonical = T5_MAP_ALIASES[mapAlias] ?? null;
return { game, mapAlias, mapCanonical, lobby };
}