add support for custom colors (#2103)

## Description:

Added a colors tab in territory patterns modal so players can select
their color.

Refactored the PrivilegeChecker, removed custom flag checks since we no
longer support custom flags.

<img width="479" height="345" alt="Screenshot 2025-09-27 at 5 01 17 PM"
src="https://github.com/user-attachments/assets/ad96da65-f0eb-4731-a861-e6e5fcb4566a"
/>
## 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:

evan
This commit is contained in:
evanpelle
2025-10-09 20:47:20 -07:00
committed by GitHub
parent 2521466191
commit 584fa9fb5d
15 changed files with 220 additions and 336 deletions
+8 -82
View File
@@ -13,9 +13,6 @@ import {
ClientMessageSchema,
ID,
PartialGameRecordSchema,
PlayerCosmeticRefs,
PlayerCosmetics,
PlayerPattern,
ServerErrorMessage,
} from "../core/Schemas";
import { replacer } from "../core/Util";
@@ -26,7 +23,6 @@ import { GameManager } from "./GameManager";
import { getUserMe, verifyClientToken } from "./jwt";
import { logger } from "./Logger";
import { assertNever } from "../core/Util";
import { PrivilegeRefresher } from "./PrivilegeRefresher";
import { initWorkerMetrics } from "./WorkerMetrics";
@@ -366,15 +362,15 @@ export async function startWorker() {
}
}
const { perm, cosmetics, error } = checkCosmetics(
clientMsg.cosmetics,
flares ?? [],
);
if (perm === "forbidden") {
log.warn(`Forbidden: ${error}`, {
const cosmeticResult = privilegeRefresher
.get()
.isAllowed(flares ?? [], clientMsg.cosmetics ?? {});
if (cosmeticResult.type === "forbidden") {
log.warn(`Forbidden: ${cosmeticResult.reason}`, {
clientID: clientMsg.clientID,
});
ws.close(1002, error);
ws.close(1002, cosmeticResult.reason);
return;
}
@@ -388,7 +384,7 @@ export async function startWorker() {
ip,
clientMsg.username,
ws,
cosmetics,
cosmeticResult.cosmetics,
);
const wasFound = gm.addClient(
@@ -424,76 +420,6 @@ export async function startWorker() {
});
});
function checkCosmetics(
cosmetics: PlayerCosmeticRefs | undefined,
flares: readonly string[],
): {
perm: "forbidden" | "allowed";
cosmetics?: PlayerCosmetics | undefined;
error?: string;
} {
if (cosmetics === undefined) {
return {
perm: "allowed",
cosmetics: undefined,
};
}
// Check if the flag is allowed
if (cosmetics.flag !== undefined) {
if (cosmetics.flag.startsWith("!")) {
const allowed = privilegeRefresher
.get()
.isCustomFlagAllowed(cosmetics.flag, flares);
if (allowed !== true) {
log.warn(`Custom flag ${allowed}: ${cosmetics.flag}`);
return {
perm: "forbidden",
error: `Custom flag ${allowed}`,
};
}
}
}
let pattern: PlayerPattern | undefined;
// Check if the pattern is allowed
if (cosmetics.patternName !== undefined) {
const result = privilegeRefresher
.get()
.isPatternAllowed(
flares,
cosmetics.patternName,
cosmetics.patternColorPaletteName ?? null,
);
switch (result.type) {
case "allowed":
pattern = result.pattern;
break;
case "unknown":
log.warn(`Pattern ${cosmetics.patternName} unknown`);
return {
perm: "forbidden",
error: "Could not look up pattern, backend may be offline",
};
case "forbidden":
log.warn(`Pattern ${cosmetics.patternName}: ${result.reason}`);
return {
perm: "forbidden",
error: `Pattern ${cosmetics.patternName}: ${result.reason}`,
};
default:
assertNever(result);
}
}
return {
perm: "allowed",
cosmetics: {
flag: cosmetics.flag,
pattern: pattern,
},
};
}
// The load balancer will handle routing to this server based on path
const PORT = config.workerPortByIndex(workerId);
server.listen(PORT, () => {