mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-22 16:56:38 +00:00
load archived game if not found
This commit is contained in:
@@ -1,46 +1,16 @@
|
||||
import { Executor } from "../core/execution/ExecutionManager";
|
||||
import {
|
||||
Cell,
|
||||
Game,
|
||||
PlayerID,
|
||||
GameMapType,
|
||||
Difficulty,
|
||||
GameType,
|
||||
} from "../core/game/Game";
|
||||
import { PlayerID, GameMapType, Difficulty, GameType } from "../core/game/Game";
|
||||
import { EventBus } from "../core/EventBus";
|
||||
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
|
||||
import {
|
||||
InputHandler,
|
||||
MouseUpEvent,
|
||||
ZoomEvent,
|
||||
DragEvent,
|
||||
MouseDownEvent,
|
||||
} from "./InputHandler";
|
||||
import {
|
||||
ClientID,
|
||||
ClientIntentMessageSchema,
|
||||
ClientJoinMessageSchema,
|
||||
ClientMessageSchema,
|
||||
GameConfig,
|
||||
GameID,
|
||||
Intent,
|
||||
ServerMessage,
|
||||
ServerMessageSchema,
|
||||
ServerSyncMessage,
|
||||
Turn,
|
||||
} from "../core/Schemas";
|
||||
import {
|
||||
loadTerrainFromFile,
|
||||
loadTerrainMap,
|
||||
} from "../core/game/TerrainMapLoader";
|
||||
import { InputHandler, MouseUpEvent } from "./InputHandler";
|
||||
import { ClientID, GameConfig, GameID, ServerMessage } from "../core/Schemas";
|
||||
import { loadTerrainMap } from "../core/game/TerrainMapLoader";
|
||||
import {
|
||||
SendAttackIntentEvent,
|
||||
SendSpawnIntentEvent,
|
||||
Transport,
|
||||
} from "./Transport";
|
||||
import { createCanvas } from "./Utils";
|
||||
import { MessageType } from "../core/game/Game";
|
||||
import { DisplayMessageUpdate, ErrorUpdate } from "../core/game/GameUpdates";
|
||||
import { ErrorUpdate } from "../core/game/GameUpdates";
|
||||
import { WorkerClient } from "../core/worker/WorkerClient";
|
||||
import { consolex, initRemoteSender } from "../core/Consolex";
|
||||
import { getConfig, getServerConfig } from "../core/configuration/Config";
|
||||
@@ -219,6 +189,14 @@ export class ClientGameRunner {
|
||||
if (turn.turnNumber < this.turnsSeen) {
|
||||
continue;
|
||||
}
|
||||
while (turn.turnNumber - 1 > this.turnsSeen) {
|
||||
this.worker.sendTurn({
|
||||
turnNumber: this.turnsSeen,
|
||||
gameID: turn.gameID,
|
||||
intents: [],
|
||||
});
|
||||
this.turnsSeen++;
|
||||
}
|
||||
this.worker.sendTurn(turn);
|
||||
this.turnsSeen++;
|
||||
}
|
||||
|
||||
@@ -121,15 +121,15 @@ export class PlayerExecution implements Execution {
|
||||
|
||||
private surroundedBySamePlayer(cluster: Set<TileRef>): false | Player {
|
||||
const enemies = new Set<number>();
|
||||
for (const ref of cluster) {
|
||||
for (const tile of cluster) {
|
||||
if (
|
||||
this.mg.isOceanShore(ref) ||
|
||||
this.mg.neighbors(ref).some((n) => !this.mg.hasOwner(n))
|
||||
this.mg.isOceanShore(tile) ||
|
||||
this.mg.neighbors(tile).some((n) => !this.mg.hasOwner(n))
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
this.mg
|
||||
.neighbors(ref)
|
||||
.neighbors(tile)
|
||||
.filter((n) => this.mg.ownerID(n) != this.player.smallID())
|
||||
.forEach((p) => enemies.add(this.mg.ownerID(p)));
|
||||
if (enemies.size != 1) {
|
||||
|
||||
@@ -128,6 +128,52 @@ async function archiveToGCS(gameRecord: GameRecord) {
|
||||
console.log(`${gameRecord.id}: game record successfully written to GCS`);
|
||||
}
|
||||
|
||||
export async function readGameRecord(gameId: GameID): Promise<GameRecord> {
|
||||
try {
|
||||
const file = bucket.file(gameId);
|
||||
|
||||
// Check if file exists
|
||||
const [exists] = await file.exists();
|
||||
if (!exists) {
|
||||
throw new Error(`Game record ${gameId} not found in GCS`);
|
||||
}
|
||||
|
||||
// Download and parse file content
|
||||
const [content] = await file.download();
|
||||
const gameRecord = JSON.parse(content.toString());
|
||||
|
||||
// Validate the parsed content against the schema
|
||||
const validatedRecord = GameRecordSchema.parse(gameRecord);
|
||||
|
||||
console.log(`${gameId}: Successfully read game record from GCS`);
|
||||
return validatedRecord;
|
||||
} catch (error) {
|
||||
console.error(`${gameId}: Error reading game record from GCS: ${error}`, {
|
||||
message: error?.message || error,
|
||||
stack: error?.stack,
|
||||
name: error?.name,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
export async function gameRecordExists(gameId: GameID): Promise<boolean> {
|
||||
try {
|
||||
const file = bucket.file(gameId);
|
||||
const [exists] = await file.exists();
|
||||
return exists;
|
||||
} catch (error) {
|
||||
console.error(`${gameId}: Error checking archive existence: ${error}`, {
|
||||
message: error?.message || error,
|
||||
stack: error?.stack,
|
||||
name: error?.name,
|
||||
...(error && typeof error === "object" ? error : {}),
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function anonymizeIPv4(ipv4: string): string | null {
|
||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ export class GameManager {
|
||||
return this.games.filter((g) => g.phase() == phase);
|
||||
}
|
||||
|
||||
addClient(client: Client, gameID: GameID, lastTurn: number) {
|
||||
addClient(client: Client, gameID: GameID, lastTurn: number): boolean {
|
||||
const game = this.games.find((g) => g.id == gameID);
|
||||
if (!game) {
|
||||
console.log(`game id ${gameID} not found`);
|
||||
return;
|
||||
if (game) {
|
||||
game.addClient(client, lastTurn);
|
||||
return true;
|
||||
}
|
||||
game.addClient(client, lastTurn);
|
||||
return false;
|
||||
}
|
||||
|
||||
updateGameConfig(gameID: GameID, gameConfig: GameConfig) {
|
||||
|
||||
+23
-8
@@ -10,6 +10,7 @@ import {
|
||||
GameRecord,
|
||||
GameRecordSchema,
|
||||
LogSeverity,
|
||||
ServerStartGameMessageSchema,
|
||||
} from "../core/Schemas";
|
||||
import {
|
||||
GameEnv,
|
||||
@@ -19,7 +20,7 @@ import {
|
||||
import { slog } from "./StructuredLog";
|
||||
import { Client } from "./Client";
|
||||
import { GamePhase, GameServer } from "./GameServer";
|
||||
import { archive } from "./Archive";
|
||||
import { archive, gameRecordExists, readGameRecord } from "./Archive";
|
||||
import { DiscordBot } from "./DiscordBot";
|
||||
import {
|
||||
sanitizeUsername,
|
||||
@@ -176,13 +177,14 @@ app.put("/private_lobby/:id", (req, res) => {
|
||||
});
|
||||
});
|
||||
|
||||
app.get("/lobby/:id/exists", (req, res) => {
|
||||
app.get("/lobby/:id/exists", async (req, res) => {
|
||||
const lobbyId = req.params.id;
|
||||
console.log(`checking lobby ${lobbyId} exists`);
|
||||
const lobbyExists = gm.hasActiveGame(lobbyId);
|
||||
|
||||
let gameExists = gm.hasActiveGame(lobbyId);
|
||||
if (!gameExists) {
|
||||
gameExists = await gameRecordExists(lobbyId);
|
||||
}
|
||||
res.json({
|
||||
exists: lobbyExists,
|
||||
exists: gameExists,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -212,7 +214,7 @@ app.get("*", function (req, res) {
|
||||
});
|
||||
|
||||
wss.on("connection", (ws, req) => {
|
||||
ws.on("message", (message: string) => {
|
||||
ws.on("message", async (message: string) => {
|
||||
try {
|
||||
const clientMsg: ClientMessage = ClientMessageSchema.parse(
|
||||
JSON.parse(message),
|
||||
@@ -233,7 +235,7 @@ wss.on("connection", (ws, req) => {
|
||||
return;
|
||||
}
|
||||
clientMsg.username = sanitizeUsername(clientMsg.username);
|
||||
gm.addClient(
|
||||
const wasFound = gm.addClient(
|
||||
new Client(
|
||||
clientMsg.clientID,
|
||||
clientMsg.persistentID,
|
||||
@@ -244,6 +246,19 @@ wss.on("connection", (ws, req) => {
|
||||
clientMsg.gameID,
|
||||
clientMsg.lastTurn,
|
||||
);
|
||||
if (!wasFound) {
|
||||
console.log(`game ${clientMsg.gameID} not found, loading from gcs`);
|
||||
const record = await readGameRecord(clientMsg.gameID);
|
||||
ws.send(
|
||||
JSON.stringify(
|
||||
ServerStartGameMessageSchema.parse({
|
||||
type: "start",
|
||||
turns: record.turns,
|
||||
config: record.gameConfig,
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
if (clientMsg.type == "log") {
|
||||
slog({
|
||||
|
||||
Reference in New Issue
Block a user