From a2aa7823a4fa79b743027365a6f0889cd0b3b450 Mon Sep 17 00:00:00 2001 From: VariableVince <24507472+VariableVince@users.noreply.github.com> Date: Tue, 19 May 2026 04:19:02 +0200 Subject: [PATCH] Display player flags next to their names again (#3965) ## Description: Display flags again. ## 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: tryout33 --- src/client/WebGLFrameBuilder.ts | 8 ++++++- src/core/AssetUrls.ts | 16 +++++++++++++ tests/AssetUrls.test.ts | 41 ++++++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/client/WebGLFrameBuilder.ts b/src/client/WebGLFrameBuilder.ts index aa14c8f65..e0d2efcbd 100644 --- a/src/client/WebGLFrameBuilder.ts +++ b/src/client/WebGLFrameBuilder.ts @@ -1,5 +1,6 @@ import { Colord } from "colord"; import { base64url } from "jose"; +import { extractFlagName } from "../core/AssetUrls"; import { decodePatternData } from "../core/PatternDecoder"; import { PlayerType } from "../core/game/Game"; import { GameView } from "../core/game/GameView"; @@ -127,6 +128,11 @@ export class WebGLFrameBuilder { this.writePaletteEntry(smallID, p.territoryColor(), p.borderColor()); + let flag = p.cosmetics.flag; + if (flag) { + flag = extractFlagName(flag); + } + const pattern = p.cosmetics.pattern; if (pattern && pattern.patternData) { try { @@ -148,7 +154,7 @@ export class WebGLFrameBuilder { newPlayers.push({ ...p.static, - flag: p.cosmetics.flag, + flag: flag, color: p.territoryColor().toHex(), }); } diff --git a/src/core/AssetUrls.ts b/src/core/AssetUrls.ts index fb293e08a..27a1f6384 100644 --- a/src/core/AssetUrls.ts +++ b/src/core/AssetUrls.ts @@ -100,6 +100,22 @@ export function assetUrl(path: string): string { return buildAssetUrl(path, getAssetManifest(), getCdnBase()); } +/** + * Extracts a clean flag name from a cosmetic flag string, which could be + * a literal "flag:Name" token or a full CDN URL (e.g. ".../flags/Name.hash.svg"). + */ +export function extractFlagName(flagData: string): string { + if (flagData.startsWith("flag:")) { + return flagData.slice(5); + } + const match = flagData.match(/\/flags\/([^?#]+)\.svg/); + if (match) { + const raw = match[1].replace(/\.[a-f0-9]{8,12}$/i, ""); + return safeDecodeAssetSegment(raw); + } + return flagData; +} + // Rewrites Vite's emitted /assets/... references in the built index.html to // use the cdnBaseRaw EJS placeholder, so RenderHtml.ts can prefix them with // CDN_BASE at request time. Scoped to src=/href= attribute values so inline diff --git a/tests/AssetUrls.test.ts b/tests/AssetUrls.test.ts index 78a97ff92..b125be584 100644 --- a/tests/AssetUrls.test.ts +++ b/tests/AssetUrls.test.ts @@ -1,5 +1,9 @@ import { describe, expect, test } from "vitest"; -import { buildAssetUrl, rewriteAssetsForCdn } from "../src/core/AssetUrls"; +import { + buildAssetUrl, + extractFlagName, + rewriteAssetsForCdn, +} from "../src/core/AssetUrls"; describe("AssetUrls", () => { test("returns hashed URLs for direct asset matches", () => { @@ -102,6 +106,41 @@ describe("AssetUrls", () => { }); }); +describe("extractFlagName", () => { + test("extracts from flag: prefix", () => { + expect(extractFlagName("flag:Abbasid Caliphate")).toBe("Abbasid Caliphate"); + }); + + test("extracts and decodes from URL", () => { + expect( + extractFlagName( + "https://cdn.ofedge.dev/game_assets/_assets/flags/Abbasid%20Caliphate.ed17b262829c.svg", + ), + ).toBe("Abbasid Caliphate"); + expect(extractFlagName("/flags/Abbasid%20Caliphate.svg")).toBe( + "Abbasid Caliphate", + ); + }); + + test("returns raw string if not matching flag: or /flags/ URL", () => { + expect(extractFlagName("Some Random String")).toBe("Some Random String"); + }); + + test("handles malformed percent-encodings without throwing", () => { + expect(extractFlagName("/flags/Bad%C2Encoding.12345678.svg")).toBe( + "Bad%C2Encoding", + ); + }); + + test("handles edge cases like names containing colons or URLs with hashes", () => { + expect( + extractFlagName( + "/flags/Name%3AWith%3AColons.12345678.svg?query=1#anchor", + ), + ).toBe("Name:With:Colons"); + }); +}); + describe("rewriteAssetsForCdn", () => { test("rewrites src=/assets/ to EJS placeholder", () => { const out = rewriteAssetsForCdn(