mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 13:39:46 +00:00
aa451e217f
## Description: Fix player rename in pre-game lobby on rejoin Previously, when a player left a lobby, changed their name, and rejoined, the server reused the original Client object without updating the username. Now rejoinClient accepts the new username and applies it if the game hasn't started yet, while still preserving names mid-game for consistency. ## 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: w.o.n
146 lines
3.5 KiB
TypeScript
146 lines
3.5 KiB
TypeScript
import { Logger } from "winston";
|
|
import WebSocket from "ws";
|
|
import { ServerConfig } from "../core/configuration/Config";
|
|
import {
|
|
Difficulty,
|
|
GameMapSize,
|
|
GameMapType,
|
|
GameMode,
|
|
GameType,
|
|
} from "../core/game/Game";
|
|
import { GameConfig, GameID, PublicGameType } from "../core/Schemas";
|
|
import { Client } from "./Client";
|
|
import { GamePhase, GameServer } from "./GameServer";
|
|
|
|
export class GameManager {
|
|
private games: Map<GameID, GameServer> = new Map();
|
|
|
|
constructor(
|
|
private config: ServerConfig,
|
|
private log: Logger,
|
|
) {
|
|
setInterval(() => this.tick(), 1000);
|
|
}
|
|
|
|
public game(id: GameID): GameServer | null {
|
|
return this.games.get(id) ?? null;
|
|
}
|
|
|
|
public publicLobbies(): GameServer[] {
|
|
return Array.from(this.games.values()).filter(
|
|
(g) => g.phase() === GamePhase.Lobby && g.isPublic(),
|
|
);
|
|
}
|
|
|
|
joinClient(
|
|
client: Client,
|
|
gameID: GameID,
|
|
): "joined" | "kicked" | "rejected" | "not_found" {
|
|
const game = this.games.get(gameID);
|
|
if (!game) return "not_found";
|
|
return game.joinClient(client);
|
|
}
|
|
|
|
rejoinClient(
|
|
ws: WebSocket,
|
|
persistentID: string,
|
|
gameID: GameID,
|
|
lastTurn: number = 0,
|
|
newUsername?: string,
|
|
): boolean {
|
|
const game = this.games.get(gameID);
|
|
if (!game) return false;
|
|
return game.rejoinClient(ws, persistentID, lastTurn, newUsername);
|
|
}
|
|
|
|
createGame(
|
|
id: GameID,
|
|
gameConfig: GameConfig | undefined,
|
|
creatorPersistentID?: string,
|
|
startsAt?: number,
|
|
publicGameType?: PublicGameType,
|
|
) {
|
|
const game = new GameServer(
|
|
id,
|
|
this.log,
|
|
Date.now(),
|
|
this.config,
|
|
{
|
|
donateGold: false,
|
|
donateTroops: false,
|
|
gameMap: GameMapType.World,
|
|
gameType: GameType.Private,
|
|
gameMapSize: GameMapSize.Normal,
|
|
difficulty: Difficulty.Medium,
|
|
disableNations: false,
|
|
infiniteGold: false,
|
|
infiniteTroops: false,
|
|
maxTimerValue: undefined,
|
|
instantBuild: false,
|
|
randomSpawn: false,
|
|
gameMode: GameMode.FFA,
|
|
bots: 400,
|
|
disabledUnits: [],
|
|
...gameConfig,
|
|
},
|
|
creatorPersistentID,
|
|
startsAt,
|
|
publicGameType,
|
|
);
|
|
this.games.set(id, game);
|
|
return game;
|
|
}
|
|
|
|
activeGames(): number {
|
|
return this.games.size;
|
|
}
|
|
|
|
activeClients(): number {
|
|
let totalClients = 0;
|
|
this.games.forEach((game: GameServer) => {
|
|
totalClients += game.activeClients.length;
|
|
});
|
|
return totalClients;
|
|
}
|
|
|
|
desyncCount(): number {
|
|
let totalDesyncs = 0;
|
|
this.games.forEach((game: GameServer) => {
|
|
totalDesyncs += game.desyncCount;
|
|
});
|
|
return totalDesyncs;
|
|
}
|
|
|
|
tick() {
|
|
const active = new Map<GameID, GameServer>();
|
|
for (const [id, game] of this.games) {
|
|
const phase = game.phase();
|
|
if (phase === GamePhase.Active) {
|
|
if (!game.hasStarted()) {
|
|
// Prestart tells clients to start loading the game.
|
|
game.prestart();
|
|
// Start game on delay to allow time for clients to connect.
|
|
setTimeout(() => {
|
|
try {
|
|
game.start();
|
|
} catch (error) {
|
|
this.log.error(`error starting game ${id}: ${error}`);
|
|
}
|
|
}, 2000);
|
|
}
|
|
}
|
|
|
|
if (phase === GamePhase.Finished) {
|
|
try {
|
|
game.end();
|
|
} catch (error) {
|
|
this.log.error(`error ending game ${id}: ${error}`);
|
|
}
|
|
} else {
|
|
active.set(id, game);
|
|
}
|
|
}
|
|
this.games = active;
|
|
}
|
|
}
|