You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
179 lines
7.4 KiB
179 lines
7.4 KiB
import { sdkConfig } from 'db://assets/script/sdk/sdkConfig';
|
|
import { GBundle, getBootResPakage } from 'db://assets/script/game/ConfigRes';
|
|
import { LoadScheduler } from '../res/LoadScheduler';
|
|
import { BundleLifecycle } from '../res/BundleLifecycle';
|
|
import { PreloadPredictor } from '../res/PreloadPredictor';
|
|
import { BattleBackgroundLoadGate } from '../res/BattleBackgroundLoadGate';
|
|
import { ResourcePhase, ResourcePhaseGate } from '../res/ResourcePhaseGate';
|
|
import { MemoryProfile } from 'db://assets/script/game/MemoryProfile';
|
|
import { DeviceCapability } from 'db://assets/script/game/DeviceCapability';
|
|
import { ThermalGovernor } from 'db://assets/script/game/ThermalGovernor';
|
|
import { AppLifecycle } from 'db://assets/script/game/AppLifecycle';
|
|
import { PerfReport } from 'db://assets/script/game/PerfReport';
|
|
import { fetchHotUpdateTables } from 'db://assets/script/game/ConfigHotUpdate';
|
|
|
|
export interface BootPipelineCallbacks {
|
|
/** 进度增量(Boot 各阶段累加到 GameLaunch 进度条) */
|
|
onProgress?: (delta: number, weight?: number) => void;
|
|
/** Boot 标题文案 */
|
|
onTitle?: (text: string) => void;
|
|
/** Boot 分包资源加载完成(允许部分失败,见日志) */
|
|
onBootAssetsReady?: () => void;
|
|
/** SDK 与 gg.data.init 完成 */
|
|
onSdkAndDataReady?: () => void;
|
|
}
|
|
|
|
/**
|
|
* 启动编排:核心表 / 最小 Boot 资源 / SDK 网络初始化三路并行;
|
|
* gg.data.init 在「核心表 + SDK」均就绪后执行(存档合并依赖双方结果)。
|
|
* Boot 只拉 first/comm/home;icons 在三分包落地后静默开下;BootRes 完成后并行点火战斗分包。
|
|
*/
|
|
export class BootPipeline {
|
|
/** Boot 分包资源是否已就绪 */
|
|
private _bootAssetsReady = false;
|
|
/** SDK 与存档是否已就绪 */
|
|
private _sdkAndDataReady = false;
|
|
private _bootStartTime = 0;
|
|
/** SDK init 返回的远端用户数据 */
|
|
private _sdkUserData: unknown = null;
|
|
|
|
/** Boot 分包资源是否已就绪 */
|
|
get bootAssetsReady(): boolean { return this._bootAssetsReady; }
|
|
/** SDK 与存档是否已就绪 */
|
|
get sdkAndDataReady(): boolean { return this._sdkAndDataReady; }
|
|
|
|
/**
|
|
* 启动主阶段:并行加载核心表、Boot 资源、SDK;数据 init 在表+SDK 后执行。
|
|
* @param callbacks Boot 进度与就绪回调
|
|
*/
|
|
async start(callbacks?: BootPipelineCallbacks): Promise<void> {
|
|
this._bootStartTime = Date.now();
|
|
LoadScheduler.getInstance().start();
|
|
AppLifecycle.install();
|
|
|
|
callbacks?.onTitle?.('正在更新资源...');
|
|
|
|
const tablesPromise = this._loadBootTables(callbacks);
|
|
const assetsPromise = this._loadBootPhase(callbacks);
|
|
const sdkPromise = this._initSdkNetwork();
|
|
|
|
await Promise.all([tablesPromise, sdkPromise]);
|
|
await this._finishDataInit(callbacks);
|
|
await assetsPromise;
|
|
|
|
sdkConfig.resLoadFinishTime = Date.now() - this._bootStartTime;
|
|
}
|
|
|
|
/** 进游戏后延迟阶段:机型探测、内存档位、预载预测等 */
|
|
startDeferredPhase(): void {
|
|
void this._startDeferredPhaseAsync();
|
|
}
|
|
|
|
private async _startDeferredPhaseAsync(): Promise<void> {
|
|
await DeviceCapability.ensurePlatformProbe();
|
|
MemoryProfile.resolve();
|
|
MemoryProfile.applyLoadPolicy();
|
|
ThermalGovernor.install();
|
|
PerfReport.reportBootTier();
|
|
BattleBackgroundLoadGate.install();
|
|
ResourcePhaseGate.getInstance().setPhase(ResourcePhase.Boot);
|
|
void this._loadDeferredTables();
|
|
PreloadPredictor.getInstance().start();
|
|
}
|
|
|
|
/** 战斗/大厅核心表 + 弹窗依赖表 */
|
|
private _loadBootTables(callbacks?: BootPipelineCallbacks): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
gg.data.table.loadBootTables((c, t) => {
|
|
callbacks?.onProgress?.(c / t * 0.25, 0.25);
|
|
}, () => {
|
|
resolve();
|
|
});
|
|
});
|
|
}
|
|
|
|
private _loadDeferredTables(): void {
|
|
gg.data.table.loadDeferredTables(null, () => {
|
|
});
|
|
}
|
|
|
|
/** Boot 分包剩余数 */
|
|
private _bootBundleLeft = 0;
|
|
/** Boot 阶段 resolve */
|
|
private _bootPhaseResolve: (() => void) | null = null;
|
|
/** Boot 阶段回调 */
|
|
private _bootPhaseCallbacks: BootPipelineCallbacks | undefined;
|
|
|
|
private _loadBootPhase(callbacks?: BootPipelineCallbacks): Promise<void> {
|
|
return new Promise((resolve) => {
|
|
this._bootPhaseResolve = resolve;
|
|
this._bootPhaseCallbacks = callbacks;
|
|
const names = [GBundle.First, GBundle.Comm, GBundle.Home];
|
|
this._bootBundleLeft = names.length;
|
|
for (let i = 0; i < names.length; i++) {
|
|
this._loadOneBootBundle(names[i]);
|
|
}
|
|
});
|
|
}
|
|
|
|
/** 加载单个 Boot 分包(经 BundleLifecycle,避免与 ensureLoaded 同名双飞) */
|
|
private _loadOneBootBundle(name: string): void {
|
|
void BundleLifecycle.getInstance().ensureLoaded(name).then((bundle) => {
|
|
if (!bundle) {
|
|
console.error('[BootPipeline] boot bundle', name);
|
|
}
|
|
this._onOneBootBundle();
|
|
});
|
|
}
|
|
|
|
private _onOneBootBundle = (): void => {
|
|
this._bootBundleLeft--;
|
|
if (this._bootBundleLeft > 0) return;
|
|
this._bootPhaseCallbacks?.onProgress?.(0.1, 0.1);
|
|
PreloadPredictor.getInstance().startEarlyIconsDownload();
|
|
gg.res.loadResPakg(getBootResPakage(), this._onBootResProgress, this._onBootResComplete);
|
|
};
|
|
|
|
private _onBootResProgress = (c: number, t: number): void => {
|
|
this._bootPhaseCallbacks?.onProgress?.(c / t * 0.4, 0.4);
|
|
};
|
|
|
|
private _onBootResComplete = (result: { ok: boolean; failed: string[] }): void => {
|
|
if (!result.ok) {
|
|
PerfReport.reportLoadFail('boot', result.failed.length);
|
|
console.error('[BootPipeline] Boot 资源部分失败', result.failed);
|
|
}
|
|
this._bootAssetsReady = true;
|
|
PreloadPredictor.getInstance().startEarlyBattleBundleDownload();
|
|
this._bootPhaseCallbacks?.onBootAssetsReady?.();
|
|
const resolve = this._bootPhaseResolve;
|
|
this._bootPhaseResolve = null;
|
|
this._bootPhaseCallbacks = undefined;
|
|
resolve?.();
|
|
};
|
|
|
|
/** SDK 网络/平台初始化(不依赖配表与 Boot 资源,可与加载并行) */
|
|
private async _initSdkNetwork(): Promise<void> {
|
|
const startTime2 = Date.now();
|
|
this._sdkUserData = await gg.sdk.init();
|
|
const uid = await ZYSDK.ZYSDK.getUserId();
|
|
gg.data.doc.uid = uid;
|
|
gg.sdk.sdk30r?.setOpenId(uid);
|
|
sdkConfig.sdkLoadFinishTime = Date.now() - startTime2;
|
|
}
|
|
|
|
/** 核心表与 SDK 均就绪后合并存档并标记数据层可进游戏 */
|
|
private async _finishDataInit(callbacks?: BootPipelineCallbacks): Promise<void> {
|
|
// 先取 SDK uid,init 重建 Doc 后回填,避免被 initDoc 清空
|
|
const uid = await ZYSDK.ZYSDK.getUserId();
|
|
gg.data.init(sdkConfig.config.pointData.gid, this._sdkUserData);
|
|
if (!gg.data.doc.uid && uid != null && uid !== '') {
|
|
gg.data.doc.uid = String(uid);
|
|
}
|
|
// 不阻塞进游戏:本地表已就绪,远端成功后再 applyHotUpdateTables 覆盖
|
|
void fetchHotUpdateTables();
|
|
callbacks?.onProgress?.(0.25, 0.25);
|
|
this._sdkAndDataReady = true;
|
|
callbacks?.onSdkAndDataReady?.();
|
|
}
|
|
}
|
|
|