mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 16:40:16 +00:00
## Description Have the Game Server archive when it receives a win event instead of when the game completes. It will still archive when the game completes if there are no winners. It only uses the winner message from clients that are in sync and are not kicked. * Fixed a bug with the stats collection, the arrays were not expanded enough causing NaN to be inserted into the array which caused Zod validation to fail * Fixed a bug with the win modal, was incorrectly showing player as winner even if they lost. ## 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: <DISCORD USERNAME>
This commit is contained in:
@@ -187,7 +187,9 @@ export class ClientGameRunner {
|
||||
}
|
||||
|
||||
private saveGame(update: WinUpdate) {
|
||||
if (this.myPlayer === null) throw new Error("Not initialized");
|
||||
if (this.myPlayer === null) {
|
||||
return;
|
||||
}
|
||||
const players: PlayerRecord[] = [
|
||||
{
|
||||
playerID: this.myPlayer.id(),
|
||||
|
||||
@@ -207,10 +207,13 @@ export class WinModal extends LitElement implements Layer {
|
||||
new SendWinnerEvent(winnerClient, wu.allPlayersStats, "player"),
|
||||
);
|
||||
}
|
||||
if (winner === this.game.myPlayer()) {
|
||||
if (
|
||||
winnerClient !== null &&
|
||||
winnerClient === this.game.myPlayer()?.clientID()
|
||||
) {
|
||||
this._title = translateText("win_modal.you_won");
|
||||
} else {
|
||||
this._title = translateText("win_modal.you_won", {
|
||||
this._title = translateText("win_modal.other_won", {
|
||||
player: winner.name(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ export class StatsImpl implements Stats {
|
||||
const p = this._makePlayerStats(player);
|
||||
if (p === undefined) return;
|
||||
if (p.attacks === undefined) p.attacks = [0];
|
||||
while (p.attacks.length < index) p.attacks.push(0);
|
||||
while (p.attacks.length <= index) p.attacks.push(0);
|
||||
p.attacks[index] += value;
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ export class StatsImpl implements Stats {
|
||||
if (p === undefined) return;
|
||||
if (p.boats === undefined) p.boats = { [type]: [0] };
|
||||
if (p.boats[type] === undefined) p.boats[type] = [0];
|
||||
while (p.boats[type].length < index) p.boats[type].push(0);
|
||||
while (p.boats[type].length <= index) p.boats[type].push(0);
|
||||
p.boats[type][index] += value;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ export class StatsImpl implements Stats {
|
||||
if (p === undefined) return;
|
||||
if (p.bombs === undefined) p.bombs = { [type]: [0] };
|
||||
if (p.bombs[type] === undefined) p.bombs[type] = [0];
|
||||
while (p.bombs[type].length < index) p.bombs[type].push(0);
|
||||
while (p.bombs[type].length <= index) p.bombs[type].push(0);
|
||||
p.bombs[type][index] += value;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export class StatsImpl implements Stats {
|
||||
const p = this._makePlayerStats(player);
|
||||
if (p === undefined) return;
|
||||
if (p.gold === undefined) p.gold = [0];
|
||||
while (p.gold.length < index) p.gold.push(0);
|
||||
while (p.gold.length <= index) p.gold.push(0);
|
||||
p.gold[index] += value;
|
||||
}
|
||||
|
||||
|
||||
+51
-31
@@ -56,6 +56,7 @@ export class GameServer {
|
||||
private _hasPrestarted = false;
|
||||
|
||||
private kickedClients: Set<ClientID> = new Set();
|
||||
private outOfSyncClients: Set<ClientID> = new Set();
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
@@ -200,7 +201,15 @@ export class GameServer {
|
||||
client.hashes.set(clientMsg.turnNumber, clientMsg.hash);
|
||||
}
|
||||
if (clientMsg.type === "winner") {
|
||||
if (
|
||||
this.outOfSyncClients.has(client.clientID) ||
|
||||
this.kickedClients.has(client.clientID) ||
|
||||
this.winner !== null
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.winner = clientMsg;
|
||||
this.archiveGame();
|
||||
}
|
||||
} catch (error) {
|
||||
this.log.info(
|
||||
@@ -382,40 +391,16 @@ export class GameServer {
|
||||
}
|
||||
this.log.info(`ending game with ${this.turns.length} turns`);
|
||||
try {
|
||||
if (this.allClients.size > 0) {
|
||||
const playerRecords: PlayerRecord[] = Array.from(
|
||||
this.allClients.values(),
|
||||
).map((client) => {
|
||||
const stats = this.winner?.allPlayersStats[client.clientID];
|
||||
if (stats === undefined) {
|
||||
this.log.warn(
|
||||
`Unable to find stats for clientID ${client.clientID}`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
playerID: client.playerID,
|
||||
clientID: client.clientID,
|
||||
username: client.username,
|
||||
persistentID: client.persistentID,
|
||||
stats,
|
||||
} satisfies PlayerRecord;
|
||||
});
|
||||
archive(
|
||||
createGameRecord(
|
||||
this.id,
|
||||
this.gameStartInfo.config,
|
||||
playerRecords,
|
||||
this.turns,
|
||||
this._startTime ?? 0,
|
||||
Date.now(),
|
||||
this.winner?.winner ?? null,
|
||||
this.winner?.winnerType ?? null,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
if (this.allClients.size === 0) {
|
||||
this.log.info("no clients joined, not archiving game", {
|
||||
gameID: this.id,
|
||||
});
|
||||
} else if (this.winner !== null) {
|
||||
this.log.info("game already archived", {
|
||||
gameID: this.id,
|
||||
});
|
||||
} else {
|
||||
this.archiveGame();
|
||||
}
|
||||
} catch (error) {
|
||||
let errorDetails;
|
||||
@@ -549,6 +534,40 @@ export class GameServer {
|
||||
}
|
||||
}
|
||||
|
||||
private archiveGame() {
|
||||
this.log.info("archiving game", {
|
||||
gameID: this.id,
|
||||
winner: this.winner?.winner,
|
||||
});
|
||||
const playerRecords: PlayerRecord[] = Array.from(
|
||||
this.allClients.values(),
|
||||
).map((client) => {
|
||||
const stats = this.winner?.allPlayersStats[client.clientID];
|
||||
if (stats === undefined) {
|
||||
this.log.warn(`Unable to find stats for clientID ${client.clientID}`);
|
||||
}
|
||||
return {
|
||||
playerID: client.playerID,
|
||||
clientID: client.clientID,
|
||||
username: client.username,
|
||||
persistentID: client.persistentID,
|
||||
stats,
|
||||
} satisfies PlayerRecord;
|
||||
});
|
||||
archive(
|
||||
createGameRecord(
|
||||
this.id,
|
||||
this.gameStartInfo.config,
|
||||
playerRecords,
|
||||
this.turns,
|
||||
this._startTime ?? 0,
|
||||
Date.now(),
|
||||
this.winner?.winner ?? null,
|
||||
this.winner?.winnerType ?? null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private handleSynchronization() {
|
||||
if (this.activeClients.length <= 1) {
|
||||
return;
|
||||
@@ -586,6 +605,7 @@ export class GameServer {
|
||||
|
||||
const desyncMsg = JSON.stringify(serverDesync.data);
|
||||
for (const c of outOfSyncClients) {
|
||||
this.outOfSyncClients.add(c.clientID);
|
||||
if (this.sentDesyncMessageClients.has(c.clientID)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user