import { ILoadManager, ILoadTask, ILoadTaskStarter, LoadDataType, LoadTask } from '@motajs/loader'; import { IMotaDataLoader, IMotaDataLoaderHooks } from './types'; import { PrefixedJSONCProcessor } from './jsoncProcessor'; import { ICoreStateCoreConfig } from '../common'; import { Hookable, HookController, IHookController, logger } from '@motajs/common'; export class MotaDataLoader extends Hookable implements IMotaDataLoader { /** 加载完毕时兑现的 `Promise` */ private loadedPromise: Promise; /** 调用以使 `loadedPromise` 兑现 */ private loadResolve: () => void; /** 当前是否正在加载 */ private loading: boolean = false; /** 当前是否已经加载完毕 */ private dataLoaded: boolean = false; /** 核心配置文件路径 */ private coreConfigs: Map = new Map(); /** 额外配置对象的路径 */ private extraConfigs: Map = new Map(); /** 配置文件列表 */ private configMap: Map = new Map(); /** 自定义加载任务 */ private customTask: Set> = new Set(); constructor( readonly manager: ILoadManager, readonly starter: ILoadTaskStarter ) { super(); const { promise, resolve } = Promise.withResolvers(); this.loadedPromise = promise; this.loadResolve = resolve; } protected createController( hook: Partial ): IHookController { return new HookController(this, hook); } addCoreConfig(identifier: string, url: string): void { this.coreConfigs.set(identifier, url); } addExtraConfig(identifier: string, url: string): void { this.extraConfigs.set(identifier, url); } addCustomTask(task: LoadTask): Promise { this.customTask.add(task); return task.loaded(); } /** * 解析加载路径,根据运行环境确认应当使用什么路径格式 * @param url 加载路径 */ private resolveURL(url: string) { if (import.meta.env.DEV) { // 开发时 content 文件都在 src 文件夹下,因此前置 src return `${import.meta.env.BASE_URL}src/${url}`; } else { // 而构建后就会把文件移动至游戏根目录下,因此不需要前置 src return `${import.meta.env.BASE_URL}${url}`; } } async *start(): AsyncIterable { if (this.loading || this.dataLoaded) return; // 首先加载核心配置 const processor = new PrefixedJSONCProcessor( '// --- SYSTEM PREFIX END --- //' ); for (const [id, url] of this.coreConfigs) { const coreTask = new LoadTask< LoadDataType.Text, ICoreStateCoreConfig >({ identifier: id, url: this.resolveURL(url), dataType: LoadDataType.Text }); coreTask.setProcessor(processor); coreTask.setStarter(this.starter); this.manager.addTask(coreTask); } yield* this.manager.load(); // 然后加载其他配置文件 await Promise.all( this.forEachHook(hook => hook.onCoreConfigLoaded?.(this)) ); for (const [id, url] of this.extraConfigs) { const task = new LoadTask({ identifier: id, url: this.resolveURL(`content/${url}`), dataType: LoadDataType.Text }); task.setProcessor(processor); task.setStarter(this.starter); this.manager.addTask(task); task.loaded().then(obj => { if (this.configMap.has(id)) { logger.error(68, id); return; } this.configMap.set(id, obj); }); } yield* this.manager.load(); await Promise.all( this.forEachHook(hook => hook.onExtraConfigLoaded?.(this)) ); for (const task of this.customTask) { this.manager.addTask(task); } yield* this.manager.load(); this.loadResolve(); } getConfig(identifier: string): T | null { return this.configMap.get(identifier) ?? null; } loaded(): Promise { return this.loadedPromise; } }