diff --git a/src/client/Main.ts b/src/client/Main.ts
index a8f9a82ee..2eb479cb5 100644
--- a/src/client/Main.ts
+++ b/src/client/Main.ts
@@ -206,6 +206,7 @@ class Client {
// TODO: Update the page for logged in user
loginDiscordButton.translationKey = "main.logged_in";
const { user, player } = userMeResponse;
+ TerritoryModal.onUserMe(userMeResponse);
});
}
diff --git a/src/client/TerritoryPatternsModal.ts b/src/client/TerritoryPatternsModal.ts
index 61d99e698..b4335ed73 100644
--- a/src/client/TerritoryPatternsModal.ts
+++ b/src/client/TerritoryPatternsModal.ts
@@ -1,6 +1,7 @@
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 "./components/Difficulties";
import "./components/Maps";
import {
@@ -29,6 +30,8 @@ export class territoryPatternsModal extends LitElement {
@state() private hoveredPattern: string | null = null;
@state() private hoverPosition = { x: 0, y: 0 };
+ @state() private roles: string[] = [];
+
private resizeObserver: ResizeObserver;
constructor() {
@@ -49,10 +52,6 @@ export class territoryPatternsModal extends LitElement {
containers.forEach((container) => this.resizeObserver.observe(container));
this.updatePreview();
});
-
- this.setLockedPatterns(["evan"], {
- evan: "This pattern is locked because it is restricted.",
- });
}
disconnectedCallback() {
@@ -60,16 +59,73 @@ export class territoryPatternsModal extends LitElement {
this.resizeObserver.disconnect();
}
+ onUserMe(userMeResponse: UserMeResponse) {
+ const { user, player } = userMeResponse;
+ if (player) {
+ const { publicId, roles } = player;
+ if (roles) {
+ this.roles = roles;
+ }
+ }
+ this.requestUpdate();
+ }
+
+ private checkPatternPermission(roles: string[]) {
+ if (
+ roles.includes("1286745100411473930") || // creator
+ roles.includes("1286738076386856991") || // admin
+ roles.includes("1338654590043820148") // mod
+ ) {
+ return;
+ }
+
+ this.setLockedPatterns(
+ ["evan", "openfront"],
+ "This pattern is available only to moderators and above.",
+ );
+
+ if (
+ roles.includes("1359441841371480176") || // money haters
+ roles.includes("1330243292306341969") // early access supporter
+ ) {
+ return;
+ }
+
+ const restrictedForDonorsOnly = [
+ "diagonal",
+ "cross",
+ "mini_cross",
+ "horizontal_stripes",
+ "sparse_dots",
+ "diagonal_stripe",
+ "mountain_ridge",
+ "scattered_dots",
+ "circuit_board",
+ "vertical_bars",
+ ".w.",
+ ];
+
+ this.setLockedPatterns(
+ restrictedForDonorsOnly,
+ "This pattern is available only to donors (money haters or early access supporters).",
+ );
+
+ // Future permission logic here
+ return;
+ }
+
createRenderRoot() {
return this;
}
render() {
+ this.resetLockedPatterns();
+ this.checkPatternPermission(this.roles);
return html`
${this.hoveredPattern && this.lockedReasons[this.hoveredPattern]
? html`
@@ -258,12 +314,16 @@ export class territoryPatternsModal extends LitElement {
render(previewHTML, this.previewButton);
}
- private setLockedPatterns(
- lockedPatterns: string[],
- reasons: Record,
- ) {
- this.lockedPatterns = lockedPatterns;
- this.lockedReasons = reasons;
+ private setLockedPatterns(lockedPatterns: string[], reason: string) {
+ this.lockedPatterns.push(...lockedPatterns);
+ for (const key of lockedPatterns) {
+ this.lockedReasons[key] = reason;
+ }
+ }
+
+ private resetLockedPatterns() {
+ this.lockedPatterns = [];
+ this.lockedReasons = {};
}
private isPatternLocked(patternKey: string): boolean {
diff --git a/src/server/Worker.ts b/src/server/Worker.ts
index 20edbc04f..6a2afadbd 100644
--- a/src/server/Worker.ts
+++ b/src/server/Worker.ts
@@ -2,6 +2,7 @@ import express, { NextFunction, Request, Response } from "express";
import rateLimit from "express-rate-limit";
import http from "http";
import ipAnonymize from "ip-anonymize";
+import { createRequire } from "module";
import path from "path";
import { fileURLToPath } from "url";
import { WebSocket, WebSocketServer } from "ws";
@@ -22,6 +23,8 @@ import { gatekeeper, LimiterType } from "./Gatekeeper";
import { getUserMe, verifyClientToken } from "./jwt";
import { logger } from "./Logger";
import { initWorkerMetrics } from "./WorkerMetrics";
+const require = createRequire(import.meta.url);
+const territory_patterns = require("../../resources/territory_patterns.json");
const config = getServerConfigFromServer();
@@ -329,8 +332,59 @@ export function startWorker() {
}
}
- if (roles === null || !roles.includes("1338654590043820148")) {
- clientMsg.pattern = undefined; // test
+ if (clientMsg.pattern !== undefined) {
+ const isCreator = roles?.includes("1286745100411473930");
+ const isAdmin = roles?.includes("1286738076386856991");
+ const isMod = roles?.includes("1338654590043820148");
+ const isMoneyHater = roles?.includes("1359441841371480176");
+ const isEarlyAccess = roles?.includes("1330243292306341969");
+
+ const isAllowedBase64 = Object.values(
+ territory_patterns,
+ ).includes(clientMsg.pattern);
+
+ const evanBlockedPatterns = [
+ territory_patterns["openfront"],
+ territory_patterns["evan"],
+ ];
+ const isEvanPattern = evanBlockedPatterns.includes(
+ clientMsg.pattern,
+ );
+
+ const restrictedBase64Patterns = [
+ territory_patterns["diagonal"],
+ territory_patterns["cross"],
+ territory_patterns["mini_cross"],
+ territory_patterns["horizontal_stripes"],
+ territory_patterns["sparse_dots"],
+ territory_patterns["diagonal_stripe"],
+ territory_patterns["mountain_ridge"],
+ territory_patterns["scattered_dots"],
+ territory_patterns["circuit_board"],
+ territory_patterns["vertical_bars"],
+ territory_patterns[".w."],
+ ];
+ const isRestrictedPattern = restrictedBase64Patterns.includes(
+ clientMsg.pattern,
+ );
+
+ if (!(isCreator || isAdmin || isMod)) {
+ if (isMoneyHater || isEarlyAccess) {
+ if (!isAllowedBase64 || isEvanPattern) {
+ log.warn(`pattern blocked (evan/openfront or unlisted)`);
+ return;
+ }
+ } else {
+ if (
+ !isAllowedBase64 ||
+ isRestrictedPattern ||
+ isEvanPattern
+ ) {
+ log.warn(`pattern blocked (restricted or unlisted)`);
+ return;
+ }
+ }
+ }
}
// TODO: Validate client settings based on roles