diff --git a/src/client/ClientGameRunner.ts b/src/client/ClientGameRunner.ts index d561c0e88..977759e9c 100644 --- a/src/client/ClientGameRunner.ts +++ b/src/client/ClientGameRunner.ts @@ -525,6 +525,14 @@ async function createClientGame( { signal: graphicsListenerAbort.signal }, ); + // Re-resolve names drawn on the map when the anonymous-names setting toggles + // so they switch live, like the leaderboard. + globalThis.addEventListener( + `${USER_SETTINGS_CHANGED_EVENT}:settings.anonymousNames`, + () => webglBuilder.refreshNames(gameView), + { signal: graphicsListenerAbort.signal }, + ); + // Re-resolve settings and copy them onto the renderer's live object in // place (passes hold a reference to it, so they pick the change up). const regenerateRenderSettings = (): void => { diff --git a/src/client/WebGLFrameBuilder.ts b/src/client/WebGLFrameBuilder.ts index 17b76b9b0..0e6422a96 100644 --- a/src/client/WebGLFrameBuilder.ts +++ b/src/client/WebGLFrameBuilder.ts @@ -70,6 +70,19 @@ export class WebGLFrameBuilder { this.view.updatePalette(this.palette); } + /** + * Re-resolve every player's display name (e.g. after toggling the + * anonymous-names setting) and push it to the renderer so the names drawn on + * the map switch live, matching the leaderboard. + */ + refreshNames(gameView: GameView): void { + const displayNames = new Map(); + for (const p of gameView.players()) { + displayNames.set(p.id(), p.displayName()); + } + this.view.refreshNames(displayNames); + } + update(gameView: GameView): void { this.syncPlayers(gameView); this.syncPlayerSpawns(gameView); @@ -224,6 +237,9 @@ export class WebGLFrameBuilder { newPlayers.push({ ...p.static, + // displayName() honors the anonymous-names setting; static.displayName + // is always the real name. + displayName: p.displayName(), flag: flagUrl, color: p.territoryColor().toHex(), }); diff --git a/src/client/render/gl/MapRenderer.ts b/src/client/render/gl/MapRenderer.ts index 20777d2b3..08ba9d862 100644 --- a/src/client/render/gl/MapRenderer.ts +++ b/src/client/render/gl/MapRenderer.ts @@ -163,6 +163,9 @@ export class MapRenderer { ): void { this.renderer?.updateNames(names, players, snap, statusData); } + refreshNames(displayNames: Map): void { + this.renderer?.refreshNames(displayNames); + } updateRelations(data: Uint8Array, size: number): void { this.renderer?.updateRelations(data, size); } diff --git a/src/client/render/gl/Renderer.ts b/src/client/render/gl/Renderer.ts index 5d01e518e..64e6b7454 100644 --- a/src/client/render/gl/Renderer.ts +++ b/src/client/render/gl/Renderer.ts @@ -779,6 +779,11 @@ export class GPURenderer { } } + /** Re-resolve player name strings live (e.g. anonymous-names toggle). */ + refreshNames(displayNames: Map): void { + this.namePass.refreshNames(displayNames); + } + updateRelations(data: Uint8Array, size: number): void { this.borderPass.updateRelations(data, size); this.affiliationPalette.updateRelations(data, size); diff --git a/src/client/render/gl/passes/name-pass/index.ts b/src/client/render/gl/passes/name-pass/index.ts index 6072c82de..b09835d70 100644 --- a/src/client/render/gl/passes/name-pass/index.ts +++ b/src/client/render/gl/passes/name-pass/index.ts @@ -244,6 +244,22 @@ export class NamePass { } } + /** + * Replace cached name strings (e.g. after the anonymous-names setting toggles) + * and force a re-upload on the next updateNames pass. slot.static is the same + * object as the playerByID entry, so updating displayName here is what the + * nameLen === 0 re-upload branch reads. + */ + refreshNames(displayNames: Map): void { + for (const [id, name] of displayNames) { + const p = this.playerByID.get(id); + if (p === undefined) continue; + p.displayName = name; + const slot = this.slots.get(id); + if (slot !== undefined) slot.nameLen = 0; + } + } + /** * Request the texture layer for a slot's flag (called once at slot creation). * If the image is already loaded the layer index is set immediately; otherwise