From 0402e609a4b79faa1b5814304e846f54833d5d3e Mon Sep 17 00:00:00 2001 From: Aotumuri Date: Wed, 23 Apr 2025 12:26:45 +0900 Subject: [PATCH] replaces player names with randomized name (#340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR replaces player names with randomized name The goal is to reduce exposure to inappropriate or offensive names. Additionally, content creators no longer need to worry about displaying other players’ usernames. スクリーンショット 2025-03-25 23 03 37 スクリーンショット 2025-03-25 23 03 48 Use the upper left button (Ninja) to change settings. スクリーンショット 2025-03-25 23 04 03 Fixes #489 --------- Co-authored-by: evanpelle --- src/client/Main.ts | 10 ++++++++ src/client/RandomNameButton.ts | 30 +++++++++++++++++++++++ src/client/graphics/layers/NameLayer.ts | 5 ++++ src/client/graphics/layers/OptionsMenu.ts | 10 ++++++++ src/client/index.html | 3 +++ src/core/Util.ts | 21 ++++++++++++++++ src/core/game/GameView.ts | 23 ++++++++++++++--- src/core/game/UserSettings.ts | 7 ++++++ 8 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 src/client/RandomNameButton.ts diff --git a/src/client/Main.ts b/src/client/Main.ts index 567a6673b..0d00af662 100644 --- a/src/client/Main.ts +++ b/src/client/Main.ts @@ -21,6 +21,8 @@ import { LangSelector } from "./LangSelector"; import { LanguageModal } from "./LanguageModal"; import "./PublicLobby"; import { PublicLobby } from "./PublicLobby"; +import "./RandomNameButton"; +import { RandomNameButton } from "./RandomNameButton"; import { SinglePlayerModal } from "./SinglePlayerModal"; import { UserSettingModal } from "./UserSettingModal"; import "./UsernameInput"; @@ -46,6 +48,7 @@ class Client { private usernameInput: UsernameInput | null = null; private flagInput: FlagInput | null = null; private darkModeButton: DarkModeButton | null = null; + private randomNameButton: RandomNameButton | null = null; private joinModal: JoinPrivateLobbyModal; private publicLobby: PublicLobby; @@ -80,6 +83,13 @@ class Client { consolex.warn("Dark mode button element not found"); } + this.randomNameButton = document.querySelector( + "random-name-button", + ) as RandomNameButton; + if (!this.randomNameButton) { + consolex.warn("Random name button element not found"); + } + this.usernameInput = document.querySelector( "username-input", ) as UsernameInput; diff --git a/src/client/RandomNameButton.ts b/src/client/RandomNameButton.ts new file mode 100644 index 000000000..4617c784f --- /dev/null +++ b/src/client/RandomNameButton.ts @@ -0,0 +1,30 @@ +import { LitElement, html } from "lit"; +import { customElement, state } from "lit/decorators.js"; +import { UserSettings } from "../core/game/UserSettings"; + +@customElement("random-name-button") +export class RandomNameButton extends LitElement { + private userSettings: UserSettings = new UserSettings(); + @state() private randomName: boolean = this.userSettings.anonymousNames(); + + createRenderRoot() { + return this; + } + + toggleRandomName() { + this.userSettings.toggleRandomName(); + this.randomName = this.userSettings.anonymousNames(); + } + + render() { + return html` + + `; + } +} diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 6166c1d76..1ecdfba02 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -195,6 +195,7 @@ export class NameLayer implements Layer { nameDiv.style.alignItems = "center"; const nameSpan = document.createElement("span"); + nameSpan.className = "player-name-span"; nameSpan.innerHTML = player.name(); nameDiv.appendChild(nameSpan); element.appendChild(nameDiv); @@ -262,6 +263,10 @@ export class NameLayer implements Layer { nameDiv.style.fontSize = `${render.fontSize}px`; nameDiv.style.lineHeight = `${render.fontSize}px`; nameDiv.style.color = render.fontColor; + const span = nameDiv.querySelector(".player-name-span"); + if (span) { + span.innerHTML = render.player.name(); + } if (flagDiv) { flagDiv.style.height = `${render.fontSize}px`; } diff --git a/src/client/graphics/layers/OptionsMenu.ts b/src/client/graphics/layers/OptionsMenu.ts index 165c99afd..cad75e929 100644 --- a/src/client/graphics/layers/OptionsMenu.ts +++ b/src/client/graphics/layers/OptionsMenu.ts @@ -106,6 +106,10 @@ export class OptionsMenu extends LitElement implements Layer { this.eventBus.emit(new RefreshGraphicsEvent()); } + private onToggleRandomNameModeButtonClick() { + this.userSettings.toggleRandomName(); + } + private onToggleFocusLockedButtonClick() { this.userSettings.toggleFocusLocked(); this.requestUpdate(); @@ -196,6 +200,12 @@ export class OptionsMenu extends LitElement implements Layer { title: "Dark Mode", children: "🌙: " + (this.userSettings.darkMode() ? "On" : "Off"), })} + ${button({ + onClick: this.onToggleRandomNameModeButtonClick, + title: "Random name mode", + children: + "🥷: " + (this.userSettings.anonymousNames() ? "On" : "Off"), + })} ${button({ onClick: this.onToggleLeftClickOpensMenu, title: "Left click", diff --git a/src/client/index.html b/src/client/index.html index f6669b532..ba6a0f4a4 100644 --- a/src/client/index.html +++ b/src/client/index.html @@ -208,6 +208,9 @@
+ + +
diff --git a/src/core/Util.ts b/src/core/Util.ts index 62304353c..489d53bb1 100644 --- a/src/core/Util.ts +++ b/src/core/Util.ts @@ -13,6 +13,11 @@ import { Turn, } from "./Schemas"; +import { + BOT_NAME_PREFIXES, + BOT_NAME_SUFFIXES, +} from "./execution/utils/BotNames"; + export function manhattanDistWrapped( c1: Cell, c2: Cell, @@ -286,3 +291,19 @@ export function withinInt(num: bigint, min: bigint, max: bigint): bigint { const atLeastMin = maxInt(num, min); return minInt(atLeastMin, max); } + +export function createRandomName( + name: string, + playerType: string, +): string | null { + let randomName = null; + if (playerType === "HUMAN") { + const hash = simpleHash(name); + const prefixIndex = hash % BOT_NAME_PREFIXES.length; + const suffixIndex = + Math.floor(hash / BOT_NAME_PREFIXES.length) % BOT_NAME_SUFFIXES.length; + + randomName = `👤 ${BOT_NAME_PREFIXES[prefixIndex]} ${BOT_NAME_SUFFIXES[suffixIndex]}`; + } + return randomName; +} diff --git a/src/core/game/GameView.ts b/src/core/game/GameView.ts index 3f7d9bf5e..877ec19cf 100644 --- a/src/core/game/GameView.ts +++ b/src/core/game/GameView.ts @@ -1,5 +1,6 @@ import { Config } from "../configuration/Config"; import { ClientID, GameID, PlayerStats } from "../Schemas"; +import { createRandomName } from "../Util"; import { WorkerClient } from "../worker/WorkerClient"; import { Cell, @@ -123,11 +124,22 @@ export class UnitView { } export class PlayerView { + public anonymousName: string; + constructor( private game: GameView, public data: PlayerUpdate, public nameData: NameViewData, - ) {} + ) { + if (data.clientID == game.myClientID()) { + this.anonymousName = this.data.name; + } else { + this.anonymousName = createRandomName( + this.data.name, + this.data.playerType, + ); + } + } async actions(tile: TileRef): Promise { return this.game.worker.playerInteraction( @@ -166,11 +178,16 @@ export class PlayerView { return this.data.flag; } name(): string { - return this.data.name; + return userSettings.anonymousNames() && this.anonymousName !== null + ? this.anonymousName + : this.data.name; } displayName(): string { - return this.data.displayName; + return userSettings.anonymousNames() && this.anonymousName !== null + ? this.anonymousName + : this.data.name; } + clientID(): ClientID { return this.data.clientID; } diff --git a/src/core/game/UserSettings.ts b/src/core/game/UserSettings.ts index 3c22c8ac5..f6049ce7a 100644 --- a/src/core/game/UserSettings.ts +++ b/src/core/game/UserSettings.ts @@ -15,6 +15,9 @@ export class UserSettings { emojis() { return this.get("settings.emojis", true); } + anonymousNames() { + return this.get("settings.anonymousNames", false); + } darkMode() { return this.get("settings.darkMode", false); @@ -42,6 +45,10 @@ export class UserSettings { this.set("settings.emojis", !this.emojis()); } + toggleRandomName() { + this.set("settings.anonymousNames", !this.anonymousNames()); + } + toggleDarkMode() { this.set("settings.darkMode", !this.darkMode()); if (this.darkMode()) {