refactor: 加载系统

This commit is contained in:
unanmed 2026-09-18 20:30:40 +08:00
parent d3598e9e27
commit f5a6ed8cf6
16 changed files with 430 additions and 145 deletions

View File

@ -1,5 +1,5 @@
import {
ILoadProgressTotal,
ILoadManager,
LoadDataType,
ILoadTask,
LoadTask,
@ -51,7 +51,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
private readonly rowSplitter: ITextureSplitter<number>;
constructor(
readonly progress: ILoadProgressTotal,
readonly progress: ILoadManager,
private readonly dataLoader: IMotaDataLoader,
private readonly ac: IMotaAudioContext,
private readonly sounds: ISoundPlayer<SoundIds>,
@ -207,9 +207,11 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-font/${font}`,
dataType: LoadDataType.ArrayBuffer,
// @ts-expect-error 需要重构
processor: this.fontProcessor,
progress: this.progress
});
task.setProcessor(this.fontProcessor);
this.addCustomLoadTask(task, data => this.fontLoaded(font, data));
});
@ -220,6 +222,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-image/${image}`,
dataType: LoadDataType.Blob,
// @ts-expect-error 需要重构
processor: this.imageProcessor,
progress: this.progress
});
@ -238,6 +241,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-sound/${sound}`,
dataType: LoadDataType.Uint8Array,
// @ts-expect-error 需要重构
processor: this.audioProcessor,
progress: this.progress
});
@ -251,6 +255,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-tileset/${tileset}`,
dataType: LoadDataType.Blob,
// @ts-expect-error 需要重构
processor: this.imageProcessor,
progress: this.progress
});
@ -268,6 +273,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-autotile/${key}`,
dataType: LoadDataType.Blob,
// @ts-expect-error 需要重构
processor: this.imageProcessor,
progress: this.progress
});
@ -294,6 +300,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url,
identifier: `@system-material/${materialName}`,
dataType: LoadDataType.Blob,
// @ts-expect-error 需要重构
processor: this.imageProcessor,
progress: this.progress
});
@ -308,6 +315,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url: animatesUrl,
identifier: '@system-animates',
dataType: LoadDataType.Text,
// @ts-expect-error 需要重构
processor: this.textProcessor,
progress: this.progress
});
@ -454,6 +462,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url: `loadList.json`,
dataType: LoadDataType.JSON,
identifier: '@system-loadList',
// @ts-expect-error 需要重构
processor: this.dataLoader.jsonProcessor,
progress: { onProgress() {} }
});
@ -465,6 +474,7 @@ export class MotaAssetsLoader implements IMotaAssetsLoader {
url: loadList.file,
identifier: `@system-zip/${loadList.file}`,
dataType: LoadDataType.ArrayBuffer,
// @ts-expect-error 需要重构
processor: this.zipProcessor,
progress: this.progress
});

View File

@ -1,5 +1,5 @@
import {
ILoadProgressTotal,
ILoadManager,
ILoadTask,
ILoadTaskProcessor,
LoadDataType
@ -37,7 +37,7 @@ export interface ICompressedMotaAssetsLoadList {
export interface IMotaAssetsLoader {
/** 加载进度对象 */
readonly progress: ILoadProgressTotal;
readonly progress: ILoadManager;
/** 当前是否正在加载 */
readonly loading: boolean;
/** 当前是否已经加载完毕 */

View File

@ -63,7 +63,7 @@ export class ClientCore extends CoreState implements IClientCore {
readonly bgmPlayer: IBGMPlayer<BgmIds>;
constructor() {
super();
super({});
//#region Layer 4

View File

@ -74,6 +74,7 @@ export const LoadScene = defineComponent<ILoadProps>(props => {
loader.load().then(() => {
loadEnd();
});
// @ts-expect-error 需要重构
for await (const _ of loader.progress) {
taskProgress.set(loader.progress.getLoadedTasks());
byteProgress.set(loader.progress.getLoadedByte());

View File

@ -1,5 +1,5 @@
import {
ILoadProgressTotal,
ILoadManager,
LoadDataType,
ILoadTask,
ILoadTaskProcessor
@ -24,7 +24,7 @@ export class MotaDataLoader implements IMotaDataLoader {
readonly jsonProcessor: ILoadTaskProcessor<LoadDataType.JSON, any>;
constructor(readonly progress: ILoadProgressTotal) {
constructor(readonly progress: ILoadManager) {
this.jsonProcessor = new LoadJSONProcessor();
}

View File

@ -1,5 +1,5 @@
import {
ILoadProgressTotal,
ILoadManager,
ILoadTask,
ILoadTaskProcessor,
LoadDataType
@ -9,7 +9,7 @@ export type CustomLoadFunc<R> = (data: R) => Promise<void>;
export interface IMotaDataLoader {
/** 加载进度对象 */
readonly progress: ILoadProgressTotal;
readonly progress: ILoadManager;
/** json 处理器 */
readonly jsonProcessor: ILoadTaskProcessor<LoadDataType.JSON, any>;

View File

@ -1,5 +1,5 @@
import { logger } from '@motajs/common';
import { ILoadProgressTotal, LoadProgressTotal } from '@motajs/loader';
import { ILoadManager, LoadManager } from '@motajs/loader';
import {
IRoleFaceBinder,
IFaceManager,
@ -48,7 +48,7 @@ import {
IPathfindingSystem,
PathfindingSystem
} from '@user/data-system';
import { ICoreState, ISaveableExecutor } from './types';
import { ICoreState, ICoreStateConfig, ISaveableExecutor } from './types';
import {
CommonAuraConverter,
EnemyLegacyBridge,
@ -93,7 +93,7 @@ export class CoreState implements ICoreState {
readonly pathfinding: IPathfindingSystem;
// Layer 3 用户层,也就是最顶层的内容,一般仅用于初始化以及仅供渲染端调用的顶层模块
readonly loadProgress: ILoadProgressTotal;
readonly loadProgress: ILoadManager;
readonly dataLoader: IMotaDataLoader;
/** 可存档对象映射 */
@ -106,7 +106,7 @@ export class CoreState implements ICoreState {
ISaveableExecutor<any>
> = new Map();
constructor() {
constructor(config: Readonly<ICoreStateConfig>) {
//#region L0 初始化
// 朝向
@ -143,7 +143,7 @@ export class CoreState implements ICoreState {
const heroState = new HeroState(this, dir8, heroAttribute);
this.hero = heroState;
this.loadProgress = new LoadProgressTotal();
this.loadProgress = new LoadManager();
this.dataLoader = new MotaDataLoader(this.loadProgress);
// 怪物管理器
@ -323,7 +323,3 @@ export class CoreState implements ICoreState {
//#endregion
}
export function createCoreState(): CoreState {
return new CoreState();
}

View File

@ -7,4 +7,4 @@ import { CoreState } from './core';
* 此对象是数据端状态,本身不负责任何渲染操作,仅会向渲染端发送数据让渲染端渲染,不要把渲染操作直接放到此对象上,
* 否则可能导致录像验证失败。
*/
export const state = new CoreState();
export const state = new CoreState({});

View File

@ -1,8 +1,10 @@
import { IMotaDataLoader, IStateBase } from '@user/data-base';
import { ILoadProgressTotal } from '@motajs/loader';
import { ILoadManager } from '@motajs/loader';
import { IStateSystem } from '@user/data-system';
import { ISaveableContent } from '@user/data-common';
export interface ICoreStateConfig {}
export interface ISaveableExecutor<T> {
/**
* 当数据读取后执行的函数,允许对其他存档对象进行读取
@ -15,7 +17,7 @@ export interface ISaveableExecutor<T> {
export interface ICoreState
extends IStateSystem, ISaveableContent<ReadonlyMap<string, unknown>> {
/** 加载进度对象 */
readonly loadProgress: ILoadProgressTotal;
readonly loadProgress: ILoadManager;
/** 数据端加载对象 */
readonly dataLoader: IMotaDataLoader;

View File

@ -0,0 +1,44 @@
import { describe, expect, it } from 'vitest';
import { TileStore } from '@user/data-common';
import { LegacyTileData, TileLegacyBridge } from '../src/legacy/tile';
describe('TileLegacyBridge events-map contract', () => {
// 验证旧样板事件对象会转换为 TileStore 可消费的默认事件映射
it('converts legacy events into the TileStore event map', () => {
const bridge = new TileLegacyBridge();
const store = new TileStore<LegacyTileData>();
store.attachLegacyConverter(bridge);
const legacy: LegacyTileData = {
id: 'yellowWall',
cls: 'terrains',
events: { 10: 'onEnter', 20: 'onTouch' },
noPass: true
};
const converted = store.fromLegacy(1, legacy);
expect(converted.events).toEqual({ 10: 'onEnter', 20: 'onTouch' });
expect(store.getEvent(1)).toEqual(
new Map([
[10, 'onEnter'],
[20, 'onTouch']
])
);
expect(converted.eventPass).toBe(false);
expect(converted).not.toHaveProperty('trigger');
});
// 验证旧样板缺少事件输入时仍生成合法的空事件映射
it('uses an empty events map when legacy input has no events', () => {
const bridge = new TileLegacyBridge();
const legacy: LegacyTileData = {
id: 'whiteWall',
cls: 'terrains'
};
const converted = bridge.fromLegacy(2, legacy);
expect(converted.events).toEqual({});
expect(converted).not.toHaveProperty('trigger');
});
});

View File

@ -29,7 +29,7 @@
"27": "No autotile connection data, please ensure you have created autotile connection map.",
"28": "Cannot compile map render shader.",
"29": "Cannot get uniform location of map render shader.",
"30": "",
"30": "Load task needs a load starter binding before loading.",
"31": "No asset data is specified when rending map.",
"32": "Every layer added to map renderer must share the same size. Different layer: $1.",
"33": "Map layer transfered to vertex generator must belong to the renderer that the generator use.",

View File

@ -1,4 +1,5 @@
export * from './progress';
export * from './starter';
export * from './stream';
export * from './task';
export * from './types';

View File

@ -1,17 +1,22 @@
import { clamp } from 'lodash-es';
import { ILoadProgressTotal, ILoadTask, LoadDataType } from './types';
import { logger } from '@motajs/common';
import { ILoadManager, ILoadManagerHooks, ILoadTask } from './types';
import {
Hookable,
HookController,
IHookController,
logger
} from '@motajs/common';
export class LoadProgressTotal<
T extends LoadDataType = LoadDataType,
R = any
> implements ILoadProgressTotal<T, R> {
export class LoadManager
extends Hookable<ILoadManagerHooks>
implements ILoadManager
{
/** 当前已经附着的加载任务 */
private readonly attached: Map<ILoadTask<T, R>, number> = new Map();
private readonly attached: Map<ILoadTask, number> = new Map();
/** 当前已经加载完毕的任务 */
readonly loadedTasks: Set<ILoadTask<T, R>> = new Set();
readonly loadedTasks: Set<ILoadTask> = new Set();
/** 当前已经添加的任务 */
readonly addedTasks: Set<ILoadTask<T, R>> = new Set();
readonly addedTasks: Set<ILoadTask> = new Set();
/** 总加载量 */
private total: number = 0;
@ -23,26 +28,37 @@ export class LoadProgressTotal<
/** 兑现当前的 `nextPromise` */
private nextResolve: () => void;
async *[Symbol.asyncIterator]() {
while (true) {
if (this.loadedTasks.size === this.addedTasks.size) {
return;
}
yield this.nextPromise;
}
}
constructor() {
super();
const { promise, resolve } = Promise.withResolvers<void>();
this.nextPromise = promise;
this.nextResolve = resolve;
}
addTask(task: ILoadTask<T, R>) {
this.addedTasks.add(task);
protected createController(
hook: Partial<ILoadManagerHooks>
): IHookController<ILoadManagerHooks> {
return new HookController(this, hook);
}
onProgress(task: ILoadTask<T, R>, loaded: number, total: number): void {
addTask(task: ILoadTask) {
this.addedTasks.add(task);
task.addHook({
onProgress: (loaded, total) => this.onProgress(task, loaded, total)
});
}
async *load(): AsyncIterable<number> {
while (true) {
if (this.loadedTasks.size === this.addedTasks.size) {
return;
}
await this.nextPromise;
yield this.loaded;
}
}
onProgress(task: ILoadTask, loaded: number, total: number): void {
if (!this.addedTasks.has(task)) {
logger.warn(95);
return;

View File

@ -0,0 +1,11 @@
import { ILoadTaskRequest, ILoadTaskStarter } from './types';
export class WebLoadStarter implements ILoadTaskStarter {
start(request: ILoadTaskRequest): Promise<Response> {
return fetch(request.url, {
method: request.method,
body: request.body,
headers: request.headers
});
}
}

View File

@ -2,26 +2,39 @@ import { sumBy } from 'lodash-es';
import {
ILoadDataTypeMap,
ILoadTask,
ILoadTaskHooks,
ILoadTaskInit,
ILoadTaskProcessor,
ILoadTaskProgress,
ILoadTaskStarter,
IResponseLike,
IResponseReaderLike,
LoadDataType,
RequestMethod
} from './types';
import {
Hookable,
HookController,
IHookController,
logger
} from '@motajs/common';
/** 文字解码 */
const loadTextDecoder = new TextDecoder();
export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
export class LoadTask<T extends LoadDataType, R>
extends Hookable<ILoadTaskHooks<T, R>>
implements ILoadTask<T, R>
{
readonly dataType: T;
readonly identifier: string;
readonly url: string | URL;
readonly processor: ILoadTaskProcessor<T, R>;
readonly progress: ILoadTaskProgress<T, R>;
readonly url: string;
readonly method?: RequestMethod;
readonly body?: BodyInit;
readonly headers?: HeadersInit;
processor: ILoadTaskProcessor<T, R> | null = null;
starter: ILoadTaskStarter | null = null;
contentLoaded: boolean = false;
loadedByte: number = 0;
totalByte: number = 0;
@ -34,12 +47,12 @@ export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
/** 加载结果 */
private loadedData: R | null = null;
constructor(init: ILoadTaskInit<T, R>) {
constructor(init: ILoadTaskInit<T>) {
super();
this.dataType = init.dataType;
this.identifier = init.identifier;
this.url = this.resolveURL(init.url);
this.processor = init.processor;
this.progress = init.progress;
this.url = init.url;
this.method = init.method;
this.body = init.body;
this.headers = init.headers;
@ -49,16 +62,26 @@ export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
this.loadResolve = resolve;
}
private resolveURL(url: string | URL) {
if (typeof url === 'string') {
return `${import.meta.env.BASE_URL}${url}`;
} else {
return url;
}
protected createController(
hook: Partial<ILoadTaskHooks<T, R>>
): IHookController<ILoadTaskHooks<T, R>> {
return new HookController(this, hook);
}
setProcessor(processor: ILoadTaskProcessor<T, R> | null): void {
this.processor = processor;
}
setStarter(starter: ILoadTaskStarter): void {
this.starter = starter;
}
/**
* 当不使用流加载时,使用此方法直接处理响应对象
* @param response 响应体对象
*/
private processUnstreamableResponse(
response: Response
response: IResponseLike
): Promise<ILoadDataTypeMap[T]> {
switch (this.dataType) {
case LoadDataType.ArrayBuffer:
@ -74,6 +97,10 @@ export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
}
}
/**
* 当使用流加载时,处理每次流加载的分块
* @param chunks 流式加载的二进制分块
*/
private processStreamChunkResponse(
chunks: Uint8Array<ArrayBuffer>[]
): ILoadDataTypeMap[T] {
@ -102,23 +129,44 @@ export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
}
}
private async processResponse(response: Response) {
const reader = response.body?.getReader();
const contentLength = response.headers.get('Content-Length') ?? '0';
const total = parseInt(contentLength, 10);
this.loadedByte = 0;
this.totalByte = total;
this.progress.onProgress(this, 0, total);
if (!reader) {
const data = await this.processUnstreamableResponse(response);
this.loadedByte = this.totalByte;
this.contentLoaded = true;
this.progress.onProgress(this, this.loadedByte, this.totalByte);
const processed = await this.processor.process(data, this);
this.loadedData = processed;
this.loadResolve(processed);
return;
/**
* 处理加载数据
* @param data 加载获取的原始数据
*/
private processData(data: ILoadDataTypeMap[T]): Promise<R> {
if (this.processor) {
return this.processor.process(data, this);
} else {
return Promise.resolve(data as R);
}
}
/**
* 使用非流式加载方式进行加载
* @param response 响应体
* @param total 加载总字节数
*/
private async *loadUnstream(
response: IResponseLike,
total: number
): AsyncGenerator<number, ILoadDataTypeMap[T]> {
const data = await this.processUnstreamableResponse(response);
this.loadedByte = this.totalByte;
this.contentLoaded = true;
this.forEachHook(hook => hook.onProgress?.(total, total));
yield total;
return data;
}
/**
* 使用流式加载方式进行加载
* @param reader 流式读取器
* @param total 加载总字节数
*/
private async *loadStream(
reader: IResponseReaderLike,
total: number
): AsyncGenerator<number, ILoadDataTypeMap[T]> {
let received = 0;
const chunks: Uint8Array<ArrayBuffer>[] = [];
while (true) {
@ -129,23 +177,70 @@ export class LoadTask<T extends LoadDataType, R> implements ILoadTask<T, R> {
}
if (done) this.contentLoaded = true;
this.loadedByte = received;
this.progress.onProgress(this, received, total);
if (done) break;
this.forEachHook(hook => hook.onProgress?.(received, total));
yield received;
if (done) {
this.loadedByte = total;
yield total;
break;
}
}
const data = this.processStreamChunkResponse(chunks);
const processed = await this.processor.process(data, this);
this.loadedData = processed;
this.loadResolve(processed);
return data;
}
async start(): Promise<void> {
const response = await fetch(this.url, {
method: this.method,
body: this.body,
headers: this.headers
});
this.processResponse(response);
return;
/**
* 执行加载任务,自动决定使用流式加载或非流式加载
* @param response 响应体
* @param total 加载总字节数
*/
private loadTask(
response: IResponseLike,
total: number
): AsyncGenerator<number, ILoadDataTypeMap[T]> {
const reader = response.body?.getReader();
if (reader) {
return this.loadStream(reader, total);
} else {
return this.loadUnstream(response, total);
}
}
/**
* 处理响应体,并进行加载工作
* @param response 响应体
*/
private async *processResponse(
response: IResponseLike
): AsyncIterable<number> {
const contentLength = response.headers.get('Content-Length') ?? '0';
const total = parseInt(contentLength, 10);
this.loadedByte = 0;
this.totalByte = total;
this.forEachHook(hook => hook.onLoadStart?.(total));
this.forEachHook(hook => hook.onProgress?.(0, total));
const iter = this.loadTask(response, total);
const data = yield* iter;
const processed = await this.processData(data);
this.loadedData = processed;
this.loadResolve(processed);
this.forEachHook(hook => hook.onLoadEnd?.(processed, data, total));
}
async *start(): AsyncIterable<number> {
if (this.loadedData) return;
if (!this.starter) {
logger.error(30);
return;
}
const response = await this.starter.start(this);
yield* this.processResponse(response);
// 加载完毕后就可以清空钩子了
this.forEachHook(hook => hook).forEach(v => this.removeHook(v));
}
loaded(): Promise<R> {

View File

@ -1,5 +1,7 @@
//#region 流传输
import { IHookable, IHookBase } from '@motajs/common';
export interface IStreamController {
/** 当前是否正在加载 */
readonly loading: boolean;
@ -85,33 +87,6 @@ export const enum LoadDataType {
JSON
}
export interface ILoadDataTypeMap {
[LoadDataType.ArrayBuffer]: ArrayBuffer;
[LoadDataType.Uint8Array]: Uint8Array<ArrayBuffer>;
[LoadDataType.Blob]: Blob;
[LoadDataType.Text]: string;
[LoadDataType.JSON]: any;
}
export interface ILoadTaskProcessor<T extends LoadDataType, R> {
/**
* 处理加载内容
* @param response 处理前加载结果
* @param task 加载任务对象
*/
process(response: ILoadDataTypeMap[T], task: ILoadTask<T, R>): Promise<R>;
}
export interface ILoadTaskProgress<T extends LoadDataType, R> {
/**
* 更新加载进度
* @param task 加载任务对象
* @param loaded 已加载的字节数
* @param total 文件总计字节数,如果此值为零说明无法读取到 `Content-Length`
*/
onProgress(task: ILoadTask<T, R>, loaded: number, total: number): void;
}
export const enum RequestMethod {
GET = 'GET',
POST = 'POST',
@ -124,17 +99,17 @@ export const enum RequestMethod {
PATCH = 'PATCH'
}
export interface ILoadTaskInit<T extends LoadDataType, R> {
/** 请求响应格式 */
readonly dataType: T;
/** 加载任务标识符 */
readonly identifier: string;
export interface ILoadDataTypeMap {
[LoadDataType.ArrayBuffer]: ArrayBuffer;
[LoadDataType.Uint8Array]: Uint8Array<ArrayBuffer>;
[LoadDataType.Blob]: Blob;
[LoadDataType.Text]: string;
[LoadDataType.JSON]: any;
}
export interface ILoadTaskRequest {
/** 加载目标 URL */
readonly url: string | URL;
/** 加载的处理对象,用于处理加载结果等 */
readonly processor: ILoadTaskProcessor<T, R>;
/** 加载进度对象,用于监控加载进度 */
readonly progress: ILoadTaskProgress<T, R>;
readonly url: string;
/** 请求模式 */
readonly method?: RequestMethod;
/** 请求体 */
@ -143,21 +118,141 @@ export interface ILoadTaskInit<T extends LoadDataType, R> {
readonly headers?: HeadersInit;
}
export interface ILoadTask<T extends LoadDataType, R> extends ILoadTaskInit<
T,
R
> {
export interface ILoadTaskProcessor<T extends LoadDataType, R> {
/**
* 处理加载内容
* @param response 处理前加载结果
* @param task 加载任务对象
*/
process(response: ILoadDataTypeMap[T], task: ILoadTask<T, R>): Promise<R>;
}
export interface IResponseReaderLike {
/**
* 进行下一个分块的读取
*/
read(): Promise<ReadableStreamReadResult<Uint8Array<ArrayBuffer>>>;
}
export interface IResponseBodyLike {
/**
* 获取流式读取器
*/
getReader(): ReadableStreamDefaultReader;
}
export interface IResponseHeadersLike {
/**
* 获取指定响应头的内容
* @param name 响应头字段名称
*/
get(name: string): string | null;
}
export interface IResponseLike {
/** 请求地址 */
readonly url: string;
/** 响应体 */
readonly body: IResponseBodyLike | null;
/** 响应头 */
readonly headers: IResponseHeadersLike;
/**
* 输出为 `ArrayBuffer`
*/
arrayBuffer(): Promise<ArrayBuffer>;
/**
* 输出为 `Blob`
*/
blob(): Promise<Blob>;
/**
* 输出为 `Uint8Array`
*/
bytes(): Promise<Uint8Array<ArrayBuffer>>;
/**
* 输出为 `JSON`
*/
json(): Promise<any>;
/**
* 输出为纯文本
*/
text(): Promise<string>;
}
export interface ILoadTaskStarter {
/**
* 进行加载请求,获取 `Response` 对象
* @param request 请求配置
*/
start(request: ILoadTaskRequest): Promise<IResponseLike>;
}
export interface ILoadTaskInit<
T extends LoadDataType
> extends ILoadTaskRequest {
/** 请求响应格式 */
readonly dataType: T;
/** 加载任务标识符 */
readonly identifier: string;
}
export interface ILoadTaskHooks<T extends LoadDataType, R> extends IHookBase {
/**
* 当加载开始时触发
* @param total 加载总字节数
*/
onLoadStart?(total: number): void;
/**
* 当加载进度变动时触发
* @param loaded 已加载的字节数
* @param total 加载总字节数
*/
onProgress?(loaded: number, total: number): void;
/**
* 当加载完成时触发
* @param data 经过处理后的加载内容
* @param raw 加载完毕时获取的原始数据
* @param total 加载总字节数
*/
onLoadEnd?(data: R, raw: ILoadDataTypeMap[T], total: number): void;
}
export interface ILoadTask<T extends LoadDataType = LoadDataType, R = any>
extends ILoadTaskInit<T>, IHookable<ILoadTaskHooks<T, R>> {
/** 当前是否加载完毕 */
readonly contentLoaded: boolean;
/** 已经加载的字节数 */
readonly loadedByte: number;
/** 该加载任务的总体字节数 */
readonly totalByte: number;
/** 当前任务使用的数据处理器 */
readonly processor: ILoadTaskProcessor<T, R> | null;
/** 当前任务使用的加载启动器 */
readonly starter: ILoadTaskStarter | null;
/**
* 设置当前任务使用的数据处理器
* @param processor 数据处理器
*/
setProcessor(processor: ILoadTaskProcessor<T, R> | null): void;
/**
* 设置当前任务使用的加载启动器
* @param starter 加载启动器
*/
setStarter(starter: ILoadTaskStarter): void;
/**
* 开始此加载计划,返回一个 `Promise`,当得到服务器的响应后兑现
* @returns 异步迭代器,当加载进度变动时进行一次迭代,迭代值为当前加载的字节数
*/
start(): Promise<void>;
start(): AsyncIterable<number>;
/**
* 返回一个 `Promise`,当本计划加载完毕后兑现,兑现结果是加载结果
@ -170,29 +265,43 @@ export interface ILoadTask<T extends LoadDataType, R> extends ILoadTaskInit<
getLoadedData(): R | null;
}
//#endregion
//#region 内置组件
export interface ILoadProgressTotal<
T extends LoadDataType = LoadDataType,
R = any
> extends ILoadTaskProgress<T, R> {
/** 已经添加的加载任务对象 */
readonly addedTasks: Set<ILoadTask<T, R>>;
/** 当前已经加载完毕的任务对象 */
readonly loadedTasks: Set<ILoadTask<T, R>>;
export interface ILoadManagerHooks extends IHookBase {
/**
* 当开始加载时触发
*/
onStartLoad?(): void;
/**
* 迭代加载进度,当 `yield` 的值被兑现时,说明加载进度更新
* 当加载进度发生变化时触发
* @param loaded 已加载的字节数
* @param total 加载总字节数,此数值可能不准确,因为必须等所有的加载全部连接完成时才能够得知总字节数
*/
[Symbol.asyncIterator](): AsyncGenerator<void, void, void>;
onProgress?(loaded: number, total: number): void;
/**
* 当所有加载任务完成时触发
* @param total 加载的总字节数
*/
onLoadEnd?(total: number): void;
}
export interface ILoadManager extends IHookable<ILoadManagerHooks> {
/** 已经添加的加载任务对象 */
readonly addedTasks: Set<ILoadTask>;
/** 当前已经加载完毕的任务对象 */
readonly loadedTasks: Set<ILoadTask>;
/**
* 向该进度监听器添加加载任务对象
* @param task 加载任务对象
*/
addTask(task: ILoadTask<T, R>): void;
addTask(task: ILoadTask): void;
/**
* 开始执行加载
* @returns 加载的异步迭代器,当加载进度更新时迭代,迭代值为当前已加载的总字节数
*/
load(): AsyncIterable<number>;
/**
* 获取总体已加载的字节数