mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 09:40:44 +00:00
Fix compact map playercount 🔧 (#3361)
## Description: The recently improved `supportsCompactMapForTeams` only checked that each team had ≥2 players but not that there were ≥2 teams. On very small maps with compact + Duos (rare), this allowed lobbies with 1 team of 2, which makes no sense (Wonder noticed that randomly while looking at the homepage). Added a `numberOfTeams ≥ 2` check so the compact map modifier isn't active when the player count can't sustain multiple teams. The 125-player performance cap was also applied inside `calculateMapPlayerCounts` before the compact 75% reduction, so a map sized for 200 players would get capped to 125 then reduced to 31 instead of the expected 50. Moved the cap (`MAX_PLAYER_COUNT`) to after the compact reduction. ## Please complete the following: - [X] I have added screenshots for all UI updates - [X] I process any text displayed to the user through translateText() and I've added it to the en.json file - [X] I have added relevant tests to the test directory - [X] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: FloPinguin
This commit is contained in:
+36
-15
@@ -21,6 +21,9 @@ import { getMapLandTiles } from "./MapLandTiles";
|
||||
const log = logger.child({});
|
||||
const ARCADE_MAPS = new Set(mapCategories.arcade);
|
||||
|
||||
// Hard cap on player count for performance. Applied after compact-map reduction.
|
||||
const MAX_PLAYER_COUNT = 125;
|
||||
|
||||
// How many times each map should appear in the playlist.
|
||||
// Note: The Partial should eventually be removed for better type safety.
|
||||
const frequency: Partial<Record<GameMapName, number>> = {
|
||||
@@ -164,7 +167,7 @@ export class MapPlaylist {
|
||||
}
|
||||
|
||||
// Crowded modifier: if the map's biggest player count (first number of calculateMapPlayerCounts) is 60 or lower (small maps),
|
||||
// set player count to 125 (or 60 if compact map is also enabled)
|
||||
// set player count to MAX_PLAYER_COUNT (or 60 if compact map is also enabled)
|
||||
let crowdedMaxPlayers: number | undefined;
|
||||
if (isCrowded) {
|
||||
crowdedMaxPlayers = await this.getCrowdedMaxPlayers(map, isCompact);
|
||||
@@ -537,12 +540,15 @@ export class MapPlaylist {
|
||||
const [l, , s] = this.calculateMapPlayerCounts(landTiles);
|
||||
// Worst case: smallest tier with team mode 1.5x multiplier, capped at l
|
||||
let p = Math.min(Math.ceil(s * 1.5), l);
|
||||
// Apply compact 75% player reduction
|
||||
p = Math.max(3, Math.floor(p * 0.25));
|
||||
// Apply compact 75% player reduction, then cap for performance
|
||||
p = Math.min(Math.max(3, Math.floor(p * 0.25)), MAX_PLAYER_COUNT);
|
||||
// Apply team adjustment
|
||||
p = this.adjustForTeams(p, playerTeams);
|
||||
// Check at least 2 players per team
|
||||
return this.playersPerTeam(p, playerTeams) >= 2;
|
||||
// Check at least 2 players per team AND at least 2 teams
|
||||
return (
|
||||
this.playersPerTeam(p, playerTeams) >= 2 &&
|
||||
this.numberOfTeams(p, playerTeams) >= 2
|
||||
);
|
||||
}
|
||||
|
||||
private playersPerTeam(
|
||||
@@ -563,6 +569,24 @@ export class MapPlaylist {
|
||||
}
|
||||
}
|
||||
|
||||
private numberOfTeams(
|
||||
adjustedPlayerCount: number,
|
||||
playerTeams: TeamCountConfig,
|
||||
): number {
|
||||
switch (playerTeams) {
|
||||
case Duos:
|
||||
return Math.floor(adjustedPlayerCount / 2);
|
||||
case Trios:
|
||||
return Math.floor(adjustedPlayerCount / 3);
|
||||
case Quads:
|
||||
return Math.floor(adjustedPlayerCount / 4);
|
||||
case HumansVsNations:
|
||||
return 2; // always 2 teams
|
||||
default:
|
||||
return playerTeams; // numeric value IS the team count
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Centralised spawn-immunity duration logic.
|
||||
* - HumansVsNations: always 5s (nations can't benefit from longer PVP immunity)
|
||||
@@ -586,9 +610,10 @@ export class MapPlaylist {
|
||||
isCompact: boolean,
|
||||
): Promise<number | undefined> {
|
||||
const landTiles = await getMapLandTiles(map);
|
||||
const [firstPlayerCount] = this.calculateMapPlayerCounts(landTiles);
|
||||
const [rawFirstPlayerCount] = this.calculateMapPlayerCounts(landTiles);
|
||||
const firstPlayerCount = Math.min(rawFirstPlayerCount, MAX_PLAYER_COUNT);
|
||||
if (firstPlayerCount <= 60) {
|
||||
return isCompact ? 60 : 125;
|
||||
return isCompact ? 60 : MAX_PLAYER_COUNT;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -608,6 +633,8 @@ export class MapPlaylist {
|
||||
if (isCompactMap) {
|
||||
p = Math.max(3, Math.floor(p * 0.25));
|
||||
}
|
||||
// Cap for performance
|
||||
p = Math.min(p, MAX_PLAYER_COUNT);
|
||||
return this.adjustForTeams(p, numPlayerTeams);
|
||||
}
|
||||
|
||||
@@ -641,7 +668,6 @@ export class MapPlaylist {
|
||||
/**
|
||||
* Calculate player counts from land tiles
|
||||
* For every 1,000,000 land tiles, take 50 players
|
||||
* Limit to max 125 players for performance
|
||||
* Second value is 75% of calculated value, third is 50%
|
||||
* All values are rounded to the nearest 5
|
||||
*/
|
||||
@@ -650,12 +676,7 @@ export class MapPlaylist {
|
||||
): [number, number, number] {
|
||||
const roundToNearest5 = (n: number) => Math.round(n / 5) * 5;
|
||||
|
||||
const base = roundToNearest5((landTiles / 1_000_000) * 50);
|
||||
const limitedBase = Math.min(Math.max(base, 5), 125);
|
||||
return [
|
||||
limitedBase,
|
||||
roundToNearest5(limitedBase * 0.75),
|
||||
roundToNearest5(limitedBase * 0.5),
|
||||
];
|
||||
const base = Math.max(roundToNearest5((landTiles / 1_000_000) * 50), 5);
|
||||
return [base, roundToNearest5(base * 0.75), roundToNearest5(base * 0.5)];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user