diff --git a/src/client/JoinLobbyModal.ts b/src/client/JoinLobbyModal.ts index 3d6ff86c9..38b41ec69 100644 --- a/src/client/JoinLobbyModal.ts +++ b/src/client/JoinLobbyModal.ts @@ -8,6 +8,7 @@ import { renderDuration, translateText, } from "../client/Utils"; +import { assetUrl } from "../core/AssetUrls"; import { EventBus } from "../core/EventBus"; import { ClientInfo, @@ -431,7 +432,9 @@ export class JoinLobbyModal extends BaseModal { const c = this.gameConfig; const mapName = getMapName(c.gameMap); const normalizedMap = normaliseMapKey(c.gameMap); - const thumbnailUrl = `/maps/${encodeURIComponent(normalizedMap)}/thumbnail.webp`; + const thumbnailUrl = assetUrl( + `maps/${encodeURIComponent(normalizedMap)}/thumbnail.webp`, + ); const isTeam = c.gameMode === GameMode.Team; let modeSubtitle: string; diff --git a/src/client/TerrainMapFileLoader.ts b/src/client/TerrainMapFileLoader.ts index c3b185ac9..90cbd5763 100644 --- a/src/client/TerrainMapFileLoader.ts +++ b/src/client/TerrainMapFileLoader.ts @@ -1,6 +1,6 @@ +import { assetUrl } from "../core/AssetUrls"; import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader"; -export const terrainMapFileLoader = new FetchGameMapLoader( - `/maps`, - window.GIT_COMMIT, +export const terrainMapFileLoader = new FetchGameMapLoader(() => + assetUrl("maps"), ); diff --git a/src/core/game/BinaryLoaderGameMapLoader.ts b/src/core/game/BinaryLoaderGameMapLoader.ts index ff1c5207d..892e421b2 100644 --- a/src/core/game/BinaryLoaderGameMapLoader.ts +++ b/src/core/game/BinaryLoaderGameMapLoader.ts @@ -1,3 +1,4 @@ +import { assetUrl } from "../AssetUrls"; import { GameMapType } from "./Game"; import { GameMapLoader, MapData } from "./GameMapLoader"; import { MapManifest } from "./TerrainMapLoader"; @@ -36,7 +37,7 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader { }) .then((buf) => new Uint8Array(buf)); - const mapBasePath = `/maps/${fileName}`; + const mapBasePath = assetUrl(`maps/${fileName}`); const mapData = { mapBin: this.createLazyLoader(() => loadBinary(`${mapBasePath}/map.bin`)), diff --git a/src/core/game/FetchGameMapLoader.ts b/src/core/game/FetchGameMapLoader.ts index e517172d5..2c6f954e2 100644 --- a/src/core/game/FetchGameMapLoader.ts +++ b/src/core/game/FetchGameMapLoader.ts @@ -4,10 +4,7 @@ import { GameMapLoader, MapData } from "./GameMapLoader"; export class FetchGameMapLoader implements GameMapLoader { private maps: Map; - public constructor( - private readonly prefix: string, - private readonly cacheBuster?: string, - ) { + public constructor(private readonly prefix: string | (() => string)) { this.maps = new Map(); } @@ -38,16 +35,12 @@ export class FetchGameMapLoader implements GameMapLoader { return mapData; } + private getPrefix(): string { + return typeof this.prefix === "function" ? this.prefix() : this.prefix; + } + private url(map: string, path: string) { - let url = `${this.prefix}/${map}/${path}`; - - if (this.cacheBuster) { - url += `${url.includes("?") ? "&" : "?"}v=${encodeURIComponent( - this.cacheBuster.trim(), - )}`; - } - - return url; + return `${this.getPrefix()}/${map}/${path}`; } private async loadBinaryFromUrl(url: string) { diff --git a/src/core/worker/Worker.worker.ts b/src/core/worker/Worker.worker.ts index 9bddea301..445ed9a25 100644 --- a/src/core/worker/Worker.worker.ts +++ b/src/core/worker/Worker.worker.ts @@ -1,4 +1,4 @@ -import version from "resources/version.txt?raw"; +import { assetUrl } from "../AssetUrls"; import { createGameRunner, GameRunner } from "../GameRunner"; import { FetchGameMapLoader } from "../game/FetchGameMapLoader"; import { ErrorUpdate, GameUpdateViewData } from "../game/GameUpdates"; @@ -16,7 +16,7 @@ import { const ctx: Worker = self as any; let gameRunner: Promise | null = null; -const mapLoader = new FetchGameMapLoader(`/maps`, version); +const mapLoader = new FetchGameMapLoader(() => assetUrl("maps")); // Yield threshold; not a backlog cap. Used to avoid monopolizing the worker task // and flooding the main thread with messages during catch-up. const MAX_TICKS_BEFORE_YIELD = 4; diff --git a/src/server/GamePreviewBuilder.ts b/src/server/GamePreviewBuilder.ts index 217edefd1..2c4dfa131 100644 --- a/src/server/GamePreviewBuilder.ts +++ b/src/server/GamePreviewBuilder.ts @@ -1,4 +1,5 @@ import { z } from "zod"; +import { buildAssetUrl, buildVersionedAssetBasePath } from "../core/AssetUrls"; import { ClanTagSchema, GameInfo, UsernameSchema } from "../core/Schemas"; import { formatPlayerDisplayName } from "../core/Util"; import { GameMode } from "../core/game/Game"; @@ -138,6 +139,9 @@ export function buildPreview( lobby: GameInfo | null, publicInfo: ExternalGameInfo | null, ): PreviewMeta { + const assetBasePath = buildVersionedAssetBasePath(process.env.GIT_COMMIT); + const buildAbsoluteAssetUrl = (path: string) => + new URL(buildAssetUrl(path, assetBasePath), origin).toString(); const isFinished = !!publicInfo?.info?.end; const isPrivate = lobby?.gameConfig?.gameType === "Private"; @@ -188,9 +192,12 @@ export function buildPreview( const normalizedMap = map ? map.toLowerCase().replace(/[\s.()]+/g, "") : null; const mapThumbnail = normalizedMap - ? `${origin}/maps/${encodeURIComponent(normalizedMap)}/thumbnail.webp` + ? buildAbsoluteAssetUrl( + `maps/${encodeURIComponent(normalizedMap)}/thumbnail.webp`, + ) : null; - const image = mapThumbnail ?? `${origin}/images/GameplayScreenshot.png`; + const image = + mapThumbnail ?? buildAbsoluteAssetUrl("images/GameplayScreenshot.png"); const gameType = lobby?.gameConfig?.gameType ?? config.gameType; const gameTypeLabel = gameType ? ` (${gameType})` : "";