From 4680b04656821504703e90949ec40b7713b0abfe Mon Sep 17 00:00:00 2001 From: Scott Anderson <662325+scottanderson@users.noreply.github.com> Date: Mon, 23 Jun 2025 23:14:36 -0400 Subject: [PATCH] Refactor cosmetics.json (#1263) ## Description: - Refactor cosmetics.json to use base64 as the lookup key, to reduce the complexity of lookup. - Add refine() logic to PatternSchema to check if the pattern is valid. - Split PatternDecoder class out of Cosmetic.ts to resolve temporal deadzone caused by static parsing of cosmetics.json with the new refine(). ## 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 - [x] I understand that submitting code with bugs that could have been caught through manual testing blocks releases and new features for all contributors --- resources/cosmetics/cosmetics.json | 96 ++++++++--------- resources/lang/en.json | 1 + src/client/TerritoryPatternsModal.ts | 103 +++++-------------- src/core/CosmeticSchemas.ts | 16 +-- src/core/{Cosmetics.ts => PatternDecoder.ts} | 4 - src/core/Schemas.ts | 21 +++- src/core/game/GameView.ts | 2 +- src/core/game/UserSettings.ts | 14 ++- src/server/Privilege.ts | 41 +++----- src/server/Worker.ts | 6 +- 10 files changed, 135 insertions(+), 169 deletions(-) rename src/core/{Cosmetics.ts => PatternDecoder.ts} (87%) diff --git a/resources/cosmetics/cosmetics.json b/resources/cosmetics/cosmetics.json index b0cfa94b7..bbc02dbbe 100644 --- a/resources/cosmetics/cosmetics.json +++ b/resources/cosmetics/cosmetics.json @@ -3,70 +3,70 @@ "donor": ["1359441841371480176", "1330243292306341969"], "creator": ["1286745100411473930"] }, - "pattern": { - "stripes_v": { - "pattern": "ABMIVVU=" + "patterns": { + "ABMIVVU=": { + "name": "stripes_v" }, - "stripes_h": { - "pattern": "ABMIDw8=" + "ABMIDw8=": { + "name": "stripes_h" }, - "checkerboard": { - "pattern": "ABMIpaU=" + "ABMIpaU=": { + "name": "checkerboard" }, - "choco": { - "pattern": "AFIoAAABOEAHgkAc+AN/4AMcgAAA" + "AFIoAAABOEAHgkAc+AN/4AMcgAAA": { + "name": "choco" }, - "diagonal": { - "pattern": "AHE4AQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIA=", - "role_group": ["donor"] + "AHE4AQACAAQACAAQACAAQACAAAABAAIABAAIABAAIABAAIA=": { + "name": "diagonal", + "role_group": "donor" }, - "cross": { - "pattern": "AHE4AYACQAQgCBAQCCAEQAKAAYABQAIgBBAICBAEIAJAAYA=", - "role_group": ["donor"] + "AHE4AYACQAQgCBAQCCAEQAKAAYABQAIgBBAICBAEIAJAAYA=": { + "name": "cross", + "role_group": "donor" }, - "mini_cross": { - "pattern": "AHEYA8AMMDAMwAPAAzAMDDADwA==", - "role_group": ["donor"] + "AHEYA8AMMDAMwAPAAzAMDDADwA==": { + "name": "mini_cross", + "role_group": "donor" }, - "horizontal_stripes": { - "pattern": "AHE4//8AAAAAAAAAAAAAAAAAAP//AAAAAAAAAAAAAAAAAAA=", - "role_group": ["donor"] + "AHE4//8AAAAAAAAAAAAAAAAAAP//AAAAAAAAAAAAAAAAAAA=": { + "name": "horizontal_stripes", + "role_group": "donor" }, - "sparse_dots": { - "pattern": "AHE4AQEAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAA=", - "role_group": ["donor"] + "AHE4AQEAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAAAAAAAAAA=": { + "name": "sparse_dots", + "role_group": "donor" }, - "evan": { - "pattern": "ALIUAAAAnsRIgiRZjuRpAiNJHiNJAAAA", - "role_group": ["creator"] + "ALIUAAAAnsRIgiRZjuRpAiNJHiNJAAAA": { + "name": "evan", + "role_group": "creator" }, - "diagonal_stripe": { - "pattern": "AHEYAYACQAQgCBAQCCAEQAKAAQ==", - "role_group": ["donor"] + "AHEYAYACQAQgCBAQCCAEQAKAAQ==": { + "name": "diagonal_stripe", + "role_group": "donor" }, - "mountain_ridge": { - "pattern": "AHEYAAAYGDw8fn7//35+PDwYGA==", - "role_group": ["donor"] + "AHEYAAAYGDw8fn7//35+PDwYGA==": { + "name": "mountain_ridge", + "role_group": "donor" }, - "scattered_dots": { - "pattern": "AHEYAAACIAAAAAAAAAAACBAAAA==", - "role_group": ["donor"] + "AHEYAAACIAAAAAAAAAAACBAAAA==": { + "name": "scattered_dots", + "role_group": "donor" }, - "circuit_board": { - "pattern": "AHEYw8PDwwwMDAwwDDAMw8PDww==", - "role_group": ["donor"] + "AHEYw8PDwwwMDAwwDDAMw8PDww==": { + "name": "circuit_board", + "role_group": "donor" }, - "vertical_bars": { - "pattern": "AHEYSZJJkkmSSZJJkkmSSZJJkg==", - "role_group": ["donor"] + "AHEYSZJJkkmSSZJJkkmSSZJJkg==": { + "name": "vertical_bars", + "role_group": "donor" }, - "-w-": { - "pattern": "AHEYAAAAAAAAAkCCQUQiLnQWaA==", - "role_group": ["donor"] + "AHEYAAAAAAAAAkCCQUQiLnQWaA==": { + "name": "-w-", + "role_group": "donor" }, - "openfront": { - "pattern": "AAIiAAAAAAAAAAAAAAAAAAAAAIDD8YnweTiiD5FIYEIgEpkIRCKBCoFIpCIQeTwyPB6RjEAkEIgQKEQiApFAIEIgEYkIOAKfCIGIIyIAAAAAAAAAAAA=", - "role_group": ["creator"] + "AAIiAAAAAAAAAAAAAAAAAAAAAIDD8YnweTiiD5FIYEIgEpkIRCKBCoFIpCIQeTwyPB6RjEAkEIgQKEQiApFAIEIgEYkIOAKfCIGIIyIAAAAAAAAAAAA=": { + "name": "openfront", + "role_group": "creator" } } } diff --git a/resources/lang/en.json b/resources/lang/en.json index 7da48dfcb..7b8f42502 100644 --- a/resources/lang/en.json +++ b/resources/lang/en.json @@ -479,6 +479,7 @@ }, "pattern": { "default": "Default", + "custom": "Custom", "stripes_v": "Vertical Stripes", "stripes_h": "Horizontal Stripes", "checkerboard": "Checkerboard", diff --git a/src/client/TerritoryPatternsModal.ts b/src/client/TerritoryPatternsModal.ts index 143ca9352..a37152895 100644 --- a/src/client/TerritoryPatternsModal.ts +++ b/src/client/TerritoryPatternsModal.ts @@ -2,8 +2,9 @@ import type { TemplateResult } from "lit"; import { html, LitElement, render } from "lit"; import { customElement, query, state } from "lit/decorators.js"; import { UserMeResponse } from "../core/ApiSchemas"; -import { PatternDecoder, territoryPatterns } from "../core/Cosmetics"; +import { COSMETICS } from "../core/CosmeticSchemas"; import { UserSettings } from "../core/game/UserSettings"; +import { PatternDecoder } from "../core/PatternDecoder"; import "./components/Difficulties"; import "./components/Maps"; import { translateText } from "./Utils"; @@ -18,7 +19,7 @@ export class TerritoryPatternsModal extends LitElement { public previewButton: HTMLElement | null = null; public buttonWidth: number = 100; - @state() private selectedPattern: string | undefined = undefined; + @state() private selectedPattern: string | undefined; @state() private lockedPatterns: string[] = []; @state() private lockedReasons: Record = {}; @@ -37,15 +38,7 @@ export class TerritoryPatternsModal extends LitElement { connectedCallback() { super.connectedCallback(); - const b64 = this.userSettings.getSelectedPattern(); - if (b64) { - const found = Object.entries(territoryPatterns.pattern).find( - ([key, pattern]) => pattern.pattern === b64, - ); - this.selectedPattern = found ? found[0] : "custom"; - } else { - this.selectedPattern = undefined; - } + this.selectedPattern = this.userSettings.getSelectedPattern(); window.addEventListener("keydown", this.handleKeyDown); this.updateComplete.then(() => { const containers = this.renderRoot.querySelectorAll(".preview-container"); @@ -79,8 +72,7 @@ export class TerritoryPatternsModal extends LitElement { } private checkPatternPermission(roles: string[]) { - const patterns = territoryPatterns.pattern ?? {}; - + const patterns = COSMETICS.patterns; for (const key in patterns) { const patternData = patterns[key]; const roleGroup: string[] | string | undefined = patternData.role_group; @@ -158,21 +150,10 @@ export class TerritoryPatternsModal extends LitElement { return null; } - private renderPatternButton( - key: string, - pattern: (typeof territoryPatterns.pattern)[string], - ): TemplateResult { + private renderPatternButton(key: string): TemplateResult { const isLocked = this.isPatternLocked(key); - const isSelected = - this.selectedPattern === key || - (key === "custom" && this.selectedPattern === "custom"); - let previewPattern = pattern; - if (key === "custom") { - const b64 = this.userSettings.getSelectedPattern(); - if (b64) { - previewPattern = { pattern: b64 } as any; - } - } + const isSelected = this.selectedPattern === key; + const name = COSMETICS.patterns[key]?.name ?? "custom"; return html` `; } private renderPatternGrid(): TemplateResult { - const patterns = territoryPatterns.pattern ?? {}; - const buttons: TemplateResult[] = []; - for (const key in patterns) { - if (!this.showChocoPattern && key === "choco") continue; - const result = this.renderPatternButton(key, patterns[key]); + for (const key in COSMETICS.patterns) { + const value = COSMETICS.patterns[key]; + if (!this.showChocoPattern && value.name === "choco") continue; + const result = this.renderPatternButton(key); buttons.push(result); } @@ -231,11 +205,11 @@ export class TerritoryPatternsModal extends LitElement { >