feat(core): 为 World 添加独立的服务容器 (#222)

* feat(core): 为 World 添加独立的服务容器

* test(core): 为 World 服务容器添加完整测试覆盖
This commit is contained in:
YHH
2025-11-14 09:55:31 +08:00
committed by GitHub
parent 609baace73
commit 3a0544629d
3 changed files with 135 additions and 8 deletions

View File

@@ -44,7 +44,13 @@ class MyService implements IService {
### 访问服务容器
Core 类内置了服务容器,可以通过 `Core.services` 访问
ECS Framework 提供了三级服务容器
> **版本说明**World 服务容器功能在 v2.2.13+ 版本中可用
#### Core 级别服务容器
应用程序全局服务容器,可以通过 `Core.services` 访问:
```typescript
import { Core } from '@esengine/ecs-framework';
@@ -52,10 +58,53 @@ import { Core } from '@esengine/ecs-framework';
// 初始化Core
Core.create({ debug: true });
// 访问服务容器
// 访问全局服务容器
const container = Core.services;
```
#### World 级别服务容器
每个 World 拥有独立的服务容器,用于管理 World 范围内的服务:
```typescript
import { World } from '@esengine/ecs-framework';
// 创建 World
const world = new World({ name: 'GameWorld' });
// 访问 World 级别的服务容器
const worldContainer = world.services;
// 注册 World 级别的服务
world.services.registerSingleton(RoomManager);
```
#### Scene 级别服务容器
每个 Scene 拥有独立的服务容器,用于管理 Scene 范围内的服务:
```typescript
// 访问 Scene 级别的服务容器
const sceneContainer = scene.services;
// 注册 Scene 级别的服务
scene.services.registerSingleton(PhysicsSystem);
```
#### 服务容器层级
```
Core.services (应用程序全局)
└─ World.services (World 级别)
└─ Scene.services (Scene 级别)
```
不同级别的服务容器是独立的,服务不会自动向上或向下查找。选择合适的容器级别:
- **Core.services**: 应用程序级别的全局服务(配置、插件管理器等)
- **World.services**: World 级别的服务(房间管理器、多人游戏状态等)
- **Scene.services**: Scene 级别的服务ECS 系统、场景特定逻辑等)
### 注册服务
#### 注册单例服务

View File

@@ -2,6 +2,7 @@ import { IScene } from './IScene';
import { Scene } from './Scene';
import { createLogger } from '../Utils/Logger';
import { PerformanceMonitor } from '../Utils/PerformanceMonitor';
import { ServiceContainer } from '../Core/ServiceContainer';
const logger = createLogger('World');
@@ -65,6 +66,13 @@ export interface IWorldConfig {
* World类 - ECS世界管理器
*
* World是Scene的容器每个World可以管理多个Scene。
* World拥有独立的服务容器用于管理World级别的全局服务。
*
* 服务容器层级:
* - Core.services: 应用程序全局服务
* - World.services: World级别服务每个World独立
* - Scene.services: Scene级别服务每个Scene独立
*
* 这种设计允许创建独立的游戏世界,如:
* - 游戏房间每个房间一个World
* - 不同的游戏模式
@@ -75,10 +83,16 @@ export interface IWorldConfig {
* // 创建游戏房间的World
* const roomWorld = new World({ name: 'Room_001' });
*
* // 注册World级别的服务
* roomWorld.services.registerSingleton(RoomManager);
*
* // 在World中创建Scene
* const gameScene = roomWorld.createScene('game', new Scene());
* const uiScene = roomWorld.createScene('ui', new Scene());
*
* // 在Scene中使用World级别的服务
* const roomManager = roomWorld.services.resolve(RoomManager);
*
* // 更新整个World
* roomWorld.update(deltaTime);
* ```
@@ -89,6 +103,7 @@ export class World {
private readonly _scenes: Map<string, IScene> = new Map();
private readonly _activeScenes: Set<string> = new Set();
private readonly _globalSystems: IGlobalSystem[] = [];
private readonly _services: ServiceContainer;
private _isActive: boolean = false;
private _createdAt: number;
@@ -103,6 +118,17 @@ export class World {
this.name = this._config.name!;
this._createdAt = Date.now();
this._services = new ServiceContainer();
}
// ===== 服务容器 =====
/**
* World级别的服务容器
* 用于管理World范围内的全局服务
*/
public get services(): ServiceContainer {
return this._services;
}
// ===== Scene管理 =====
@@ -398,6 +424,9 @@ export class World {
}
this._globalSystems.length = 0;
// 清空服务容器
this._services.clear();
this._scenes.clear();
this._activeScenes.clear();
}

View File

@@ -4,6 +4,7 @@ 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';
import { IService } from '../../src/Core/ServiceContainer';
// 测试用组件
class TestComponent extends Component {
@@ -48,16 +49,26 @@ class TestGlobalSystem implements IGlobalSystem {
class TestSceneSystem extends EntitySystem {
public updateCount = 0;
constructor() {
super(Matcher.empty().all(PlayerComponent));
}
protected override process(): void {
this.updateCount++;
}
}
// 测试用服务
class TestWorldService implements IService {
public disposed = false;
public value = 'test';
dispose(): void {
this.disposed = true;
}
}
// 测试用Scene
class TestScene extends Scene {
public initializeCalled = false;
@@ -438,27 +449,65 @@ describe('World', () => {
});
});
describe('服务容器', () => {
test('应该能访问World级别的服务容器', () => {
const services = world.services;
expect(services).toBeDefined();
expect(services).toHaveProperty('registerSingleton');
expect(services).toHaveProperty('resolve');
});
test('应该能在World服务容器中注册和解析服务', () => {
world.services.registerSingleton(TestWorldService);
const service = world.services.resolve(TestWorldService);
expect(service).toBeDefined();
expect(service.value).toBe('test');
expect(service.disposed).toBe(false);
});
test('World销毁时应该清理服务容器中的服务', () => {
const service = new TestWorldService();
world.services.registerInstance(TestWorldService, service);
world.destroy();
expect(service.disposed).toBe(true);
});
test('World服务容器应该独立于Scene服务容器', () => {
const scene = world.createScene('test-scene');
world.services.registerSingleton(TestWorldService);
expect(world.services.isRegistered(TestWorldService)).toBe(true);
expect(scene.services.isRegistered(TestWorldService)).toBe(false);
});
});
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();
});