mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-02 06:08:21 +00:00
Enable @total-typescript/ts-reset (#1761)
## Description: Enable `@total-typescript/ts-reset` Fixes #1760 ## 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 - [ ] I have read and accepted the CLA agreement (only required once).
This commit is contained in:
Generated
+8
@@ -43,6 +43,7 @@
|
||||
"@eslint/compat": "^1.2.7",
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@swc/jest": "^0.2.39",
|
||||
"@total-typescript/ts-reset": "^0.6.1",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@types/chai": "^4.3.17",
|
||||
"@types/d3": "^7.4.3",
|
||||
@@ -6604,6 +6605,13 @@
|
||||
"@swc/counter": "^0.1.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@total-typescript/ts-reset": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@total-typescript/ts-reset/-/ts-reset-0.6.1.tgz",
|
||||
"integrity": "sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@eslint/js": "^9.21.0",
|
||||
"@swc/jest": "^0.2.39",
|
||||
"@types/benchmark": "^2.1.5",
|
||||
"@total-typescript/ts-reset": "^0.6.1",
|
||||
"@types/chai": "^4.3.17",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/express": "^4.17.23",
|
||||
|
||||
@@ -229,7 +229,7 @@ export class ClientGameRunner {
|
||||
players,
|
||||
// Not saving turns locally
|
||||
[],
|
||||
startTime(),
|
||||
startTime() ?? 0,
|
||||
Date.now(),
|
||||
update.winner,
|
||||
this.lobby.serverConfig,
|
||||
|
||||
+14
-2
@@ -1,4 +1,8 @@
|
||||
import { UserMeResponse } from "../core/ApiSchemas";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
StripeCreateCheckoutSessionResponseSchema,
|
||||
UserMeResponse,
|
||||
} from "../core/ApiSchemas";
|
||||
import { Cosmetics, CosmeticsSchema, Pattern } from "../core/CosmeticSchemas";
|
||||
import { getApiBase, getAuthHeader } from "./jwt";
|
||||
|
||||
@@ -59,7 +63,15 @@ export async function handlePurchase(priceId: string) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { url } = await response.json();
|
||||
const json = await response.json();
|
||||
const parsed = StripeCreateCheckoutSessionResponseSchema.safeParse(json);
|
||||
if (!parsed.success) {
|
||||
const error = z.prettifyError(parsed.error);
|
||||
console.error("Invalid checkout session response:", error);
|
||||
alert("Checkout failed. Please try again later.");
|
||||
return;
|
||||
}
|
||||
const { url } = parsed.data;
|
||||
|
||||
// Redirect to Stripe checkout
|
||||
window.location.href = url;
|
||||
|
||||
@@ -159,7 +159,7 @@ export class InputHandler {
|
||||
groundAttack: "KeyG",
|
||||
modifierKey: "ControlLeft",
|
||||
altKey: "AltLeft",
|
||||
...JSON.parse(localStorage.getItem("settings.keybinds") ?? "{}"),
|
||||
...(JSON.parse(localStorage.getItem("settings.keybinds") ?? "{}") ?? {}),
|
||||
};
|
||||
|
||||
// Mac users might have different keybinds
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { GameInfo, GameRecord } from "../core/Schemas";
|
||||
import { GameInfo } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
import {
|
||||
WorkerApiArchivedGameLobbySchema,
|
||||
WorkerApiGameIdExistsSchema,
|
||||
} from "../core/WorkerSchemas";
|
||||
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
||||
import { JoinLobbyEvent } from "./Main";
|
||||
import "./components/baseComponents/Button";
|
||||
@@ -198,7 +202,8 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
const gameInfo = await response.json();
|
||||
const json = await response.json();
|
||||
const gameInfo = WorkerApiGameIdExistsSchema.parse(json);
|
||||
|
||||
if (gameInfo.exists) {
|
||||
this.message = translateText("private_lobby.joined_waiting");
|
||||
@@ -231,7 +236,8 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
|
||||
const archiveData = await archiveResponse.json();
|
||||
const json = await archiveResponse.json();
|
||||
const archiveData = WorkerApiArchivedGameLobbySchema.parse(json);
|
||||
|
||||
if (
|
||||
archiveData.success === false &&
|
||||
@@ -247,13 +253,11 @@ export class JoinPrivateLobbyModal extends LitElement {
|
||||
}
|
||||
|
||||
if (archiveData.exists) {
|
||||
const gameRecord = archiveData.gameRecord as GameRecord;
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("join-lobby", {
|
||||
detail: {
|
||||
gameID: lobbyId,
|
||||
gameRecord: gameRecord,
|
||||
gameRecord: archiveData.gameRecord,
|
||||
clientID: generateID(),
|
||||
} as JoinLobbyEvent,
|
||||
bubbles: true,
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
import { GameConfig, GameID, GameRecord } from "../core/Schemas";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
GameConfig,
|
||||
GameConfigSchema,
|
||||
GameID,
|
||||
GameRecord,
|
||||
GameRecordSchema,
|
||||
ID,
|
||||
} from "../core/Schemas";
|
||||
import { replacer } from "../core/Util";
|
||||
|
||||
export interface LocalStatsData {
|
||||
[key: GameID]: {
|
||||
lobby: Partial<GameConfig>;
|
||||
const LocalStatsDataSchema = z.record(
|
||||
ID,
|
||||
z.object({
|
||||
lobby: GameConfigSchema.partial(),
|
||||
// Only once the game is over
|
||||
gameRecord?: GameRecord;
|
||||
};
|
||||
}
|
||||
gameRecord: GameRecordSchema.optional(),
|
||||
}),
|
||||
);
|
||||
type LocalStatsData = z.infer<typeof LocalStatsDataSchema>;
|
||||
|
||||
let _startTime: number;
|
||||
let _startTime: number | undefined;
|
||||
|
||||
function getStats(): LocalStatsData {
|
||||
const statsStr = localStorage.getItem("game-records");
|
||||
return statsStr ? JSON.parse(statsStr) : {};
|
||||
try {
|
||||
return LocalStatsDataSchema.parse(
|
||||
JSON.parse(localStorage.getItem("game-records") ?? "{}"),
|
||||
);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function save(stats: LocalStatsData) {
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const LockSchema = z.object({
|
||||
owner: z.string(),
|
||||
timestamp: z.number(),
|
||||
});
|
||||
|
||||
export class MultiTabDetector {
|
||||
private readonly tabId = `${Date.now()}-${Math.random()}`;
|
||||
private readonly lockKey = "multi-tab-lock";
|
||||
@@ -60,7 +67,7 @@ export class MultiTabDetector {
|
||||
if (e.key === this.lockKey && e.newValue) {
|
||||
let other: { owner: string; timestamp: number };
|
||||
try {
|
||||
other = JSON.parse(e.newValue);
|
||||
other = LockSchema.parse(JSON.parse(e.newValue));
|
||||
} catch (e) {
|
||||
console.error("Failed to parse lock", e);
|
||||
return;
|
||||
@@ -99,7 +106,7 @@ export class MultiTabDetector {
|
||||
const raw = localStorage.getItem(this.lockKey);
|
||||
if (!raw) return null;
|
||||
try {
|
||||
return JSON.parse(raw);
|
||||
return LockSchema.parse(JSON.parse(raw));
|
||||
} catch (e) {
|
||||
console.error("Failed to parse lock", raw, e);
|
||||
return null;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, state } from "lit/decorators.js";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { ApiPublicLobbiesResponseSchema } from "../core/ExpressSchemas";
|
||||
import { GameMapType, GameMode } from "../core/game/Game";
|
||||
import { GameID, GameInfo } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
@@ -77,7 +78,8 @@ export class PublicLobby extends LitElement {
|
||||
const response = await fetch(`/api/public_lobbies`);
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
const json = await response.json();
|
||||
const data = ApiPublicLobbiesResponseSchema.parse(json);
|
||||
return data.lobbies;
|
||||
} catch (error) {
|
||||
console.error("Error fetching lobbies:", error);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, query, state } from "lit/decorators.js";
|
||||
import { z } from "zod";
|
||||
import { translateText } from "../client/Utils";
|
||||
import { UserSettings } from "../core/game/UserSettings";
|
||||
import "./components/baseComponents/setting/SettingKeybind";
|
||||
@@ -8,6 +9,8 @@ import "./components/baseComponents/setting/SettingNumber";
|
||||
import "./components/baseComponents/setting/SettingSlider";
|
||||
import "./components/baseComponents/setting/SettingToggle";
|
||||
|
||||
const KeybindSchema = z.record(z.string(), z.string());
|
||||
|
||||
@customElement("user-setting")
|
||||
export class UserSettingModal extends LitElement {
|
||||
private userSettings: UserSettings = new UserSettings();
|
||||
@@ -25,7 +28,7 @@ export class UserSettingModal extends LitElement {
|
||||
const savedKeybinds = localStorage.getItem("settings.keybinds");
|
||||
if (savedKeybinds) {
|
||||
try {
|
||||
this.keybinds = JSON.parse(savedKeybinds);
|
||||
this.keybinds = KeybindSchema.parse(JSON.parse(savedKeybinds));
|
||||
} catch (e) {
|
||||
console.warn("Invalid keybinds JSON:", e);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// This file contains schemas for api.openfront.io
|
||||
import { z } from "zod";
|
||||
import { base64urlToUuid } from "./Base64";
|
||||
|
||||
@@ -48,3 +49,19 @@ export const UserMeResponseSchema = z.object({
|
||||
}),
|
||||
});
|
||||
export type UserMeResponse = z.infer<typeof UserMeResponseSchema>;
|
||||
|
||||
export const StripeCreateCheckoutSessionResponseSchema = z.object({
|
||||
id: z.string(),
|
||||
object: z.literal("checkout.session"),
|
||||
url: z.string(),
|
||||
payment_status: z.enum(["paid", "unpaid", "no_payment_required"]),
|
||||
status: z.enum(["open", "complete", "expired"]),
|
||||
client_reference_id: z.string().optional(),
|
||||
customer: z.string().optional(),
|
||||
payment_intent: z.string().optional(),
|
||||
subscription: z.string().optional(),
|
||||
metadata: z.partialRecord(z.string(), z.string()),
|
||||
});
|
||||
export type StripeCreateCheckoutSessionResponse = z.infer<
|
||||
typeof StripeCreateCheckoutSessionResponseSchema
|
||||
>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { z } from "zod/v4";
|
||||
import { z } from "zod";
|
||||
import { RequiredPatternSchema } from "./Schemas";
|
||||
|
||||
export const ProductSchema = z.object({
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// This file contians schemas for the primary openfront express server
|
||||
import { z } from "zod";
|
||||
import { GameInfoSchema } from "./Schemas";
|
||||
|
||||
export const ApiEnvResponseSchema = z.object({
|
||||
game_env: z.string(),
|
||||
});
|
||||
export type ApiEnvResponse = z.infer<typeof ApiEnvResponseSchema>;
|
||||
|
||||
export const ApiPublicLobbiesResponseSchema = z.object({
|
||||
lobbies: GameInfoSchema.array(),
|
||||
});
|
||||
export type ApiPublicLobbiesResponse = z.infer<
|
||||
typeof ApiPublicLobbiesResponseSchema
|
||||
>;
|
||||
+16
-11
@@ -112,17 +112,6 @@ export type Player = z.infer<typeof PlayerSchema>;
|
||||
export type GameStartInfo = z.infer<typeof GameStartInfoSchema>;
|
||||
const PlayerTypeSchema = z.enum(PlayerType);
|
||||
|
||||
export interface GameInfo {
|
||||
gameID: GameID;
|
||||
clients?: ClientInfo[];
|
||||
numClients?: number;
|
||||
msUntilStart?: number;
|
||||
gameConfig?: GameConfig;
|
||||
}
|
||||
export interface ClientInfo {
|
||||
clientID: ClientID;
|
||||
username: string;
|
||||
}
|
||||
export enum LogSeverity {
|
||||
Debug = "DEBUG",
|
||||
Info = "INFO",
|
||||
@@ -192,6 +181,22 @@ export const ID = z
|
||||
export const AllPlayersStatsSchema = z.record(ID, PlayerStatsSchema);
|
||||
|
||||
export const UsernameSchema = SafeString;
|
||||
|
||||
export const ClientInfoSchema = z.object({
|
||||
clientID: ID,
|
||||
username: UsernameSchema,
|
||||
});
|
||||
export type ClientInfo = z.infer<typeof ClientInfoSchema>;
|
||||
|
||||
export const GameInfoSchema = z.object({
|
||||
clients: ClientInfoSchema.array().optional(),
|
||||
gameConfig: GameConfigSchema.optional(),
|
||||
gameID: ID,
|
||||
msUntilStart: z.number().int().nonnegative().optional(),
|
||||
numClients: z.number().int().nonnegative().optional(),
|
||||
});
|
||||
export type GameInfo = z.infer<typeof GameInfoSchema>;
|
||||
|
||||
const countryCodes = countries.map((c) => c.code);
|
||||
export const FlagSchema = z
|
||||
.string()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// This file contians schemas for the openfront worker express server
|
||||
import { z } from "zod";
|
||||
import { GameConfigSchema } from "./Schemas";
|
||||
import { GameConfigSchema, GameRecordSchema } from "./Schemas";
|
||||
|
||||
export const CreateGameInputSchema = GameConfigSchema.or(
|
||||
z
|
||||
@@ -9,3 +10,33 @@ export const CreateGameInputSchema = GameConfigSchema.or(
|
||||
);
|
||||
|
||||
export const GameInputSchema = GameConfigSchema.partial();
|
||||
|
||||
export const WorkerApiGameIdExistsSchema = z.object({
|
||||
exists: z.boolean(),
|
||||
});
|
||||
export type WorkerApiGameIdExists = z.infer<typeof WorkerApiGameIdExistsSchema>;
|
||||
|
||||
export const WorkerApiArchivedGameLobbySchema = z.union([
|
||||
z.object({
|
||||
error: z.literal("Game not found"),
|
||||
exists: z.literal(false),
|
||||
success: z.literal(false),
|
||||
}),
|
||||
z.object({
|
||||
details: z.object({
|
||||
actualCommit: z.string(),
|
||||
expectedCommit: z.string(),
|
||||
}),
|
||||
error: z.literal("Version mismatch"),
|
||||
exists: z.literal(true),
|
||||
success: z.literal(false),
|
||||
}),
|
||||
z.object({
|
||||
exists: z.literal(true),
|
||||
gameRecord: GameRecordSchema,
|
||||
success: z.literal(true),
|
||||
}),
|
||||
]);
|
||||
export type WorkerApiArchivedGameLobby = z.infer<
|
||||
typeof WorkerApiArchivedGameLobbySchema
|
||||
>;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ApiEnvResponseSchema } from "../ExpressSchemas";
|
||||
import { UserSettings } from "../game/UserSettings";
|
||||
import { GameConfig } from "../Schemas";
|
||||
import { Config, GameEnv, ServerConfig } from "./Config";
|
||||
@@ -36,7 +37,8 @@ export async function getServerConfigFromClient(): Promise<ServerConfig> {
|
||||
`Failed to fetch server config: ${response.status} ${response.statusText}`,
|
||||
);
|
||||
}
|
||||
const config = await response.json();
|
||||
const json = await response.json();
|
||||
const config = ApiEnvResponseSchema.parse(json);
|
||||
// Log the retrieved configuration
|
||||
console.log("Server config loaded:", config);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { GameMapType } from "./Game";
|
||||
import { GameMapLoader, MapData } from "./GameMapLoader";
|
||||
import { MapManifestSchema } from "./TerrainMapLoader";
|
||||
|
||||
export class FetchGameMapLoader implements GameMapLoader {
|
||||
private maps: Map<GameMapType, MapData>;
|
||||
@@ -66,6 +67,6 @@ export class FetchGameMapLoader implements GameMapLoader {
|
||||
throw new Error(`Failed to load ${url}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
return response.json().then(MapManifestSchema.parse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { z } from "zod";
|
||||
import { GameMapType } from "./Game";
|
||||
import { GameMap, GameMapImpl } from "./GameMap";
|
||||
import { GameMapLoader } from "./GameMapLoader";
|
||||
@@ -10,25 +11,28 @@ export type TerrainMapData = {
|
||||
|
||||
const loadedMaps = new Map<GameMapType, TerrainMapData>();
|
||||
|
||||
export interface MapMetadata {
|
||||
width: number;
|
||||
height: number;
|
||||
num_land_tiles: number;
|
||||
}
|
||||
export const MapMetadataSchema = z.object({
|
||||
height: z.number(),
|
||||
num_land_tiles: z.number(),
|
||||
width: z.number(),
|
||||
});
|
||||
export type MapMetadata = z.infer<typeof MapMetadataSchema>;
|
||||
|
||||
export interface MapManifest {
|
||||
name: string;
|
||||
map: MapMetadata;
|
||||
mini_map: MapMetadata;
|
||||
nations: Nation[];
|
||||
}
|
||||
export const NationSchema = z.object({
|
||||
coordinates: z.tuple([z.number(), z.number()]),
|
||||
flag: z.string(),
|
||||
name: z.string(),
|
||||
strength: z.number(),
|
||||
});
|
||||
export type Nation = z.infer<typeof NationSchema>;
|
||||
|
||||
export interface Nation {
|
||||
coordinates: [number, number];
|
||||
flag: string;
|
||||
name: string;
|
||||
strength: number;
|
||||
}
|
||||
export const MapManifestSchema = z.object({
|
||||
map: MapMetadataSchema,
|
||||
mini_map: MapMetadataSchema,
|
||||
name: z.string(),
|
||||
nations: NationSchema.array(),
|
||||
});
|
||||
export type MapManifest = z.infer<typeof MapManifestSchema>;
|
||||
|
||||
export async function loadTerrainMap(
|
||||
map: GameMapType,
|
||||
|
||||
@@ -144,7 +144,12 @@ export class UnitGrid {
|
||||
searchRange,
|
||||
);
|
||||
const rangeSquared = searchRange * searchRange;
|
||||
const typeSet = Array.isArray(types) ? new Set(types) : new Set([types]);
|
||||
const typeSet = new Set(
|
||||
// Using typeof check instead of Array.isArray due to a typescript
|
||||
// narrowing limitation. For more information, see the full issue
|
||||
// discussion at https://github.com/mattpocock/ts-reset/issues/48
|
||||
typeof types === "object" ? types : [types],
|
||||
);
|
||||
for (let cy = startGridY; cy <= endGridY; cy++) {
|
||||
for (let cx = startGridX; cx <= endGridX; cx++) {
|
||||
for (const type of typeSet) {
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
import "@total-typescript/ts-reset";
|
||||
@@ -1,6 +1,7 @@
|
||||
import { spawn } from "child_process";
|
||||
import { promises as fs } from "fs";
|
||||
import yaml from "js-yaml";
|
||||
import { z } from "zod";
|
||||
import { logger } from "./Logger";
|
||||
|
||||
const log = logger.child({
|
||||
@@ -41,6 +42,12 @@ interface CloudflaredConfig {
|
||||
}>;
|
||||
}
|
||||
|
||||
const CloudflareTunnelConfigSchema = z.object({
|
||||
a: z.string(),
|
||||
s: z.string(),
|
||||
t: z.string(),
|
||||
});
|
||||
|
||||
export class Cloudflare {
|
||||
private baseUrl = "https://api.cloudflare.com/client/v4";
|
||||
|
||||
@@ -157,14 +164,12 @@ export class Cloudflare {
|
||||
tunnelName: string,
|
||||
): Promise<void> {
|
||||
log.info(`Creating local config for tunnel ${subdomain}.${domain}...`);
|
||||
const tokenData = JSON.parse(
|
||||
Buffer.from(tunnelToken, "base64").toString("utf8"),
|
||||
const tokenData = CloudflareTunnelConfigSchema.parse(
|
||||
JSON.parse(Buffer.from(tunnelToken, "base64").toString("utf8")),
|
||||
);
|
||||
|
||||
const credentials = {
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
AccountTag: tokenData.a || this.accountId,
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
TunnelID: tokenData.t || tunnelId,
|
||||
TunnelName: tunnelName,
|
||||
TunnelSecret: tokenData.s,
|
||||
|
||||
+10
-4
@@ -5,6 +5,10 @@ import http from "http";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
import {
|
||||
ApiEnvResponse,
|
||||
ApiPublicLobbiesResponse,
|
||||
} from "../core/ExpressSchemas";
|
||||
import { GameInfo, ID } from "../core/Schemas";
|
||||
import { generateID } from "../core/Util";
|
||||
import { gatekeeper, LimiterType } from "./Gatekeeper";
|
||||
@@ -59,7 +63,9 @@ app.use(
|
||||
}),
|
||||
);
|
||||
|
||||
let publicLobbiesJsonStr = "";
|
||||
let publicLobbiesJsonStr = JSON.stringify({
|
||||
lobbies: [],
|
||||
} satisfies ApiPublicLobbiesResponse);
|
||||
|
||||
const publicLobbyIDs: Set<string> = new Set();
|
||||
|
||||
@@ -145,8 +151,8 @@ export async function startMaster() {
|
||||
app.get(
|
||||
"/api/env",
|
||||
gatekeeper.httpHandler(LimiterType.Get, async (req, res) => {
|
||||
const envConfig = {
|
||||
game_env: process.env.GAME_ENV,
|
||||
const envConfig: ApiEnvResponse = {
|
||||
game_env: process.env.GAME_ENV ?? "",
|
||||
};
|
||||
if (!envConfig.game_env) return res.sendStatus(500);
|
||||
res.json(envConfig);
|
||||
@@ -266,7 +272,7 @@ async function fetchLobbies(): Promise<number> {
|
||||
// Update the JSON string
|
||||
publicLobbiesJsonStr = JSON.stringify({
|
||||
lobbies: lobbyInfos,
|
||||
});
|
||||
} satisfies ApiPublicLobbiesResponse);
|
||||
|
||||
return publicLobbyIDs.size;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,11 @@ import { GameEnv } from "../core/configuration/Config";
|
||||
import { getServerConfigFromServer } from "../core/configuration/ConfigLoader";
|
||||
import { GameType } from "../core/game/Game";
|
||||
import { GameRecord, GameRecordSchema, ID } from "../core/Schemas";
|
||||
import { CreateGameInputSchema, GameInputSchema } from "../core/WorkerSchemas";
|
||||
import {
|
||||
CreateGameInputSchema,
|
||||
GameInputSchema,
|
||||
WorkerApiGameIdExists,
|
||||
} from "../core/WorkerSchemas";
|
||||
import { archive, readGameRecord } from "./Archive";
|
||||
import { GameManager } from "./GameManager";
|
||||
import { gatekeeper, LimiterType } from "./Gatekeeper";
|
||||
@@ -201,7 +205,7 @@ export async function startWorker() {
|
||||
const lobbyId = req.params.id;
|
||||
res.json({
|
||||
exists: gm.game(lobbyId) !== null,
|
||||
});
|
||||
} satisfies WorkerApiGameIdExists);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -147,9 +147,7 @@ describe("AutoUpgrade Feature", () => {
|
||||
const event = new AutoUpgradeEvent(100, 200);
|
||||
const eventString = JSON.stringify(event);
|
||||
const parsedEvent = JSON.parse(eventString);
|
||||
|
||||
expect(parsedEvent.x).toBe(100);
|
||||
expect(parsedEvent.y).toBe(200);
|
||||
expect(parsedEvent).toStrictEqual({ x: 100, y: 200 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+10
-1
@@ -1,5 +1,6 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { z } from "zod";
|
||||
|
||||
describe("LangCode Filename Check", () => {
|
||||
const langDir = path.join(__dirname, "../resources/lang");
|
||||
@@ -14,9 +15,17 @@ describe("LangCode Filename Check", () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const schema = z.object({
|
||||
lang: z.object({
|
||||
lang_code: z.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(langDir, file);
|
||||
const jsonData = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
||||
const jsonData = schema.parse(
|
||||
JSON.parse(fs.readFileSync(filePath, "utf-8")),
|
||||
);
|
||||
|
||||
const fileNameWithoutExt = path.basename(file, ".json");
|
||||
const langCode = jsonData.lang?.lang_code;
|
||||
|
||||
@@ -21,7 +21,14 @@ describe("Lang SVG Field and File Existence Check", () => {
|
||||
try {
|
||||
const filePath = path.join(langDir, file);
|
||||
const jsonData = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
||||
const langSvg = jsonData.lang?.svg;
|
||||
const langSvg =
|
||||
jsonData &&
|
||||
typeof jsonData === "object" &&
|
||||
"lang" in jsonData &&
|
||||
jsonData.lang &&
|
||||
typeof jsonData.lang === "object" &&
|
||||
"svg" in jsonData.lang &&
|
||||
jsonData.lang.svg;
|
||||
if (typeof langSvg !== "string" || langSvg.length === 0) {
|
||||
errors.push(
|
||||
`[${file}]: lang.svg is missing or not a non-empty string`,
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"num_land_tiles": 10000,
|
||||
"width": 100
|
||||
},
|
||||
"nations": [],
|
||||
"name": "Big Plains"
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"num_land_tiles": 48,
|
||||
"width": 8
|
||||
},
|
||||
"nations": [],
|
||||
"name": "Half Land Half Ocean"
|
||||
}
|
||||
|
||||
@@ -9,5 +9,6 @@
|
||||
"num_land_tiles": 50,
|
||||
"width": 8
|
||||
},
|
||||
"nations": [],
|
||||
"name": "Ocean and Land"
|
||||
}
|
||||
|
||||
+1
@@ -9,5 +9,6 @@
|
||||
"num_land_tiles": 2500,
|
||||
"width": 50
|
||||
},
|
||||
"nations": [],
|
||||
"name": "Plains"
|
||||
}
|
||||
|
||||
+10
-4
@@ -1,5 +1,6 @@
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { z } from "zod";
|
||||
import {
|
||||
Difficulty,
|
||||
Game,
|
||||
@@ -12,7 +13,7 @@ import {
|
||||
import { createGame } from "../../src/core/game/GameImpl";
|
||||
import {
|
||||
genTerrainFromBin,
|
||||
MapManifest,
|
||||
MapManifestSchema,
|
||||
} from "../../src/core/game/TerrainMapLoader";
|
||||
import { UserSettings } from "../../src/core/game/UserSettings";
|
||||
import { GameConfig } from "../../src/core/Schemas";
|
||||
@@ -44,9 +45,14 @@ export async function setup(
|
||||
|
||||
const mapBinBuffer = fs.readFileSync(mapBinPath);
|
||||
const miniMapBinBuffer = fs.readFileSync(miniMapBinPath);
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(manifestPath, "utf8"),
|
||||
) satisfies MapManifest;
|
||||
const str = fs.readFileSync(manifestPath, "utf8");
|
||||
const raw = JSON.parse(str);
|
||||
const parsed = MapManifestSchema.safeParse(raw);
|
||||
if (!parsed.success) {
|
||||
const error = z.prettifyError(parsed.error);
|
||||
throw new Error(`Error parsing ${manifestPath}: ${error}`);
|
||||
}
|
||||
const manifest = parsed.data;
|
||||
|
||||
const gameMap = await genTerrainFromBin(manifest.map, mapBinBuffer);
|
||||
const miniGameMap = await genTerrainFromBin(
|
||||
|
||||
Reference in New Issue
Block a user