diff --git a/docs/guide/world-manager.md b/docs/guide/world-manager.md index 9d378a67..268f831a 100644 --- a/docs/guide/world-manager.md +++ b/docs/guide/world-manager.md @@ -435,7 +435,7 @@ const worldManager = Core.services.resolve(WorldManager); // { // maxWorlds: 50, // autoCleanup: true, -// cleanupInterval: 30000 // 30 秒 +// cleanupFrameInterval: 1800 // 间隔多少帧清理闲置 World // } ``` diff --git a/packages/core/src/ECS/WorldManager.ts b/packages/core/src/ECS/WorldManager.ts index 86ffcd93..107e17a9 100644 --- a/packages/core/src/ECS/WorldManager.ts +++ b/packages/core/src/ECS/WorldManager.ts @@ -19,9 +19,9 @@ export interface IWorldManagerConfig { autoCleanup?: boolean; /** - * 清理间隔(毫秒) + * 清理间隔(帧数) */ - cleanupInterval?: number; + cleanupFrameInterval?: number; /** * 是否启用调试模式 @@ -63,17 +63,16 @@ export interface IWorldManagerConfig { * ``` */ export class WorldManager implements IService { - private readonly _config: IWorldManagerConfig; + private readonly _config: Required; private readonly _worlds: Map = new Map(); - private readonly _activeWorlds: Set = new Set(); - private _cleanupTimer: ReturnType | null = null; private _isRunning: boolean = false; + private _framesSinceCleanup: number = 0; public constructor(config: IWorldManagerConfig = {}) { this._config = { maxWorlds: 50, autoCleanup: true, - cleanupInterval: 30000, // 30秒 + cleanupFrameInterval: 1800, // 1800帧 debug: false, ...config }; @@ -84,10 +83,8 @@ export class WorldManager implements IService { logger.info('WorldManager已初始化', { maxWorlds: this._config.maxWorlds, autoCleanup: this._config.autoCleanup, - cleanupInterval: this._config.cleanupInterval + cleanupFrameInterval: this._config.cleanupFrameInterval }); - - this.startCleanupTimer(); } // ===== World管理 ===== @@ -131,11 +128,6 @@ export class WorldManager implements IService { return false; } - // 如果World正在运行,先停止它 - if (this._activeWorlds.has(worldId)) { - this.setWorldActive(worldId, false); - } - // 销毁World world.destroy(); this._worlds.delete(worldId); @@ -176,11 +168,9 @@ export class WorldManager implements IService { } if (active) { - this._activeWorlds.add(worldId); world.start(); logger.debug(`激活World: ${worldId}`); } else { - this._activeWorlds.delete(worldId); world.stop(); logger.debug(`停用World: ${worldId}`); } @@ -190,7 +180,8 @@ export class WorldManager implements IService { * 检查World是否激活 */ public isWorldActive(worldId: string): boolean { - return this._activeWorlds.has(worldId); + const world = this._worlds.get(worldId); + return world?.isActive ?? false; } // ===== 批量操作 ===== @@ -212,9 +203,8 @@ export class WorldManager implements IService { public updateAll(): void { if (!this._isRunning) return; - for (const worldId of this._activeWorlds) { - const world = this._worlds.get(worldId); - if (world && world.isActive) { + for (const world of this._worlds.values()) { + if (world.isActive) { // 更新World的全局System world.updateGlobalSystems(); @@ -222,6 +212,20 @@ export class WorldManager implements IService { world.updateScenes(); } } + + // 基于帧的自动清理 + if (this._config.autoCleanup) { + this._framesSinceCleanup++; + + if (this._framesSinceCleanup >= this._config.cleanupFrameInterval) { + this.cleanup(); + this._framesSinceCleanup = 0; // 重置计数器 + + if (this._config.debug) { + logger.debug(`执行定期清理World (间隔: ${this._config.cleanupFrameInterval} 帧)`); + } + } + } } /** @@ -229,9 +233,8 @@ export class WorldManager implements IService { */ public getActiveWorlds(): World[] { const activeWorlds: World[] = []; - for (const worldId of this._activeWorlds) { - const world = this._worlds.get(worldId); - if (world) { + for (const world of this._worlds.values()) { + if (world.isActive) { activeWorlds.push(world); } } @@ -244,8 +247,8 @@ export class WorldManager implements IService { public startAll(): void { this._isRunning = true; - for (const worldId of this._worlds.keys()) { - this.setWorldActive(worldId, true); + for (const world of this._worlds.values()) { + world.start(); } logger.info('启动所有World'); @@ -257,8 +260,8 @@ export class WorldManager implements IService { public stopAll(): void { this._isRunning = false; - for (const worldId of this._activeWorlds) { - this.setWorldActive(worldId, false); + for (const world of this._worlds.values()) { + world.stop(); } logger.info('停止所有World'); @@ -297,7 +300,7 @@ export class WorldManager implements IService { public getStats() { const stats = { totalWorlds: this._worlds.size, - activeWorlds: this._activeWorlds.size, + activeWorlds: this.activeWorldCount, totalScenes: 0, totalEntities: 0, totalSystems: 0, @@ -316,7 +319,7 @@ export class WorldManager implements IService { stats.worlds.push({ id: worldId, name: world.name, - isActive: this._activeWorlds.has(worldId), + isActive: world.isActive, sceneCount: world.sceneCount, ...worldStats }); @@ -333,7 +336,7 @@ export class WorldManager implements IService { ...this.getStats(), worlds: Array.from(this._worlds.entries()).map(([worldId, world]) => ({ id: worldId, - isActive: this._activeWorlds.has(worldId), + isActive: world.isActive, status: world.getStatus() })) }; @@ -370,9 +373,6 @@ export class WorldManager implements IService { public destroy(): void { logger.info('正在销毁WorldManager...'); - // 停止清理定时器 - this.stopCleanupTimer(); - // 停止所有World this.stopAll(); @@ -383,7 +383,6 @@ export class WorldManager implements IService { } this._worlds.clear(); - this._activeWorlds.clear(); this._isRunning = false; logger.info('WorldManager已销毁'); @@ -399,62 +398,29 @@ export class WorldManager implements IService { // ===== 私有方法 ===== - /** - * 启动清理定时器 - */ - private startCleanupTimer(): void { - if (!this._config.autoCleanup || this._cleanupTimer) { - return; - } - - this._cleanupTimer = setInterval(() => { - this.cleanup(); - }, this._config.cleanupInterval); - - logger.debug(`启动World清理定时器,间隔: ${this._config.cleanupInterval}ms`); - } - - /** - * 停止清理定时器 - */ - private stopCleanupTimer(): void { - if (this._cleanupTimer) { - clearInterval(this._cleanupTimer); - this._cleanupTimer = null; - logger.debug('停止World清理定时器'); - } - } - /** * 判断World是否应该被清理 + * 清理策略: + * 1. World未激活 + * 2. 没有Scene或所有Scene都是空的 + * 3. 创建时间超过10分钟 */ private shouldCleanupWorld(world: World): boolean { - // 清理策略: - // 1. World未激活 - // 2. 没有Scene或所有Scene都是空的 - // 3. 创建时间超过10分钟 - if (world.isActive) { return false; } + const age = Date.now() - world.createdAt; + const isOldEnough = age > 10 * 60 * 1000; // 10分钟 + if (world.sceneCount === 0) { - const age = Date.now() - world.createdAt; - return age > 10 * 60 * 1000; // 10分钟 + return isOldEnough; } // 检查是否所有Scene都是空的 const allScenes = world.getAllScenes(); - const hasEntities = allScenes.some((scene) => - scene.entities && scene.entities.count > 0 - ); - - if (!hasEntities) { - const age = Date.now() - world.createdAt; - return age > 10 * 60 * 1000; // 10分钟 - } - - return false; + const hasEntities = allScenes.some((scene) => scene.entities && scene.entities.count > 0); + return !hasEntities && isOldEnough; } // ===== 访问器 ===== @@ -470,7 +436,11 @@ export class WorldManager implements IService { * 获取激活World数量 */ public get activeWorldCount(): number { - return this._activeWorlds.size; + let count = 0; + for (const world of this._worlds.values()) { + if (world.isActive) count++; + } + return count; } /** diff --git a/packages/core/tests/ECS/WorldManager.test.ts b/packages/core/tests/ECS/WorldManager.test.ts index 538d5a00..44b1275e 100644 --- a/packages/core/tests/ECS/WorldManager.test.ts +++ b/packages/core/tests/ECS/WorldManager.test.ts @@ -1,14 +1,11 @@ import { WorldManager, IWorldManagerConfig } from '../../src/ECS/WorldManager'; -import { World, IWorldConfig } from '../../src/ECS/World'; -import { Scene } from '../../src/ECS/Scene'; -import { EntitySystem } from '../../src/ECS/Systems/EntitySystem'; +import { IWorldConfig, World } from '../../src/ECS/World'; import { Component } from '../../src/ECS/Component'; -import { Matcher } from '../../src/ECS/Utils/Matcher'; // 测试用组件 class TestComponent extends Component { public value: number = 0; - + constructor(value: number = 0) { super(); this.value = value; @@ -19,19 +16,19 @@ class TestComponent extends Component { class TestGlobalSystem { public readonly name = 'TestGlobalSystem'; public updateCount: number = 0; - + public initialize(): void { // 初始化 } - + public update(): void { this.updateCount++; } - + public reset(): void { this.updateCount = 0; } - + public destroy(): void { // 销毁 } @@ -49,7 +46,7 @@ describe('WorldManager', () => { // 清理所有World if (worldManager) { const worldIds = worldManager.getWorldIds(); - worldIds.forEach(id => { + worldIds.forEach((id) => { worldManager.removeWorld(id); }); // 清理定时器 @@ -92,13 +89,13 @@ describe('WorldManager', () => { describe('World管理', () => { test('创建World应该成功', () => { const world = worldManager.createWorld('test-world'); - + expect(world).toBeDefined(); expect(world.name).toBe('test-world'); expect(worldManager.getWorld('test-world')).toBeDefined(); expect(worldManager.getWorldIds()).toContain('test-world'); }); - + test('创建World时传入配置应该正确', () => { const worldConfig: IWorldConfig = { name: 'ConfiguredWorld', @@ -106,20 +103,20 @@ describe('WorldManager', () => { maxScenes: 5, autoCleanup: false }; - + const world = worldManager.createWorld('configured-world', worldConfig); expect(world.name).toBe('configured-world'); }); - + test('重复的World ID应该抛出错误', () => { worldManager.createWorld('duplicate-world'); - + expect(() => { worldManager.createWorld('duplicate-world'); }).toThrow("World ID 'duplicate-world' 已存在"); }); - + test('超出最大World数量应该抛出错误', () => { const limitedManager = new WorldManager({ maxWorlds: 2 }); @@ -128,301 +125,454 @@ describe('WorldManager', () => { expect(() => { limitedManager.createWorld('world3'); - }).toThrow("已达到最大World数量限制: 2"); + }).toThrow('已达到最大World数量限制: 2'); // 清理 limitedManager.destroy(); }); - + test('获取World应该正确', () => { const world = worldManager.createWorld('get-world'); const retrievedWorld = worldManager.getWorld('get-world'); - + expect(retrievedWorld).toBe(world); }); - + test('获取不存在的World应该返回null', () => { const world = worldManager.getWorld('non-existent'); expect(world).toBeNull(); }); - + test('检查World存在性应该正确', () => { expect(worldManager.getWorld('non-existent')).toBeNull(); - + worldManager.createWorld('exists'); expect(worldManager.getWorld('exists')).toBeDefined(); }); - + test('销毁World应该正确清理', () => { const world = worldManager.createWorld('destroy-world'); world.start(); - + const destroyed = worldManager.removeWorld('destroy-world'); - + expect(destroyed).toBe(true); expect(worldManager.getWorld('destroy-world')).toBeNull(); }); - + test('销毁不存在的World应该返回false', () => { const destroyed = worldManager.removeWorld('non-existent'); expect(destroyed).toBe(false); }); - + test('获取所有World ID应该正确', () => { worldManager.createWorld('world1'); worldManager.createWorld('world2'); worldManager.createWorld('world3'); - + const worldIds = worldManager.getWorldIds(); - + expect(worldIds).toHaveLength(3); expect(worldIds).toContain('world1'); expect(worldIds).toContain('world2'); expect(worldIds).toContain('world3'); }); }); - + describe('活跃World管理', () => { test('启动World应该加入活跃列表', () => { const world = worldManager.createWorld('active-world'); - + worldManager.setWorldActive('active-world', true); - + const activeWorlds = worldManager.getActiveWorlds(); expect(activeWorlds).toHaveLength(1); expect(activeWorlds[0]).toBe(world); }); - + test('停止World应该从活跃列表移除', () => { - const world = worldManager.createWorld('inactive-world'); + worldManager.createWorld('inactive-world'); worldManager.setWorldActive('inactive-world', true); - + worldManager.setWorldActive('inactive-world', false); - + const activeWorlds = worldManager.getActiveWorlds(); expect(activeWorlds).toHaveLength(0); }); - + test('销毁激活的World应该从活跃列表移除', () => { - const world = worldManager.createWorld('destroy-active'); + worldManager.createWorld('destroy-active'); worldManager.setWorldActive('destroy-active', true); - + worldManager.removeWorld('destroy-active'); - + const activeWorlds = worldManager.getActiveWorlds(); expect(activeWorlds).toHaveLength(0); }); - + test('多个World的激活状态应该独立管理', () => { const world1 = worldManager.createWorld('world1'); const world2 = worldManager.createWorld('world2'); const world3 = worldManager.createWorld('world3'); - + worldManager.setWorldActive('world1', true); worldManager.setWorldActive('world3', true); // world2 保持未启动 - + const activeWorlds = worldManager.getActiveWorlds(); - + expect(activeWorlds).toHaveLength(2); expect(activeWorlds).toContain(world1); expect(activeWorlds).toContain(world3); expect(activeWorlds).not.toContain(world2); }); }); - + describe('统计和监控', () => { test('获取WorldManager状态应该正确', () => { worldManager.createWorld('status-world1'); - const world2 = worldManager.createWorld('status-world2'); + worldManager.createWorld('status-world2'); worldManager.setWorldActive('status-world2', true); - + const status = worldManager.getStats(); - + expect(status.totalWorlds).toBe(2); expect(status.activeWorlds).toBe(1); expect(status.config.maxWorlds).toBeGreaterThan(0); expect(status.memoryUsage).toBeGreaterThanOrEqual(0); expect(status.isRunning).toBeDefined(); }); - + test('获取所有World统计应该包含详细信息', () => { const world1 = worldManager.createWorld('stats-world1'); - const world2 = worldManager.createWorld('stats-world2'); - + worldManager.createWorld('stats-world2'); + // 为world1添加一些内容 const scene1 = world1.createScene('scene1'); scene1.createEntity('entity1'); worldManager.setWorldActive('stats-world1', true); - + // world2保持空 - + const allStats = worldManager.getDetailedStatus().worlds; - + expect(allStats).toHaveLength(2); - - const world1Stats = allStats.find(stat => stat.id === 'stats-world1'); - const world2Stats = allStats.find(stat => stat.id === 'stats-world2'); - + + const world1Stats = allStats.find((stat) => stat.id === 'stats-world1'); + const world2Stats = allStats.find((stat) => stat.id === 'stats-world2'); + expect(world1Stats).toBeDefined(); expect(world2Stats).toBeDefined(); expect(world1Stats?.isActive).toBe(true); expect(world2Stats?.isActive).toBe(false); }); - + test('空WorldManager的统计应该正确', () => { const status = worldManager.getStats(); const allStats = worldManager.getDetailedStatus().worlds; - + expect(status.totalWorlds).toBe(0); expect(status.activeWorlds).toBe(0); expect(allStats).toHaveLength(0); }); }); - + describe('清理功能', () => { test('清理空闲World应该移除符合条件的World', () => { // 创建一个空的World - const emptyWorld = worldManager.createWorld('empty-world'); - + worldManager.createWorld('empty-world'); + // 创建一个有内容的World const fullWorld = worldManager.createWorld('full-world'); const scene = fullWorld.createScene('scene'); scene.createEntity('entity'); fullWorld.start(); - + // 执行清理 const cleanedCount = worldManager.cleanup(); - + // 由于清理逻辑可能基于时间或其他条件,这里主要测试不会出错 expect(cleanedCount).toBeGreaterThanOrEqual(0); expect(() => worldManager.cleanup()).not.toThrow(); }); + + test('应该在指定帧数后自动执行清理', () => { + jest.useFakeTimers(); + + const manager = new WorldManager({ + autoCleanup: true, + cleanupFrameInterval: 10 // 10 帧后清理 + }); + + manager.createWorld('test'); + // 模拟一个空的老 World (超过 10 分钟) + jest.advanceTimersByTime(11 * 60 * 1000); + + // 执行 9 帧,不应该清理 + for (let i = 0; i < 9; i++) { + manager.updateAll(); + } + expect(manager.getWorld('test')).not.toBeNull(); + + // 第 10 帧,应该清理 + manager.updateAll(); + expect(manager.getWorld('test')).toBeNull(); + + jest.useRealTimers(); + manager.destroy(); + }); + + test('autoCleanup=false 时不应该自动清理', () => { + jest.useFakeTimers(); + + const manager = new WorldManager({ + autoCleanup: false + }); + + manager.createWorld('test'); + jest.advanceTimersByTime(20 * 60 * 1000); + + // 执行多帧 + for (let i = 0; i < 2000; i++) { + manager.updateAll(); + } + + // 不应该清理 + expect(manager.getWorld('test')).not.toBeNull(); + + jest.useRealTimers(); + manager.destroy(); + }); + + test('清理后计数器应该重置', () => { + jest.useFakeTimers(); + + const manager = new WorldManager({ + autoCleanup: true, + cleanupFrameInterval: 10 + }); + + manager.createWorld('world1'); + jest.advanceTimersByTime(11 * 60 * 1000); + + // 第 10 帧触发清理 + for (let i = 0; i < 10; i++) { + manager.updateAll(); + } + + // world1 被清理 + expect(manager.getWorld('world1')).toBeNull(); + + // 创建新 World + manager.createWorld('world2'); + jest.advanceTimersByTime(11 * 60 * 1000); + + // 又要等 10 帧才能清理 (计数器已重置) + for (let i = 0; i < 9; i++) { + manager.updateAll(); + } + expect(manager.getWorld('world2')).not.toBeNull(); + + manager.updateAll(); + expect(manager.getWorld('world2')).toBeNull(); + + jest.useRealTimers(); + manager.destroy(); + }); + + test('应该使用配置的 cleanupFrameInterval', () => { + const manager = new WorldManager({ + autoCleanup: true, + cleanupFrameInterval: 5 + }); + + expect(manager.config.cleanupFrameInterval).toBe(5); + + manager.destroy(); + }); }); - + describe('World更新协调', () => { test('更新所有活跃World应该正确', () => { const world1 = worldManager.createWorld('update-world1'); const world2 = worldManager.createWorld('update-world2'); - const world3 = worldManager.createWorld('update-world3'); - + worldManager.createWorld('update-world3'); + // 添加一些内容到World中 const scene1 = world1.createScene('scene1'); const scene2 = world2.createScene('scene2'); - + scene1.createEntity('entity1'); scene2.createEntity('entity2'); - + // 启动部分World worldManager.setWorldActive('update-world1', true); worldManager.setWorldActive('update-world2', true); // world3保持未启动 - + // 手动调用更新(通常由Core.update()调用) const activeWorlds = worldManager.getActiveWorlds(); - + expect(() => { - activeWorlds.forEach(world => { + activeWorlds.forEach((world) => { world.updateGlobalSystems(); world.updateScenes(); }); }).not.toThrow(); - + expect(activeWorlds).toHaveLength(2); }); + + test('updateAll 应该只更新活跃的 World', () => { + const world1 = worldManager.createWorld('world1'); + const world2 = worldManager.createWorld('world2'); + + const system1 = new TestGlobalSystem(); + const system2 = new TestGlobalSystem(); + + world1.addGlobalSystem(system1); + world2.addGlobalSystem(system2); + + worldManager.setWorldActive('world1', true); + // world2 保持未激活 + + worldManager.updateAll(); + + expect(system1.updateCount).toBe(1); // world1 被更新 + expect(system2.updateCount).toBe(0); // world2 未被更新 + }); + + test('isRunning=false 时 updateAll 不应该更新任何 World', () => { + const world = worldManager.createWorld('world'); + const system = new TestGlobalSystem(); + world.addGlobalSystem(system); + worldManager.setWorldActive('world', true); + + worldManager.stopAll(); // isRunning = false + worldManager.updateAll(); + + expect(system.updateCount).toBe(0); + }); + + test('updateAll 应该正确处理多个活跃和非活跃 World', () => { + const worlds: World[] = []; + const systems: TestGlobalSystem[] = []; + + // 创建 5 个 World + for (let i = 0; i < 5; i++) { + const world = worldManager.createWorld(`world${i}`); + const system = new TestGlobalSystem(); + world.addGlobalSystem(system); + worlds.push(world); + systems.push(system); + } + + // 激活第 0, 2, 4 个 + worldManager.setWorldActive('world0', true); + worldManager.setWorldActive('world2', true); + worldManager.setWorldActive('world4', true); + + worldManager.updateAll(); + + expect(systems[0].updateCount).toBe(1); // 活跃 + expect(systems[1].updateCount).toBe(0); // 非活跃 + expect(systems[2].updateCount).toBe(1); // 活跃 + expect(systems[3].updateCount).toBe(0); // 非活跃 + expect(systems[4].updateCount).toBe(1); // 活跃 + }); }); - + describe('边界情况和错误处理', () => { test('World ID为空字符串应该抛出错误', () => { expect(() => { worldManager.createWorld(''); }).toThrow(); }); - + test('World ID为null或undefined应该抛出错误', () => { expect(() => { - worldManager.createWorld(null as any); + worldManager.createWorld(null as unknown as string); }).toThrow(); - + expect(() => { - worldManager.createWorld(undefined as any); + worldManager.createWorld(undefined as unknown as string); }).toThrow(); }); - + test('极限情况下的大量World管理', () => { const worldCount = 50; const worldIds: string[] = []; - + // 创建大量World for (let i = 0; i < worldCount; i++) { const worldId = `mass-world-${i}`; worldIds.push(worldId); - + expect(() => { worldManager.createWorld(worldId); }).not.toThrow(); } - + expect(worldManager.getWorldIds()).toHaveLength(worldCount); - + // 启动一半的World for (let i = 0; i < worldCount / 2; i++) { worldManager.setWorldActive(worldIds[i], true); } - + expect(worldManager.getActiveWorlds()).toHaveLength(worldCount / 2); - + // 批量清理 - worldIds.forEach(id => { + worldIds.forEach((id) => { expect(() => { worldManager.removeWorld(id); }).not.toThrow(); }); - + expect(worldManager.getWorldIds()).toHaveLength(0); }); - + test('销毁后获取World应该返回null', () => { worldManager.createWorld('temp-world'); worldManager.removeWorld('temp-world'); - + expect(worldManager.getWorld('temp-world')).toBeNull(); }); }); - + describe('内存管理', () => { test('销毁所有World后内存应该被释放', () => { // 创建多个World并添加内容 for (let i = 0; i < 10; i++) { const world = worldManager.createWorld(`memory-world-${i}`); const scene = world.createScene('scene'); - + // 添加一些实体和系统 for (let j = 0; j < 5; j++) { const entity = scene.createEntity(`entity-${j}`); entity.addComponent(new TestComponent(j)); } - + world.addGlobalSystem(new TestGlobalSystem()); worldManager.setWorldActive(`memory-world-${i}`, true); } - + const beforeCleanup = worldManager.getStats(); expect(beforeCleanup.totalWorlds).toBe(10); expect(beforeCleanup.activeWorlds).toBe(10); - + // 清理所有World const worldIds = worldManager.getWorldIds(); - worldIds.forEach(id => { + worldIds.forEach((id) => { worldManager.removeWorld(id); }); - + const afterCleanup = worldManager.getStats(); expect(afterCleanup.totalWorlds).toBe(0); expect(afterCleanup.activeWorlds).toBe(0); }); }); - + describe('配置验证', () => { test('无效的maxWorlds配置应该按传入值使用', () => { const invalidConfig: IWorldManagerConfig = { @@ -461,4 +611,95 @@ describe('WorldManager', () => { manager.destroy(); }); }); -}); \ No newline at end of file + + describe('isWorldActive()', () => { + test('应该正确返回 World 激活状态', () => { + worldManager.createWorld('test'); + + // 初始未激活 + expect(worldManager.isWorldActive('test')).toBe(false); + + // 激活后 + worldManager.setWorldActive('test', true); + expect(worldManager.isWorldActive('test')).toBe(true); + + // 停用后 + worldManager.setWorldActive('test', false); + expect(worldManager.isWorldActive('test')).toBe(false); + }); + + test('不存在的 World 应该返回 false', () => { + expect(worldManager.isWorldActive('non-existent')).toBe(false); + }); + + test('应该与 world.isActive 保持一致', () => { + const world = worldManager.createWorld('test'); + + expect(worldManager.isWorldActive('test')).toBe(world.isActive); + + worldManager.setWorldActive('test', true); + expect(worldManager.isWorldActive('test')).toBe(world.isActive); + + worldManager.setWorldActive('test', false); + expect(worldManager.isWorldActive('test')).toBe(world.isActive); + }); + }); + + describe('activeWorldCount', () => { + test('应该正确返回激活的 World 数量', () => { + expect(worldManager.activeWorldCount).toBe(0); + + worldManager.createWorld('world1'); + worldManager.createWorld('world2'); + worldManager.createWorld('world3'); + + expect(worldManager.activeWorldCount).toBe(0); + + worldManager.setWorldActive('world1', true); + expect(worldManager.activeWorldCount).toBe(1); + + worldManager.setWorldActive('world2', true); + expect(worldManager.activeWorldCount).toBe(2); + + worldManager.setWorldActive('world1', false); + expect(worldManager.activeWorldCount).toBe(1); + }); + + test('应该与 getActiveWorlds().length 一致', () => { + worldManager.createWorld('world1'); + worldManager.createWorld('world2'); + worldManager.setWorldActive('world1', true); + + expect(worldManager.activeWorldCount).toBe(worldManager.getActiveWorlds().length); + }); + }); + + describe('getAllWorlds()', () => { + test('应该返回所有 World', () => { + const world1 = worldManager.createWorld('world1'); + const world2 = worldManager.createWorld('world2'); + const world3 = worldManager.createWorld('world3'); + + const allWorlds = worldManager.getAllWorlds(); + + expect(allWorlds).toHaveLength(3); + expect(allWorlds).toContain(world1); + expect(allWorlds).toContain(world2); + expect(allWorlds).toContain(world3); + }); + + test('空 WorldManager 应该返回空数组', () => { + const allWorlds = worldManager.getAllWorlds(); + expect(allWorlds).toHaveLength(0); + }); + + test('返回的数组修改不应该影响内部状态', () => { + worldManager.createWorld('world1'); + + const allWorlds = worldManager.getAllWorlds(); + allWorlds.push({} as unknown as World); + + expect(worldManager.getAllWorlds()).toHaveLength(1); + }); + }); +});