miaam
Version:
Miaam Web Game Framework
171 lines (135 loc) • 4.15 kB
JavaScript
import { Loader } from 'pixi.js';
import TilemapLoader from 'miaam-assets/loaders/runtime/tilemap';
import TilesetLoader from 'miaam-assets/loaders/runtime/tileset';
import GameManager from './Game.manager.js';
import IllegalArgumentError from '../error/IllegalArgument.error.js';
/**
* @public
* @class
*/
class AssetsManager {
/**
* @type {AssetsManager}
*/
static #instance;
index;
#chunkDependencyMemo;
#loader;
#middlewares;
/**
* @private
* @constructor
*/
constructor() {
const { assets, chunks } = GameManager.instance.index;
this.index = { assets, chunks };
this.#chunkDependencyMemo = {};
this.#loader = new Loader();
this.#middlewares = [];
this.#getDefaultMiddlewares().forEach((middleware) => this.use(middleware));
}
/**
* @returns {AssetsManager}
*/
static get instance() {
if (!this.#instance) {
this.#instance = new AssetsManager();
}
return this.#instance;
}
importChunk = async ({ chunk, onProgress }) => {
const chunkName = chunk.source.substring(1).replaceAll(/[/\\.]/gm, '_');
try {
await this.importChunkAssets({ chunkName, onProgress });
const module = await chunk.load();
return module;
} catch (errors) {
if (errors.length) {
// eslint-disable-next-line no-console
errors.forEach((error) => console.error(error));
} else {
// eslint-disable-next-line no-console
console.error(errors);
}
return undefined;
}
};
importChunkAssets = async ({ chunkName, onProgress = () => {} }) => {
const dependencies = this.#resolveChunkDependencies(chunkName);
const newDependencies = dependencies.filter((dependency) => !this.#loader.resources[dependency]);
newDependencies.forEach(
(dependency) => !this.#loader.resources[dependency] && this.#loader.add(dependency, dependency)
);
this.#loader.onProgress.add((_loader, resource) => {
onProgress(this.#loader.progress, resource);
});
const assetsPromise = new Promise((resolve, reject) => {
const errors = [];
this.#loader.onComplete.add(() => {
this.#detachLoaderListener();
if (errors.length) {
this.#loader.reset();
reject(errors);
return;
}
newDependencies.forEach((dependency) => {
const resource = this.#loader.resources[dependency];
this.#middlewares.forEach((middleware) => middleware.use(resource, () => {}));
});
resolve();
});
this.#loader.onError.add((_error, _loader, resource) => {
errors.push(`Cannot load ${resource.name}`);
});
this.#loader.load();
});
return assetsPromise;
};
#resolveChunkDependencies = (chunkName) => {
if (this.#chunkDependencyMemo[chunkName]) {
return this.#chunkDependencyMemo[chunkName];
}
const dependencies = new Set();
const visited = {};
const chunkDependencies = this.index.chunks[chunkName];
if (!chunkDependencies) {
throw new IllegalArgumentError('Requested chunk cannot be found');
}
chunkDependencies.forEach((chunkDependency) => {
if (visited[chunkDependency]) return;
const resolveAssetDependency = (file) => {
visited[file] = true;
const fileDependencies = this.index.assets[file];
if (fileDependencies) {
fileDependencies.forEach(
(fileDependency) => !visited[fileDependency] && resolveAssetDependency(fileDependency)
);
}
dependencies.add(file);
};
resolveAssetDependency(chunkDependency);
});
this.#chunkDependencyMemo[chunkName] = [...dependencies];
return this.#chunkDependencyMemo[chunkName];
};
getResource = (resourcePath) => this.#loader.resources[resourcePath];
use = (loaderPlugin) => {
loaderPlugin.add(this.#loader);
this.#middlewares.push(loaderPlugin);
};
detachAllMiddlewares = () => {
this.#middlewares = [];
};
/* ================================ UTILITIES ================================ */
#detachLoaderListener = () => {
this.#loader.onProgress.detachAll();
this.#loader.onComplete.detachAll();
this.#loader.onError.detachAll();
};
#getDefaultMiddlewares = () => [new TilemapLoader({}), new TilesetLoader({})];
}
export default {
get instance() {
return AssetsManager.instance;
},
};