import { GameMapType } from "./Game"; import { GameMapLoader, MapData } from "./GameMapLoader"; export class FetchGameMapLoader implements GameMapLoader { private maps: Map; public constructor( private readonly pathResolver: string | ((path: string) => string), ) { this.maps = new Map(); } public getMapData(map: GameMapType): MapData { const cachedMap = this.maps.get(map); if (cachedMap) { return cachedMap; } const key = Object.keys(GameMapType).find( (k) => GameMapType[k as keyof typeof GameMapType] === map, ); const fileName = key?.toLowerCase(); if (!fileName) { throw new Error(`Unknown map: ${map}`); } const mapData = { mapBin: () => this.loadBinaryFromUrl(this.url(fileName, "map.bin")), map4xBin: () => this.loadBinaryFromUrl(this.url(fileName, "map4x.bin")), map16xBin: () => this.loadBinaryFromUrl(this.url(fileName, "map16x.bin")), manifest: () => this.loadJsonFromUrl(this.url(fileName, "manifest.json")), webpPath: this.url(fileName, "thumbnail.webp"), } satisfies MapData; this.maps.set(map, mapData); return mapData; } 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.resolveUrl(`${map}/${path}`); } private async loadBinaryFromUrl(url: string) { const startTime = performance.now(); const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to load ${url}: ${response.statusText}`); } const data = await response.arrayBuffer(); console.log( `[MapLoader] ${url}: ${(performance.now() - startTime).toFixed(0)}ms`, ); return new Uint8Array(data); } private async loadJsonFromUrl(url: string) { const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to load ${url}: ${response.statusText}`); } return response.json(); } }