mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-29 03:44:40 +00:00
Merge branch 'main' into patterned-territory
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { S3 } from "@aws-sdk/client-s3";
|
||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
import { AnalyticsRecord, GameID, GameRecord } from "../core/Schemas";
|
||||
import { replacer } from "../core/Util";
|
||||
import { logger } from "./Logger";
|
||||
|
||||
const config = getServerConfigFromServer();
|
||||
@@ -60,7 +61,7 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) {
|
||||
await r2.putObject({
|
||||
Bucket: bucket,
|
||||
Key: `${analyticsFolder}/${analyticsKey}`,
|
||||
Body: JSON.stringify(analyticsData),
|
||||
Body: JSON.stringify(analyticsData, replacer),
|
||||
ContentType: "application/json",
|
||||
});
|
||||
|
||||
@@ -78,19 +79,18 @@ async function archiveAnalyticsToR2(gameRecord: GameRecord) {
|
||||
|
||||
async function archiveFullGameToR2(gameRecord: GameRecord) {
|
||||
// Create a deep copy to avoid modifying the original
|
||||
const recordCopy = JSON.parse(JSON.stringify(gameRecord));
|
||||
const recordCopy = structuredClone(gameRecord);
|
||||
|
||||
// Players may see this so make sure to clear PII
|
||||
recordCopy.players.forEach((p) => {
|
||||
p.ip = "REDACTED";
|
||||
recordCopy.info.players.forEach((p) => {
|
||||
p.persistentID = "REDACTED";
|
||||
});
|
||||
|
||||
try {
|
||||
await r2.putObject({
|
||||
Bucket: bucket,
|
||||
Key: `${gameFolder}/${recordCopy.id}`,
|
||||
Body: JSON.stringify(recordCopy),
|
||||
Key: `${gameFolder}/${recordCopy.info.gameID}`,
|
||||
Body: JSON.stringify(recordCopy, replacer),
|
||||
ContentType: "application/json",
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
import WebSocket from "ws";
|
||||
import { TokenPayload } from "../core/ApiSchemas";
|
||||
import { PlayerID, Tick } from "../core/game/Game";
|
||||
import { Tick } from "../core/game/Game";
|
||||
import { ClientID } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
|
||||
export class Client {
|
||||
public lastPing: number;
|
||||
|
||||
public hashes: Map<Tick, number> = new Map();
|
||||
|
||||
public readonly playerID: PlayerID = generateID();
|
||||
|
||||
constructor(
|
||||
public readonly clientID: ClientID,
|
||||
public readonly persistentID: string,
|
||||
|
||||
+49
-32
@@ -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(
|
||||
@@ -291,7 +300,6 @@ export class GameServer {
|
||||
gameID: this.id,
|
||||
config: this.gameConfig,
|
||||
players: this.activeClients.map((c) => ({
|
||||
playerID: c.playerID,
|
||||
username: c.username,
|
||||
clientID: c.clientID,
|
||||
pattern: c.pattern,
|
||||
@@ -383,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;
|
||||
@@ -550,6 +534,38 @@ 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 {
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
private handleSynchronization() {
|
||||
if (this.activeClients.length <= 1) {
|
||||
return;
|
||||
@@ -587,6 +603,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