@nimbus2d/core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/dist/index.mjs +18433 -0
  2. package/package.json +39 -0
  3. package/src/base/EventEmitter.js +187 -0
  4. package/src/base/Subsystem.js +183 -0
  5. package/src/base/index.js +6 -0
  6. package/src/core/Application/AppState.js +27 -0
  7. package/src/core/Application/Application.js +480 -0
  8. package/src/core/Application/DefaultConfig.js +63 -0
  9. package/src/core/Application/index.js +7 -0
  10. package/src/core/AssetManager/AssetManager.js +711 -0
  11. package/src/core/AssetManager/AssetTypes.js +108 -0
  12. package/src/core/AssetManager/adapters/IResourceAdapter.js +50 -0
  13. package/src/core/AssetManager/adapters/PixiAdapter.js +90 -0
  14. package/src/core/AssetManager/core/RefCounter.js +167 -0
  15. package/src/core/AssetManager/core/ResourceCache.js +281 -0
  16. package/src/core/AssetManager/core/ResourceEntry.js +191 -0
  17. package/src/core/AssetManager/core/ResourceEventBus.js +127 -0
  18. package/src/core/AssetManager/handlers/IResourceHandler.js +45 -0
  19. package/src/core/AssetManager/index.js +26 -0
  20. package/src/core/AssetManager/plugins/IResourcePlugin.js +56 -0
  21. package/src/core/Camera/Camera.js +534 -0
  22. package/src/core/Camera/CameraTypes.js +59 -0
  23. package/src/core/Camera/index.js +13 -0
  24. package/src/core/ConfigSystem/ConfigLayer.js +230 -0
  25. package/src/core/ConfigSystem/ConfigLayerType.js +31 -0
  26. package/src/core/ConfigSystem/ConfigMerger.js +50 -0
  27. package/src/core/ConfigSystem/ConfigPath.js +96 -0
  28. package/src/core/ConfigSystem/ConfigSystem.js +378 -0
  29. package/src/core/ConfigSystem/ConfigTypes.js +29 -0
  30. package/src/core/ConfigSystem/ConfigWatcher.js +84 -0
  31. package/src/core/ConfigSystem/index.js +19 -0
  32. package/src/core/EventBus/EventBus.js +797 -0
  33. package/src/core/EventBus/Interceptor.js +26 -0
  34. package/src/core/EventBus/PerformanceStats.js +105 -0
  35. package/src/core/EventBus/index.js +7 -0
  36. package/src/core/Lifecycle/Lifecycle.js +265 -0
  37. package/src/core/Lifecycle/LifecycleState.js +31 -0
  38. package/src/core/Lifecycle/index.js +6 -0
  39. package/src/core/Logger/ConsoleHandler.js +55 -0
  40. package/src/core/Logger/LogHandler.js +34 -0
  41. package/src/core/Logger/LogLevel.js +38 -0
  42. package/src/core/Logger/Logger.js +234 -0
  43. package/src/core/Logger/index.js +8 -0
  44. package/src/core/PluginSystem/Plugin.js +156 -0
  45. package/src/core/PluginSystem/PluginEntry.js +102 -0
  46. package/src/core/PluginSystem/PluginState.js +30 -0
  47. package/src/core/PluginSystem/PluginSystem.js +530 -0
  48. package/src/core/PluginSystem/index.js +8 -0
  49. package/src/core/PoolManager/DefaultConfig.js +19 -0
  50. package/src/core/PoolManager/ObjectPool.js +326 -0
  51. package/src/core/PoolManager/PoolManager.js +253 -0
  52. package/src/core/PoolManager/PoolTypes.js +92 -0
  53. package/src/core/PoolManager/index.js +15 -0
  54. package/src/core/SceneManager/FadeTransition.js +64 -0
  55. package/src/core/SceneManager/MaskTransition.js +234 -0
  56. package/src/core/SceneManager/Scene.js +221 -0
  57. package/src/core/SceneManager/SceneManager.js +658 -0
  58. package/src/core/SceneManager/SceneState.js +29 -0
  59. package/src/core/SceneManager/SlideTransition.js +113 -0
  60. package/src/core/SceneManager/Transition.js +102 -0
  61. package/src/core/SceneManager/ZoomTransition.js +119 -0
  62. package/src/core/SceneManager/index.js +12 -0
  63. package/src/core/SpriteGPULayer/SpriteGPULayer.js +321 -0
  64. package/src/core/SpriteGPULayer/SpriteGPULayerTypes.js +49 -0
  65. package/src/core/SpriteGPULayer/index.js +11 -0
  66. package/src/core/Timeline/Clip.js +101 -0
  67. package/src/core/Timeline/DefaultConfig.js +41 -0
  68. package/src/core/Timeline/Easing.js +176 -0
  69. package/src/core/Timeline/Timeline.js +458 -0
  70. package/src/core/Timeline/TimelineTypes.js +89 -0
  71. package/src/core/Timeline/Track.js +202 -0
  72. package/src/core/Timeline/index.js +22 -0
  73. package/src/errors/ConfigError.js +47 -0
  74. package/src/errors/LifecycleError.js +58 -0
  75. package/src/errors/NimbusError.js +43 -0
  76. package/src/errors/PluginError.js +138 -0
  77. package/src/errors/ResourceError.js +166 -0
  78. package/src/errors/SceneError.js +124 -0
  79. package/src/errors/SubsystemError.js +68 -0
  80. package/src/errors/index.js +53 -0
  81. package/src/index.js +86 -0
  82. package/src/types/index.js +141 -0
  83. package/src/utils/MemoryChecker.js +374 -0
  84. package/src/utils/Validator.js +60 -0
  85. package/src/utils/index.js +84 -0
@@ -0,0 +1,711 @@
1
+ // ============================================================================
2
+ // Nimbus2D - AssetManager 资源管理
3
+ // ----------------------------------------------------------------------------
4
+ // 职责:
5
+ // 1. 门面 + 适配器(IResourceAdapter → PixiAdapter)+ 处理器 + 插件钩子
6
+ // 2. 引用计数 + 缓存(分组+标签)+ 独立 EventBus
7
+ // 3. 8 种资源类型:IMAGE/SPRITESHEET/ATLAS/AUDIO/FONT/DATA/BINARY/SHADER
8
+ // 4. PixiAdapter 核心内置自动注册(支持 6 种类型)
9
+ // 5. 3 种加载策略:LAZY/EAGER/GROUP
10
+ // 6. 双通道事件广播(内部 EventBus + 核心 EventBus)
11
+ //
12
+ // 继承关系:
13
+ // EventEmitter → Subsystem → AssetManager(Lifecycle 内第二个初始化)
14
+ // ============================================================================
15
+
16
+ import { Subsystem } from '../../base/Subsystem.js'
17
+ import { ResourceCache } from './core/ResourceCache.js'
18
+ import { RefCounter } from './core/RefCounter.js'
19
+ import { ResourceEventBus } from './core/ResourceEventBus.js'
20
+ import { ResourceEntry } from './core/ResourceEntry.js'
21
+ import { PixiAdapter } from './adapters/PixiAdapter.js'
22
+ import {
23
+ ResourceType,
24
+ ResourceState,
25
+ LoadStrategy,
26
+ ResourceEventType,
27
+ ResourceNotFoundError,
28
+ ResourceDuplicateError,
29
+ ResourceStateError,
30
+ ResourceLoadError,
31
+ AdapterError,
32
+ ResourceDestroyedError
33
+ } from './AssetTypes.js'
34
+
35
+ class AssetManager extends Subsystem {
36
+ /**
37
+ * 构造函数
38
+ * @param {import('../EventBus/EventBus.js').EventBus} eventBus - 全局事件总线
39
+ * @param {import('../ConfigSystem/ConfigSystem.js').ConfigSystem} config - 配置系统实例
40
+ * @param {import('../Logger/Logger.js').Logger} logger - 日志系统实例
41
+ * @param {object} pixi - PixiJS Application 实例(用于 PixiAdapter 自动注册)
42
+ */
43
+ constructor(eventBus, config, logger, pixi) {
44
+ super(eventBus, config, logger)
45
+
46
+ /** @type {object} PixiJS Application 实例 */
47
+ this._pixi = pixi
48
+
49
+ /** @type {ResourceCache} 资源缓存 */
50
+ this._cache = new ResourceCache()
51
+
52
+ /** @type {RefCounter} 引用计数器 */
53
+ this._refCounter = new RefCounter()
54
+
55
+ /** @type {ResourceEventBus} 内部事件总线 */
56
+ this._internalBus = new ResourceEventBus()
57
+
58
+ /** @type {Map<string, import('./adapters/IResourceAdapter.js').IResourceAdapter>} 适配器映射(type → adapter) */
59
+ this._adapters = new Map()
60
+
61
+ /** @type {import('./handlers/IResourceHandler.js').IResourceHandler[]} 处理器列表 */
62
+ this._handlers = []
63
+
64
+ /** @type {import('./plugins/IResourcePlugin.js').IResourcePlugin[]} 插件列表 */
65
+ this._plugins = []
66
+
67
+ /** @type {Set<string>} 加载中的资源 ID(防止重复加载) */
68
+ this._loadingIds = new Set()
69
+ }
70
+
71
+ // --------------------------------------------------------------------------
72
+ // 适配器管理
73
+ // --------------------------------------------------------------------------
74
+
75
+ /**
76
+ * 注册适配器
77
+ * @param {string} type - 资源类型(ResourceType 枚举值)
78
+ * @param {import('./adapters/IResourceAdapter.js').IResourceAdapter} adapter - 适配器实例
79
+ */
80
+ registerAdapter(type, adapter) {
81
+ if (this._isDestroyed) throw new ResourceDestroyedError()
82
+ this._adapters.set(type, adapter)
83
+ }
84
+
85
+ /**
86
+ * 注销适配器
87
+ * @param {string} type - 资源类型
88
+ * @returns {boolean} 是否注销成功
89
+ */
90
+ unregisterAdapter(type) {
91
+ return this._adapters.delete(type)
92
+ }
93
+
94
+ // --------------------------------------------------------------------------
95
+ // 处理器管理
96
+ // --------------------------------------------------------------------------
97
+
98
+ /**
99
+ * 添加处理器
100
+ * @param {import('./handlers/IResourceHandler.js').IResourceHandler} handler - 处理器实例
101
+ */
102
+ addHandler(handler) {
103
+ if (!this._handlers.includes(handler)) {
104
+ this._handlers.push(handler)
105
+ }
106
+ }
107
+
108
+ /**
109
+ * 移除处理器
110
+ * @param {import('./handlers/IResourceHandler.js').IResourceHandler} handler - 处理器实例
111
+ */
112
+ removeHandler(handler) {
113
+ const index = this._handlers.indexOf(handler)
114
+ if (index !== -1) {
115
+ this._handlers.splice(index, 1)
116
+ }
117
+ }
118
+
119
+ // --------------------------------------------------------------------------
120
+ // 资源插件管理
121
+ // --------------------------------------------------------------------------
122
+
123
+ /**
124
+ * 添加资源插件
125
+ * @param {import('./plugins/IResourcePlugin.js').IResourcePlugin} plugin - 资源插件实例
126
+ */
127
+ addPlugin(plugin) {
128
+ if (!this._plugins.includes(plugin)) {
129
+ this._plugins.push(plugin)
130
+ }
131
+ }
132
+
133
+ /**
134
+ * 移除资源插件
135
+ * @param {import('./plugins/IResourcePlugin.js').IResourcePlugin} plugin - 资源插件实例
136
+ */
137
+ removePlugin(plugin) {
138
+ const index = this._plugins.indexOf(plugin)
139
+ if (index !== -1) {
140
+ this._plugins.splice(index, 1)
141
+ }
142
+ }
143
+
144
+ // --------------------------------------------------------------------------
145
+ // 资源注册
146
+ // --------------------------------------------------------------------------
147
+
148
+ /**
149
+ * 注册资源
150
+ * @param {string} id - 资源唯一标识
151
+ * @param {Object} options - 注册选项
152
+ * @param {string} options.type - 资源类型(ResourceType 枚举值)
153
+ * @param {string} options.src - 资源路径
154
+ * @param {string} [options.strategy=LoadStrategy.LAZY] - 加载策略
155
+ * @param {string} [options.group] - 分组名称
156
+ * @param {string[]} [options.tags=[]] - 标签列表
157
+ * @param {Object} [options.adapterOptions={}] - 适配器选项
158
+ */
159
+ register(id, options) {
160
+ if (this._isDestroyed) throw new ResourceDestroyedError()
161
+
162
+ // 检查重复
163
+ if (this._cache.has(id)) {
164
+ throw new ResourceDuplicateError(id)
165
+ }
166
+
167
+ // 创建资源条目
168
+ const entry = new ResourceEntry(id, options)
169
+ this._cache.add(entry)
170
+
171
+ // 广播事件
172
+ this._emitEvent(ResourceEventType.REGISTERED, { id, entry })
173
+
174
+ // EAGER 策略:注册后立即加载
175
+ if (entry.strategy === LoadStrategy.EAGER) {
176
+ // 异步加载不等待,错误通过事件通知
177
+ this.load(id).catch(() => {})
178
+ }
179
+ }
180
+
181
+ /**
182
+ * 注销资源
183
+ * @param {string} id - 资源 ID
184
+ */
185
+ unregister(id) {
186
+ if (this._isDestroyed) throw new ResourceDestroyedError()
187
+
188
+ const entry = this._cache.get(id)
189
+ if (!entry) return
190
+
191
+ // 已加载的资源需先卸载
192
+ if (entry.isLoaded) {
193
+ this._doUnload(entry)
194
+ }
195
+
196
+ // 移除引用计数记录
197
+ this._refCounter.removeResource(id)
198
+
199
+ // 从缓存移除
200
+ this._cache.delete(id)
201
+
202
+ // 广播事件
203
+ this._emitEvent(ResourceEventType.UNREGISTERED, { id })
204
+ }
205
+
206
+ // --------------------------------------------------------------------------
207
+ // 资源加载
208
+ // --------------------------------------------------------------------------
209
+
210
+ /**
211
+ * 加载单个资源
212
+ * @param {string} id - 资源 ID
213
+ * @returns {Promise<*>} - 资源数据
214
+ */
215
+ async load(id) {
216
+ if (this._isDestroyed) throw new ResourceDestroyedError()
217
+
218
+ const entry = this._getEntryOrThrow(id)
219
+
220
+ // 已加载直接返回
221
+ if (entry.isLoaded) return entry.data
222
+
223
+ // 正在加载中,等待完成
224
+ if (entry.state === ResourceState.LOADING) {
225
+ return this._waitForLoad(id)
226
+ }
227
+
228
+ // 获取适配器
229
+ const adapter = this._getAdapterOrThrow(entry.type)
230
+
231
+ // 标记加载中
232
+ entry.setLoading()
233
+ this._loadingIds.add(id)
234
+ this._emitEvent(ResourceEventType.LOAD_START, { id })
235
+
236
+ try {
237
+ // 执行 beforeLoad 插件钩子
238
+ let modifiedEntry = entry
239
+ for (const plugin of this._plugins) {
240
+ modifiedEntry = await plugin.beforeLoad(modifiedEntry)
241
+ }
242
+
243
+ // 通过适配器加载
244
+ const data = await adapter.load(modifiedEntry)
245
+
246
+ // 执行 afterLoad 插件钩子
247
+ let modifiedData = data
248
+ for (const plugin of this._plugins) {
249
+ modifiedData = await plugin.afterLoad(modifiedEntry, modifiedData)
250
+ }
251
+
252
+ // 标记已加载
253
+ entry.setLoaded(modifiedData)
254
+
255
+ // 通知处理器
256
+ for (const handler of this._handlers) {
257
+ try {
258
+ handler.onLoadComplete(entry, modifiedData)
259
+ } catch (e) {
260
+ // 处理器错误不影响加载流程
261
+ }
262
+ }
263
+
264
+ // 广播事件
265
+ this._emitEvent(ResourceEventType.LOAD_COMPLETE, { id, data: modifiedData })
266
+
267
+ return modifiedData
268
+ } catch (e) {
269
+ // 标记加载失败
270
+ const error = e instanceof Error ? e : new Error(String(e))
271
+ entry.setError(error)
272
+
273
+ // 通知处理器
274
+ for (const handler of this._handlers) {
275
+ try {
276
+ handler.onLoadError(entry, error)
277
+ } catch (handlerErr) {
278
+ // 处理器错误不影响错误流程
279
+ }
280
+ }
281
+
282
+ // 广播事件
283
+ this._emitEvent(ResourceEventType.LOAD_ERROR, { id, error })
284
+ this._emitEvent(ResourceEventType.ERROR, { id, error })
285
+
286
+ throw new ResourceLoadError(id, error)
287
+ } finally {
288
+ this._loadingIds.delete(id)
289
+ }
290
+ }
291
+
292
+ /**
293
+ * 加载多个资源
294
+ * @param {string[]} ids - 资源 ID 列表
295
+ * @returns {Promise<Map<string, *>>} - 资源数据映射(id → data)
296
+ */
297
+ async loadAll(ids) {
298
+ const results = new Map()
299
+ const promises = ids.map(async (id) => {
300
+ const data = await this.load(id)
301
+ results.set(id, data)
302
+ })
303
+ await Promise.all(promises)
304
+ return results
305
+ }
306
+
307
+ /**
308
+ * 加载分组
309
+ * @param {string} groupName - 分组名称
310
+ * @returns {Promise<Map<string, *>>} - 资源数据映射
311
+ */
312
+ async loadGroup(groupName) {
313
+ const ids = this._cache.getGroupIds(groupName)
314
+ return this.loadAll(ids)
315
+ }
316
+
317
+ // --------------------------------------------------------------------------
318
+ // 资源卸载
319
+ // --------------------------------------------------------------------------
320
+
321
+ /**
322
+ * 卸载单个资源
323
+ * @param {string} id - 资源 ID
324
+ * @returns {Promise<void>}
325
+ */
326
+ async unload(id) {
327
+ if (this._isDestroyed) throw new ResourceDestroyedError()
328
+
329
+ const entry = this._cache.get(id)
330
+ if (!entry) return
331
+
332
+ // 引用计数 > 0 时不卸载
333
+ if (this._refCounter.getCount(id) > 0) {
334
+ this._safeLog('warn', `资源 ${id} 引用计数 > 0,跳过卸载`)
335
+ return
336
+ }
337
+
338
+ await this._doUnload(entry)
339
+ }
340
+
341
+ /**
342
+ * 卸载分组
343
+ * @param {string} groupName - 分组名称
344
+ * @returns {Promise<void>}
345
+ */
346
+ async unloadGroup(groupName) {
347
+ const entries = this._cache.getGroupEntries(groupName)
348
+ for (const entry of entries) {
349
+ if (this._refCounter.getCount(entry.id) === 0) {
350
+ await this._doUnload(entry)
351
+ }
352
+ }
353
+ }
354
+
355
+ /**
356
+ * 清空所有资源
357
+ * @returns {Promise<void>}
358
+ */
359
+ async clear() {
360
+ const entries = this._cache.getAllEntries()
361
+ for (const entry of entries) {
362
+ try {
363
+ if (entry.isLoaded) {
364
+ await this._doUnload(entry)
365
+ }
366
+ } catch (e) {
367
+ // 清空阶段错误不中断
368
+ }
369
+ }
370
+ this._cache.clear()
371
+ this._refCounter.clear()
372
+ }
373
+
374
+ // --------------------------------------------------------------------------
375
+ // 引用计数
376
+ // --------------------------------------------------------------------------
377
+
378
+ /**
379
+ * 引用资源(异步,LAZY 策略时触发加载)
380
+ * @param {string} id - 资源 ID
381
+ * @param {string} holder - 持有者标识(如场景名)
382
+ * @returns {Promise<*>} - 资源数据
383
+ */
384
+ async retain(id, holder) {
385
+ if (this._isDestroyed) throw new ResourceDestroyedError()
386
+
387
+ this._getEntryOrThrow(id)
388
+
389
+ // 增加引用计数
390
+ this._refCounter.retain(id, holder)
391
+
392
+ // 广播事件
393
+ this._emitEvent(ResourceEventType.RETAIN, { id, holder })
394
+
395
+ // 如果未加载,触发加载
396
+ const entry = this._cache.get(id)
397
+ if (entry && !entry.isLoaded && entry.state !== ResourceState.LOADING) {
398
+ return this.load(id)
399
+ }
400
+
401
+ return entry ? entry.data : null
402
+ }
403
+
404
+ /**
405
+ * 释放引用
406
+ * @param {string} id - 资源 ID
407
+ * @param {string} holder - 持有者标识
408
+ */
409
+ release(id, holder) {
410
+ if (this._isDestroyed) return
411
+
412
+ const newCount = this._refCounter.release(id, holder)
413
+
414
+ // 广播事件
415
+ this._emitEvent(ResourceEventType.RELEASE, { id, holder })
416
+
417
+ // 引用计数为 0 时,可根据配置决定是否自动卸载
418
+ // 目前不自动卸载,由用户手动调用 unload 或 unloadGroup
419
+ }
420
+
421
+ /**
422
+ * 释放 holder 的所有引用
423
+ * @param {string} holder - 持有者标识
424
+ */
425
+ releaseAllByHolder(holder) {
426
+ this._refCounter.releaseAllByHolder(holder)
427
+ }
428
+
429
+ // --------------------------------------------------------------------------
430
+ // 分组管理
431
+ // --------------------------------------------------------------------------
432
+
433
+ /**
434
+ * 创建分组
435
+ * @param {string} name - 分组名称
436
+ * @param {string[]} [ids=[]] - 初始资源 ID 列表
437
+ */
438
+ createGroup(name, ids = []) {
439
+ this._cache.createGroup(name, ids)
440
+ }
441
+
442
+ /**
443
+ * 添加到分组
444
+ * @param {string} name - 分组名称
445
+ * @param {string} id - 资源 ID
446
+ */
447
+ addToGroup(name, id) {
448
+ this._cache.addToGroup(name, id)
449
+ }
450
+
451
+ /**
452
+ * 从分组移除
453
+ * @param {string} name - 分组名称
454
+ * @param {string} id - 资源 ID
455
+ */
456
+ removeFromGroup(name, id) {
457
+ this._cache.removeFromGroup(name, id)
458
+ }
459
+
460
+ // --------------------------------------------------------------------------
461
+ // 查询方法
462
+ // --------------------------------------------------------------------------
463
+
464
+ /**
465
+ * 获取资源数据
466
+ * @param {string} id - 资源 ID
467
+ * @returns {*|null} 资源数据,未加载返回 null
468
+ */
469
+ get(id) {
470
+ const entry = this._cache.get(id)
471
+ return entry && entry.isLoaded ? entry.data : null
472
+ }
473
+
474
+ /**
475
+ * 是否已注册
476
+ * @param {string} id - 资源 ID
477
+ * @returns {boolean}
478
+ */
479
+ has(id) {
480
+ return this._cache.has(id)
481
+ }
482
+
483
+ /**
484
+ * 获取资源状态
485
+ * @param {string} id - 资源 ID
486
+ * @returns {string|null} ResourceState 枚举值,未找到返回 null
487
+ */
488
+ getState(id) {
489
+ const entry = this._cache.get(id)
490
+ return entry ? entry.state : null
491
+ }
492
+
493
+ /**
494
+ * 获取资源条目
495
+ * @param {string} id - 资源 ID
496
+ * @returns {ResourceEntry|null}
497
+ */
498
+ getEntry(id) {
499
+ return this._cache.get(id)
500
+ }
501
+
502
+ /**
503
+ * 获取统计信息
504
+ * @returns {Object}
505
+ */
506
+ getStats() {
507
+ return this._cache.getStats()
508
+ }
509
+
510
+ // --------------------------------------------------------------------------
511
+ // 生命周期(继承自 Subsystem)
512
+ // --------------------------------------------------------------------------
513
+
514
+ /**
515
+ * 初始化:自动注册 PixiAdapter
516
+ * @returns {Promise<void>}
517
+ */
518
+ async initialize() {
519
+ await super.initialize()
520
+
521
+ // 自动注册 PixiAdapter(核心内置)
522
+ if (this._pixi) {
523
+ const pixiAdapter = new PixiAdapter(this._pixi)
524
+ // 为 PixiAdapter 支持的 6 种类型各注册一次(通过公共 getter 访问)
525
+ for (const type of pixiAdapter.supportedTypes) {
526
+ this._adapters.set(type, pixiAdapter)
527
+ }
528
+ }
529
+ }
530
+
531
+ /**
532
+ * 销毁:清理所有资源
533
+ * @returns {Promise<void>}
534
+ */
535
+ async destroy() {
536
+ if (this._isDestroyed) return
537
+
538
+ // 清理所有资源(错误不中断)
539
+ try {
540
+ await this.clear()
541
+ } catch (e) {
542
+ // 忽略清理错误
543
+ }
544
+
545
+ // 清理适配器
546
+ this._adapters.clear()
547
+
548
+ // 清理处理器和插件
549
+ this._handlers = []
550
+ this._plugins = []
551
+
552
+ // 销毁内部事件总线
553
+ this._internalBus.destroy()
554
+ this._internalBus = null
555
+
556
+ // 清理缓存和引用计数
557
+ this._cache.clear()
558
+ this._refCounter.clear()
559
+
560
+ this._pixi = null
561
+ this._loadingIds.clear()
562
+
563
+ await super.destroy()
564
+ }
565
+
566
+ // --------------------------------------------------------------------------
567
+ // 私有方法
568
+ // --------------------------------------------------------------------------
569
+
570
+ /**
571
+ * 获取资源条目,不存在则抛错
572
+ * @param {string} id - 资源 ID
573
+ * @returns {ResourceEntry}
574
+ * @throws {ResourceNotFoundError}
575
+ * @private
576
+ */
577
+ _getEntryOrThrow(id) {
578
+ const entry = this._cache.get(id)
579
+ if (!entry) {
580
+ throw new ResourceNotFoundError(id)
581
+ }
582
+ return entry
583
+ }
584
+
585
+ /**
586
+ * 获取适配器,不存在则抛错
587
+ * @param {string} type - 资源类型
588
+ * @returns {import('./adapters/IResourceAdapter.js').IResourceAdapter}
589
+ * @throws {AdapterError}
590
+ * @private
591
+ */
592
+ _getAdapterOrThrow(type) {
593
+ const adapter = this._adapters.get(type)
594
+ if (!adapter) {
595
+ throw new AdapterError(`未找到资源类型 ${type} 的适配器`)
596
+ }
597
+ return adapter
598
+ }
599
+
600
+ /**
601
+ * 执行卸载逻辑
602
+ * @param {ResourceEntry} entry - 资源条目
603
+ * @returns {Promise<void>}
604
+ * @private
605
+ */
606
+ async _doUnload(entry) {
607
+ if (!entry.isLoaded) return
608
+
609
+ const adapter = this._adapters.get(entry.type)
610
+
611
+ try {
612
+ // 执行 beforeUnload 插件钩子
613
+ for (const plugin of this._plugins) {
614
+ plugin.beforeUnload(entry)
615
+ }
616
+
617
+ // 通过适配器卸载
618
+ if (adapter) {
619
+ await adapter.unload(entry)
620
+ }
621
+
622
+ // 执行 afterUnload 插件钩子
623
+ for (const plugin of this._plugins) {
624
+ plugin.afterUnload(entry)
625
+ }
626
+
627
+ // 标记已卸载
628
+ entry.setUnloaded()
629
+
630
+ // 通知处理器
631
+ for (const handler of this._handlers) {
632
+ try {
633
+ handler.onUnload(entry)
634
+ } catch (e) {
635
+ // 处理器错误不影响卸载流程
636
+ }
637
+ }
638
+
639
+ // 广播事件
640
+ this._emitEvent(ResourceEventType.UNLOAD, { id: entry.id })
641
+ } catch (e) {
642
+ // 卸载错误记录但不中断
643
+ this._safeLog('error', `资源 ${entry.id} 卸载失败`, { error: e })
644
+ }
645
+ }
646
+
647
+ /**
648
+ * 等待资源加载完成
649
+ * @param {string} id - 资源 ID
650
+ * @returns {Promise<*>} - 资源数据
651
+ * @private
652
+ */
653
+ _waitForLoad(id) {
654
+ return new Promise((resolve, reject) => {
655
+ // 监听加载完成或失败
656
+ const offComplete = this._internalBus.on(ResourceEventType.LOAD_COMPLETE, (payload) => {
657
+ if (payload.id === id) {
658
+ offComplete()
659
+ offError()
660
+ resolve(payload.data)
661
+ }
662
+ })
663
+
664
+ const offError = this._internalBus.on(ResourceEventType.LOAD_ERROR, (payload) => {
665
+ if (payload.id === id) {
666
+ offComplete()
667
+ offError()
668
+ reject(new ResourceLoadError(id, payload.error))
669
+ }
670
+ })
671
+ })
672
+ }
673
+
674
+ /**
675
+ * 双通道事件广播
676
+ * @param {string} eventType - 事件类型
677
+ * @param {Object} payload - 事件数据
678
+ * @private
679
+ */
680
+ _emitEvent(eventType, payload) {
681
+ // 内部通道
682
+ this._internalBus.emit(eventType, payload)
683
+ // 核心通道(带 asset: 前缀)
684
+ if (this._eventBus) {
685
+ try {
686
+ this._eventBus.emit(`asset:${eventType}`, payload)
687
+ } catch (e) {
688
+ // EventBus 错误不影响资源管理流程
689
+ }
690
+ }
691
+ }
692
+
693
+ /**
694
+ * 防御性日志方法
695
+ * @param {string} level - 日志级别
696
+ * @param {string} message - 日志消息
697
+ * @param {Object} [context={}] - 上下文
698
+ * @private
699
+ */
700
+ _safeLog(level, message, context = {}) {
701
+ try {
702
+ if (this._logger && !this._logger.isDestroyed) {
703
+ this._logger[level](message, context)
704
+ }
705
+ } catch (e) {
706
+ // 完全忽略日志错误
707
+ }
708
+ }
709
+ }
710
+
711
+ export { AssetManager }