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
This commit is contained in:
VariableVince
2026-05-19 04:19:02 +02:00
committed by GitHub
parent 17e3ac4b05
commit a2aa7823a4
3 changed files with 63 additions and 2 deletions
+7 -1
View File
@@ -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(),
});
}
+16
View File
@@ -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
+40 -1
View File
@@ -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(