Resolve versioned map assets per file

This commit is contained in:
scamiv
2026-03-22 17:48:53 +01:00
parent 1e5f77f512
commit 9e6140667b
5 changed files with 49 additions and 14 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import { assetUrl } from "../core/AssetUrls";
import { FetchGameMapLoader } from "../core/game/FetchGameMapLoader";
export const terrainMapFileLoader = new FetchGameMapLoader(() =>
assetUrl("maps"),
export const terrainMapFileLoader = new FetchGameMapLoader((path) =>
assetUrl(`maps/${path}`),
);
+7 -7
View File
@@ -37,25 +37,25 @@ export class BinaryLoaderGameMapLoader implements GameMapLoader {
})
.then((buf) => new Uint8Array(buf));
const mapBasePath = assetUrl(`maps/${fileName}`);
const mapAssetUrl = (path: string) => assetUrl(`maps/${fileName}/${path}`);
const mapData = {
mapBin: this.createLazyLoader(() => loadBinary(`${mapBasePath}/map.bin`)),
mapBin: this.createLazyLoader(() => loadBinary(mapAssetUrl("map.bin"))),
map4xBin: this.createLazyLoader(() =>
loadBinary(`${mapBasePath}/map4x.bin`),
loadBinary(mapAssetUrl("map4x.bin")),
),
map16xBin: this.createLazyLoader(() =>
loadBinary(`${mapBasePath}/map16x.bin`),
loadBinary(mapAssetUrl("map16x.bin")),
),
manifest: this.createLazyLoader(() =>
fetch(`${mapBasePath}/manifest.json`).then((res) => {
fetch(mapAssetUrl("manifest.json")).then((res) => {
if (!res.ok) {
throw new Error(`Failed to load ${mapBasePath}/manifest.json`);
throw new Error(`Failed to load ${mapAssetUrl("manifest.json")}`);
}
return res.json() as Promise<MapManifest>;
}),
),
webpPath: `${mapBasePath}/thumbnail.webp`,
webpPath: mapAssetUrl("thumbnail.webp"),
} satisfies MapData;
this.maps.set(map, mapData);
+9 -4
View File
@@ -4,7 +4,9 @@ import { GameMapLoader, MapData } from "./GameMapLoader";
export class FetchGameMapLoader implements GameMapLoader {
private maps: Map<GameMapType, MapData>;
public constructor(private readonly prefix: string | (() => string)) {
public constructor(
private readonly pathResolver: string | ((path: string) => string),
) {
this.maps = new Map<GameMapType, MapData>();
}
@@ -35,12 +37,15 @@ export class FetchGameMapLoader implements GameMapLoader {
return mapData;
}
private getPrefix(): string {
return typeof this.prefix === "function" ? this.prefix() : this.prefix;
private resolveUrl(path: string): string {
if (typeof this.pathResolver === "function") {
return this.pathResolver(path);
}
return `${this.pathResolver}/${path}`;
}
private url(map: string, path: string) {
return `${this.getPrefix()}/${map}/${path}`;
return this.resolveUrl(`${map}/${path}`);
}
private async loadBinaryFromUrl(url: string) {
+1 -1
View File
@@ -17,7 +17,7 @@ import {
const ctx: Worker = self as any;
globalThis.__ASSET_MANIFEST__ = __ASSET_MANIFEST__;
let gameRunner: Promise<GameRunner> | null = null;
const mapLoader = new FetchGameMapLoader(() => assetUrl("maps"));
const mapLoader = new FetchGameMapLoader((path) => assetUrl(`maps/${path}`));
// 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;
@@ -0,0 +1,30 @@
import { describe, expect, test, vi } from "vitest";
import { FetchGameMapLoader } from "../../../src/core/game/FetchGameMapLoader";
import { GameMapType } from "../../../src/core/game/Game";
describe("FetchGameMapLoader", () => {
test("resolves each map file through the provided path resolver", async () => {
const fetchMock = vi.fn(async (url: string) => ({
ok: true,
arrayBuffer: async () => new ArrayBuffer(0),
json: async () => ({ url }),
statusText: "OK",
}));
vi.stubGlobal("fetch", fetchMock);
const loader = new FetchGameMapLoader(
(path) => `/_assets/maps/${path}.hashed`,
);
const mapData = loader.getMapData(GameMapType.BritanniaClassic);
expect(mapData.webpPath).toBe(
"/_assets/maps/britanniaclassic/thumbnail.webp.hashed",
);
await mapData.manifest();
expect(fetchMock).toHaveBeenCalledWith(
"/_assets/maps/britanniaclassic/manifest.json.hashed",
);
});
});