Files
esengine/packages/core/src/ECS/WorldManager.ts

487 lines
12 KiB
TypeScript
Raw Normal View History

import { World, IWorldConfig } from './World';
import { createLogger } from '../Utils/Logger';
import type { IService } from '../Core/ServiceContainer';
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
*
*
* - MMO游戏的多房间管理
* -
* -
*
* @example
* ```typescript
* // 创建WorldManager实例
* const worldManager = new WorldManager({
* maxWorlds: 100,
* autoCleanup: true
* });
*
* // 创建游戏房间World
* const room1 = worldManager.createWorld('room_001', {
* name: 'GameRoom_001',
* maxScenes: 5
* });
* room1.setActive(true);
*
* // 游戏循环
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime);
* worldManager.updateAll(); // 更新所有活跃World
* }
* ```
*/
export class WorldManager implements IService {
private readonly _config: IWorldManagerConfig;
private readonly _worlds: Map<string, World> = new Map();
private readonly _activeWorlds: Set<string> = new Set();
private _cleanupTimer: ReturnType<typeof setInterval> | null = null;
private _isRunning: boolean = false;
public constructor(config: IWorldManagerConfig = {}) {
this._config = {
maxWorlds: 50,
autoCleanup: true,
cleanupInterval: 30000, // 30秒
debug: false,
...config
};
// 默认启动运行状态
this._isRunning = true;
logger.info('WorldManager已初始化', {
maxWorlds: this._config.maxWorlds,
autoCleanup: this._config.autoCleanup,
cleanupInterval: this._config.cleanupInterval
});
this.startCleanupTimer();
}
// ===== 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);
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
*
*
* World的全局系统和场景
*
* @example
* ```typescript
* function gameLoop(deltaTime: number) {
* Core.update(deltaTime); // 更新全局服务
* worldManager.updateAll(); // 更新所有World
* }
* ```
*/
public updateAll(): void {
if (!this._isRunning) return;
for (const worldId of this._activeWorlds) {
const world = this._worlds.get(worldId);
if (world && world.isActive) {
// 更新World的全局System
world.updateGlobalSystems();
// 更新World中的所有Scene
world.updateScenes();
}
}
}
/**
* World
*/
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已销毁');
}
/**
* IService dispose
* destroy
*/
public dispose(): void {
this.destroy();
}
// ===== 私有方法 =====
/**
*
*/
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 };
}
}