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