feat: timer starts if enough player or enough time (#229)

Avoid these huge lobbies
This commit is contained in:
Ilan Schemoul
2025-03-12 22:18:34 +01:00
committed by GitHub
parent cef8a61d9c
commit 07ad7912bc
7 changed files with 32 additions and 7 deletions
+10 -2
View File
@@ -88,6 +88,8 @@ export class PublicLobby extends LitElement {
const minutes = Math.floor(timeRemaining / 60);
const seconds = timeRemaining % 60;
const timeDisplay = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
const playersRemainingBeforeMax =
lobby.gameConfig.maxPlayers - lobby.numClients;
return html`
<button
@@ -118,8 +120,8 @@ export class PublicLobby extends LitElement {
</div>
<div class="flex flex-col items-start">
<div class="text-md font-medium text-blue-100">
${lobby.numClients}
${lobby.numClients === 1 ? "Player" : "Players"} waiting
${lobby.numClients} / ${lobby.gameConfig.maxPlayers} players
waiting
</div>
</div>
<div class="flex items-center">
@@ -129,6 +131,12 @@ export class PublicLobby extends LitElement {
${timeDisplay}
</div>
</div>
<div class="flex flex-col items-start">
<div class="text-md font-medium text-blue-100">
Game starts when ${playersRemainingBeforeMax} more players join
or in ${timeDisplay} seconds.
</div>
</div>
</div>
</div>
</button>
+1
View File
@@ -110,6 +110,7 @@ const GameConfigSchema = z.object({
infiniteGold: z.boolean(),
infiniteTroops: z.boolean(),
instantBuild: z.boolean(),
maxPlayers: z.number().optional(),
});
const SafeString = z
+2 -1
View File
@@ -90,7 +90,8 @@ function getServerConfig(gameEnv: string) {
export interface ServerConfig {
turnIntervalMs(): number;
gameCreationRate(highTraffic: boolean): number;
lobbyLifetime(highTraffic): number;
lobbyLifetime(highTraffic: boolean): number;
lobbyMaxPlayers(): number;
discordRedirectURI(): string;
numWorkers(): number;
workerIndex(gameID: GameID): number;
+3
View File
@@ -54,6 +54,9 @@ export abstract class DefaultServerConfig implements ServerConfig {
return 50 * 1000;
}
}
lobbyMaxPlayers(): number {
return Math.random() < 0.1 ? 100 : 35;
}
lobbyLifetime(highTraffic: boolean): number {
return this.gameCreationRate(highTraffic) * 2;
}
+4
View File
@@ -20,6 +20,10 @@ export class DevServerConfig extends DefaultServerConfig {
return 5 * 1000;
}
lobbyMaxPlayers(): number {
return Math.random() < 0.5 ? 2 : 3;
}
discordRedirectURI(): string {
return "http://localhost:3000/auth/callback";
}
+8 -1
View File
@@ -380,7 +380,14 @@ export class GameServer {
}
}
if (now - this.createdAt < this.config.lobbyLifetime(this.highTraffic)) {
const msSinceCreation = now - this.createdAt;
const lessThanLifetime =
msSinceCreation < this.config.lobbyLifetime(this.highTraffic);
const notEnoughPlayers =
this.gameConfig.gameType == GameType.Public &&
this.gameConfig.maxPlayers &&
this.activeClients.length < this.gameConfig.maxPlayers;
if (lessThanLifetime && notEnoughPlayers) {
return GamePhase.Lobby;
}
const warmupOver =
+4 -3
View File
@@ -8,7 +8,7 @@ import {
GameEnv,
getServerConfigFromServer,
} from "../core/configuration/Config";
import { GameInfo } from "../core/Schemas";
import { GameConfig, GameInfo } from "../core/Schemas";
import path from "path";
import rateLimit from "express-rate-limit";
import { fileURLToPath } from "url";
@@ -199,7 +199,7 @@ async function fetchLobbies(): Promise<void> {
});
lobbyInfos.forEach((l) => {
if (l.msUntilStart <= 250) {
if (l.msUntilStart <= 250 || l.gameConfig.maxPlayers == l.numClients) {
publicLobbyIDs.delete(l.gameID);
}
});
@@ -217,6 +217,7 @@ async function schedulePublicGame() {
// Create the default public game config (from your GameManager)
const defaultGameConfig = {
gameMap: getNextMap(),
maxPlayers: config.lobbyMaxPlayers(),
gameType: GameType.Public,
difficulty: Difficulty.Medium,
infiniteGold: false,
@@ -224,7 +225,7 @@ async function schedulePublicGame() {
instantBuild: false,
disableNPCs: false,
bots: 400,
};
} as GameConfig;
const workerPath = config.workerPath(gameID);