新增world概念(多world管理多scene概念)现在支持多个world多个scene同时更新
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@esengine/ecs-framework",
|
||||
"version": "2.1.44",
|
||||
"version": "2.1.45",
|
||||
"description": "用于Laya、Cocos Creator等JavaScript游戏引擎的高性能ECS框架",
|
||||
"main": "bin/index.js",
|
||||
"types": "bin/index.d.ts",
|
||||
|
||||
@@ -6,8 +6,8 @@ import { Time } from './Utils/Time';
|
||||
import { PerformanceMonitor } from './Utils/PerformanceMonitor';
|
||||
import { PoolManager } from './Utils/Pool/PoolManager';
|
||||
import { ECSFluentAPI, createECSAPI } from './ECS/Core/FluentAPI';
|
||||
import { Scene } from './ECS/Scene';
|
||||
import { IScene } from './ECS/IScene';
|
||||
import { WorldManager } from './ECS/WorldManager';
|
||||
import { DebugManager } from './Utils/Debug';
|
||||
import { ICoreConfig, IECSDebugConfig } from './Types';
|
||||
import { BigIntFactory, EnvironmentInfo } from './ECS/Utils/BigIntCompatibility';
|
||||
@@ -46,6 +46,20 @@ export class Core {
|
||||
* 当设置为true时,游戏循环将暂停执行。
|
||||
*/
|
||||
public static paused = false;
|
||||
|
||||
/**
|
||||
* 默认World ID
|
||||
*
|
||||
* 用于单Scene模式的默认World标识
|
||||
*/
|
||||
private static readonly DEFAULT_WORLD_ID = '__default__';
|
||||
|
||||
/**
|
||||
* 默认Scene ID
|
||||
*
|
||||
* 用于单Scene模式的默认Scene标识
|
||||
*/
|
||||
private static readonly DEFAULT_SCENE_ID = '__main__';
|
||||
|
||||
/**
|
||||
* 全局核心实例
|
||||
@@ -71,12 +85,6 @@ export class Core {
|
||||
*/
|
||||
public readonly debug: boolean;
|
||||
|
||||
/**
|
||||
* 待切换的场景
|
||||
*
|
||||
* 存储下一帧要切换到的场景实例。
|
||||
*/
|
||||
public _nextScene: IScene | null = null;
|
||||
|
||||
/**
|
||||
* 全局管理器集合
|
||||
@@ -113,10 +121,6 @@ export class Core {
|
||||
*/
|
||||
public _ecsAPI?: ECSFluentAPI;
|
||||
|
||||
/**
|
||||
* 当前活动场景
|
||||
*/
|
||||
public _scene?: IScene;
|
||||
|
||||
/**
|
||||
* 调试管理器
|
||||
@@ -125,6 +129,13 @@ export class Core {
|
||||
*/
|
||||
public _debugManager?: DebugManager;
|
||||
|
||||
/**
|
||||
* World管理器
|
||||
*
|
||||
* 管理多个World实例,支持多房间/多世界架构。
|
||||
*/
|
||||
public _worldManager?: WorldManager;
|
||||
|
||||
/**
|
||||
* Core配置
|
||||
*/
|
||||
@@ -194,82 +205,63 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前活动的场景
|
||||
* 获取当前活动的场景(属性访问器)
|
||||
*
|
||||
* @returns 当前场景实例,如果没有则返回null
|
||||
*/
|
||||
public static get scene(): IScene | null {
|
||||
if (!this._instance)
|
||||
return null;
|
||||
return this._instance._scene || null;
|
||||
return this.getScene();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置当前场景(已废弃)
|
||||
* 获取当前活动的场景(方法调用)
|
||||
*
|
||||
* @deprecated 请使用 Core.setScene() 方法代替。scene setter 可能导致场景延迟激活的时序问题,
|
||||
* 而 setScene() 提供更好的类型安全性和可预测的激活时序。
|
||||
*
|
||||
* 迁移示例:
|
||||
* ```typescript
|
||||
* // 旧方式(已废弃)
|
||||
* Core.scene = myScene;
|
||||
*
|
||||
* // 新方式(推荐)
|
||||
* Core.setScene(myScene);
|
||||
* ```
|
||||
*
|
||||
* 如果当前没有场景,会立即切换;否则会在下一帧切换。
|
||||
*
|
||||
* @param value - 场景实例
|
||||
* @returns 当前场景实例,如果没有则返回null
|
||||
*/
|
||||
public static set scene(value: IScene | null) {
|
||||
if (!value) return;
|
||||
|
||||
if (this._instance._scene == null) {
|
||||
this._instance.setSceneInternal(value);
|
||||
} else {
|
||||
this._instance._nextScene = value;
|
||||
public static getScene<T extends IScene>(): T | null {
|
||||
if (!this._instance) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 确保默认World存在
|
||||
this._instance.ensureDefaultWorld();
|
||||
|
||||
const defaultWorld = this._instance._worldManager!.getWorld(this.DEFAULT_WORLD_ID);
|
||||
return defaultWorld?.getScene(this.DEFAULT_SCENE_ID) as T || null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 类型安全的场景设置方法(推荐)
|
||||
*
|
||||
* 这是设置场景的推荐方法,提供更好的类型安全性和可预测的激活时序。
|
||||
* 相比于 scene setter,此方法能确保场景正确初始化和激活。
|
||||
*
|
||||
* 如果当前没有场景,会立即切换;否则会在下一帧切换。
|
||||
* 设置当前场景
|
||||
*
|
||||
* @param scene - 要设置的场景实例
|
||||
* @returns 设置的场景实例,便于链式调用
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const myScene = new MyScene();
|
||||
* Core.setScene(myScene);
|
||||
*
|
||||
* // 链式调用
|
||||
* const scene = Core.setScene(new MyScene()).addSystem(new MySystem());
|
||||
* ```
|
||||
*/
|
||||
public static setScene<T extends IScene>(scene: T): T {
|
||||
if (this._instance._scene == null) {
|
||||
this._instance.setSceneInternal(scene);
|
||||
} else {
|
||||
this._instance._nextScene = scene;
|
||||
if (!this._instance) {
|
||||
throw new Error("Core实例未创建,请先调用Core.create()");
|
||||
}
|
||||
|
||||
// 确保默认World存在
|
||||
this._instance.ensureDefaultWorld();
|
||||
|
||||
const defaultWorld = this._instance._worldManager!.getWorld(this.DEFAULT_WORLD_ID)!;
|
||||
|
||||
// 移除旧的主Scene(如果存在)
|
||||
if (defaultWorld.getScene(this.DEFAULT_SCENE_ID)) {
|
||||
defaultWorld.removeScene(this.DEFAULT_SCENE_ID);
|
||||
}
|
||||
|
||||
// 添加新Scene到默认World
|
||||
defaultWorld.createScene(this.DEFAULT_SCENE_ID, scene);
|
||||
defaultWorld.setSceneActive(this.DEFAULT_SCENE_ID, true);
|
||||
|
||||
// 触发场景切换回调
|
||||
this._instance.onSceneChanged();
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的场景获取方法
|
||||
*
|
||||
* @returns 当前场景实例
|
||||
*/
|
||||
public static getScene<T extends IScene>(): T | null {
|
||||
return this._instance?._scene as T || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建Core实例
|
||||
@@ -466,15 +458,61 @@ export class Core {
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部场景设置方法
|
||||
* 获取WorldManager实例
|
||||
*
|
||||
* @param scene - 要设置的场景实例
|
||||
* @returns WorldManager实例,如果未初始化则自动创建
|
||||
*/
|
||||
private setSceneInternal(scene: IScene): void {
|
||||
this._scene = scene;
|
||||
this.onSceneChanged();
|
||||
this._scene.initialize();
|
||||
this._scene.begin();
|
||||
public static getWorldManager(): WorldManager {
|
||||
if (!this._instance) {
|
||||
throw new Error("Core实例未创建,请先调用Core.create()");
|
||||
}
|
||||
|
||||
if (!this._instance._worldManager) {
|
||||
// 多World模式的配置(用户主动获取WorldManager)
|
||||
this._instance._worldManager = WorldManager.getInstance({
|
||||
maxWorlds: 50,
|
||||
autoCleanup: true,
|
||||
cleanupInterval: 60000,
|
||||
debug: this._instance._config.debug
|
||||
});
|
||||
}
|
||||
|
||||
return this._instance._worldManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用World管理
|
||||
*
|
||||
* 显式启用World功能,用于多房间/多世界架构
|
||||
*/
|
||||
public static enableWorldManager(): WorldManager {
|
||||
return this.getWorldManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* 确保默认World存在
|
||||
*
|
||||
* 内部方法,用于懒初始化默认World
|
||||
*/
|
||||
private ensureDefaultWorld(): void {
|
||||
if (!this._worldManager) {
|
||||
this._worldManager = WorldManager.getInstance({
|
||||
maxWorlds: 1, // 单场景用户只需要1个World
|
||||
autoCleanup: false, // 单场景不需要自动清理
|
||||
cleanupInterval: 0, // 禁用清理定时器
|
||||
debug: this._config.debug
|
||||
});
|
||||
}
|
||||
|
||||
// 检查默认World是否存在
|
||||
if (!this._worldManager.getWorld(Core.DEFAULT_WORLD_ID)) {
|
||||
this._worldManager.createWorld(Core.DEFAULT_WORLD_ID, {
|
||||
name: 'DefaultWorld',
|
||||
maxScenes: 1,
|
||||
autoCleanup: false
|
||||
});
|
||||
this._worldManager.setWorldActive(Core.DEFAULT_WORLD_ID, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -485,28 +523,19 @@ export class Core {
|
||||
public onSceneChanged() {
|
||||
Time.sceneChanged();
|
||||
|
||||
// 获取当前Scene(从默认World)
|
||||
const currentScene = Core.getScene();
|
||||
|
||||
// 初始化ECS API(如果场景支持)
|
||||
if (this._scene && this._scene.querySystem && this._scene.eventSystem) {
|
||||
this._ecsAPI = createECSAPI(this._scene, this._scene.querySystem, this._scene.eventSystem);
|
||||
if (currentScene && currentScene.querySystem && currentScene.eventSystem) {
|
||||
this._ecsAPI = createECSAPI(currentScene, currentScene.querySystem, currentScene.eventSystem);
|
||||
}
|
||||
|
||||
// 延迟调试管理器通知,避免在场景初始化过程中干扰属性
|
||||
if (this._debugManager) {
|
||||
// 使用 requestAnimationFrame 确保在场景完全初始化后再收集数据
|
||||
if (typeof requestAnimationFrame !== 'undefined') {
|
||||
requestAnimationFrame(() => {
|
||||
if (this._debugManager) {
|
||||
this._debugManager.onSceneChanged();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 兜底:使用 setTimeout
|
||||
setTimeout(() => {
|
||||
if (this._debugManager) {
|
||||
this._debugManager.onSceneChanged();
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
queueMicrotask(() => {
|
||||
this._debugManager?.onSceneChanged();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,23 +596,25 @@ export class Core {
|
||||
// 更新对象池管理器
|
||||
this._poolManager.update();
|
||||
|
||||
// 处理场景切换
|
||||
if (this._nextScene != null) {
|
||||
if (this._scene != null)
|
||||
this._scene.end();
|
||||
// 更新所有World
|
||||
if (this._worldManager) {
|
||||
const worldsStartTime = this._performanceMonitor.startMonitoring('Worlds.update');
|
||||
const activeWorlds = this._worldManager.getActiveWorlds();
|
||||
let totalWorldEntities = 0;
|
||||
|
||||
this._scene = this._nextScene;
|
||||
this._nextScene = null;
|
||||
this.onSceneChanged();
|
||||
this._scene.begin();
|
||||
}
|
||||
for (const world of activeWorlds) {
|
||||
// 更新World的全局System
|
||||
world.updateGlobalSystems();
|
||||
|
||||
// 更新World中的所有Scene
|
||||
world.updateScenes();
|
||||
|
||||
// 更新当前场景
|
||||
if (this._scene != null && this._scene.update) {
|
||||
const sceneStartTime = this._performanceMonitor.startMonitoring('Scene.update');
|
||||
this._scene.update();
|
||||
const entityCount = this._scene.entities?.count || 0;
|
||||
this._performanceMonitor.endMonitoring('Scene.update', sceneStartTime, entityCount);
|
||||
// 统计实体数量(用于性能监控)
|
||||
const worldStats = world.getStats();
|
||||
totalWorldEntities += worldStats.totalEntities;
|
||||
}
|
||||
|
||||
this._performanceMonitor.endMonitoring('Worlds.update', worldsStartTime, totalWorldEntities);
|
||||
}
|
||||
|
||||
// 更新调试管理器(基于FPS的数据发送)
|
||||
|
||||
504
packages/core/src/ECS/World.ts
Normal file
504
packages/core/src/ECS/World.ts
Normal file
@@ -0,0 +1,504 @@
|
||||
import { IScene } from './IScene';
|
||||
import { Scene } from './Scene';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
|
||||
const logger = createLogger('World');
|
||||
|
||||
/**
|
||||
* 全局系统接口
|
||||
* 全局系统是在World级别运行的系统,不依赖特定Scene
|
||||
*/
|
||||
export interface IGlobalSystem {
|
||||
/**
|
||||
* 系统名称
|
||||
*/
|
||||
readonly name: string;
|
||||
|
||||
/**
|
||||
* 初始化系统
|
||||
*/
|
||||
initialize?(): void;
|
||||
|
||||
/**
|
||||
* 更新系统
|
||||
*/
|
||||
update(deltaTime?: number): void;
|
||||
|
||||
/**
|
||||
* 重置系统
|
||||
*/
|
||||
reset?(): void;
|
||||
|
||||
/**
|
||||
* 销毁系统
|
||||
*/
|
||||
destroy?(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* World配置接口
|
||||
*/
|
||||
export interface IWorldConfig {
|
||||
/**
|
||||
* World名称
|
||||
*/
|
||||
name?: string;
|
||||
|
||||
/**
|
||||
* 是否启用调试模式
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* 最大Scene数量限制
|
||||
*/
|
||||
maxScenes?: number;
|
||||
|
||||
/**
|
||||
* 是否自动清理空Scene
|
||||
*/
|
||||
autoCleanup?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* World类 - ECS世界管理器
|
||||
*
|
||||
* World是Scene的容器,每个World可以管理多个Scene。
|
||||
* 这种设计允许创建独立的游戏世界,如:
|
||||
* - 游戏房间(每个房间一个World)
|
||||
* - 不同的游戏模式
|
||||
* - 独立的模拟环境
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 创建游戏房间的World
|
||||
* const roomWorld = new World({ name: 'Room_001' });
|
||||
*
|
||||
* // 在World中创建Scene
|
||||
* const gameScene = roomWorld.createScene('game', new Scene());
|
||||
* const uiScene = roomWorld.createScene('ui', new Scene());
|
||||
*
|
||||
* // 更新整个World
|
||||
* roomWorld.update(deltaTime);
|
||||
* ```
|
||||
*/
|
||||
export class World {
|
||||
public readonly name: string;
|
||||
private readonly _config: IWorldConfig;
|
||||
private readonly _scenes: Map<string, IScene> = new Map();
|
||||
private readonly _activeScenes: Set<string> = new Set();
|
||||
private readonly _globalSystems: IGlobalSystem[] = [];
|
||||
private _isActive: boolean = false;
|
||||
private _createdAt: number;
|
||||
|
||||
constructor(config: IWorldConfig = {}) {
|
||||
this._config = {
|
||||
name: 'World',
|
||||
debug: false,
|
||||
maxScenes: 10,
|
||||
autoCleanup: true,
|
||||
...config
|
||||
};
|
||||
|
||||
this.name = this._config.name!;
|
||||
this._createdAt = Date.now();
|
||||
|
||||
logger.info(`创建World: ${this.name}`);
|
||||
}
|
||||
|
||||
// ===== Scene管理 =====
|
||||
|
||||
/**
|
||||
* 创建并添加Scene到World
|
||||
*/
|
||||
public createScene<T extends IScene>(sceneId: string, sceneInstance?: T): T {
|
||||
if (this._scenes.has(sceneId)) {
|
||||
throw new Error(`Scene ID '${sceneId}' 已存在于World '${this.name}' 中`);
|
||||
}
|
||||
|
||||
if (this._scenes.size >= this._config.maxScenes!) {
|
||||
throw new Error(`World '${this.name}' 已达到最大Scene数量限制: ${this._config.maxScenes}`);
|
||||
}
|
||||
|
||||
// 如果没有提供Scene实例,创建默认Scene
|
||||
const scene = sceneInstance || (new Scene() as unknown as T);
|
||||
|
||||
// 设置Scene的标识
|
||||
if ('id' in scene) {
|
||||
(scene as any).id = sceneId;
|
||||
}
|
||||
if ('name' in scene && !scene.name) {
|
||||
scene.name = sceneId;
|
||||
}
|
||||
|
||||
this._scenes.set(sceneId, scene);
|
||||
|
||||
// 初始化Scene
|
||||
scene.initialize();
|
||||
|
||||
logger.info(`在World '${this.name}' 中创建Scene: ${sceneId}`);
|
||||
return scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除Scene
|
||||
*/
|
||||
public removeScene(sceneId: string): boolean {
|
||||
const scene = this._scenes.get(sceneId);
|
||||
if (!scene) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果Scene正在运行,先停止它
|
||||
if (this._activeScenes.has(sceneId)) {
|
||||
this.setSceneActive(sceneId, false);
|
||||
}
|
||||
|
||||
// 清理Scene资源
|
||||
scene.end();
|
||||
this._scenes.delete(sceneId);
|
||||
|
||||
logger.info(`从World '${this.name}' 中移除Scene: ${sceneId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Scene
|
||||
*/
|
||||
public getScene<T extends IScene>(sceneId: string): T | null {
|
||||
return this._scenes.get(sceneId) as T || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有Scene ID
|
||||
*/
|
||||
public getSceneIds(): string[] {
|
||||
return Array.from(this._scenes.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有Scene
|
||||
*/
|
||||
public getAllScenes(): IScene[] {
|
||||
return Array.from(this._scenes.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Scene激活状态
|
||||
*/
|
||||
public setSceneActive(sceneId: string, active: boolean): void {
|
||||
const scene = this._scenes.get(sceneId);
|
||||
if (!scene) {
|
||||
logger.warn(`Scene '${sceneId}' 不存在于World '${this.name}' 中`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
this._activeScenes.add(sceneId);
|
||||
// 启动Scene
|
||||
if (scene.begin) {
|
||||
scene.begin();
|
||||
}
|
||||
logger.debug(`在World '${this.name}' 中激活Scene: ${sceneId}`);
|
||||
} else {
|
||||
this._activeScenes.delete(sceneId);
|
||||
// 可选择性地停止Scene,或者让它继续运行但不更新
|
||||
logger.debug(`在World '${this.name}' 中停用Scene: ${sceneId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Scene是否激活
|
||||
*/
|
||||
public isSceneActive(sceneId: string): boolean {
|
||||
return this._activeScenes.has(sceneId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃Scene数量
|
||||
*/
|
||||
public getActiveSceneCount(): number {
|
||||
return this._activeScenes.size;
|
||||
}
|
||||
|
||||
// ===== 全局System管理 =====
|
||||
|
||||
/**
|
||||
* 添加全局System
|
||||
* 全局System会在所有激活Scene之前更新
|
||||
*/
|
||||
public addGlobalSystem<T extends IGlobalSystem>(system: T): T {
|
||||
if (this._globalSystems.includes(system)) {
|
||||
return system;
|
||||
}
|
||||
|
||||
this._globalSystems.push(system);
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
|
||||
logger.debug(`在World '${this.name}' 中添加全局System: ${system.name}`);
|
||||
return system;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除全局System
|
||||
*/
|
||||
public removeGlobalSystem(system: IGlobalSystem): boolean {
|
||||
const index = this._globalSystems.indexOf(system);
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._globalSystems.splice(index, 1);
|
||||
if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
|
||||
logger.debug(`从World '${this.name}' 中移除全局System: ${system.name}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局System
|
||||
*/
|
||||
public getGlobalSystem<T extends IGlobalSystem>(type: new (...args: any[]) => T): T | null {
|
||||
for (const system of this._globalSystems) {
|
||||
if (system instanceof type) {
|
||||
return system as T;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ===== World生命周期 =====
|
||||
|
||||
/**
|
||||
* 启动World
|
||||
*/
|
||||
public start(): void {
|
||||
if (this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isActive = true;
|
||||
|
||||
// 启动所有全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.initialize) {
|
||||
system.initialize();
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(`启动World: ${this.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止World
|
||||
*/
|
||||
public stop(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 停止所有Scene
|
||||
for (const sceneId of this._activeScenes) {
|
||||
this.setSceneActive(sceneId, false);
|
||||
}
|
||||
|
||||
// 重置所有全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
}
|
||||
|
||||
this._isActive = false;
|
||||
logger.info(`停止World: ${this.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新World中的全局System
|
||||
* 注意:此方法由Core.update()调用,不应直接调用
|
||||
*/
|
||||
public updateGlobalSystems(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.update) {
|
||||
system.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新World中的所有激活Scene
|
||||
* 注意:此方法由Core.update()调用,不应直接调用
|
||||
*/
|
||||
public updateScenes(): void {
|
||||
if (!this._isActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 更新所有激活的Scene
|
||||
for (const sceneId of this._activeScenes) {
|
||||
const scene = this._scenes.get(sceneId);
|
||||
if (scene && scene.update) {
|
||||
scene.update();
|
||||
}
|
||||
}
|
||||
|
||||
// 自动清理(如果启用)
|
||||
if (this._config.autoCleanup && this.shouldAutoCleanup()) {
|
||||
this.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁World
|
||||
*/
|
||||
public destroy(): void {
|
||||
logger.info(`销毁World: ${this.name}`);
|
||||
|
||||
// 停止World
|
||||
this.stop();
|
||||
|
||||
// 销毁所有Scene
|
||||
const sceneIds = Array.from(this._scenes.keys());
|
||||
for (const sceneId of sceneIds) {
|
||||
this.removeScene(sceneId);
|
||||
}
|
||||
|
||||
// 清理全局System
|
||||
for (const system of this._globalSystems) {
|
||||
if (system.destroy) {
|
||||
system.destroy();
|
||||
} else if (system.reset) {
|
||||
system.reset();
|
||||
}
|
||||
}
|
||||
this._globalSystems.length = 0;
|
||||
|
||||
this._scenes.clear();
|
||||
this._activeScenes.clear();
|
||||
}
|
||||
|
||||
// ===== 状态信息 =====
|
||||
|
||||
/**
|
||||
* 获取World状态
|
||||
*/
|
||||
public getStatus() {
|
||||
return {
|
||||
name: this.name,
|
||||
isActive: this._isActive,
|
||||
sceneCount: this._scenes.size,
|
||||
activeSceneCount: this._activeScenes.size,
|
||||
globalSystemCount: this._globalSystems.length,
|
||||
createdAt: this._createdAt,
|
||||
config: { ...this._config },
|
||||
scenes: Array.from(this._scenes.keys()).map(sceneId => ({
|
||||
id: sceneId,
|
||||
isActive: this._activeScenes.has(sceneId),
|
||||
name: this._scenes.get(sceneId)?.name || sceneId
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取World统计信息
|
||||
*/
|
||||
public getStats() {
|
||||
const stats = {
|
||||
totalEntities: 0,
|
||||
totalSystems: this._globalSystems.length,
|
||||
memoryUsage: 0,
|
||||
performance: {
|
||||
averageUpdateTime: 0,
|
||||
maxUpdateTime: 0
|
||||
}
|
||||
};
|
||||
|
||||
// 统计所有Scene的实体数量
|
||||
for (const scene of this._scenes.values()) {
|
||||
if (scene.entities) {
|
||||
stats.totalEntities += scene.entities.count;
|
||||
}
|
||||
if (scene.systems) {
|
||||
stats.totalSystems += scene.systems.length;
|
||||
}
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
// ===== 私有方法 =====
|
||||
|
||||
/**
|
||||
* 检查是否应该执行自动清理
|
||||
*/
|
||||
private shouldAutoCleanup(): boolean {
|
||||
// 简单的清理策略:如果有空Scene且超过5分钟没有实体
|
||||
const currentTime = Date.now();
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
|
||||
for (const [sceneId, scene] of this._scenes) {
|
||||
if (!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行清理操作
|
||||
*/
|
||||
private cleanup(): void {
|
||||
const sceneIds = Array.from(this._scenes.keys());
|
||||
const currentTime = Date.now();
|
||||
const cleanupThreshold = 5 * 60 * 1000; // 5分钟
|
||||
|
||||
for (const sceneId of sceneIds) {
|
||||
const scene = this._scenes.get(sceneId);
|
||||
if (scene &&
|
||||
!this._activeScenes.has(sceneId) &&
|
||||
scene.entities &&
|
||||
scene.entities.count === 0 &&
|
||||
(currentTime - this._createdAt) > cleanupThreshold) {
|
||||
|
||||
this.removeScene(sceneId);
|
||||
logger.debug(`自动清理空Scene: ${sceneId} from World ${this.name}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== 访问器 =====
|
||||
|
||||
/**
|
||||
* 检查World是否激活
|
||||
*/
|
||||
public get isActive(): boolean {
|
||||
return this._isActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Scene数量
|
||||
*/
|
||||
public get sceneCount(): number {
|
||||
return this._scenes.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取创建时间
|
||||
*/
|
||||
public get createdAt(): number {
|
||||
return this._createdAt;
|
||||
}
|
||||
}
|
||||
463
packages/core/src/ECS/WorldManager.ts
Normal file
463
packages/core/src/ECS/WorldManager.ts
Normal file
@@ -0,0 +1,463 @@
|
||||
import { World, IWorldConfig } from './World';
|
||||
import { createLogger } from '../Utils/Logger';
|
||||
|
||||
const logger = createLogger('WorldManager');
|
||||
|
||||
/**
|
||||
* WorldManager配置接口
|
||||
*/
|
||||
export interface IWorldManagerConfig {
|
||||
/**
|
||||
* 最大World数量
|
||||
*/
|
||||
maxWorlds?: number;
|
||||
|
||||
/**
|
||||
* 是否自动清理空World
|
||||
*/
|
||||
autoCleanup?: boolean;
|
||||
|
||||
/**
|
||||
* 清理间隔(毫秒)
|
||||
*/
|
||||
cleanupInterval?: number;
|
||||
|
||||
/**
|
||||
* 是否启用调试模式
|
||||
*/
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* World管理器 - 管理所有World实例
|
||||
*
|
||||
* WorldManager是全局单例,负责管理所有World的生命周期。
|
||||
* 每个World都是独立的ECS环境,可以包含多个Scene。
|
||||
*
|
||||
* 设计理念:
|
||||
* - Core负责单Scene的传统ECS管理
|
||||
* - World负责多Scene的管理和协调
|
||||
* - WorldManager负责多World的全局管理
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 获取全局WorldManager
|
||||
* const worldManager = WorldManager.getInstance();
|
||||
*
|
||||
* // 创建游戏房间World
|
||||
* const roomWorld = worldManager.createWorld('room_001', {
|
||||
* name: 'GameRoom_001',
|
||||
* maxScenes: 5
|
||||
* });
|
||||
*
|
||||
* // 在游戏循环中更新所有World
|
||||
* worldManager.updateAll(deltaTime);
|
||||
* ```
|
||||
*/
|
||||
export class WorldManager {
|
||||
private static _instance: WorldManager | null = null;
|
||||
|
||||
private readonly _config: IWorldManagerConfig;
|
||||
private readonly _worlds: Map<string, World> = new Map();
|
||||
private readonly _activeWorlds: Set<string> = new Set();
|
||||
private _cleanupTimer: NodeJS.Timeout | null = null;
|
||||
private _isRunning: boolean = false;
|
||||
|
||||
private constructor(config: IWorldManagerConfig = {}) {
|
||||
this._config = {
|
||||
maxWorlds: 50,
|
||||
autoCleanup: true,
|
||||
cleanupInterval: 30000, // 30秒
|
||||
debug: false,
|
||||
...config
|
||||
};
|
||||
|
||||
logger.info('WorldManager已初始化', {
|
||||
maxWorlds: this._config.maxWorlds,
|
||||
autoCleanup: this._config.autoCleanup,
|
||||
cleanupInterval: this._config.cleanupInterval
|
||||
});
|
||||
|
||||
this.startCleanupTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取WorldManager单例实例
|
||||
*/
|
||||
public static getInstance(config?: IWorldManagerConfig): WorldManager {
|
||||
if (!this._instance) {
|
||||
this._instance = new WorldManager(config);
|
||||
}
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置WorldManager实例(主要用于测试)
|
||||
*/
|
||||
public static reset(): void {
|
||||
if (this._instance) {
|
||||
this._instance.destroy();
|
||||
this._instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
// ===== World管理 =====
|
||||
|
||||
/**
|
||||
* 创建新World
|
||||
*/
|
||||
public createWorld(worldId: string, config?: IWorldConfig): World {
|
||||
if (!worldId || typeof worldId !== 'string' || worldId.trim() === '') {
|
||||
throw new Error('World ID不能为空');
|
||||
}
|
||||
|
||||
if (this._worlds.has(worldId)) {
|
||||
throw new Error(`World ID '${worldId}' 已存在`);
|
||||
}
|
||||
|
||||
if (this._worlds.size >= this._config.maxWorlds!) {
|
||||
throw new Error(`已达到最大World数量限制: ${this._config.maxWorlds}`);
|
||||
}
|
||||
|
||||
const worldConfig: IWorldConfig = {
|
||||
name: worldId,
|
||||
debug: this._config.debug,
|
||||
...config
|
||||
};
|
||||
|
||||
const world = new World(worldConfig);
|
||||
this._worlds.set(worldId, world);
|
||||
|
||||
logger.info(`创建World: ${worldId}`, { config: worldConfig });
|
||||
return world;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除World
|
||||
*/
|
||||
public removeWorld(worldId: string): boolean {
|
||||
const world = this._worlds.get(worldId);
|
||||
if (!world) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果World正在运行,先停止它
|
||||
if (this._activeWorlds.has(worldId)) {
|
||||
this.setWorldActive(worldId, false);
|
||||
}
|
||||
|
||||
// 销毁World
|
||||
world.destroy();
|
||||
this._worlds.delete(worldId);
|
||||
|
||||
logger.info(`移除World: ${worldId}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取World
|
||||
*/
|
||||
public getWorld(worldId: string): World | null {
|
||||
return this._worlds.get(worldId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有World ID
|
||||
*/
|
||||
public getWorldIds(): string[] {
|
||||
return Array.from(this._worlds.keys());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有World
|
||||
*/
|
||||
public getAllWorlds(): World[] {
|
||||
return Array.from(this._worlds.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置World激活状态
|
||||
*/
|
||||
public setWorldActive(worldId: string, active: boolean): void {
|
||||
const world = this._worlds.get(worldId);
|
||||
if (!world) {
|
||||
logger.warn(`World '${worldId}' 不存在`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
this._activeWorlds.add(worldId);
|
||||
world.start();
|
||||
logger.debug(`激活World: ${worldId}`);
|
||||
} else {
|
||||
this._activeWorlds.delete(worldId);
|
||||
world.stop();
|
||||
logger.debug(`停用World: ${worldId}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查World是否激活
|
||||
*/
|
||||
public isWorldActive(worldId: string): boolean {
|
||||
return this._activeWorlds.has(worldId);
|
||||
}
|
||||
|
||||
// ===== 批量操作 =====
|
||||
|
||||
/**
|
||||
* 获取所有激活的World
|
||||
* 注意:此方法供Core.update()使用
|
||||
*/
|
||||
public getActiveWorlds(): World[] {
|
||||
const activeWorlds: World[] = [];
|
||||
for (const worldId of this._activeWorlds) {
|
||||
const world = this._worlds.get(worldId);
|
||||
if (world) {
|
||||
activeWorlds.push(world);
|
||||
}
|
||||
}
|
||||
return activeWorlds;
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动所有World
|
||||
*/
|
||||
public startAll(): void {
|
||||
this._isRunning = true;
|
||||
|
||||
for (const worldId of this._worlds.keys()) {
|
||||
this.setWorldActive(worldId, true);
|
||||
}
|
||||
|
||||
logger.info('启动所有World');
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止所有World
|
||||
*/
|
||||
public stopAll(): void {
|
||||
this._isRunning = false;
|
||||
|
||||
for (const worldId of this._activeWorlds) {
|
||||
this.setWorldActive(worldId, false);
|
||||
}
|
||||
|
||||
logger.info('停止所有World');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找满足条件的World
|
||||
*/
|
||||
public findWorlds(predicate: (world: World) => boolean): World[] {
|
||||
const results: World[] = [];
|
||||
for (const world of this._worlds.values()) {
|
||||
if (predicate(world)) {
|
||||
results.push(world);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据名称查找World
|
||||
*/
|
||||
public findWorldByName(name: string): World | null {
|
||||
for (const world of this._worlds.values()) {
|
||||
if (world.name === name) {
|
||||
return world;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ===== 统计和监控 =====
|
||||
|
||||
/**
|
||||
* 获取WorldManager统计信息
|
||||
*/
|
||||
public getStats() {
|
||||
const stats = {
|
||||
totalWorlds: this._worlds.size,
|
||||
activeWorlds: this._activeWorlds.size,
|
||||
totalScenes: 0,
|
||||
totalEntities: 0,
|
||||
totalSystems: 0,
|
||||
memoryUsage: 0,
|
||||
isRunning: this._isRunning,
|
||||
config: { ...this._config },
|
||||
worlds: [] as any[]
|
||||
};
|
||||
|
||||
for (const [worldId, world] of this._worlds) {
|
||||
const worldStats = world.getStats();
|
||||
stats.totalScenes += worldStats.totalSystems; // World的getStats可能需要调整
|
||||
stats.totalEntities += worldStats.totalEntities;
|
||||
stats.totalSystems += worldStats.totalSystems;
|
||||
|
||||
stats.worlds.push({
|
||||
id: worldId,
|
||||
name: world.name,
|
||||
isActive: this._activeWorlds.has(worldId),
|
||||
sceneCount: world.sceneCount,
|
||||
...worldStats
|
||||
});
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取详细状态信息
|
||||
*/
|
||||
public getDetailedStatus() {
|
||||
return {
|
||||
...this.getStats(),
|
||||
worlds: Array.from(this._worlds.entries()).map(([worldId, world]) => ({
|
||||
id: worldId,
|
||||
isActive: this._activeWorlds.has(worldId),
|
||||
status: world.getStatus()
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
// ===== 生命周期管理 =====
|
||||
|
||||
/**
|
||||
* 清理空World
|
||||
*/
|
||||
public cleanup(): number {
|
||||
const worldsToRemove: string[] = [];
|
||||
|
||||
for (const [worldId, world] of this._worlds) {
|
||||
if (this.shouldCleanupWorld(world)) {
|
||||
worldsToRemove.push(worldId);
|
||||
}
|
||||
}
|
||||
|
||||
for (const worldId of worldsToRemove) {
|
||||
this.removeWorld(worldId);
|
||||
}
|
||||
|
||||
if (worldsToRemove.length > 0) {
|
||||
logger.debug(`清理了 ${worldsToRemove.length} 个World`);
|
||||
}
|
||||
|
||||
return worldsToRemove.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁WorldManager
|
||||
*/
|
||||
public destroy(): void {
|
||||
logger.info('正在销毁WorldManager...');
|
||||
|
||||
// 停止清理定时器
|
||||
this.stopCleanupTimer();
|
||||
|
||||
// 停止所有World
|
||||
this.stopAll();
|
||||
|
||||
// 销毁所有World
|
||||
const worldIds = Array.from(this._worlds.keys());
|
||||
for (const worldId of worldIds) {
|
||||
this.removeWorld(worldId);
|
||||
}
|
||||
|
||||
this._worlds.clear();
|
||||
this._activeWorlds.clear();
|
||||
this._isRunning = false;
|
||||
|
||||
logger.info('WorldManager已销毁');
|
||||
}
|
||||
|
||||
// ===== 私有方法 =====
|
||||
|
||||
/**
|
||||
* 启动清理定时器
|
||||
*/
|
||||
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是否应该被清理
|
||||
*/
|
||||
private shouldCleanupWorld(world: World): boolean {
|
||||
// 清理策略:
|
||||
// 1. World未激活
|
||||
// 2. 没有Scene或所有Scene都是空的
|
||||
// 3. 创建时间超过10分钟
|
||||
|
||||
if (world.isActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (world.sceneCount === 0) {
|
||||
const age = Date.now() - world.createdAt;
|
||||
return age > 10 * 60 * 1000; // 10分钟
|
||||
}
|
||||
|
||||
// 检查是否所有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;
|
||||
}
|
||||
|
||||
// ===== 访问器 =====
|
||||
|
||||
/**
|
||||
* 获取World总数
|
||||
*/
|
||||
public get worldCount(): number {
|
||||
return this._worlds.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取激活World数量
|
||||
*/
|
||||
public get activeWorldCount(): number {
|
||||
return this._activeWorlds.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否正在运行
|
||||
*/
|
||||
public get isRunning(): boolean {
|
||||
return this._isRunning;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
*/
|
||||
public get config(): IWorldManagerConfig {
|
||||
return { ...this._config };
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ export * from './Utils';
|
||||
export * from './Decorators';
|
||||
export { Scene } from './Scene';
|
||||
export { IScene, ISceneFactory, ISceneConfig } from './IScene';
|
||||
export { World, IWorldConfig } from './World';
|
||||
export { WorldManager, IWorldManagerConfig } from './WorldManager';
|
||||
export { EntityManager, EntityQueryBuilder } from './Core/EntityManager';
|
||||
export * from './Core/Events';
|
||||
export * from './Core/Query';
|
||||
|
||||
589
packages/core/tests/ECS/Core/WorldCoreIntegration.test.ts
Normal file
589
packages/core/tests/ECS/Core/WorldCoreIntegration.test.ts
Normal file
@@ -0,0 +1,589 @@
|
||||
import { Core } from '../../../src/Core';
|
||||
import { Scene } from '../../../src/ECS/Scene';
|
||||
import { World, IGlobalSystem } from '../../../src/ECS/World';
|
||||
import { WorldManager } from '../../../src/ECS/WorldManager';
|
||||
import { EntitySystem } from '../../../src/ECS/Systems/EntitySystem';
|
||||
import { Component } from '../../../src/ECS/Component';
|
||||
import { Matcher } from '../../../src/ECS/Utils/Matcher';
|
||||
import { Entity } from '../../../src/ECS/Entity';
|
||||
|
||||
// 测试用组件
|
||||
class TestComponent extends Component {
|
||||
public value: number = 0;
|
||||
|
||||
constructor(value: number = 0) {
|
||||
super();
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkComponent extends Component {
|
||||
public playerId: string;
|
||||
|
||||
constructor(playerId: string) {
|
||||
super();
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.playerId = '';
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用系统
|
||||
class TestGlobalSystem extends EntitySystem {
|
||||
public processedEntities: Entity[] = [];
|
||||
public updateCount: number = 0;
|
||||
|
||||
constructor() {
|
||||
super(Matcher.empty().all(TestComponent));
|
||||
}
|
||||
|
||||
protected override process(entities: Entity[]): void {
|
||||
this.processedEntities = [...entities];
|
||||
this.updateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 正确的全局系统实现
|
||||
class NetworkSyncGlobalSystem implements IGlobalSystem {
|
||||
public readonly name = 'NetworkSyncSystem';
|
||||
public updateCount: number = 0;
|
||||
|
||||
public initialize(): void {
|
||||
// 初始化网络连接等
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.updateCount++;
|
||||
// 同步网络数据等全局逻辑
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.updateCount = 0;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
// 清理网络连接等
|
||||
}
|
||||
}
|
||||
|
||||
// Scene级别的EntitySystem(正确的用法)
|
||||
class NetworkSyncSystem extends EntitySystem {
|
||||
public syncCount: number = 0;
|
||||
|
||||
constructor() {
|
||||
super(Matcher.empty().all(NetworkComponent));
|
||||
}
|
||||
|
||||
protected override process(entities: Entity[]): void {
|
||||
this.syncCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// World级别的网络同步全局系统
|
||||
class NetworkGlobalSystem implements IGlobalSystem {
|
||||
public readonly name = 'NetworkGlobalSystem';
|
||||
public syncCount: number = 0;
|
||||
|
||||
public initialize(): void {
|
||||
// 初始化网络连接
|
||||
}
|
||||
|
||||
public update(): void {
|
||||
this.syncCount++;
|
||||
// 全局网络同步逻辑
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this.syncCount = 0;
|
||||
}
|
||||
|
||||
public destroy(): void {
|
||||
// 清理网络连接
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用Scene
|
||||
class TestScene extends Scene {
|
||||
public updateCallCount: number = 0;
|
||||
|
||||
public override update(): void {
|
||||
super.update();
|
||||
this.updateCallCount++;
|
||||
}
|
||||
}
|
||||
|
||||
describe('World与Core集成测试', () => {
|
||||
beforeEach(() => {
|
||||
// 重置Core和WorldManager
|
||||
if ((Core as any)._instance) {
|
||||
(Core as any)._instance = null;
|
||||
}
|
||||
WorldManager['_instance'] = null;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// 清理资源
|
||||
if ((Core as any)._instance) {
|
||||
const worldManager = Core.getWorldManager?.();
|
||||
if (worldManager) {
|
||||
const worldIds = worldManager.getWorldIds();
|
||||
worldIds.forEach(id => {
|
||||
worldManager.removeWorld(id);
|
||||
});
|
||||
}
|
||||
(Core as any)._instance = null;
|
||||
}
|
||||
WorldManager['_instance'] = null;
|
||||
});
|
||||
|
||||
describe('融合设计基础功能', () => {
|
||||
test('单Scene模式应该保持向后兼容', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
// 传统单Scene用法
|
||||
const scene = new Scene();
|
||||
scene.name = 'TestScene';
|
||||
|
||||
Core.setScene(scene);
|
||||
|
||||
const retrievedScene = Core.getScene();
|
||||
expect(retrievedScene).toBe(scene);
|
||||
expect(retrievedScene?.name).toBe('TestScene');
|
||||
});
|
||||
|
||||
test('启用WorldManager后应该支持多World功能', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
expect(worldManager).toBeDefined();
|
||||
|
||||
const world = worldManager.createWorld('TestWorld');
|
||||
expect(world).toBeDefined();
|
||||
expect(world.name).toBe('TestWorld');
|
||||
});
|
||||
|
||||
test('getWorldManager应该自动创建WorldManager', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
// 获取WorldManager会自动创建实例
|
||||
const worldManager = Core.getWorldManager();
|
||||
expect(worldManager).toBeDefined();
|
||||
|
||||
// 多次获取应该返回同一个实例
|
||||
const worldManager2 = Core.getWorldManager();
|
||||
expect(worldManager2).toBe(worldManager);
|
||||
});
|
||||
|
||||
test('单Scene模式下Core.update应该正常工作', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
const scene = new TestScene();
|
||||
Core.setScene(scene);
|
||||
|
||||
// 模拟更新
|
||||
Core.update(0.016);
|
||||
|
||||
expect(scene.updateCallCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('默认World机制', () => {
|
||||
test('设置Scene应该自动创建默认World', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
const scene = new Scene();
|
||||
Core.setScene(scene);
|
||||
|
||||
// 启用WorldManager后应该能看到默认World
|
||||
Core.enableWorldManager();
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
expect(worldManager.getWorld('__default__')).toBeDefined();
|
||||
|
||||
const defaultWorld = worldManager.getWorld('__default__');
|
||||
expect(defaultWorld).toBeDefined();
|
||||
expect(defaultWorld?.getScene('__main__')).toBe(scene);
|
||||
});
|
||||
|
||||
test('默认World的Scene应该正确激活', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
const scene = new Scene();
|
||||
Core.setScene(scene);
|
||||
|
||||
Core.enableWorldManager();
|
||||
const worldManager = Core.getWorldManager();
|
||||
const defaultWorld = worldManager.getWorld('__default__');
|
||||
|
||||
expect(defaultWorld?.isSceneActive('__main__')).toBe(true);
|
||||
});
|
||||
|
||||
test('替换默认Scene应该正确处理', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
const scene1 = new Scene();
|
||||
scene1.name = 'Scene1';
|
||||
Core.setScene(scene1);
|
||||
|
||||
const scene2 = new Scene();
|
||||
scene2.name = 'Scene2';
|
||||
Core.setScene(scene2);
|
||||
|
||||
const currentScene = Core.getScene();
|
||||
expect(currentScene).toBe(scene2);
|
||||
expect(currentScene?.name).toBe('Scene2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('多World更新机制', () => {
|
||||
test('Core.update应该更新所有活跃World', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
// 创建多个World
|
||||
const world1 = worldManager.createWorld('World1');
|
||||
const world2 = worldManager.createWorld('World2');
|
||||
const world3 = worldManager.createWorld('World3');
|
||||
|
||||
// 为每个World创建Scene和System
|
||||
const scene1 = world1.createScene('scene1', new TestScene());
|
||||
const scene2 = world2.createScene('scene2', new TestScene());
|
||||
const scene3 = world3.createScene('scene3', new TestScene());
|
||||
|
||||
// 启动部分World
|
||||
worldManager.setWorldActive('World1', true);
|
||||
worldManager.setWorldActive('World2', true);
|
||||
// world3保持未启动
|
||||
|
||||
world1.setSceneActive('scene1', true);
|
||||
world2.setSceneActive('scene2', true);
|
||||
|
||||
// 执行更新
|
||||
Core.update(0.016);
|
||||
|
||||
// 检查只有激活的World被更新
|
||||
expect(scene1.updateCallCount).toBeGreaterThan(0);
|
||||
expect(scene2.updateCallCount).toBeGreaterThan(0);
|
||||
expect(scene3.updateCallCount).toBe(0);
|
||||
});
|
||||
|
||||
test('全局系统应该在Scene更新前执行', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
const world = worldManager.createWorld('TestWorld');
|
||||
|
||||
// 添加正确设计的全局系统(业务逻辑系统,不是EntitySystem)
|
||||
const globalSystem = new NetworkSyncGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
|
||||
// 创建Scene
|
||||
const scene = world.createScene('testScene');
|
||||
|
||||
worldManager.setWorldActive('TestWorld', true);
|
||||
world.setSceneActive('testScene', true);
|
||||
|
||||
// 执行更新
|
||||
Core.update(0.016);
|
||||
|
||||
// 验证全局System被正确更新
|
||||
expect(globalSystem.updateCount).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('多房间游戏服务器场景', () => {
|
||||
test('多个游戏房间应该独立运行', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
// 创建两个游戏房间
|
||||
const room1 = worldManager.createWorld('Room_001');
|
||||
const room2 = worldManager.createWorld('Room_002');
|
||||
|
||||
// 为每个房间设置Scene
|
||||
const gameScene1 = room1.createScene('game');
|
||||
const gameScene2 = room2.createScene('game');
|
||||
|
||||
// 为每个房间添加全局网络系统
|
||||
const netSystem1 = new NetworkGlobalSystem();
|
||||
const netSystem2 = new NetworkGlobalSystem();
|
||||
|
||||
room1.addGlobalSystem(netSystem1);
|
||||
room2.addGlobalSystem(netSystem2);
|
||||
|
||||
// 在每个房间创建玩家
|
||||
const player1 = gameScene1.createEntity('Player1');
|
||||
player1.addComponent(new NetworkComponent('player_123'));
|
||||
|
||||
const player2 = gameScene2.createEntity('Player2');
|
||||
player2.addComponent(new NetworkComponent('player_456'));
|
||||
|
||||
// 启动房间
|
||||
worldManager.setWorldActive('Room_001', true);
|
||||
worldManager.setWorldActive('Room_002', true);
|
||||
room1.setSceneActive('game', true);
|
||||
room2.setSceneActive('game', true);
|
||||
|
||||
// 模拟游戏循环
|
||||
for (let i = 0; i < 5; i++) {
|
||||
Core.update(0.016);
|
||||
}
|
||||
|
||||
// 验证每个房间独立运行
|
||||
expect(netSystem1.syncCount).toBeGreaterThan(0);
|
||||
expect(netSystem2.syncCount).toBeGreaterThan(0);
|
||||
expect(room1.getActiveSceneCount()).toBe(1);
|
||||
expect(room2.getActiveSceneCount()).toBe(1);
|
||||
});
|
||||
|
||||
test('房间销毁应该完全清理资源', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
// 创建房间
|
||||
const room = worldManager.createWorld('TempRoom');
|
||||
const scene = room.createScene('game');
|
||||
|
||||
// 添加内容
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const entity = scene.createEntity(`Entity${i}`);
|
||||
entity.addComponent(new TestComponent(i));
|
||||
}
|
||||
|
||||
room.addGlobalSystem(new NetworkSyncGlobalSystem());
|
||||
worldManager.setWorldActive('TempRoom', true);
|
||||
room.setSceneActive('game', true);
|
||||
|
||||
// 验证房间正常运行
|
||||
Core.update(0.016);
|
||||
|
||||
const beforeDestroy = worldManager.getStats();
|
||||
expect(beforeDestroy.totalWorlds).toBe(1);
|
||||
expect(beforeDestroy.activeWorlds).toBe(1);
|
||||
|
||||
// 销毁房间
|
||||
worldManager.removeWorld('TempRoom');
|
||||
|
||||
const afterDestroy = worldManager.getStats();
|
||||
expect(afterDestroy.totalWorlds).toBe(0);
|
||||
expect(afterDestroy.activeWorlds).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('客户端多层Scene架构', () => {
|
||||
test('分层Scene应该同时运行', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
const clientWorld = worldManager.createWorld('ClientWorld');
|
||||
|
||||
// 创建不同层的Scene
|
||||
const gameplayScene = clientWorld.createScene('gameplay', new TestScene());
|
||||
const uiScene = clientWorld.createScene('ui', new TestScene());
|
||||
const effectsScene = clientWorld.createScene('effects', new TestScene());
|
||||
|
||||
// 启动世界并激活所有Scene
|
||||
worldManager.setWorldActive('ClientWorld', true);
|
||||
clientWorld.setSceneActive('gameplay', true);
|
||||
clientWorld.setSceneActive('ui', true);
|
||||
clientWorld.setSceneActive('effects', true);
|
||||
|
||||
// 执行更新
|
||||
Core.update(0.016);
|
||||
|
||||
// 验证所有Scene都被更新
|
||||
expect(gameplayScene.updateCallCount).toBeGreaterThan(0);
|
||||
expect(uiScene.updateCallCount).toBeGreaterThan(0);
|
||||
expect(effectsScene.updateCallCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('Scene的动态激活和停用', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
const world = worldManager.createWorld('DynamicWorld');
|
||||
|
||||
const gameScene = world.createScene('game', new TestScene());
|
||||
const menuScene = world.createScene('menu', new TestScene());
|
||||
|
||||
worldManager.setWorldActive('DynamicWorld', true);
|
||||
|
||||
// 初始状态:只有游戏Scene激活
|
||||
world.setSceneActive('game', true);
|
||||
world.setSceneActive('menu', false);
|
||||
|
||||
Core.update(0.016);
|
||||
|
||||
const gameCount1 = gameScene.updateCallCount;
|
||||
const menuCount1 = menuScene.updateCallCount;
|
||||
|
||||
// 切换到菜单
|
||||
world.setSceneActive('game', false);
|
||||
world.setSceneActive('menu', true);
|
||||
|
||||
Core.update(0.016);
|
||||
|
||||
const gameCount2 = gameScene.updateCallCount;
|
||||
const menuCount2 = menuScene.updateCallCount;
|
||||
|
||||
// 验证Scene状态切换
|
||||
expect(gameCount2).toBe(gameCount1); // 游戏Scene停止更新
|
||||
expect(menuCount2).toBeGreaterThan(menuCount1); // 菜单Scene开始更新
|
||||
});
|
||||
});
|
||||
|
||||
describe('性能和稳定性', () => {
|
||||
test('大量World和Scene应该稳定运行', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
const worldCount = 20;
|
||||
const scenePerWorld = 3;
|
||||
|
||||
// 创建大量World和Scene
|
||||
for (let i = 0; i < worldCount; i++) {
|
||||
const world = worldManager.createWorld(`World${i}`);
|
||||
|
||||
for (let j = 0; j < scenePerWorld; j++) {
|
||||
const scene = world.createScene(`Scene${j}`, new TestScene());
|
||||
|
||||
// 添加一些实体
|
||||
for (let k = 0; k < 5; k++) {
|
||||
const entity = scene.createEntity(`Entity${k}`);
|
||||
entity.addComponent(new TestComponent(k));
|
||||
}
|
||||
|
||||
world.setSceneActive(`Scene${j}`, true);
|
||||
}
|
||||
|
||||
worldManager.setWorldActive(`World${i}`, true);
|
||||
}
|
||||
|
||||
// 验证所有资源创建成功
|
||||
expect(worldManager.getWorldIds()).toHaveLength(worldCount);
|
||||
expect(worldManager.getActiveWorlds()).toHaveLength(worldCount);
|
||||
|
||||
// 执行多次更新测试稳定性
|
||||
for (let i = 0; i < 10; i++) {
|
||||
expect(() => {
|
||||
Core.update(0.016);
|
||||
}).not.toThrow();
|
||||
}
|
||||
|
||||
// 验证更新正常工作
|
||||
const activeWorlds = worldManager.getActiveWorlds();
|
||||
activeWorlds.forEach(world => {
|
||||
const scenes = world.getAllScenes();
|
||||
scenes.forEach(scene => {
|
||||
if (scene instanceof TestScene && world.isSceneActive(scene.name)) {
|
||||
expect(scene.updateCallCount).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('频繁的World创建和销毁应该不影响性能', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
// 频繁创建和销毁World
|
||||
for (let cycle = 0; cycle < 10; cycle++) {
|
||||
// 创建批次World
|
||||
const worldIds: string[] = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const worldId = `Cycle${cycle}_World${i}`;
|
||||
worldIds.push(worldId);
|
||||
|
||||
const world = worldManager.createWorld(worldId);
|
||||
const scene = world.createScene('test');
|
||||
scene.createEntity('entity');
|
||||
|
||||
worldManager.setWorldActive(worldId, true);
|
||||
world.setSceneActive('test', true);
|
||||
}
|
||||
|
||||
// 更新一次
|
||||
Core.update(0.016);
|
||||
|
||||
// 销毁批次World
|
||||
worldIds.forEach(id => {
|
||||
worldManager.removeWorld(id);
|
||||
});
|
||||
|
||||
// 验证清理完成
|
||||
expect(worldManager.getWorldIds()).toHaveLength(0);
|
||||
expect(worldManager.getActiveWorlds()).toHaveLength(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理和边界情况', () => {
|
||||
test('Core未初始化时操作应该抛出合适错误', () => {
|
||||
// getScene 会返回 null 而不是抛出错误
|
||||
expect(Core.getScene()).toBeNull();
|
||||
|
||||
expect(() => {
|
||||
Core.setScene(new Scene());
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('在World销毁后继续操作应该安全', () => {
|
||||
Core.create({ debug: false });
|
||||
Core.enableWorldManager();
|
||||
|
||||
const worldManager = Core.getWorldManager();
|
||||
const world = worldManager.createWorld('DestroyTest');
|
||||
|
||||
worldManager.setWorldActive('DestroyTest', true);
|
||||
worldManager.removeWorld('DestroyTest');
|
||||
|
||||
// 对已销毁的World进行操作应该不会崩溃
|
||||
expect(() => {
|
||||
world.updateGlobalSystems();
|
||||
world.updateScenes();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('混合使用单Scene和多World模式', () => {
|
||||
Core.create({ debug: false });
|
||||
|
||||
// 直接启用WorldManager(避免先使用单Scene创建限制性配置)
|
||||
const worldManager = Core.getWorldManager();
|
||||
|
||||
// 然后使用单Scene模式
|
||||
const singleScene = new Scene();
|
||||
Core.setScene(singleScene);
|
||||
|
||||
// 验证默认World被创建
|
||||
expect(worldManager.getWorld('__default__')).toBeDefined();
|
||||
|
||||
// 创建额外的World
|
||||
const extraWorld = worldManager.createWorld('ExtraWorld');
|
||||
worldManager.setWorldActive('ExtraWorld', true);
|
||||
|
||||
// 两种模式应该能共存
|
||||
expect(() => {
|
||||
Core.update(0.016);
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
466
packages/core/tests/ECS/World.test.ts
Normal file
466
packages/core/tests/ECS/World.test.ts
Normal file
@@ -0,0 +1,466 @@
|
||||
import { World, IWorldConfig, IGlobalSystem } from '../../src/ECS/World';
|
||||
import { Scene } from '../../src/ECS/Scene';
|
||||
import { EntitySystem } from '../../src/ECS/Systems/EntitySystem';
|
||||
import { Entity } from '../../src/ECS/Entity';
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class PlayerComponent extends Component {
|
||||
public playerId: string;
|
||||
|
||||
constructor(playerId: string) {
|
||||
super();
|
||||
this.playerId = playerId;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用全局系统
|
||||
class TestGlobalSystem implements IGlobalSystem {
|
||||
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 {
|
||||
// 销毁逻辑
|
||||
}
|
||||
}
|
||||
|
||||
class TestSceneSystem extends EntitySystem {
|
||||
public updateCount = 0;
|
||||
|
||||
constructor() {
|
||||
super(Matcher.empty().all(PlayerComponent));
|
||||
}
|
||||
|
||||
protected override process(): void {
|
||||
this.updateCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用Scene
|
||||
class TestScene extends Scene {
|
||||
public initializeCalled = false;
|
||||
public beginCalled = false;
|
||||
public endCalled = false;
|
||||
|
||||
public override initialize(): void {
|
||||
this.initializeCalled = true;
|
||||
super.initialize();
|
||||
}
|
||||
|
||||
public override begin(): void {
|
||||
this.beginCalled = true;
|
||||
super.begin();
|
||||
}
|
||||
|
||||
public override end(): void {
|
||||
this.endCalled = true;
|
||||
super.end();
|
||||
}
|
||||
}
|
||||
|
||||
describe('World', () => {
|
||||
let world: World;
|
||||
|
||||
beforeEach(() => {
|
||||
world = new World({ name: 'TestWorld' });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (world) {
|
||||
world.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
describe('基础功能', () => {
|
||||
test('创建World时应该设置正确的配置', () => {
|
||||
const config: IWorldConfig = {
|
||||
name: 'GameWorld',
|
||||
debug: true,
|
||||
maxScenes: 5,
|
||||
autoCleanup: false
|
||||
};
|
||||
|
||||
const testWorld = new World(config);
|
||||
|
||||
expect(testWorld.name).toBe('GameWorld');
|
||||
expect(testWorld.sceneCount).toBe(0);
|
||||
expect(testWorld.isActive).toBe(false);
|
||||
expect(testWorld.createdAt).toBeGreaterThan(0);
|
||||
|
||||
testWorld.destroy();
|
||||
});
|
||||
|
||||
test('默认配置应该正确', () => {
|
||||
const defaultWorld = new World();
|
||||
|
||||
expect(defaultWorld.name).toBe('World');
|
||||
expect(defaultWorld.sceneCount).toBe(0);
|
||||
expect(defaultWorld.isActive).toBe(false);
|
||||
|
||||
defaultWorld.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene管理', () => {
|
||||
test('创建Scene应该成功', () => {
|
||||
const scene = world.createScene('test-scene');
|
||||
|
||||
expect(scene).toBeDefined();
|
||||
expect(world.sceneCount).toBe(1);
|
||||
expect(world.getSceneIds()).toContain('test-scene');
|
||||
});
|
||||
|
||||
test('创建Scene时传入自定义Scene实例', () => {
|
||||
const customScene = new TestScene();
|
||||
const scene = world.createScene('custom-scene', customScene);
|
||||
|
||||
expect(scene).toBe(customScene);
|
||||
expect(scene.initializeCalled).toBe(true);
|
||||
expect(world.sceneCount).toBe(1);
|
||||
});
|
||||
|
||||
test('重复的Scene ID应该抛出错误', () => {
|
||||
world.createScene('duplicate');
|
||||
|
||||
expect(() => {
|
||||
world.createScene('duplicate');
|
||||
}).toThrow("Scene ID 'duplicate' 已存在于World 'TestWorld' 中");
|
||||
});
|
||||
|
||||
test('超出最大Scene数量限制应该抛出错误', () => {
|
||||
const limitedWorld = new World({ maxScenes: 2 });
|
||||
|
||||
limitedWorld.createScene('scene1');
|
||||
limitedWorld.createScene('scene2');
|
||||
|
||||
expect(() => {
|
||||
limitedWorld.createScene('scene3');
|
||||
}).toThrow("World 'World' 已达到最大Scene数量限制: 2");
|
||||
|
||||
limitedWorld.destroy();
|
||||
});
|
||||
|
||||
test('获取Scene应该正确', () => {
|
||||
const scene = world.createScene('get-test');
|
||||
const retrievedScene = world.getScene('get-test');
|
||||
|
||||
expect(retrievedScene).toBe(scene);
|
||||
});
|
||||
|
||||
test('获取不存在的Scene应该返回null', () => {
|
||||
const scene = world.getScene('non-existent');
|
||||
expect(scene).toBeNull();
|
||||
});
|
||||
|
||||
test('移除Scene应该正确清理', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('remove-test', testScene);
|
||||
world.setSceneActive('remove-test', true);
|
||||
|
||||
const removed = world.removeScene('remove-test');
|
||||
|
||||
expect(removed).toBe(true);
|
||||
expect(world.sceneCount).toBe(0);
|
||||
expect(world.getScene('remove-test')).toBeNull();
|
||||
expect(testScene.endCalled).toBe(true);
|
||||
});
|
||||
|
||||
test('移除不存在的Scene应该返回false', () => {
|
||||
const removed = world.removeScene('non-existent');
|
||||
expect(removed).toBe(false);
|
||||
});
|
||||
|
||||
test('获取所有Scene应该正确', () => {
|
||||
const scene1 = world.createScene('scene1');
|
||||
const scene2 = world.createScene('scene2');
|
||||
|
||||
const allScenes = world.getAllScenes();
|
||||
|
||||
expect(allScenes).toHaveLength(2);
|
||||
expect(allScenes).toContain(scene1);
|
||||
expect(allScenes).toContain(scene2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scene激活管理', () => {
|
||||
test('激活Scene应该正确', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('active-test', testScene);
|
||||
|
||||
world.setSceneActive('active-test', true);
|
||||
|
||||
expect(world.isSceneActive('active-test')).toBe(true);
|
||||
expect(world.getActiveSceneCount()).toBe(1);
|
||||
expect(testScene.beginCalled).toBe(true);
|
||||
});
|
||||
|
||||
test('停用Scene应该正确', () => {
|
||||
world.createScene('deactive-test');
|
||||
world.setSceneActive('deactive-test', true);
|
||||
|
||||
world.setSceneActive('deactive-test', false);
|
||||
|
||||
expect(world.isSceneActive('deactive-test')).toBe(false);
|
||||
expect(world.getActiveSceneCount()).toBe(0);
|
||||
});
|
||||
|
||||
test('激活不存在的Scene应该记录警告', () => {
|
||||
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation();
|
||||
|
||||
world.setSceneActive('non-existent', true);
|
||||
|
||||
// 注意:这里需要检查具体的日志实现,可能需要调整
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('全局System管理', () => {
|
||||
test('添加全局System应该成功', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const addedSystem = world.addGlobalSystem(globalSystem);
|
||||
|
||||
expect(addedSystem).toBe(globalSystem);
|
||||
expect(world.getGlobalSystem(TestGlobalSystem)).toBe(globalSystem);
|
||||
});
|
||||
|
||||
test('重复添加相同System应该返回原System', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const firstAdd = world.addGlobalSystem(globalSystem);
|
||||
const secondAdd = world.addGlobalSystem(globalSystem);
|
||||
|
||||
expect(firstAdd).toBe(secondAdd);
|
||||
expect(firstAdd).toBe(globalSystem);
|
||||
});
|
||||
|
||||
test('移除全局System应该成功', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
|
||||
const removed = world.removeGlobalSystem(globalSystem);
|
||||
|
||||
expect(removed).toBe(true);
|
||||
expect(world.getGlobalSystem(TestGlobalSystem)).toBeNull();
|
||||
});
|
||||
|
||||
test('移除不存在的System应该返回false', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
const removed = world.removeGlobalSystem(globalSystem);
|
||||
|
||||
expect(removed).toBe(false);
|
||||
});
|
||||
|
||||
test('获取不存在的System类型应该返回null', () => {
|
||||
const system = world.getGlobalSystem(TestGlobalSystem);
|
||||
expect(system).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('World生命周期', () => {
|
||||
test('启动World应该正确', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
|
||||
world.start();
|
||||
|
||||
expect(world.isActive).toBe(true);
|
||||
});
|
||||
|
||||
test('重复启动World应该无效果', () => {
|
||||
world.start();
|
||||
const firstActive = world.isActive;
|
||||
|
||||
world.start();
|
||||
|
||||
expect(world.isActive).toBe(firstActive);
|
||||
});
|
||||
|
||||
test('停止World应该停用所有Scene', () => {
|
||||
const testScene = new TestScene();
|
||||
world.createScene('stop-test', testScene);
|
||||
world.setSceneActive('stop-test', true);
|
||||
world.start();
|
||||
|
||||
world.stop();
|
||||
|
||||
expect(world.isActive).toBe(false);
|
||||
expect(world.isSceneActive('stop-test')).toBe(false);
|
||||
});
|
||||
|
||||
test('销毁World应该清理所有资源', () => {
|
||||
const testScene = new TestScene();
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
|
||||
world.createScene('destroy-test', testScene);
|
||||
world.addGlobalSystem(globalSystem);
|
||||
world.start();
|
||||
|
||||
world.destroy();
|
||||
|
||||
expect(world.sceneCount).toBe(0);
|
||||
expect(world.isActive).toBe(false);
|
||||
expect(testScene.endCalled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('更新逻辑', () => {
|
||||
test('updateGlobalSystems应该更新全局系统', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
world.start();
|
||||
|
||||
// 创建测试Scene
|
||||
const scene = world.createScene('update-test');
|
||||
world.setSceneActive('update-test', true);
|
||||
|
||||
// 直接测试全局系统更新
|
||||
world.updateGlobalSystems();
|
||||
|
||||
// 验证全局System被正确调用
|
||||
expect(globalSystem.updateCount).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('未激活的World不应该更新', () => {
|
||||
const globalSystem = new TestGlobalSystem();
|
||||
world.addGlobalSystem(globalSystem);
|
||||
// 不启动World
|
||||
|
||||
world.updateGlobalSystems();
|
||||
|
||||
expect(globalSystem.updateCount).toBe(0);
|
||||
});
|
||||
|
||||
test('updateScenes应该更新激活的Scene', () => {
|
||||
const scene1 = world.createScene('scene1');
|
||||
const scene2 = world.createScene('scene2');
|
||||
|
||||
scene1.addEntityProcessor(new TestSceneSystem());
|
||||
scene2.addEntityProcessor(new TestSceneSystem());
|
||||
|
||||
world.start();
|
||||
world.setSceneActive('scene1', true);
|
||||
// scene2保持未激活
|
||||
|
||||
world.updateScenes();
|
||||
|
||||
// 这里需要根据具体的Scene更新实现来验证
|
||||
// 由于Scene.update()的具体实现可能不同,这里主要测试调用不出错
|
||||
expect(() => world.updateScenes()).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('状态和统计', () => {
|
||||
test('获取World状态应该正确', () => {
|
||||
world.createScene('status-scene1');
|
||||
world.createScene('status-scene2');
|
||||
world.setSceneActive('status-scene1', true);
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
world.start();
|
||||
|
||||
const status = world.getStatus();
|
||||
|
||||
expect(status.name).toBe('TestWorld');
|
||||
expect(status.isActive).toBe(true);
|
||||
expect(status.sceneCount).toBe(2);
|
||||
expect(status.activeSceneCount).toBe(1);
|
||||
expect(status.globalSystemCount).toBe(1);
|
||||
expect(status.createdAt).toBeGreaterThan(0);
|
||||
expect(status.scenes).toHaveLength(2);
|
||||
|
||||
const activeScene = status.scenes.find(s => s.id === 'status-scene1');
|
||||
expect(activeScene?.isActive).toBe(true);
|
||||
|
||||
const inactiveScene = status.scenes.find(s => s.id === 'status-scene2');
|
||||
expect(inactiveScene?.isActive).toBe(false);
|
||||
});
|
||||
|
||||
test('获取World统计应该包含基本信息', () => {
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
|
||||
const scene = world.createScene('stats-scene');
|
||||
const entity = scene.createEntity('stats-entity');
|
||||
entity.addComponent(new TestComponent());
|
||||
|
||||
const stats = world.getStats();
|
||||
|
||||
expect(stats).toHaveProperty('totalEntities');
|
||||
expect(stats).toHaveProperty('totalSystems');
|
||||
expect(stats).toHaveProperty('memoryUsage');
|
||||
expect(stats).toHaveProperty('performance');
|
||||
expect(stats.totalSystems).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('自动清理功能', () => {
|
||||
test('自动清理应该移除空闲Scene', async () => {
|
||||
// 创建一个启用自动清理的World
|
||||
const autoCleanWorld = new World({
|
||||
name: 'AutoCleanWorld',
|
||||
autoCleanup: true,
|
||||
maxScenes: 10
|
||||
});
|
||||
|
||||
// 创建一个空Scene
|
||||
autoCleanWorld.createScene('empty-scene');
|
||||
autoCleanWorld.start();
|
||||
|
||||
// 手动触发清理检查
|
||||
autoCleanWorld.updateScenes();
|
||||
|
||||
// 由于清理策略基于时间,这里主要测试不会出错
|
||||
expect(() => autoCleanWorld.updateScenes()).not.toThrow();
|
||||
|
||||
autoCleanWorld.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理', () => {
|
||||
test('Scene ID为空时应该创建默认ID', () => {
|
||||
expect(() => {
|
||||
world.createScene('');
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('极限情况下的资源管理', () => {
|
||||
// 创建大量Scene
|
||||
for (let i = 0; i < 5; i++) {
|
||||
world.createScene(`scene_${i}`);
|
||||
world.setSceneActive(`scene_${i}`, true);
|
||||
}
|
||||
|
||||
// 添加多个全局System
|
||||
for (let i = 0; i < 3; i++) {
|
||||
world.addGlobalSystem(new TestGlobalSystem());
|
||||
}
|
||||
|
||||
world.start();
|
||||
|
||||
// 测试批量清理
|
||||
expect(() => world.destroy()).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
464
packages/core/tests/ECS/WorldManager.test.ts
Normal file
464
packages/core/tests/ECS/WorldManager.test.ts
Normal file
@@ -0,0 +1,464 @@
|
||||
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 { 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;
|
||||
}
|
||||
}
|
||||
|
||||
// 测试用全局系统
|
||||
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 {
|
||||
// 销毁
|
||||
}
|
||||
}
|
||||
|
||||
describe('WorldManager', () => {
|
||||
let worldManager: WorldManager;
|
||||
|
||||
beforeEach(() => {
|
||||
// 重置单例
|
||||
WorldManager['_instance'] = null;
|
||||
worldManager = WorldManager.getInstance();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// 清理所有World
|
||||
if (worldManager) {
|
||||
const worldIds = worldManager.getWorldIds();
|
||||
worldIds.forEach(id => {
|
||||
worldManager.removeWorld(id);
|
||||
});
|
||||
// 清理定时器
|
||||
worldManager.destroy();
|
||||
}
|
||||
WorldManager['_instance'] = null;
|
||||
});
|
||||
|
||||
describe('单例模式', () => {
|
||||
test('获取实例应该返回相同的实例', () => {
|
||||
const instance1 = WorldManager.getInstance();
|
||||
const instance2 = WorldManager.getInstance();
|
||||
|
||||
expect(instance1).toBe(instance2);
|
||||
});
|
||||
|
||||
test('使用配置创建实例应该正确', () => {
|
||||
WorldManager['_instance'] = null;
|
||||
|
||||
const config: IWorldManagerConfig = {
|
||||
maxWorlds: 10,
|
||||
autoCleanup: true,
|
||||
debug: false
|
||||
};
|
||||
|
||||
const instance = WorldManager.getInstance(config);
|
||||
|
||||
expect(instance).toBeDefined();
|
||||
expect(instance).toBe(WorldManager.getInstance());
|
||||
});
|
||||
});
|
||||
|
||||
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',
|
||||
debug: true,
|
||||
maxScenes: 5,
|
||||
autoCleanup: false
|
||||
};
|
||||
|
||||
const world = worldManager.createWorld('configured-world', worldConfig);
|
||||
|
||||
expect(world.name).toBe('ConfiguredWorld');
|
||||
});
|
||||
|
||||
test('重复的World ID应该抛出错误', () => {
|
||||
worldManager.createWorld('duplicate-world');
|
||||
|
||||
expect(() => {
|
||||
worldManager.createWorld('duplicate-world');
|
||||
}).toThrow("World ID 'duplicate-world' 已存在");
|
||||
});
|
||||
|
||||
test('超出最大World数量应该抛出错误', () => {
|
||||
WorldManager['_instance'] = null;
|
||||
const limitedManager = WorldManager.getInstance({ maxWorlds: 2 });
|
||||
|
||||
limitedManager.createWorld('world1');
|
||||
limitedManager.createWorld('world2');
|
||||
|
||||
expect(() => {
|
||||
limitedManager.createWorld('world3');
|
||||
}).toThrow("已达到最大World数量限制: 2");
|
||||
|
||||
// 清理
|
||||
limitedManager.removeWorld('world1');
|
||||
limitedManager.removeWorld('world2');
|
||||
});
|
||||
|
||||
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.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.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.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');
|
||||
|
||||
// 为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');
|
||||
|
||||
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');
|
||||
|
||||
// 创建一个有内容的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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('World更新协调', () => {
|
||||
test('更新所有活跃World应该正确', () => {
|
||||
const world1 = worldManager.createWorld('update-world1');
|
||||
const world2 = worldManager.createWorld('update-world2');
|
||||
const 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 => {
|
||||
world.updateGlobalSystems();
|
||||
world.updateScenes();
|
||||
});
|
||||
}).not.toThrow();
|
||||
|
||||
expect(activeWorlds).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('边界情况和错误处理', () => {
|
||||
test('World ID为空字符串应该抛出错误', () => {
|
||||
expect(() => {
|
||||
worldManager.createWorld('');
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
test('World ID为null或undefined应该抛出错误', () => {
|
||||
expect(() => {
|
||||
worldManager.createWorld(null as any);
|
||||
}).toThrow();
|
||||
|
||||
expect(() => {
|
||||
worldManager.createWorld(undefined as any);
|
||||
}).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 => {
|
||||
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 => {
|
||||
worldManager.removeWorld(id);
|
||||
});
|
||||
|
||||
const afterCleanup = worldManager.getStats();
|
||||
expect(afterCleanup.totalWorlds).toBe(0);
|
||||
expect(afterCleanup.activeWorlds).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('配置验证', () => {
|
||||
test('无效的maxWorlds配置应该使用默认值', () => {
|
||||
WorldManager['_instance'] = null;
|
||||
|
||||
const invalidConfig: IWorldManagerConfig = {
|
||||
maxWorlds: -1,
|
||||
autoCleanup: true,
|
||||
debug: true
|
||||
};
|
||||
|
||||
expect(() => {
|
||||
WorldManager.getInstance(invalidConfig);
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
test('配置更新应该影响后续操作', () => {
|
||||
WorldManager['_instance'] = null;
|
||||
|
||||
const config: IWorldManagerConfig = {
|
||||
maxWorlds: 3,
|
||||
autoCleanup: true,
|
||||
debug: true
|
||||
};
|
||||
|
||||
const manager = WorldManager.getInstance(config);
|
||||
|
||||
// 创建到限制数量的World
|
||||
manager.createWorld('world1');
|
||||
manager.createWorld('world2');
|
||||
manager.createWorld('world3');
|
||||
|
||||
// 第四个应该失败
|
||||
expect(() => {
|
||||
manager.createWorld('world4');
|
||||
}).toThrow();
|
||||
|
||||
// 清理
|
||||
manager.removeWorld('world1');
|
||||
manager.removeWorld('world2');
|
||||
manager.removeWorld('world3');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user