From cc434a6c66e442c54d837ae5e42ece3a511143f9 Mon Sep 17 00:00:00 2001 From: unanmed <1319491857@qq.com> Date: Sun, 20 Sep 2026 20:53:46 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=8A=A0=E8=BD=BD=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages-user/data-base/src/load/index.ts | 3 - packages-user/data-base/src/load/loader.ts | 63 -------- packages-user/data-base/src/load/processor.ts | 66 -------- packages-user/data-base/src/load/types.ts | 38 ----- packages-user/data-state/src/common.ts | 93 +++++++++++ packages-user/data-state/src/core.ts | 23 ++- packages-user/data-state/src/loader/hook.ts | 26 ++++ packages-user/data-state/src/loader/index.ts | 0 .../data-state/src/loader/jsoncProcessor.ts | 29 ++++ packages-user/data-state/src/loader/loader.ts | 145 ++++++++++++++++++ packages-user/data-state/src/loader/types.ts | 62 ++++++++ packages-user/data-state/src/types.ts | 88 +---------- packages/common/src/logger.json | 3 + packages/loader/src/index.ts | 2 +- .../loader/src/{progress.ts => manager.ts} | 5 + src/content/client.jsonc | 1 + src/content/core.jsonc | 18 ++- src/content/defaults/enemy.jsonc | 1 + src/content/defaults/item.jsonc | 1 + src/content/defaults/map.jsonc | 1 + src/content/defaults/tile.jsonc | 1 + src/content/enemy.jsonc | 1 + src/content/item.jsonc | 1 + src/content/maps/.list.jsonc | 1 + src/content/maps/MT0.jsonc | 1 + src/content/textures/.textures.jsonc | 1 + src/content/tile.jsonc | 1 + 27 files changed, 409 insertions(+), 266 deletions(-) delete mode 100644 packages-user/data-base/src/load/index.ts delete mode 100644 packages-user/data-base/src/load/loader.ts delete mode 100644 packages-user/data-base/src/load/processor.ts delete mode 100644 packages-user/data-base/src/load/types.ts create mode 100644 packages-user/data-state/src/common.ts create mode 100644 packages-user/data-state/src/loader/hook.ts create mode 100644 packages-user/data-state/src/loader/index.ts create mode 100644 packages-user/data-state/src/loader/jsoncProcessor.ts create mode 100644 packages-user/data-state/src/loader/loader.ts create mode 100644 packages-user/data-state/src/loader/types.ts rename packages/loader/src/{progress.ts => manager.ts} (95%) diff --git a/packages-user/data-base/src/load/index.ts b/packages-user/data-base/src/load/index.ts deleted file mode 100644 index 00b1550..0000000 --- a/packages-user/data-base/src/load/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './loader'; -export * from './processor'; -export * from './types'; diff --git a/packages-user/data-base/src/load/loader.ts b/packages-user/data-base/src/load/loader.ts deleted file mode 100644 index 263825a..0000000 --- a/packages-user/data-base/src/load/loader.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - ILoadManager, - LoadDataType, - ILoadTask, - ILoadTaskProcessor -} from '@motajs/loader'; -import { CustomLoadFunc, IMotaDataLoader } from './types'; -import { LoadJSONProcessor } from './processor'; - -interface LoadTaskStore { - /** 加载任务对象 */ - readonly task: ILoadTask; - /** 当 `onLoaded` 兑现后兑现的 `Promise` */ - readonly loadPromise: Promise; - /** 兑现 `loadPromise` */ - readonly loadResolve: (data: R) => void; - /** 当加载任务完成时执行的函数 */ - readonly onLoaded: CustomLoadFunc; -} - -export class MotaDataLoader implements IMotaDataLoader { - /** 当前已添加的加载任务 */ - private readonly tasks: Set = new Set(); - - readonly jsonProcessor: ILoadTaskProcessor; - - constructor(readonly progress: ILoadManager) { - this.jsonProcessor = new LoadJSONProcessor(); - } - - //#region 对外接口 - - initSystemLoadTask(): void {} - - addCustomLoadTask( - task: ILoadTask, - onLoaded: CustomLoadFunc - ): Promise { - this.progress.addTask(task); - const { promise, resolve } = Promise.withResolvers(); - const store: LoadTaskStore = { - task, - onLoaded, - loadPromise: promise, - loadResolve: resolve - }; - this.tasks.add(store); - return promise; - } - - async load(): Promise { - const tasks = [...this.tasks].map(async task => { - task.task.start(); - const data = await task.task.loaded(); - await task.onLoaded(data); - task.loadResolve(data); - return data; - }); - await Promise.all(tasks); - } - - //#endregion -} diff --git a/packages-user/data-base/src/load/processor.ts b/packages-user/data-base/src/load/processor.ts deleted file mode 100644 index 2234b0a..0000000 --- a/packages-user/data-base/src/load/processor.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { IMotaAudioContext } from '@motajs/audio'; -import { ILoadTask, ILoadTaskProcessor, LoadDataType } from '@motajs/loader'; -import JSZip from 'jszip'; - -export class LoadImageProcessor implements ILoadTaskProcessor< - LoadDataType.Blob, - ImageBitmap -> { - process(response: Blob): Promise { - return createImageBitmap(response); - } -} - -export class LoadAudioProcessor implements ILoadTaskProcessor< - LoadDataType.Uint8Array, - AudioBuffer | null -> { - constructor(private readonly ac: IMotaAudioContext) {} - - process(response: Uint8Array): Promise { - return this.ac.decodeToAudioBuffer(response); - } -} - -export class LoadFontProcessor implements ILoadTaskProcessor< - LoadDataType.ArrayBuffer, - FontFace -> { - process( - response: ArrayBuffer, - task: ILoadTask - ): Promise { - const font = new FontFace(task.identifier, response); - if (font.status === 'loaded') return Promise.resolve(font); - else return font.load(); - } -} - -export class LoadZipProcessor implements ILoadTaskProcessor< - LoadDataType.ArrayBuffer, - JSZip -> { - async process(response: ArrayBuffer): Promise { - const zip = new JSZip(); - await zip.loadAsync(response); - return zip; - } -} - -export class LoadTextProcessor implements ILoadTaskProcessor< - LoadDataType.Text, - string -> { - process(response: string): Promise { - return Promise.resolve(response); - } -} - -export class LoadJSONProcessor implements ILoadTaskProcessor< - LoadDataType.JSON, - T -> { - process(response: any): Promise { - return Promise.resolve(response); - } -} diff --git a/packages-user/data-base/src/load/types.ts b/packages-user/data-base/src/load/types.ts deleted file mode 100644 index 4e11cb7..0000000 --- a/packages-user/data-base/src/load/types.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { - ILoadManager, - ILoadTask, - ILoadTaskProcessor, - LoadDataType -} from '@motajs/loader'; - -export type CustomLoadFunc = (data: R) => Promise; - -export interface IMotaDataLoader { - /** 加载进度对象 */ - readonly progress: ILoadManager; - - /** json 处理器 */ - readonly jsonProcessor: ILoadTaskProcessor; - - /** - * 初始化系统加载任务 - */ - initSystemLoadTask(): void; - - /** - * 添加自定义加载任务 - * @param task 自定义加载任务 - * @param onLoad 当任务加载完成时执行 - * @returns 一个 `Promise`,当添加的任务加载完毕,且 `onLoad` 返回的 `Promise` 兑现后兑现 - */ - addCustomLoadTask( - task: ILoadTask, - onLoad: CustomLoadFunc - ): Promise; - - /** - * 开始所有加载任务的加载工作 - * @returns 一个 `Promise`,当所有加载任务加载完成后兑现 - */ - load(): Promise; -} diff --git a/packages-user/data-state/src/common.ts b/packages-user/data-state/src/common.ts new file mode 100644 index 0000000..8b23837 --- /dev/null +++ b/packages-user/data-state/src/common.ts @@ -0,0 +1,93 @@ +import { IHeroAttr } from '@user/data-common'; + +//#region 数据端主配置 + +export interface ICoreStateContentDefaultConfig { + /** 怪物默认对象路径 */ + readonly enemyDefault: string; + /** 道具默认对象路径 */ + readonly itemDefault: string; + /** 地图默认对象路径 */ + readonly mapDefault: string; + /** 图块默认对象路径 */ + readonly tileDefault: string; +} + +export interface ICoreStateContentConfig { + /** 配置的默认对象配置路径 */ + readonly defaults: ICoreStateContentDefaultConfig; + /** 地图文件夹路径 */ + readonly mapsDir: string; + /** 需要加载的地图列表文件的文件路径 */ + readonly mapListDir: string; + /** 怪物数据文件路径 */ + readonly enemyDir: string; + /** 道具数据文件路径 */ + readonly itemDir: string; + /** 道具效果文件路径 */ + readonly itemEffectDir: string; + /** 图库数据文件路径 */ + readonly tileDir: string; +} + +export interface ICoreStateHeroInitConfig { + /** 勇士的初始横坐标 */ + readonly x: number; + /** 勇士的初始纵坐标 */ + readonly y: number; + /** 勇士初始所在楼层 */ + readonly floorId: string; + /** 勇士的初始装备栏 */ + readonly equipSlots: string[]; + /** 勇士的初始属性 */ + readonly attribute: Readonly; +} + +export interface ICoreStateInitConfig { + /** 初始事件 id,游戏开始时将在执行完毕此事件后正式开始 */ + readonly startEvent: string; + /** 勇士初始状态,包含起始位置、初始属性等 */ + readonly hero: ICoreStateHeroInitConfig; +} + +export interface ICoreStateFlagConfig { + /** 楼层传送是否要求在楼梯边才能使用 */ + readonly flyNearStair: boolean; + /** 楼层传送是否为平面塔模式,即传送时会传送至上一次离开此位置的位置而非楼梯口 */ + readonly flyRecordLocation: boolean; + /** 道具在首次拾取时是否显示其文字说明 */ + readonly itemHint: boolean; + /** 是否启用负伤机制,开启后怪物总伤害可以变为负值 */ + readonly enableNegativeDamage: boolean; + /** 是否限制夹击最高伤害为两夹击怪物中伤害最高者 */ + readonly betweenDamageMax: boolean; + /** 是否启用轻按机制,即可以按下空格来拾取玩家四周的物品 */ + readonly enablePick: boolean; + /** 若在寻路时需要穿过楼梯,是否允许穿越 */ + readonly ignoreChangeFLoor: boolean; + /** 若继续前进会导致勇士受到地图伤害而死亡,是否允许前进 */ + readonly allowDeadZone: boolean; + /** 是否允许点击瞬移 */ + readonly enableTeleport: boolean; + /** 是否开启录像折叠,这可以使走路的录像大幅减少,但可能导致某些机制出错,可查看文档详细了解 */ + readonly enableRouteFolding: boolean; + /** 是否开启瞬移合并机制,开启后,连续瞬移会在录像中合并为单次瞬移,但可能导致某些机制出错 */ + readonly enableTeleportMerge: boolean; +} + +export interface ICoreStateCoreConfig { + /** 游戏的唯一标识符 */ + readonly name: string; + /** 呈现给玩家的游戏名称 */ + readonly title: string; + /** 游戏版本号,仅呈现作用,对游戏本身没有任何影响 */ + readonly version: string; + /** 游戏数据配置 */ + readonly content: ICoreStateContentConfig; + /** 游戏的初始状态数据 */ + readonly init: ICoreStateInitConfig; + /** 游戏的全局设置 */ + readonly flag: ICoreStateFlagConfig; +} + +//#endregion diff --git a/packages-user/data-state/src/core.ts b/packages-user/data-state/src/core.ts index 6ff21cb..997b120 100644 --- a/packages-user/data-state/src/core.ts +++ b/packages-user/data-state/src/core.ts @@ -1,5 +1,4 @@ import { logger } from '@motajs/common'; -import { ILoadManager, LoadManager } from '@motajs/loader'; import { IRoleFaceBinder, IFaceManager, @@ -33,8 +32,6 @@ import { IHeroState, IFlagSystem, FlagSystem, - IMotaDataLoader, - MotaDataLoader, IMapState, MapState } from '@user/data-base'; @@ -70,6 +67,10 @@ import { ReplayUnequip, ReplayUseItem } from './replay'; +import { IMotaDataLoader } from './loader/types'; +import { MotaDataLoader } from './loader/loader'; +import { ILoadManager, LoadManager } from '@motajs/loader'; +import { DefaultDataLoaderHook } from './loader/hook'; export class CoreState implements ICoreState { // Layer 0 公共层,最底层的接口,不会依赖任何其他内容,一般是工具性接口及不需要存档的数据 @@ -93,8 +94,8 @@ export class CoreState implements ICoreState { readonly pathfinding: IPathfindingSystem; // Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块 - readonly loadProgress: ILoadManager; - readonly dataLoader: IMotaDataLoader; + readonly loader: IMotaDataLoader; + readonly loadManager: ILoadManager; /** 可存档对象映射 */ private readonly saveables: Map> = new Map(); @@ -143,9 +144,6 @@ export class CoreState implements ICoreState { const heroState = new HeroState(this, dir8, heroAttribute); this.hero = heroState; - this.loadProgress = new LoadManager(); - this.dataLoader = new MotaDataLoader(this.loadProgress); - // 怪物管理器 const comparer = new MainEnemyComparer(); const enemyManager = new EnemyManager(new EnemyLegacyBridge()); @@ -201,6 +199,15 @@ export class CoreState implements ICoreState { //#region L3 初始化 + // 加载 + this.loadManager = new LoadManager(); + this.loader = new MotaDataLoader( + this.loadManager, + config.coreURL, + config.loadStarter + ); + this.loader.addHook(new DefaultDataLoaderHook()); + // 存档内容 this.addSaveableContent('@system/hero', this.hero); this.addSaveableContent('@system/flags', this.flags); diff --git a/packages-user/data-state/src/loader/hook.ts b/packages-user/data-state/src/loader/hook.ts new file mode 100644 index 0000000..524f55a --- /dev/null +++ b/packages-user/data-state/src/loader/hook.ts @@ -0,0 +1,26 @@ +import { ICoreStateCoreConfig } from '../common'; +import { IMotaDataLoader, IMotaDataLoaderHooks } from './types'; + +export class DefaultDataLoaderHook implements IMotaDataLoaderHooks { + awake(): void {} + + destroy(): void {} + + onCoreConfigLoaded( + coreConfig: ICoreStateCoreConfig, + loader: IMotaDataLoader + ): Promise { + const content = coreConfig.content; + loader.addExtraConfig('enemy', content.enemyDir); + loader.addExtraConfig('item', content.itemDir); + loader.addExtraConfig('map-list', content.mapListDir); + loader.addExtraConfig('tile', content.tileDir); + const defaults = content.defaults; + loader.addExtraConfig('enemy-default', defaults.enemyDefault); + loader.addExtraConfig('item-default', defaults.itemDefault); + loader.addExtraConfig('map-default', defaults.mapDefault); + loader.addExtraConfig('tile-default', defaults.tileDefault); + + return Promise.resolve(); + } +} diff --git a/packages-user/data-state/src/loader/index.ts b/packages-user/data-state/src/loader/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages-user/data-state/src/loader/jsoncProcessor.ts b/packages-user/data-state/src/loader/jsoncProcessor.ts new file mode 100644 index 0000000..2ea7924 --- /dev/null +++ b/packages-user/data-state/src/loader/jsoncProcessor.ts @@ -0,0 +1,29 @@ +import { logger } from '@motajs/common'; +import { ILoadTask, ILoadTaskProcessor, LoadDataType } from '@motajs/loader'; + +export class PrefixedJSONCProcessor implements ILoadTaskProcessor< + LoadDataType.Text, + T +> { + constructor(readonly prefixSplit: string) {} + + process( + response: string, + task: ILoadTask + ): Promise { + const splitted = response.split(this.prefixSplit); + if (splitted.length !== 2) { + logger.error(66, task.url, this.prefixSplit); + return Promise.resolve({} as T); + } + + const raw = splitted[1]; + try { + const obj = JSON.parse(raw); + return Promise.resolve(obj as T); + } catch (e) { + logger.error(67, task.url, String(e)); + return Promise.resolve({} as T); + } + } +} diff --git a/packages-user/data-state/src/loader/loader.ts b/packages-user/data-state/src/loader/loader.ts new file mode 100644 index 0000000..1f82a87 --- /dev/null +++ b/packages-user/data-state/src/loader/loader.ts @@ -0,0 +1,145 @@ +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 extraConfigs: Map = new Map(); + /** 配置文件列表 */ + private configMap: Map = new Map(); + + /** 自定义加载任务 */ + private customTask: Set> = new Set(); + + constructor( + readonly manager: ILoadManager, + readonly coreDir: string, + 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); + } + + 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 --- //' + ); + const coreTask = new LoadTask({ + identifier: 'data-core-config', + url: this.resolveURL(this.coreDir), + dataType: LoadDataType.Text + }); + coreTask.setProcessor(processor); + coreTask.setStarter(this.starter); + this.manager.addTask(coreTask); + yield* this.manager.load(); + + // 然后加载其他配置文件 + const coreConfig = await coreTask.loaded(); + this.configMap.set('core', coreConfig); + await Promise.all( + this.forEachHook(hook => + hook.onCoreConfigLoaded?.(coreConfig, 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; + } +} diff --git a/packages-user/data-state/src/loader/types.ts b/packages-user/data-state/src/loader/types.ts new file mode 100644 index 0000000..0f66649 --- /dev/null +++ b/packages-user/data-state/src/loader/types.ts @@ -0,0 +1,62 @@ +import { IHookable, IHookBase } from '@motajs/common'; +import { ILoadManager, LoadDataType, LoadTask } from '@motajs/loader'; +import { ICoreStateCoreConfig } from '../common'; + +export interface IMotaDataLoaderHooks extends IHookBase { + /** + * 当核心配置对象加载完毕时触发,往往用于添加自定义配置文件 + * @param coreConfig 核心配置对象 + * @param loader 数据端加载对象 + */ + onCoreConfigLoaded?( + coreConfig: ICoreStateCoreConfig, + loader: IMotaDataLoader + ): Promise; + + /** + * 当所有的额外配置对象加载完毕后执行,往往用于根据配置文件添加额外加载任务 + * @param loader 数据端加载对象 + */ + onExtraConfigLoaded?(loader: IMotaDataLoader): Promise; +} + +export interface IMotaDataLoader extends IHookable { + /** 当前使用的加载管理器 */ + readonly manager: ILoadManager; + + /** + * 开始执行加载 + */ + start(): AsyncIterable; + + /** + * 获取当加载执行完毕时兑现的 `Promise` + */ + loaded(): Promise; + + /** + * 添加额外配置文件,仅允许在 `onCoreConfigLoaded` 中调用或在加载前调用,加载中及加载后后调用无效。 + * 文件必须放在 `src/content` 下,并填写相对于 `src/content` 文件夹的路径,文件包含如下要求: + * + * - 文件必须为 `jsonc` 格式 + * - 文件的注释必须全部集中在开头 + * - 文件中的最后一行注释必须是 `// --- SYSTEM PREFIX END --- //` + * @param identifier 加载任务与配置文件的标识符 + * @param url 额外配置文件路径 + */ + addExtraConfig(identifier: string, url: string): void; + + /** + * 添加自定义加载任务,仅允许在 `onCoreConfigLoaded` 和 `onExtraConfigLoaded` 中调用或在加载前调用, + * 加载中及加载后后调用无效。所有的自定义加载任务都会在配置文件加载完毕后开始加载。 + * @param task 自定义加载任务实例 + * @returns 任务加载完毕后兑现,兑现值为经过加载处理器处理后的值 + */ + addCustomTask(task: LoadTask): Promise; + + /** + * 获取指定配置文件,例如核心配置文件是 `core`,怪物配置文件是 `enemy` 等等 + * @param identifier 配置文件标识符 + */ + getConfig(identifier: string): T | null; +} diff --git a/packages-user/data-state/src/types.ts b/packages-user/data-state/src/types.ts index fa7e0a7..f38993c 100644 --- a/packages-user/data-state/src/types.ts +++ b/packages-user/data-state/src/types.ts @@ -1,7 +1,8 @@ -import { IMotaDataLoader, IStateBase } from '@user/data-base'; +import { IStateBase } from '@user/data-base'; import { ILoadManager, ILoadTaskStarter } from '@motajs/loader'; import { IStateSystem } from '@user/data-system'; -import { IHeroAttr, ISaveableContent } from '@user/data-common'; +import { ISaveableContent } from '@user/data-common'; +import { IMotaDataLoader } from './loader/types'; //#region 数据端主对象 @@ -26,10 +27,10 @@ export interface ISaveableExecutor { export interface ICoreState extends IStateSystem, ISaveableContent> { - /** 加载进度对象 */ - readonly loadProgress: ILoadManager; /** 数据端加载对象 */ - readonly dataLoader: IMotaDataLoader; + readonly loader: IMotaDataLoader; + /** 加载管理器 */ + readonly loadManager: ILoadManager; /** * 将某个存档执行器绑定至指定的可存档对象,一个可存档对象只能绑定一个执行器, @@ -44,80 +45,3 @@ export interface ICoreState } //#endregion - -//#region 数据端主配置 - -export interface ICoreStateContentConfig { - /** 地图文件夹路径 */ - readonly mapsDir: string; - /** 需要加载的地图列表文件的文件路径 */ - readonly mapListDir: string; - /** 怪物数据文件路径 */ - readonly enemyDir: string; - /** 道具数据文件路径 */ - readonly itemDir: string; - /** 图库数据文件路径 */ - readonly tileDir: string; -} - -export interface ICoreStateHeroInitConfig { - /** 勇士的初始横坐标 */ - readonly x: number; - /** 勇士的初始纵坐标 */ - readonly y: number; - /** 勇士初始所在楼层 */ - readonly floorId: string; - /** 勇士的初始装备栏 */ - readonly equipSlots: string[]; - /** 勇士的初始属性 */ - readonly attribute: Readonly; -} - -export interface ICoreStateInitConfig { - /** 初始事件 id,游戏开始时将在执行完毕此事件后正式开始 */ - readonly startEvent: string; - /** 勇士初始状态,包含起始位置、初始属性等 */ - readonly hero: ICoreStateHeroInitConfig; -} - -export interface ICoreStateFlagConfig { - /** 楼层传送是否要求在楼梯边才能使用 */ - readonly flyNearStair: boolean; - /** 楼层传送是否为平面塔模式,即传送时会传送至上一次离开此位置的位置而非楼梯口 */ - readonly flyRecordLocation: boolean; - /** 道具在首次拾取时是否显示其文字说明 */ - readonly itemHint: boolean; - /** 是否启用负伤机制,开启后怪物总伤害可以变为负值 */ - readonly enableNegativeDamage: boolean; - /** 是否限制夹击最高伤害为两夹击怪物中伤害最高者 */ - readonly betweenDamageMax: boolean; - /** 是否启用轻按机制,即可以按下空格来拾取玩家四周的物品 */ - readonly enablePick: boolean; - /** 若在寻路时需要穿过楼梯,是否允许穿越 */ - readonly ignoreChangeFLoor: boolean; - /** 若继续前进会导致勇士受到地图伤害而死亡,是否允许前进 */ - readonly allowDeadZone: boolean; - /** 是否允许点击瞬移 */ - readonly enableTeleport: boolean; - /** 是否开启录像折叠,这可以使走路的录像大幅减少,但可能导致某些机制出错,可查看文档详细了解 */ - readonly enableRouteFolding: boolean; - /** 是否开启瞬移合并机制,开启后,连续瞬移会在录像中合并为单次瞬移,但可能导致某些机制出错 */ - readonly enableTeleportMerge: boolean; -} - -export interface ICoreStateCoreConfig { - /** 游戏的唯一标识符 */ - readonly name: string; - /** 呈现给玩家的游戏名称 */ - readonly title: string; - /** 游戏版本号,仅呈现作用,对游戏本身没有任何影响 */ - readonly version: string; - /** 游戏数据配置 */ - readonly content: ICoreStateContentConfig; - /** 游戏的初始状态数据 */ - readonly init: ICoreStateInitConfig; - /** 游戏的全局设置 */ - readonly flag: ICoreStateFlagConfig; -} - -//#endregion diff --git a/packages/common/src/logger.json b/packages/common/src/logger.json index 5b49320..c25feb1 100644 --- a/packages/common/src/logger.json +++ b/packages/common/src/logger.json @@ -65,6 +65,9 @@ "63": "Cannot create game map '$2' from raw data since the '$1' container is missing, null, or not an object.", "64": "Cannot create game map '$2' from raw data since the '$1' value has an invalid type or range.", "65": "Cannot start pathfinding movement because the mover is already active.", + "66": "Load url '$1' error, since specified a prefixed jsonc parser, but no(or more than one) prefix split string '$2' found in response string.", + "67": "Load url '$1' error since the raw json string splitted by prefix split string cannot be parsed to a valid JavaScript object. Parse error info: $2", + "68": "Extra config identifier '$1' has conflicts. More than one extra config use this identifier, and the final load result is unpredictable since network decides which one to be loaded fast.", "2001": "Replay($1): Replay parameter count mismatch: expected $2, got $3.", "2002": "Replay($1): Replay parameter type mismatch: index $2, expected $3, got $4.", "2003": "Replay(move): Expected hero to be stopped before execute moving step.", diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index fd7d204..1923aa6 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -1,4 +1,4 @@ -export * from './progress'; +export * from './manager'; export * from './starter'; export * from './stream'; export * from './task'; diff --git a/packages/loader/src/progress.ts b/packages/loader/src/manager.ts similarity index 95% rename from packages/loader/src/progress.ts rename to packages/loader/src/manager.ts index 0141a3b..43e5f5a 100644 --- a/packages/loader/src/progress.ts +++ b/packages/loader/src/manager.ts @@ -49,6 +49,11 @@ export class LoadManager } async *load(): AsyncIterable { + for (const task of this.addedTasks) { + if (!task.contentLoaded) { + task.start(); + } + } while (true) { if (this.loadedTasks.size === this.addedTasks.size) { return; diff --git a/src/content/client.jsonc b/src/content/client.jsonc index 0967ef4..c91455d 100644 --- a/src/content/client.jsonc +++ b/src/content/client.jsonc @@ -1 +1,2 @@ +// --- SYSTEM PREFIX END --- // {} diff --git a/src/content/core.jsonc b/src/content/core.jsonc index 36a2d33..eebc2f7 100644 --- a/src/content/core.jsonc +++ b/src/content/core.jsonc @@ -1,14 +1,22 @@ // Type: packages-user/data-state/src/types.ts ICoreStateCoreConfig +// --- SYSTEM PREFIX END --- // { "name": "template", "title": "魔塔样板", "version": "Ver 2.C", "content": { - "mapsDir": "content/maps", - "mapListDir": "content/maps/.list.jsonc", - "enemyDir": "content/enemy.jsonc", - "itemDir": "content/item.jsonc", - "tileDir": "content/tile.jsonc" + "mapsDir": "maps", + "mapListDir": "maps/.list.jsonc", + "enemyDir": "enemy.jsonc", + "itemDir": "item.jsonc", + "itemEffectDir": "item.js", + "tileDir": "tile.jsonc", + "defaults": { + "enemyDefault": "defaults/enemy.jsonc", + "itemDefault": "defaults/item.jsonc", + "mapDefault": "defaults/map.jsonc", + "tileDefault": "defaults/tile.jsonc" + } }, "init": { "startEvent": "game-start", diff --git a/src/content/defaults/enemy.jsonc b/src/content/defaults/enemy.jsonc index 9f620f5..f970b65 100644 --- a/src/content/defaults/enemy.jsonc +++ b/src/content/defaults/enemy.jsonc @@ -1,6 +1,7 @@ // Type: packages-user/data-common/src/store/types IEnemyRawData // "@required" means the property is required. // For objects, system will compare every properties. +// --- SYSTEM PREFIX END --- // { "num": "@required", "attribute": { diff --git a/src/content/defaults/item.jsonc b/src/content/defaults/item.jsonc index 5f20c7c..ba32733 100644 --- a/src/content/defaults/item.jsonc +++ b/src/content/defaults/item.jsonc @@ -1,6 +1,7 @@ // Type: packages-user/data-common/src/store/types IItemRawData // "@required" means the property is required. // For objects, system will compare every properties. +// --- SYSTEM PREFIX END --- // { "num": "@required", "category": 0, diff --git a/src/content/defaults/map.jsonc b/src/content/defaults/map.jsonc index 5eb605f..abf08e6 100644 --- a/src/content/defaults/map.jsonc +++ b/src/content/defaults/map.jsonc @@ -1,6 +1,7 @@ // Type: packages-user/data-common/src/store/types IMapRawData // "@required" means the property is required. // For objects, system will compare every properties. +// --- SYSTEM PREFIX END --- // { "floorId": "@required", "width": 13, diff --git a/src/content/defaults/tile.jsonc b/src/content/defaults/tile.jsonc index 49eda46..bad701d 100644 --- a/src/content/defaults/tile.jsonc +++ b/src/content/defaults/tile.jsonc @@ -1,6 +1,7 @@ // Type: packages-user/data-common/src/store/types ITileRawData // "@required" means the property is required. // For objects, system will compare every properties. +// --- SYSTEM PREFIX END --- // { "num": "@required", "id": "@required", diff --git a/src/content/enemy.jsonc b/src/content/enemy.jsonc index 15c5d4c..9dcd83a 100644 --- a/src/content/enemy.jsonc +++ b/src/content/enemy.jsonc @@ -1,4 +1,5 @@ // Type: packages-user/data-common/src/store/types IEnemyRawData +// --- SYSTEM PREFIX END --- // [ { "num": 100, diff --git a/src/content/item.jsonc b/src/content/item.jsonc index f74c085..5e62588 100644 --- a/src/content/item.jsonc +++ b/src/content/item.jsonc @@ -1,5 +1,6 @@ // Type: packages-user/data-common/src/store/types IItemRawData // ItemEffect: ./item.ts +// --- SYSTEM PREFIX END --- // [ { "num": 200, diff --git a/src/content/maps/.list.jsonc b/src/content/maps/.list.jsonc index 710f557..025c76a 100644 --- a/src/content/maps/.list.jsonc +++ b/src/content/maps/.list.jsonc @@ -1,2 +1,3 @@ // Type: string[], without `.jsonc` suffix +// --- SYSTEM PREFIX END --- // ["MT0"] diff --git a/src/content/maps/MT0.jsonc b/src/content/maps/MT0.jsonc index 2d899a7..e7340b7 100644 --- a/src/content/maps/MT0.jsonc +++ b/src/content/maps/MT0.jsonc @@ -1,4 +1,5 @@ // Type: packages-user/data-common/src/store/types IMapRawData +// --- SYSTEM PREFIX END --- // { "floorId": "MT0", "width": 13, diff --git a/src/content/textures/.textures.jsonc b/src/content/textures/.textures.jsonc index 0da8040..301e5e7 100644 --- a/src/content/textures/.textures.jsonc +++ b/src/content/textures/.textures.jsonc @@ -1,2 +1,3 @@ // Type: type define required +// --- SYSTEM PREFIX END --- // {} diff --git a/src/content/tile.jsonc b/src/content/tile.jsonc index f057ca0..2485a3c 100644 --- a/src/content/tile.jsonc +++ b/src/content/tile.jsonc @@ -1,4 +1,5 @@ // Type: packages-user/data-common/src/store/types ITileRawData +// --- SYSTEM PREFIX END --- // [ { "num": 1,