From 8a6620cf8fb5675ea0536d3cd4b5de96382e37e4 Mon Sep 17 00:00:00 2001 From: han_han9 Date: Fri, 31 Oct 2025 07:58:06 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=9B=B4=E6=96=B0resManager?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/max-studio/core/res/ResManager.ts | 1402 +++++++++-------- 1 file changed, 754 insertions(+), 648 deletions(-) diff --git a/extensions/max-studio/assets/max-studio/core/res/ResManager.ts b/extensions/max-studio/assets/max-studio/core/res/ResManager.ts index e2e19b6..3b874ea 100644 --- a/extensions/max-studio/assets/max-studio/core/res/ResManager.ts +++ b/extensions/max-studio/assets/max-studio/core/res/ResManager.ts @@ -1,683 +1,789 @@ -import { Asset, assetManager, AssetManager, resources } from "cc"; -import LogUtils from "../utils/LogUtils"; -import { singleton, Singleton } from "../Singleton"; +// import { Asset, assetManager, AssetManager, resources } from "cc"; +// import LogUtils from "../utils/LogUtils"; +// import { singleton, Singleton } from "../Singleton"; +// import { Bundle } from "typescript"; -/** - * 加载状态枚举 - */ -export enum LoadState { - NONE = 0, - LOADING = 1, - LOADED = 2, - FAILED = 3, -} +import { Asset, assetManager, AssetManager, Constructor } from "cc"; +import { Singleton } from "../Singleton"; +import { StringUtils } from "../utils/StringUtils"; -/** - * 加载任务接口 - */ -interface LoadTask { - url: string; - type: "bundle" | "asset"; - bundleName?: string; - assetPath?: string; - assetType?: typeof Asset; - resolve: (result: any) => void; - reject: (error: Error) => void; -} +// /** +// * 加载状态枚举 +// */ +// export enum LoadState { +// NONE = 0, +// LOADING = 1, +// LOADED = 2, +// FAILED = 3, +// } -/** - * Bundle信息接口 - */ -interface BundleInfo { - bundle: AssetManager.Bundle; - state: LoadState; - refCount: number; - isRemote: boolean; -} +// /** +// * 加载任务接口 +// */ +// interface LoadTask { +// url: string; +// type: "bundle" | "asset"; +// bundleName?: string; +// assetPath?: string; +// promise?: Promise; +// assetType?: typeof Asset; +// resolve: (result: any) => void; +// reject: (error: Error) => void; +// } -/** - * 资源信息接口 - */ -interface AssetInfo { - asset: Asset; - state: LoadState; - refCount: number; - bundleName: string; -} +// /** +// * Bundle信息接口 +// */ +// interface BundleInfo { +// bundle: AssetManager.Bundle; +// state: LoadState; +// refCount: number; +// isRemote: boolean; +// } -/** - * 资源管理器 - */ -@singleton() -export class ResManager extends Singleton { - private static readonly TAG = "ResManager"; - private static readonly DEFAULT_BUNDLE = "resources"; +// /** +// * 资源信息接口 +// */ +// interface AssetInfo { +// asset: T; +// state: LoadState; +// refCount: number; +// bundleName: string; +// } - // Bundle缓存 - private bundleCache = new Map(); +// /** +// * 资源管理器 +// */ +// @singleton() +// export class ResManager extends Singleton { +// private static readonly TAG = "ResManager"; +// private static readonly DEFAULT_BUNDLE = "resources"; - // 资源缓存 - private assetCache = new Map(); +// // Bundle缓存 +// private bundleCache = new Map(); +// private loadingBundle = new Map(); - // 加载队列 - private loadQueue: LoadTask[] = []; +// // 资源缓存 +// private assetCache = new Map>(); +// private loadingAsset = new Map(); - // 当前正在加载的任务数量 - private loadingCount = 0; +// // 加载队列 +// private loadQueue: LoadTask[] = []; - // 最大并发加载数量 - private maxConcurrentLoads = 30; +// // 当前正在加载的任务数量 +// private loadingCount = 0; - // 是否正在处理队列 - private isProcessingQueue = false; +// // 最大并发加载数量 +// private maxConcurrentLoads = 30; - // Bundle加载等待队列 - private bundleWaitQueue = new Map>(); +// // 是否正在处理队列 +// private isProcessingQueue = false; - // 资源加载等待队列 - private assetWaitQueue = new Map>(); +// // Bundle加载等待队列 +// private bundleWaitQueue = new Map>(); - protected async onInit() { - this.initDefaultBundle(); - } +// // 资源加载等待队列 +// private assetWaitQueue = new Map>(); - /** - * 初始化默认Bundle(resources) - */ - private initDefaultBundle(): void { - this.bundleCache.set(ResManager.DEFAULT_BUNDLE, { - bundle: resources, - state: LoadState.LOADED, - refCount: 1, - isRemote: false, - }); - LogUtils.info(ResManager.TAG, "初始化默认Bundle: resources"); - } +// protected async onInit() { +// this.initDefaultBundle(); +// } - /** - * 设置最大并发加载数量 - * @param count 最大并发数 - */ - public setMaxConcurrentLoads(count: number): void { - this.maxConcurrentLoads = Math.max(1, count); - LogUtils.info(ResManager.TAG, `设置最大并发加载数量: ${this.maxConcurrentLoads}`); - } +// /** +// * 初始化默认Bundle(resources) +// */ +// private initDefaultBundle(): void { +// this.bundleCache.set(ResManager.DEFAULT_BUNDLE, { +// bundle: resources, +// state: LoadState.LOADED, +// refCount: 1, +// isRemote: false, +// }); +// LogUtils.info(ResManager.TAG, "初始化默认Bundle: resources"); +// } - /** - * 加载远端Bundle - * @param bundleName Bundle名称 - * @param url Bundle的远端URL - * @returns Promise - */ - public async loadRemoteBundle(bundleName: string, url: string): Promise { - const cachedBundle = this.bundleCache.get(bundleName); +// /** +// * 设置最大并发加载数量 +// * @param count 最大并发数 +// */ +// public setMaxConcurrentLoads(count: number): void { +// this.maxConcurrentLoads = Math.max(1, count); +// LogUtils.info(ResManager.TAG, `设置最大并发加载数量: ${this.maxConcurrentLoads}`); +// } - // 检查缓存 - if (cachedBundle) { - if (cachedBundle.state === LoadState.LOADED) { - cachedBundle.refCount++; - LogUtils.debug(ResManager.TAG, `使用缓存Bundle: ${bundleName}`); - return cachedBundle.bundle; - } else if (cachedBundle.state === LoadState.LOADING) { - // 正在加载中,等待加载完成 - return this.waitForBundleLoad(bundleName); +// /** +// * 加载远端Bundle +// * @param bundleName Bundle名称 +// * @param url Bundle的远端URL +// * @returns Promise +// */ +// public async loadRemoteBundle(bundleName: string, url: string): Promise { +// // 检查缓存 +// if (this.bundleCache.has(bundleName)) { +// const cachedBundle = this.bundleCache.get(bundleName); +// cachedBundle.refCount++; +// return cachedBundle.bundle; +// } +// if (this.loadingBundle.has(bundleName)) { +// let bundle = (await this.loadingBundle.get(bundleName).promise) as AssetManager.Bundle; +// // bundle. +// // return (await bundle.promise) as AssetManager.Bundle; +// } + +// // 标记为加载中 +// this.bundleCache.set(bundleName, { +// bundle: null, +// state: LoadState.LOADING, +// refCount: 1, +// isRemote: true, +// }); + +// return new Promise((resolve, reject) => { +// const task: LoadTask = { +// url, +// type: "bundle", +// bundleName, +// resolve, +// reject, +// }; + +// this.addToQueue(task); +// }); +// } + +// /** +// * 加载本地Bundle +// * @param bundleName Bundle名称 +// * @returns Promise +// */ +// public async loadLocalBundle(bundleName: string): Promise { +// const cachedBundle = this.bundleCache.get(bundleName); + +// // 检查缓存 +// if (cachedBundle) { +// if (cachedBundle.state === LoadState.LOADED) { +// cachedBundle.refCount++; +// LogUtils.debug(ResManager.TAG, `使用缓存Bundle: ${bundleName}`); +// return cachedBundle.bundle; +// } else if (cachedBundle.state === LoadState.LOADING) { +// // 正在加载中,等待加载完成 +// return this.waitForBundleLoad(bundleName); +// } +// } + +// // 标记为加载中 +// this.bundleCache.set(bundleName, { +// bundle: null as any, +// state: LoadState.LOADING, +// refCount: 1, +// isRemote: false, +// }); + +// return new Promise((resolve, reject) => { +// const task: LoadTask = { +// url: bundleName, // 本地Bundle使用bundleName作为url +// type: "bundle", +// bundleName, +// resolve, +// reject, +// }; + +// this.addToQueue(task); +// }); +// } + +// /** +// * 加载资源 +// * @param assetPath 资源路径 +// * @param assetType 资源类型 +// * @param bundleName Bundle名称,不传则默认为resources +// * @returns Promise +// */ +// public async loadAsset( +// assetPath: string, +// assetType: typeof Asset, +// bundleName?: string, +// ): Promise { +// const targetBundle = bundleName || ResManager.DEFAULT_BUNDLE; +// const cacheKey = `${targetBundle}/${assetPath}`; +// const cachedAsset = this.assetCache.get(cacheKey); + +// // 检查缓存 +// if (cachedAsset) { +// if (cachedAsset.state === LoadState.LOADED) { +// cachedAsset.refCount++; +// LogUtils.debug(ResManager.TAG, `使用缓存资源: ${cacheKey}`); +// return cachedAsset.asset as T; +// } else if (cachedAsset.state === LoadState.LOADING) { +// // 正在加载中,等待加载完成 +// return this.waitForAssetLoad(cacheKey); +// } +// } + +// // 确保Bundle已加载 +// await this.ensureBundleLoaded(targetBundle); + +// // 标记为加载中 +// this.assetCache.set(cacheKey, { +// asset: null, +// state: LoadState.LOADING, +// refCount: 1, +// bundleName: targetBundle, +// }); + +// return new Promise((resolve, reject) => { +// const task: LoadTask = { +// url: cacheKey, +// type: "asset", +// bundleName: targetBundle, +// assetPath, +// assetType, +// resolve, +// reject, +// }; + +// this.addToQueue(task); +// }); +// } + +// /** +// * 确保Bundle已加载 +// * @param bundleName Bundle名称 +// */ +// private async ensureBundleLoaded(bundleName: string): Promise { +// const bundleInfo = this.bundleCache.get(bundleName); + +// if (!bundleInfo || bundleInfo.state === LoadState.NONE) { +// // Bundle不存在或状态为NONE,尝试加载本地Bundle +// LogUtils.info(ResManager.TAG, `Bundle ${bundleName} 不存在或未加载,尝试加载本地Bundle`); +// try { +// await this.loadLocalBundle(bundleName); +// } catch (err) { +// LogUtils.error( +// ResManager.TAG, +// `Bundle ${bundleName} 本地加载失败,请检查Bundle是否存在或调用 loadRemoteBundle 加载远程Bundle`, +// err, +// ); +// } +// return; +// } + +// if (bundleInfo.state === LoadState.FAILED) { +// throw new Error(`Bundle ${bundleName} 加载失败`); +// } + +// if (bundleInfo.state === LoadState.LOADING) { +// // Bundle正在加载中,等待加载完成 +// LogUtils.info(ResManager.TAG, `Bundle ${bundleName} 正在加载中,等待加载完成`); +// await this.waitForBundleLoad(bundleName); +// } +// } + +// /** +// * 释放Bundle +// * @param bundleName Bundle名称 +// */ +// public releaseBundle(bundleName: string): void { +// // 不允许释放默认Bundle +// if (bundleName === ResManager.DEFAULT_BUNDLE) { +// LogUtils.warn(ResManager.TAG, "不允许释放默认Bundle: resources"); +// return; +// } + +// const bundleInfo = this.bundleCache.get(bundleName); +// if (!bundleInfo) { +// LogUtils.warn(ResManager.TAG, `Bundle ${bundleName} 不存在`); +// return; +// } + +// bundleInfo.refCount--; +// LogUtils.debug(ResManager.TAG, `Bundle ${bundleName} 引用计数: ${bundleInfo.refCount}`); + +// if (bundleInfo.refCount <= 0) { +// // 释放Bundle中的所有资源 +// this.releaseAssetsInBundle(bundleName); + +// // 释放Bundle +// if (bundleInfo.bundle && bundleInfo.isRemote) { +// assetManager.removeBundle(bundleInfo.bundle); +// } + +// this.bundleCache.delete(bundleName); +// LogUtils.info(ResManager.TAG, `释放Bundle: ${bundleName}`); +// } +// } + +// /** +// * 释放资源 +// * @param assetPath 资源路径 +// * @param bundleName Bundle名称,不传则默认为resources +// */ +// public releaseAsset(assetPath: string, bundleName?: string): void { +// const targetBundle = bundleName || ResManager.DEFAULT_BUNDLE; +// const cacheKey = `${targetBundle}/${assetPath}`; +// const assetInfo = this.assetCache.get(cacheKey); + +// if (!assetInfo) { +// LogUtils.warn(ResManager.TAG, `资源 ${cacheKey} 不存在`); +// return; +// } + +// assetInfo.refCount--; +// LogUtils.debug(ResManager.TAG, `资源 ${cacheKey} 引用计数: ${assetInfo.refCount}`); + +// if (assetInfo.refCount <= 0) { +// if (assetInfo.asset) { +// assetInfo.asset.decRef(); +// } + +// this.assetCache.delete(cacheKey); +// LogUtils.info(ResManager.TAG, `释放资源: ${cacheKey}`); +// } +// } + +// /** +// * 清理所有缓存(保留默认Bundle) +// */ +// public clearAll(): void { +// // 释放所有资源 +// for (const [, assetInfo] of this.assetCache) { +// if (assetInfo.asset) { +// assetInfo.asset.decRef(); +// } +// } +// this.assetCache.clear(); + +// // 释放所有远端Bundle +// for (const [key, bundleInfo] of this.bundleCache) { +// if (key !== ResManager.DEFAULT_BUNDLE && bundleInfo.bundle && bundleInfo.isRemote) { +// assetManager.removeBundle(bundleInfo.bundle); +// } +// } + +// // 清空Bundle缓存,但保留默认Bundle +// const defaultBundle = this.bundleCache.get(ResManager.DEFAULT_BUNDLE); +// this.bundleCache.clear(); +// if (defaultBundle) { +// this.bundleCache.set(ResManager.DEFAULT_BUNDLE, defaultBundle); +// } + +// // 清空加载队列 +// this.loadQueue = []; +// this.loadingCount = 0; +// this.bundleWaitQueue.clear(); +// this.assetWaitQueue.clear(); + +// LogUtils.info(ResManager.TAG, "清理所有缓存完成"); +// } + +// /** +// * 获取缓存状态信息 +// */ +// public getCacheInfo(): { +// bundleCount: number; +// assetCount: number; +// queueLength: number; +// loadingCount: number; +// } { +// return { +// bundleCount: this.bundleCache.size, +// assetCount: this.assetCache.size, +// queueLength: this.loadQueue.length, +// loadingCount: this.loadingCount, +// }; +// } + +// /** +// * 添加任务到队列 +// * @param task 加载任务 +// */ +// private addToQueue(task: LoadTask): void { +// this.loadQueue.push(task); +// void this.processQueue(); +// } + +// /** +// * 处理加载队列 +// */ +// private async processQueue(): Promise { +// if (this.isProcessingQueue || this.loadingCount >= this.maxConcurrentLoads) { +// return; +// } + +// this.isProcessingQueue = true; + +// while (this.loadQueue.length > 0 && this.loadingCount < this.maxConcurrentLoads) { +// const task = this.loadQueue.shift(); +// if (!task) break; + +// this.loadingCount++; +// this.executeTask(task) +// .then(() => { +// this.loadingCount--; +// void this.processQueue(); +// }) +// .catch((err) => { +// LogUtils.error(ResManager.TAG, `队列任务执行失败: ${task.url}`, err); +// this.loadingCount--; +// void this.processQueue(); +// }); +// } + +// this.isProcessingQueue = false; +// } + +// /** +// * 执行加载任务 +// * @param task 加载任务 +// */ +// private async executeTask(task: LoadTask): Promise { +// try { +// await (task.type === "bundle" ? this.loadBundleTask(task) : this.loadAssetTask(task)); +// } catch (err) { +// LogUtils.error(ResManager.TAG, `加载任务失败: ${task.url}`, err); +// task.reject(err as Error); +// } +// } + +// /** +// * 执行Bundle加载任务 +// * @param task 加载任务 +// */ +// private async loadBundleTask(task: LoadTask): Promise { +// const { url, bundleName, resolve, reject } = task; +// if (!bundleName) { +// reject(new Error("Bundle名称不能为空")); +// return; +// } + +// const bundleInfo = this.bundleCache.get(bundleName); +// const isRemote = bundleInfo?.isRemote ?? false; + +// // 根据是否为远端Bundle选择不同的加载方式 +// if (isRemote) { +// // 远端Bundle加载 +// assetManager.loadBundle(url, (err, bundle) => { +// this.handleBundleLoadResult(bundleName, err, bundle, resolve, reject); +// }); +// } else { +// // 本地Bundle加载 +// assetManager.loadBundle(bundleName, (err, bundle) => { +// this.handleBundleLoadResult(bundleName, err, bundle, resolve, reject); +// }); +// } +// } + +// /** +// * 处理Bundle加载结果 +// * @param bundleName Bundle名称 +// * @param err 错误信息 +// * @param bundle Bundle对象 +// * @param resolve Promise resolve函数 +// * @param reject Promise reject函数 +// */ +// private handleBundleLoadResult( +// bundleName: string, +// err: Error | null, +// bundle: AssetManager.Bundle, +// resolve: (result: AssetManager.Bundle) => void, +// reject: (error: Error) => void, +// ): void { +// const bundleInfo = this.bundleCache.get(bundleName); + +// if (err) { +// LogUtils.error(ResManager.TAG, `Bundle加载失败: ${bundleName}`, err); +// if (bundleInfo) { +// bundleInfo.state = LoadState.FAILED; +// } + +// // 通知所有等待的任务 +// this.notifyBundleWaiters(bundleName, false, new Error(`Bundle加载失败: ${err.message}`)); +// reject(new Error(`Bundle加载失败: ${err.message}`)); +// return; +// } + +// if (bundleInfo) { +// bundleInfo.bundle = bundle; +// bundleInfo.state = LoadState.LOADED; +// } + +// LogUtils.info(ResManager.TAG, `Bundle加载成功: ${bundleName}`); + +// // 通知所有等待的任务 +// this.notifyBundleWaiters(bundleName, true, bundle); +// resolve(bundle); +// } + +// /** +// * 执行资源加载任务 +// * @param task 加载任务 +// */ +// private async loadAssetTask(task: LoadTask): Promise { +// const { url, bundleName, assetPath, assetType, resolve, reject } = task; +// if (!bundleName || !assetPath || !assetType) { +// reject(new Error("资源加载参数不完整")); +// return; +// } + +// const bundleInfo = this.bundleCache.get(bundleName); + +// if (!bundleInfo?.bundle) { +// reject(new Error(`Bundle ${bundleName} 不存在`)); +// return; +// } + +// bundleInfo.bundle.load(assetPath, assetType, (err, asset) => { +// const assetInfo = this.assetCache.get(url); + +// if (err) { +// LogUtils.error(ResManager.TAG, `资源加载失败: ${url}`, err); +// if (assetInfo) { +// assetInfo.state = LoadState.FAILED; +// } + +// // 通知所有等待的任务 +// this.notifyAssetWaiters(url, false, new Error(`资源加载失败: ${err.message}`)); +// reject(new Error(`资源加载失败: ${err.message}`)); +// return; +// } + +// if (assetInfo) { +// assetInfo.asset = asset; +// assetInfo.state = LoadState.LOADED; +// } + +// LogUtils.info(ResManager.TAG, `资源加载成功: ${url}`); + +// // 通知所有等待的任务 +// this.notifyAssetWaiters(url, true, asset); +// resolve(asset); +// }); +// } + +// /** +// * 等待Bundle加载完成 +// * @param bundleName Bundle名称 +// */ +// private async waitForBundleLoad(bundleName: string): Promise { +// return new Promise((resolve, reject) => { +// const bundleInfo = this.bundleCache.get(bundleName); + +// if (bundleInfo?.state === LoadState.LOADED) { +// bundleInfo.refCount++; +// resolve(bundleInfo.bundle); +// return; +// } + +// if (bundleInfo?.state === LoadState.FAILED) { +// reject(new Error(`Bundle ${bundleName} 加载失败`)); +// return; +// } + +// // 添加到等待队列 +// if (!this.bundleWaitQueue.has(bundleName)) { +// this.bundleWaitQueue.set(bundleName, []); +// } +// this.bundleWaitQueue.get(bundleName).push({ resolve, reject }); +// }); +// } + +// /** +// * 等待资源加载完成 +// * @param cacheKey 缓存键 +// */ +// private async waitForAssetLoad(cacheKey: string): Promise { +// return new Promise((resolve, reject) => { +// const assetInfo = this.assetCache.get(cacheKey); + +// if (assetInfo?.state === LoadState.LOADED) { +// assetInfo.refCount++; +// resolve(assetInfo.asset as T); +// return; +// } + +// if (assetInfo?.state === LoadState.FAILED) { +// reject(new Error(`资源 ${cacheKey} 加载失败`)); +// return; +// } + +// // 添加到等待队列 +// if (!this.assetWaitQueue.has(cacheKey)) { +// this.assetWaitQueue.set(cacheKey, []); +// } +// this.assetWaitQueue.get(cacheKey).push({ resolve, reject }); +// }); +// } + +// /** +// * 通知Bundle等待者 +// * @param bundleName Bundle名称 +// * @param success 是否成功 +// * @param result 结果或错误 +// */ +// private notifyBundleWaiters(bundleName: string, success: boolean, result: any): void { +// const waiters = this.bundleWaitQueue.get(bundleName); +// if (!waiters) return; + +// for (const waiter of waiters) { +// if (success) { +// const bundleInfo = this.bundleCache.get(bundleName); +// if (bundleInfo) { +// bundleInfo.refCount++; +// } +// waiter.resolve(result); +// } else { +// waiter.reject(result); +// } +// } + +// this.bundleWaitQueue.delete(bundleName); +// } + +// /** +// * 通知资源等待者 +// * @param cacheKey 缓存键 +// * @param success 是否成功 +// * @param result 结果或错误 +// */ +// private notifyAssetWaiters(cacheKey: string, success: boolean, result: any): void { +// const waiters = this.assetWaitQueue.get(cacheKey); +// if (!waiters) return; + +// for (const waiter of waiters) { +// if (success) { +// const assetInfo = this.assetCache.get(cacheKey); +// if (assetInfo) { +// assetInfo.refCount++; +// } +// waiter.resolve(result); +// } else { +// waiter.reject(result); +// } +// } + +// this.assetWaitQueue.delete(cacheKey); +// } + +// /** +// * 释放Bundle中的所有资源 +// * @param bundleName Bundle名称 +// */ +// private releaseAssetsInBundle(bundleName: string): void { +// const assetsToRemove: string[] = []; + +// for (const [key, assetInfo] of this.assetCache) { +// if (assetInfo.bundleName === bundleName) { +// if (assetInfo.asset) { +// assetInfo.asset.decRef(); +// } +// assetsToRemove.push(key); +// } +// } + +// for (const key of assetsToRemove) { +// this.assetCache.delete(key); +// } + +// LogUtils.debug(ResManager.TAG, `释放Bundle ${bundleName} 中的 ${assetsToRemove.length} 个资源`); +// } +// } +export default class ResManager extends Singleton { + private loadingBundle = new Map>(); + private loadedBundle = new Map(); + private loadingAsset = new Map>(); + private loadedAsset = new Map(); + + protected async onInit(): Promise {} + + public async loadBundle(bundleName: string): Promise { + if (this.loadedBundle.has(bundleName)) { + let bundle = this.loadedBundle.get(bundleName); + bundle.refCount++; + return bundle; + } + + if (this.loadingBundle.has(bundleName)) { + let bundle = await this.loadingBundle.get(bundleName); + if (bundle) { + bundle.refCount++; } + return bundle; } - // 标记为加载中 - this.bundleCache.set(bundleName, { - bundle: null, - state: LoadState.LOADING, - refCount: 1, - isRemote: true, - }); - - return new Promise((resolve, reject) => { - const task: LoadTask = { - url, - type: "bundle", - bundleName, - resolve, - reject, - }; - - this.addToQueue(task); - }); - } - - /** - * 加载本地Bundle - * @param bundleName Bundle名称 - * @returns Promise - */ - public async loadLocalBundle(bundleName: string): Promise { - const cachedBundle = this.bundleCache.get(bundleName); - - // 检查缓存 - if (cachedBundle) { - if (cachedBundle.state === LoadState.LOADED) { - cachedBundle.refCount++; - LogUtils.debug(ResManager.TAG, `使用缓存Bundle: ${bundleName}`); - return cachedBundle.bundle; - } else if (cachedBundle.state === LoadState.LOADING) { - // 正在加载中,等待加载完成 - return this.waitForBundleLoad(bundleName); - } - } - - // 标记为加载中 - this.bundleCache.set(bundleName, { - bundle: null as any, - state: LoadState.LOADING, - refCount: 1, - isRemote: false, - }); - - return new Promise((resolve, reject) => { - const task: LoadTask = { - url: bundleName, // 本地Bundle使用bundleName作为url - type: "bundle", - bundleName, - resolve, - reject, - }; - - this.addToQueue(task); - }); - } - - /** - * 加载资源 - * @param assetPath 资源路径 - * @param assetType 资源类型 - * @param bundleName Bundle名称,不传则默认为resources - * @returns Promise - */ - public async loadAsset( - assetPath: string, - assetType: typeof Asset, - bundleName?: string, - ): Promise { - const targetBundle = bundleName || ResManager.DEFAULT_BUNDLE; - const cacheKey = `${targetBundle}/${assetPath}`; - const cachedAsset = this.assetCache.get(cacheKey); - - // 检查缓存 - if (cachedAsset) { - if (cachedAsset.state === LoadState.LOADED) { - cachedAsset.refCount++; - LogUtils.debug(ResManager.TAG, `使用缓存资源: ${cacheKey}`); - return cachedAsset.asset as T; - } else if (cachedAsset.state === LoadState.LOADING) { - // 正在加载中,等待加载完成 - return this.waitForAssetLoad(cacheKey); - } - } - - // 确保Bundle已加载 - await this.ensureBundleLoaded(targetBundle); - - // 标记为加载中 - this.assetCache.set(cacheKey, { - asset: null, - state: LoadState.LOADING, - refCount: 1, - bundleName: targetBundle, - }); - - return new Promise((resolve, reject) => { - const task: LoadTask = { - url: cacheKey, - type: "asset", - bundleName: targetBundle, - assetPath, - assetType, - resolve, - reject, - }; - - this.addToQueue(task); - }); - } - - /** - * 确保Bundle已加载 - * @param bundleName Bundle名称 - */ - private async ensureBundleLoaded(bundleName: string): Promise { - const bundleInfo = this.bundleCache.get(bundleName); - - if (!bundleInfo || bundleInfo.state === LoadState.NONE) { - // Bundle不存在或状态为NONE,尝试加载本地Bundle - LogUtils.info(ResManager.TAG, `Bundle ${bundleName} 不存在或未加载,尝试加载本地Bundle`); - try { - await this.loadLocalBundle(bundleName); - } catch (err) { - LogUtils.error( - ResManager.TAG, - `Bundle ${bundleName} 本地加载失败,请检查Bundle是否存在或调用 loadRemoteBundle 加载远程Bundle`, - err, - ); - } - return; - } - - if (bundleInfo.state === LoadState.FAILED) { - throw new Error(`Bundle ${bundleName} 加载失败`); - } - - if (bundleInfo.state === LoadState.LOADING) { - // Bundle正在加载中,等待加载完成 - LogUtils.info(ResManager.TAG, `Bundle ${bundleName} 正在加载中,等待加载完成`); - await this.waitForBundleLoad(bundleName); - } - } - - /** - * 释放Bundle - * @param bundleName Bundle名称 - */ - public releaseBundle(bundleName: string): void { - // 不允许释放默认Bundle - if (bundleName === ResManager.DEFAULT_BUNDLE) { - LogUtils.warn(ResManager.TAG, "不允许释放默认Bundle: resources"); - return; - } - - const bundleInfo = this.bundleCache.get(bundleName); - if (!bundleInfo) { - LogUtils.warn(ResManager.TAG, `Bundle ${bundleName} 不存在`); - return; - } - - bundleInfo.refCount--; - LogUtils.debug(ResManager.TAG, `Bundle ${bundleName} 引用计数: ${bundleInfo.refCount}`); - - if (bundleInfo.refCount <= 0) { - // 释放Bundle中的所有资源 - this.releaseAssetsInBundle(bundleName); - - // 释放Bundle - if (bundleInfo.bundle && bundleInfo.isRemote) { - assetManager.removeBundle(bundleInfo.bundle); - } - - this.bundleCache.delete(bundleName); - LogUtils.info(ResManager.TAG, `释放Bundle: ${bundleName}`); - } - } - - /** - * 释放资源 - * @param assetPath 资源路径 - * @param bundleName Bundle名称,不传则默认为resources - */ - public releaseAsset(assetPath: string, bundleName?: string): void { - const targetBundle = bundleName || ResManager.DEFAULT_BUNDLE; - const cacheKey = `${targetBundle}/${assetPath}`; - const assetInfo = this.assetCache.get(cacheKey); - - if (!assetInfo) { - LogUtils.warn(ResManager.TAG, `资源 ${cacheKey} 不存在`); - return; - } - - assetInfo.refCount--; - LogUtils.debug(ResManager.TAG, `资源 ${cacheKey} 引用计数: ${assetInfo.refCount}`); - - if (assetInfo.refCount <= 0) { - if (assetInfo.asset) { - assetInfo.asset.decRef(); - } - - this.assetCache.delete(cacheKey); - LogUtils.info(ResManager.TAG, `释放资源: ${cacheKey}`); - } - } - - /** - * 清理所有缓存(保留默认Bundle) - */ - public clearAll(): void { - // 释放所有资源 - for (const [, assetInfo] of this.assetCache) { - if (assetInfo.asset) { - assetInfo.asset.decRef(); - } - } - this.assetCache.clear(); - - // 释放所有远端Bundle - for (const [key, bundleInfo] of this.bundleCache) { - if (key !== ResManager.DEFAULT_BUNDLE && bundleInfo.bundle && bundleInfo.isRemote) { - assetManager.removeBundle(bundleInfo.bundle); - } - } - - // 清空Bundle缓存,但保留默认Bundle - const defaultBundle = this.bundleCache.get(ResManager.DEFAULT_BUNDLE); - this.bundleCache.clear(); - if (defaultBundle) { - this.bundleCache.set(ResManager.DEFAULT_BUNDLE, defaultBundle); - } - - // 清空加载队列 - this.loadQueue = []; - this.loadingCount = 0; - this.bundleWaitQueue.clear(); - this.assetWaitQueue.clear(); - - LogUtils.info(ResManager.TAG, "清理所有缓存完成"); - } - - /** - * 获取缓存状态信息 - */ - public getCacheInfo(): { - bundleCount: number; - assetCount: number; - queueLength: number; - loadingCount: number; - } { - return { - bundleCount: this.bundleCache.size, - assetCount: this.assetCache.size, - queueLength: this.loadQueue.length, - loadingCount: this.loadingCount, - }; - } - - /** - * 添加任务到队列 - * @param task 加载任务 - */ - private addToQueue(task: LoadTask): void { - this.loadQueue.push(task); - void this.processQueue(); - } - - /** - * 处理加载队列 - */ - private async processQueue(): Promise { - if (this.isProcessingQueue || this.loadingCount >= this.maxConcurrentLoads) { - return; - } - - this.isProcessingQueue = true; - - while (this.loadQueue.length > 0 && this.loadingCount < this.maxConcurrentLoads) { - const task = this.loadQueue.shift(); - if (!task) break; - - this.loadingCount++; - this.executeTask(task) - .then(() => { - this.loadingCount--; - void this.processQueue(); - }) - .catch((err) => { - LogUtils.error(ResManager.TAG, `队列任务执行失败: ${task.url}`, err); - this.loadingCount--; - void this.processQueue(); - }); - } - - this.isProcessingQueue = false; - } - - /** - * 执行加载任务 - * @param task 加载任务 - */ - private async executeTask(task: LoadTask): Promise { - try { - await (task.type === "bundle" ? this.loadBundleTask(task) : this.loadAssetTask(task)); - } catch (err) { - LogUtils.error(ResManager.TAG, `加载任务失败: ${task.url}`, err); - task.reject(err as Error); - } - } - - /** - * 执行Bundle加载任务 - * @param task 加载任务 - */ - private async loadBundleTask(task: LoadTask): Promise { - const { url, bundleName, resolve, reject } = task; - if (!bundleName) { - reject(new Error("Bundle名称不能为空")); - return; - } - - const bundleInfo = this.bundleCache.get(bundleName); - const isRemote = bundleInfo?.isRemote ?? false; - - // 根据是否为远端Bundle选择不同的加载方式 - if (isRemote) { - // 远端Bundle加载 - assetManager.loadBundle(url, (err, bundle) => { - this.handleBundleLoadResult(bundleName, err, bundle, resolve, reject); - }); - } else { - // 本地Bundle加载 + let promise = new Promise((resolve, reject) => { assetManager.loadBundle(bundleName, (err, bundle) => { - this.handleBundleLoadResult(bundleName, err, bundle, resolve, reject); + if (err) { + console.error(`加载Bundle ${bundleName} 失败: ${err}`); + resolve(null); + } else { + resolve(bundle); + } }); - } - } - - /** - * 处理Bundle加载结果 - * @param bundleName Bundle名称 - * @param err 错误信息 - * @param bundle Bundle对象 - * @param resolve Promise resolve函数 - * @param reject Promise reject函数 - */ - private handleBundleLoadResult( - bundleName: string, - err: Error | null, - bundle: AssetManager.Bundle, - resolve: (result: AssetManager.Bundle) => void, - reject: (error: Error) => void, - ): void { - const bundleInfo = this.bundleCache.get(bundleName); - - if (err) { - LogUtils.error(ResManager.TAG, `Bundle加载失败: ${bundleName}`, err); - if (bundleInfo) { - bundleInfo.state = LoadState.FAILED; - } - - // 通知所有等待的任务 - this.notifyBundleWaiters(bundleName, false, new Error(`Bundle加载失败: ${err.message}`)); - reject(new Error(`Bundle加载失败: ${err.message}`)); - return; - } - - if (bundleInfo) { - bundleInfo.bundle = bundle; - bundleInfo.state = LoadState.LOADED; - } - - LogUtils.info(ResManager.TAG, `Bundle加载成功: ${bundleName}`); - - // 通知所有等待的任务 - this.notifyBundleWaiters(bundleName, true, bundle); - resolve(bundle); - } - - /** - * 执行资源加载任务 - * @param task 加载任务 - */ - private async loadAssetTask(task: LoadTask): Promise { - const { url, bundleName, assetPath, assetType, resolve, reject } = task; - if (!bundleName || !assetPath || !assetType) { - reject(new Error("资源加载参数不完整")); - return; - } - - const bundleInfo = this.bundleCache.get(bundleName); - - if (!bundleInfo?.bundle) { - reject(new Error(`Bundle ${bundleName} 不存在`)); - return; - } - - bundleInfo.bundle.load(assetPath, assetType, (err, asset) => { - const assetInfo = this.assetCache.get(url); - - if (err) { - LogUtils.error(ResManager.TAG, `资源加载失败: ${url}`, err); - if (assetInfo) { - assetInfo.state = LoadState.FAILED; - } - - // 通知所有等待的任务 - this.notifyAssetWaiters(url, false, new Error(`资源加载失败: ${err.message}`)); - reject(new Error(`资源加载失败: ${err.message}`)); - return; - } - - if (assetInfo) { - assetInfo.asset = asset; - assetInfo.state = LoadState.LOADED; - } - - LogUtils.info(ResManager.TAG, `资源加载成功: ${url}`); - - // 通知所有等待的任务 - this.notifyAssetWaiters(url, true, asset); - resolve(asset); }); + this.loadingBundle.set(bundleName, promise); + let bundle = await promise; + this.loadingBundle.delete(bundleName); + if (bundle) { + bundle.refCount++; + this.loadedBundle.set(bundleName, bundle); + } + return bundle; } - /** - * 等待Bundle加载完成 - * @param bundleName Bundle名称 - */ - private async waitForBundleLoad(bundleName: string): Promise { - return new Promise((resolve, reject) => { - const bundleInfo = this.bundleCache.get(bundleName); + public async loadAsset(options: { + bundle?: string; + path: string; + type?: typeof Asset; + }): Promise { + options = { type: Asset, bundle: "resources", ...options }; + let cacheKey = `${options.bundle}:${options.path}`; + if (this.loadedAsset.has(cacheKey)) { + const asset = this.loadedAsset.get(cacheKey); + if (asset) { + asset.addRef(); + return asset as T; + } + } + if (this.loadingAsset.has(cacheKey)) { + let asset = await this.loadingAsset.get(cacheKey); + if (asset) { + asset.addRef(); + } + return asset as T; + } - if (bundleInfo?.state === LoadState.LOADED) { - bundleInfo.refCount++; - resolve(bundleInfo.bundle); - return; + let promise = new Promise(async (resolve, reject) => { + let bundle = await this.loadBundle(options.bundle); + if (!bundle) { + console.error(`加载Bundle ${options.bundle} 失败`); + return null; } - if (bundleInfo?.state === LoadState.FAILED) { - reject(new Error(`Bundle ${bundleName} 加载失败`)); - return; - } - - // 添加到等待队列 - if (!this.bundleWaitQueue.has(bundleName)) { - this.bundleWaitQueue.set(bundleName, []); - } - this.bundleWaitQueue.get(bundleName).push({ resolve, reject }); + bundle.load(options.path, options.type, (err, asset) => { + if (err) { + console.error(`加载Asset ${options.path} 失败: ${err}`); + resolve(null); + } else { + asset.cacheKey = cacheKey; + resolve(asset as T); + } + }); }); + this.loadingAsset.set(cacheKey, promise); + let asset = await promise; + this.loadingAsset.delete(cacheKey); + if (asset) { + asset.addRef(); + this.loadedAsset.set(cacheKey, asset); + } + return asset as T; } - /** - * 等待资源加载完成 - * @param cacheKey 缓存键 - */ - private async waitForAssetLoad(cacheKey: string): Promise { - return new Promise((resolve, reject) => { - const assetInfo = this.assetCache.get(cacheKey); - - if (assetInfo?.state === LoadState.LOADED) { - assetInfo.refCount++; - resolve(assetInfo.asset as T); - return; - } - - if (assetInfo?.state === LoadState.FAILED) { - reject(new Error(`资源 ${cacheKey} 加载失败`)); - return; - } - - // 添加到等待队列 - if (!this.assetWaitQueue.has(cacheKey)) { - this.assetWaitQueue.set(cacheKey, []); - } - this.assetWaitQueue.get(cacheKey).push({ resolve, reject }); - }); - } - - /** - * 通知Bundle等待者 - * @param bundleName Bundle名称 - * @param success 是否成功 - * @param result 结果或错误 - */ - private notifyBundleWaiters(bundleName: string, success: boolean, result: any): void { - const waiters = this.bundleWaitQueue.get(bundleName); - if (!waiters) return; - - for (const waiter of waiters) { - if (success) { - const bundleInfo = this.bundleCache.get(bundleName); - if (bundleInfo) { - bundleInfo.refCount++; - } - waiter.resolve(result); - } else { - waiter.reject(result); - } + public releaseAsset(asset: T): void { + let cacheKey = asset.cacheKey; + asset.decRef(); + if (!StringUtils.isEmpty(cacheKey) && this.loadedAsset.has(cacheKey)) { + this.loadedAsset.delete(cacheKey); } - - this.bundleWaitQueue.delete(bundleName); - } - - /** - * 通知资源等待者 - * @param cacheKey 缓存键 - * @param success 是否成功 - * @param result 结果或错误 - */ - private notifyAssetWaiters(cacheKey: string, success: boolean, result: any): void { - const waiters = this.assetWaitQueue.get(cacheKey); - if (!waiters) return; - - for (const waiter of waiters) { - if (success) { - const assetInfo = this.assetCache.get(cacheKey); - if (assetInfo) { - assetInfo.refCount++; - } - waiter.resolve(result); - } else { - waiter.reject(result); - } - } - - this.assetWaitQueue.delete(cacheKey); - } - - /** - * 释放Bundle中的所有资源 - * @param bundleName Bundle名称 - */ - private releaseAssetsInBundle(bundleName: string): void { - const assetsToRemove: string[] = []; - - for (const [key, assetInfo] of this.assetCache) { - if (assetInfo.bundleName === bundleName) { - if (assetInfo.asset) { - assetInfo.asset.decRef(); - } - assetsToRemove.push(key); - } - } - - for (const key of assetsToRemove) { - this.assetCache.delete(key); - } - - LogUtils.debug(ResManager.TAG, `释放Bundle ${bundleName} 中的 ${assetsToRemove.length} 个资源`); } }