From 3fd38e730604d119d59e7305e8e0896ce84c8da9 Mon Sep 17 00:00:00 2001 From: Evan Date: Mon, 27 Oct 2025 20:05:09 -0700 Subject: [PATCH] Add clanTag to GameRecord for archiving (#2314) ## Description: This will be used to determine clan winners in the api layer. ## 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: evan --- jest.config.ts | 6 +++++- src/client/LocalServer.ts | 2 ++ src/core/Schemas.ts | 1 + src/core/Util.ts | 9 +++++++++ src/core/game/Game.ts | 9 ++------- src/server/GameServer.ts | 3 ++- 6 files changed, 21 insertions(+), 9 deletions(-) diff --git a/jest.config.ts b/jest.config.ts index 80fb0365d..dbb8d7c35 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -11,8 +11,12 @@ export default { }, transform: { "^.+\\.tsx?$": ["@swc/jest"], + "^.+\\.mjs$": ["@swc/jest"], + "^.+\\.js$": ["@swc/jest"], }, - transformIgnorePatterns: ["node_modules/(?!(node:)/)"], + transformIgnorePatterns: [ + "node_modules/(?!(nanoid|@jsep|fastpriorityqueue|@datastructures-js)/)", + ], collectCoverageFrom: ["src/**/*.ts", "!src/**/*.d.ts"], coverageThreshold: { global: { diff --git a/src/client/LocalServer.ts b/src/client/LocalServer.ts index 09f71e66f..24eed2bff 100644 --- a/src/client/LocalServer.ts +++ b/src/client/LocalServer.ts @@ -14,6 +14,7 @@ import { import { createPartialGameRecord, decompressGameRecord, + getClanTag, replacer, } from "../core/Util"; import { LobbyConfig } from "./ClientGameRunner"; @@ -188,6 +189,7 @@ export class LocalServer { clientID: this.lobbyConfig.clientID, stats: this.allPlayersStats[this.lobbyConfig.clientID], cosmetics: this.lobbyConfig.gameStartInfo?.players[0].cosmetics, + clanTag: getClanTag(this.lobbyConfig.playerName) ?? undefined, }, ]; if (this.lobbyConfig.gameStartInfo === undefined) { diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 9f2dc53c7..43febb707 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -544,6 +544,7 @@ export const ClientMessageSchema = z.discriminatedUnion("type", [ export const PlayerRecordSchema = PlayerSchema.extend({ persistentID: PersistentIdSchema.nullable(), // WARNING: PII + clanTag: z.string().optional(), stats: PlayerStatsSchema, }); export type PlayerRecord = z.infer; diff --git a/src/core/Util.ts b/src/core/Util.ts index 41bac4163..6524e300c 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -320,3 +320,12 @@ export function sigmoid( ): number { return 1 / (1 + Math.exp(-decayRate * (value - midpoint))); } + +// Compute clan from name +export function getClanTag(name: string): string | null { + if (!name.includes("[") || !name.includes("]")) { + return null; + } + const clanMatch = name.match(/\[([a-zA-Z0-9]{2,5})\]/); + return clanMatch ? clanMatch[1].toUpperCase() : null; +} diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 001f76e1f..01dd18c5d 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -1,5 +1,6 @@ import { Config } from "../configuration/Config"; import { AllPlayersStats, ClientID } from "../Schemas"; +import { getClanTag } from "../Util"; import { GameMap, TileRef } from "./GameMap"; import { GameUpdate, @@ -407,13 +408,7 @@ export class PlayerInfo { public readonly id: PlayerID, public readonly nation?: Nation | null, ) { - // Compute clan from name - if (!name.includes("[") || !name.includes("]")) { - this.clan = null; - } else { - const clanMatch = name.match(/\[([a-zA-Z0-9]{2,5})\]/); - this.clan = clanMatch ? clanMatch[1].toUpperCase() : null; - } + this.clan = getClanTag(name); } } diff --git a/src/server/GameServer.ts b/src/server/GameServer.ts index c8adcb083..5780d5400 100644 --- a/src/server/GameServer.ts +++ b/src/server/GameServer.ts @@ -21,7 +21,7 @@ import { ServerTurnMessage, Turn, } from "../core/Schemas"; -import { createPartialGameRecord } from "../core/Util"; +import { createPartialGameRecord, getClanTag } from "../core/Util"; import { archive, finalizeGameRecord } from "./Archive"; import { Client } from "./Client"; export enum GamePhase { @@ -686,6 +686,7 @@ export class GameServer { this.allClients.get(player.clientID)?.persistentID ?? "", stats, cosmetics: player.cosmetics, + clanTag: getClanTag(player.username) ?? undefined, } satisfies PlayerRecord; }, );