From 6cca96b545fd17664134dbcff944c4b056e249a2 Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Wed, 28 Jan 2026 08:49:06 +0900 Subject: [PATCH] Anonymized/hidden names on lobby preview (#2965) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves #2962 ## Description: Anonymize lobby preview player names when “Hidden names” is enabled, using the same deterministic mapping as in-game. スクリーンショット 2026-01-20 21 13 19 スクリーンショット 2026-01-20 21 13 27 ## 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: aotumuri Co-authored-by: Ryan <7389646+ryanbarlow97@users.noreply.github.com> --- src/client/HostLobbyModal.ts | 1 + src/client/JoinPrivateLobbyModal.ts | 9 +++- src/client/components/LobbyPlayerView.ts | 52 +++++++++++++++++------- 3 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/client/HostLobbyModal.ts b/src/client/HostLobbyModal.ts index e5bb92f11..5355f9f33 100644 --- a/src/client/HostLobbyModal.ts +++ b/src/client/HostLobbyModal.ts @@ -653,6 +653,7 @@ export class HostLobbyModal extends BaseModal { .gameMode=${this.gameMode} .clients=${this.clients} .lobbyCreatorClientID=${this.lobbyCreatorClientID} + .currentClientID=${this.lobbyCreatorClientID} .teamCount=${this.teamCount} .nationCount=${this.nationCount} .disableNations=${this.disableNations} diff --git a/src/client/JoinPrivateLobbyModal.ts b/src/client/JoinPrivateLobbyModal.ts index c04b8fa93..9287b1730 100644 --- a/src/client/JoinPrivateLobbyModal.ts +++ b/src/client/JoinPrivateLobbyModal.ts @@ -28,6 +28,7 @@ export class JoinPrivateLobbyModal extends BaseModal { @state() private gameConfig: GameConfig | null = null; @state() private lobbyCreatorClientID: string | null = null; @state() private currentLobbyId: string = ""; + @state() private currentClientID: string = ""; @state() private nationCount: number = 0; private playersInterval: NodeJS.Timeout | null = null; @@ -101,6 +102,7 @@ export class JoinPrivateLobbyModal extends BaseModal { .gameMode=${this.gameConfig?.gameMode ?? GameMode.FFA} .clients=${this.players} .lobbyCreatorClientID=${this.lobbyCreatorClientID} + .currentClientID=${this.currentClientID} .teamCount=${this.gameConfig?.playerTeams ?? 2} .nationCount=${this.nationCount} .disableNations=${this.gameConfig?.disableNations ?? false} @@ -290,6 +292,7 @@ export class JoinPrivateLobbyModal extends BaseModal { this.hasJoined = false; this.message = ""; this.currentLobbyId = ""; + this.currentClientID = ""; this.nationCount = 0; this.leaveLobbyOnClose = true; @@ -418,6 +421,7 @@ export class JoinPrivateLobbyModal extends BaseModal { this.showMessage(translateText("private_lobby.joined_waiting")); this.message = ""; this.hasJoined = true; + this.currentClientID = generateID(); // If the modal closes as part of joining the game, do not leave the lobby this.leaveLobbyOnClose = false; @@ -426,7 +430,7 @@ export class JoinPrivateLobbyModal extends BaseModal { new CustomEvent("join-lobby", { detail: { gameID: lobbyId, - clientID: generateID(), + clientID: this.currentClientID, } as JoinLobbyEvent, bubbles: true, composed: true, @@ -477,12 +481,13 @@ export class JoinPrivateLobbyModal extends BaseModal { return "version_mismatch"; } + this.currentClientID = generateID(); this.dispatchEvent( new CustomEvent("join-lobby", { detail: { gameID: lobbyId, gameRecord: parsed.data, - clientID: generateID(), + clientID: this.currentClientID, } as JoinLobbyEvent, bubbles: true, composed: true, diff --git a/src/client/components/LobbyPlayerView.ts b/src/client/components/LobbyPlayerView.ts index 4c72fc21d..cc43e6930 100644 --- a/src/client/components/LobbyPlayerView.ts +++ b/src/client/components/LobbyPlayerView.ts @@ -15,7 +15,9 @@ import { } from "../../core/game/Game"; import { getCompactMapNationCount } from "../../core/game/NationCreation"; import { assignTeamsLobbyPreview } from "../../core/game/TeamAssignment"; +import { UserSettings } from "../../core/game/UserSettings"; import { ClientInfo, TeamCountConfig } from "../../core/Schemas"; +import { createRandomName } from "../../core/Util"; import { translateText } from "../Utils"; export interface TeamPreviewData { @@ -30,6 +32,7 @@ export class LobbyTeamView extends LitElement { @state() private teamPreview: TeamPreviewData[] = []; @state() private teamMaxSize: number = 0; @property({ type: String }) lobbyCreatorClientID: string = ""; + @property({ type: String }) currentClientID: string = ""; @property({ attribute: "team-count" }) teamCount: TeamCountConfig = 2; @property({ type: Function }) onKickPlayer?: (clientID: string) => void; @property({ type: Number }) nationCount: number = 0; @@ -38,6 +41,7 @@ export class LobbyTeamView extends LitElement { private theme: PastelTheme = new PastelTheme(); @state() private showTeamColors: boolean = false; + private userSettings: UserSettings = new UserSettings(); willUpdate(changedProperties: Map) { // Recompute team preview when relevant properties change @@ -108,12 +112,14 @@ export class LobbyTeamView extends LitElement { ${repeat( this.clients, (c) => c.clientID ?? c.username, - (client) => - html`
{ + const displayName = this.displayUsername(client); + return html`
- ${client.username} -
`, + ${displayName} +
`; + }, )}
@@ -151,9 +157,10 @@ export class LobbyTeamView extends LitElement { return html`${repeat( this.clients, (c) => c.clientID ?? c.username, - (client) => - html` - ${client.username} + (client) => { + const displayName = this.displayUsername(client); + return html` + ${displayName} ${client.clientID === this.lobbyCreatorClientID ? html`(${translateText("host_modal.host_badge")}) this.onKickPlayer?.(client.clientID)} aria-label=${translateText("host_modal.remove_player", { - username: client.username, + username: displayName, })} > × ` : html``} - `, + `; + }, )} `; } @@ -207,11 +215,12 @@ export class LobbyTeamView extends LitElement { : repeat( preview.players, (p) => p.clientID ?? p.username, - (p) => - html`
{ + const displayName = this.displayUsername(p); + return html`
- ${p.username} + ${displayName} ${p.clientID === this.lobbyCreatorClientID ? html`(${translateText("host_modal.host_badge")}) × ` : html``} -
`, +
`; + }, )}
@@ -353,4 +363,18 @@ export class LobbyTeamView extends LitElement { } return getCompactMapNationCount(this.nationCount, this.isCompactMap); } + + private displayUsername(client: ClientInfo): string { + if (!this.userSettings.anonymousNames()) { + return client.username; + } + + if (this.currentClientID && client.clientID === this.currentClientID) { + return client.username; + } + + return ( + createRandomName(client.username, PlayerType.Human) ?? client.username + ); + } }