mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-27 22:44:33 +00:00
0402e609a4
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. <img width="1276" alt="スクリーンショット 2025-03-25 23 03 37" src="https://github.com/user-attachments/assets/3d396bb0-336f-41a0-8d56-ff5229fe05f8" /> <img width="1048" alt="スクリーンショット 2025-03-25 23 03 48" src="https://github.com/user-attachments/assets/a72711cf-9743-4879-8f2f-b8187b10a272" /> Use the upper left button (Ninja) to change settings. <img width="1173" alt="スクリーンショット 2025-03-25 23 04 03" src="https://github.com/user-attachments/assets/2d2fcbbd-7342-40b0-97c1-ecc184e5fbb6" /> Fixes #489 --------- Co-authored-by: evanpelle <evanpelle@gmail.com>
319 lines
9.9 KiB
TypeScript
319 lines
9.9 KiB
TypeScript
import page from "page";
|
|
import favicon from "../../resources/images/Favicon.svg";
|
|
import { consolex } from "../core/Consolex";
|
|
import { GameRecord, GameStartInfo } from "../core/Schemas";
|
|
import { getServerConfigFromClient } from "../core/configuration/ConfigLoader";
|
|
import { GameType } from "../core/game/Game";
|
|
import { UserSettings } from "../core/game/UserSettings";
|
|
import { joinLobby } from "./ClientGameRunner";
|
|
import "./DarkModeButton";
|
|
import { DarkModeButton } from "./DarkModeButton";
|
|
import "./FlagInput";
|
|
import { FlagInput } from "./FlagInput";
|
|
import { GameStartingModal } from "./GameStartingModal";
|
|
import "./GoogleAdElement";
|
|
import GoogleAdElement from "./GoogleAdElement";
|
|
import { HelpModal } from "./HelpModal";
|
|
import { HostLobbyModal as HostPrivateLobbyModal } from "./HostLobbyModal";
|
|
import { JoinPrivateLobbyModal } from "./JoinPrivateLobbyModal";
|
|
import "./LangSelector";
|
|
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";
|
|
import { UsernameInput } from "./UsernameInput";
|
|
import { generateCryptoRandomUUID } from "./Utils";
|
|
import "./components/baseComponents/Button";
|
|
import "./components/baseComponents/Modal";
|
|
import "./styles.css";
|
|
|
|
export interface JoinLobbyEvent {
|
|
clientID: string;
|
|
// Multiplayer games only have gameID, gameConfig is not known until game starts.
|
|
gameID: string;
|
|
// GameConfig only exists when playing a singleplayer game.
|
|
gameStartInfo?: GameStartInfo;
|
|
// GameRecord exists when replaying an archived game.
|
|
gameRecord?: GameRecord;
|
|
}
|
|
|
|
class Client {
|
|
private gameStop: () => void;
|
|
|
|
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;
|
|
private googleAds: NodeListOf<GoogleAdElement>;
|
|
private userSettings: UserSettings = new UserSettings();
|
|
|
|
constructor() {}
|
|
|
|
initialize(): void {
|
|
const langSelector = document.querySelector(
|
|
"lang-selector",
|
|
) as LangSelector;
|
|
const LanguageModal = document.querySelector(
|
|
"lang-selector",
|
|
) as LanguageModal;
|
|
if (!langSelector) {
|
|
consolex.warn("Lang selector element not found");
|
|
}
|
|
if (!LanguageModal) {
|
|
consolex.warn("Language modal element not found");
|
|
}
|
|
|
|
this.flagInput = document.querySelector("flag-input") as FlagInput;
|
|
if (!this.flagInput) {
|
|
consolex.warn("Flag input element not found");
|
|
}
|
|
|
|
this.darkModeButton = document.querySelector(
|
|
"dark-mode-button",
|
|
) as DarkModeButton;
|
|
if (!this.darkModeButton) {
|
|
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;
|
|
if (!this.usernameInput) {
|
|
consolex.warn("Username input element not found");
|
|
}
|
|
|
|
this.publicLobby = document.querySelector("public-lobby") as PublicLobby;
|
|
this.googleAds = document.querySelectorAll(
|
|
"google-ad",
|
|
) as NodeListOf<GoogleAdElement>;
|
|
|
|
window.addEventListener("beforeunload", () => {
|
|
consolex.log("Browser is closing");
|
|
if (this.gameStop != null) {
|
|
this.gameStop();
|
|
}
|
|
});
|
|
|
|
setFavicon();
|
|
document.addEventListener("join-lobby", this.handleJoinLobby.bind(this));
|
|
document.addEventListener("leave-lobby", this.handleLeaveLobby.bind(this));
|
|
|
|
const spModal = document.querySelector(
|
|
"single-player-modal",
|
|
) as SinglePlayerModal;
|
|
spModal instanceof SinglePlayerModal;
|
|
document.getElementById("single-player").addEventListener("click", () => {
|
|
if (this.usernameInput.isValid()) {
|
|
spModal.open();
|
|
}
|
|
});
|
|
|
|
const hlpModal = document.querySelector("help-modal") as HelpModal;
|
|
hlpModal instanceof HelpModal;
|
|
document.getElementById("help-button").addEventListener("click", () => {
|
|
hlpModal.open();
|
|
});
|
|
|
|
const settingsModal = document.querySelector(
|
|
"user-setting",
|
|
) as UserSettingModal;
|
|
settingsModal instanceof UserSettingModal;
|
|
document.getElementById("settings-button").addEventListener("click", () => {
|
|
settingsModal.open();
|
|
});
|
|
|
|
const hostModal = document.querySelector(
|
|
"host-lobby-modal",
|
|
) as HostPrivateLobbyModal;
|
|
hostModal instanceof HostPrivateLobbyModal;
|
|
document
|
|
.getElementById("host-lobby-button")
|
|
.addEventListener("click", () => {
|
|
if (this.usernameInput.isValid()) {
|
|
hostModal.open();
|
|
this.publicLobby.leaveLobby();
|
|
}
|
|
});
|
|
|
|
this.joinModal = document.querySelector(
|
|
"join-private-lobby-modal",
|
|
) as JoinPrivateLobbyModal;
|
|
this.joinModal instanceof JoinPrivateLobbyModal;
|
|
document
|
|
.getElementById("join-private-lobby-button")
|
|
.addEventListener("click", () => {
|
|
if (this.usernameInput.isValid()) {
|
|
this.joinModal.open();
|
|
}
|
|
});
|
|
|
|
if (this.userSettings.darkMode()) {
|
|
document.documentElement.classList.add("dark");
|
|
} else {
|
|
document.documentElement.classList.remove("dark");
|
|
}
|
|
page("/join/:lobbyId", (ctx) => {
|
|
if (ctx.init && sessionStorage.getItem("inLobby")) {
|
|
// On page reload, go back home
|
|
page.redirect("/");
|
|
return;
|
|
}
|
|
const lobbyId = ctx.params.lobbyId;
|
|
|
|
this.joinModal.open(lobbyId);
|
|
|
|
consolex.log(`joining lobby ${lobbyId}`);
|
|
});
|
|
|
|
page();
|
|
function updateSliderProgress(slider) {
|
|
const percent =
|
|
((slider.value - slider.min) / (slider.max - slider.min)) * 100;
|
|
slider.style.setProperty("--progress", `${percent}%`);
|
|
}
|
|
|
|
document
|
|
.querySelectorAll("#bots-count, #private-lobby-bots-count")
|
|
.forEach((slider) => {
|
|
updateSliderProgress(slider);
|
|
slider.addEventListener("input", () => updateSliderProgress(slider));
|
|
});
|
|
}
|
|
|
|
private async handleJoinLobby(event: CustomEvent) {
|
|
const lobby = event.detail as JoinLobbyEvent;
|
|
consolex.log(`joining lobby ${lobby.gameID}`);
|
|
if (this.gameStop != null) {
|
|
consolex.log("joining lobby, stopping existing game");
|
|
this.gameStop();
|
|
}
|
|
const config = await getServerConfigFromClient();
|
|
|
|
this.gameStop = joinLobby(
|
|
{
|
|
gameID: lobby.gameID,
|
|
serverConfig: config,
|
|
flag:
|
|
this.flagInput.getCurrentFlag() == "xx"
|
|
? ""
|
|
: this.flagInput.getCurrentFlag(),
|
|
playerName: this.usernameInput.getCurrentUsername(),
|
|
persistentID: getPersistentIDFromCookie(),
|
|
clientID: lobby.clientID,
|
|
gameStartInfo: lobby.gameStartInfo ?? lobby.gameRecord?.gameStartInfo,
|
|
gameRecord: lobby.gameRecord,
|
|
},
|
|
() => {
|
|
console.log("Closing modals");
|
|
document.getElementById("settings-button").classList.add("hidden");
|
|
[
|
|
"single-player-modal",
|
|
"host-lobby-modal",
|
|
"join-private-lobby-modal",
|
|
"game-starting-modal",
|
|
"top-bar",
|
|
"help-modal",
|
|
"user-setting",
|
|
].forEach((tag) => {
|
|
const modal = document.querySelector(tag) as HTMLElement & {
|
|
close?: () => void;
|
|
isModalOpen?: boolean;
|
|
};
|
|
if (modal?.close) {
|
|
modal.close();
|
|
} else if ("isModalOpen" in modal) {
|
|
modal.isModalOpen = false;
|
|
}
|
|
});
|
|
this.publicLobby.stop();
|
|
document.querySelectorAll(".ad").forEach((ad) => {
|
|
(ad as HTMLElement).style.display = "none";
|
|
});
|
|
|
|
// show when the game loads
|
|
const startingModal = document.querySelector(
|
|
"game-starting-modal",
|
|
) as GameStartingModal;
|
|
startingModal instanceof GameStartingModal;
|
|
startingModal.show();
|
|
},
|
|
() => {
|
|
this.joinModal.close();
|
|
this.publicLobby.stop();
|
|
document.querySelectorAll(".ad").forEach((ad) => {
|
|
(ad as HTMLElement).style.display = "none";
|
|
});
|
|
|
|
if (event.detail.gameConfig?.gameType != GameType.Singleplayer) {
|
|
window.history.pushState({}, "", `/join/${lobby.gameID}`);
|
|
sessionStorage.setItem("inLobby", "true");
|
|
}
|
|
},
|
|
);
|
|
}
|
|
|
|
private async handleLeaveLobby(/* event: CustomEvent */) {
|
|
if (this.gameStop == null) {
|
|
return;
|
|
}
|
|
consolex.log("leaving lobby, cancelling game");
|
|
this.gameStop();
|
|
this.gameStop = null;
|
|
this.publicLobby.leaveLobby();
|
|
}
|
|
}
|
|
|
|
// Initialize the client when the DOM is loaded
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
new Client().initialize();
|
|
});
|
|
|
|
function setFavicon(): void {
|
|
const link = document.createElement("link");
|
|
link.type = "image/x-icon";
|
|
link.rel = "shortcut icon";
|
|
link.href = favicon;
|
|
document.head.appendChild(link);
|
|
}
|
|
|
|
// WARNING: DO NOT EXPOSE THIS ID
|
|
export function getPersistentIDFromCookie(): string {
|
|
const COOKIE_NAME = "player_persistent_id";
|
|
|
|
// Try to get existing cookie
|
|
const cookies = document.cookie.split(";");
|
|
for (const cookie of cookies) {
|
|
const [cookieName, cookieValue] = cookie.split("=").map((c) => c.trim());
|
|
if (cookieName === COOKIE_NAME) {
|
|
return cookieValue;
|
|
}
|
|
}
|
|
|
|
// If no cookie exists, create new ID and set cookie
|
|
const newID = generateCryptoRandomUUID();
|
|
document.cookie = [
|
|
`${COOKIE_NAME}=${newID}`,
|
|
`max-age=${5 * 365 * 24 * 60 * 60}`, // 5 years
|
|
"path=/",
|
|
"SameSite=Strict",
|
|
"Secure",
|
|
].join(";");
|
|
|
|
return newID;
|
|
}
|