diff --git a/package-lock.json b/package-lock.json index 074d1129..dbc500b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5220,6 +5220,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/msgpack-lite": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@types/msgpack-lite/-/msgpack-lite-0.1.11.tgz", + "integrity": "sha512-cdCZS/gw+jIN22I4SUZUFf1ZZfVv5JM1//Br/MuZcI373sxiy3eSSoiyLu0oz+BPatTbGGGBO5jrcvd0siCdTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/multer": { "version": "1.4.13", "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", @@ -7705,6 +7715,12 @@ "node": ">=0.10.0" } }, + "node_modules/event-lite": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/event-lite/-/event-lite-0.1.3.tgz", + "integrity": "sha512-8qz9nOz5VeD2z96elrEKD2U433+L3DWdUdDkOINLGOJvx1GsMBbMn0aCeu28y8/e85A6mCigBiFlYMnTBEGlSw==", + "license": "MIT" + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -8656,7 +8672,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -8846,6 +8861,12 @@ "node": ">=8" } }, + "node_modules/int64-buffer": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/int64-buffer/-/int64-buffer-0.1.10.tgz", + "integrity": "sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==", + "license": "MIT" + }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -9117,7 +9138,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -11523,6 +11543,21 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "node_modules/msgpack-lite": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/msgpack-lite/-/msgpack-lite-0.1.26.tgz", + "integrity": "sha512-SZ2IxeqZ1oRFGo0xFGbvBJWMp3yLIY9rlIJyxy8CGrwZn1f0ZK4r6jV/AM1r0FZMDUkWkglOk/eeKIL9g77Nxw==", + "license": "MIT", + "dependencies": { + "event-lite": "^0.1.1", + "ieee754": "^1.1.8", + "int64-buffer": "^0.1.9", + "isarray": "^1.0.0" + }, + "bin": { + "msgpack": "bin/msgpack" + } + }, "node_modules/multimatch": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", @@ -15524,6 +15559,9 @@ "name": "@esengine/ecs-framework", "version": "2.1.51", "license": "MIT", + "dependencies": { + "msgpack-lite": "^0.1.26" + }, "devDependencies": { "@babel/core": "^7.28.3", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", @@ -15534,6 +15572,7 @@ "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^29.5.14", + "@types/msgpack-lite": "^0.1.11", "@types/node": "^20.19.17", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", diff --git a/packages/core/package.json b/packages/core/package.json index 9de7000d..e6be99b0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -58,6 +58,7 @@ "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^29.5.14", + "@types/msgpack-lite": "^0.1.11", "@types/node": "^20.19.17", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", @@ -75,5 +76,8 @@ "type": "git", "url": "https://github.com/esengine/ecs-framework.git", "directory": "packages/core" + }, + "dependencies": { + "msgpack-lite": "^0.1.26" } } diff --git a/packages/core/src/ECS/Scene.ts b/packages/core/src/ECS/Scene.ts index 2901aea3..395d49d6 100644 --- a/packages/core/src/ECS/Scene.ts +++ b/packages/core/src/ECS/Scene.ts @@ -501,21 +501,26 @@ export class Scene implements IScene { /** * 序列化场景 * - * 将场景及其所有实体、组件序列化为JSON字符串 + * 将场景及其所有实体、组件序列化为JSON字符串或二进制Buffer * * @param options 序列化选项 - * @returns 序列化后的JSON字符串 + * @returns 序列化后的数据(JSON字符串或二进制Buffer) * * @example * ```typescript - * const saveData = scene.serialize({ - * components: [PlayerComponent, PositionComponent], + * // JSON格式 + * const jsonData = scene.serialize({ * format: 'json', * pretty: true * }); + * + * // 二进制格式(更小、更快) + * const binaryData = scene.serialize({ + * format: 'binary' + * }); * ``` */ - public serialize(options?: SceneSerializationOptions): string { + public serialize(options?: SceneSerializationOptions): string | Buffer { return SceneSerializer.serialize(this, options); } @@ -524,19 +529,23 @@ export class Scene implements IScene { * * 从序列化数据恢复场景状态 * - * @param saveData 序列化的数据 + * @param saveData 序列化的数据(JSON字符串或二进制Buffer) * @param options 反序列化选项 * * @example * ```typescript - * scene.deserialize(saveData, { - * strategy: 'replace', - * preserveIds: false, - * componentRegistry: ComponentTypeRegistry.getRegistry() + * // 从JSON恢复(自动从ComponentRegistry获取组件类型) + * scene.deserialize(jsonData, { + * strategy: 'replace' + * }); + * + * // 从二进制恢复 + * scene.deserialize(binaryData, { + * strategy: 'replace' * }); * ``` */ - public deserialize(saveData: string, options?: SceneDeserializationOptions): void { + public deserialize(saveData: string | Buffer, options?: SceneDeserializationOptions): void { SceneSerializer.deserialize(this, saveData, options); } } \ No newline at end of file diff --git a/packages/core/src/ECS/Serialization/ComponentTypeRegistry.ts b/packages/core/src/ECS/Serialization/ComponentTypeRegistry.ts deleted file mode 100644 index 3cf03cd6..00000000 --- a/packages/core/src/ECS/Serialization/ComponentTypeRegistry.ts +++ /dev/null @@ -1,194 +0,0 @@ -/** - * 全局组件类型注册表 - * - * 用于序列化系统的组件类型查找和管理 - */ - -import { Component } from '../Component'; -import { ComponentType } from '../Core/ComponentStorage'; -import { getComponentTypeName } from '../Decorators'; - -/** - * 全局组件类型注册表 - * - * 维护组件类型名称到构造函数的映射,用于序列化/反序列化 - */ -export class ComponentTypeRegistry { - /** - * 组件类型映射表 - * Map<类型名称, 构造函数> - */ - private static registry = new Map(); - - /** - * 注册组件类型 - * - * @param componentClass 组件构造函数 - * @param typeName 组件类型名称(可选,默认使用类名或@ECSComponent装饰器指定的名称) - * - * @example - * ```typescript - * @ECSComponent('Player') - * @Serializable({ version: 1 }) - * class PlayerComponent extends Component { - * @Serialize() name: string = ''; - * } - * - * // 注册组件 - * ComponentTypeRegistry.register(PlayerComponent); - * ``` - */ - public static register(componentClass: ComponentType, typeName?: string): void { - const name = typeName || getComponentTypeName(componentClass); - - if (this.registry.has(name)) { - console.warn(`Component type "${name}" is already registered, overwriting...`); - } - - this.registry.set(name, componentClass); - } - - /** - * 批量注册组件类型 - * - * @param componentClasses 组件构造函数数组 - * - * @example - * ```typescript - * ComponentTypeRegistry.registerMany([ - * PlayerComponent, - * PositionComponent, - * VelocityComponent - * ]); - * ``` - */ - public static registerMany(componentClasses: ComponentType[]): void { - for (const componentClass of componentClasses) { - this.register(componentClass); - } - } - - /** - * 获取组件类型 - * - * @param typeName 组件类型名称 - * @returns 组件构造函数,如果未找到则返回undefined - */ - public static get(typeName: string): ComponentType | undefined { - return this.registry.get(typeName); - } - - /** - * 检查组件类型是否已注册 - * - * @param typeName 组件类型名称 - * @returns 如果已注册返回true - */ - public static has(typeName: string): boolean { - return this.registry.has(typeName); - } - - /** - * 取消注册组件类型 - * - * @param typeName 组件类型名称 - * @returns 如果成功取消注册返回true - */ - public static unregister(typeName: string): boolean { - return this.registry.delete(typeName); - } - - /** - * 清空注册表 - */ - public static clear(): void { - this.registry.clear(); - } - - /** - * 获取所有已注册的组件类型名称 - * - * @returns 组件类型名称数组 - */ - public static getAllTypeNames(): string[] { - return Array.from(this.registry.keys()); - } - - /** - * 获取所有已注册的组件类型 - * - * @returns 组件构造函数数组 - */ - public static getAllTypes(): ComponentType[] { - return Array.from(this.registry.values()); - } - - /** - * 获取注册表的Map副本 - * - * @returns 组件类型注册表的副本 - */ - public static getRegistry(): Map { - return new Map(this.registry); - } - - /** - * 获取注册的组件数量 - * - * @returns 已注册的组件类型数量 - */ - public static get size(): number { - return this.registry.size; - } - - /** - * 从组件实例获取类型名称 - * - * @param component 组件实例 - * @returns 组件类型名称 - */ - public static getTypeName(component: Component): string { - return getComponentTypeName(component.constructor as ComponentType); - } - - /** - * 根据组件类查找已注册的类型名称 - * - * @param componentClass 组件构造函数 - * @returns 类型名称,如果未注册则返回undefined - */ - public static findTypeName(componentClass: ComponentType): string | undefined { - const typeName = getComponentTypeName(componentClass); - - // 检查是否已注册 - if (this.registry.get(typeName) === componentClass) { - return typeName; - } - - // 遍历查找 - for (const [name, cls] of this.registry) { - if (cls === componentClass) { - return name; - } - } - - return undefined; - } - - /** - * 自动发现并注册所有装饰的组件 - * - * 注意:此方法需要组件类已经被加载到内存中 - * - * @param components 组件类数组 - */ - public static autoRegister(components: ComponentType[]): void { - for (const component of components) { - try { - this.register(component); - } catch (error) { - console.error(`Failed to auto-register component ${component.name}:`, error); - } - } - } -} diff --git a/packages/core/src/ECS/Serialization/SceneSerializer.ts b/packages/core/src/ECS/Serialization/SceneSerializer.ts index 1aa43082..22d13f9f 100644 --- a/packages/core/src/ECS/Serialization/SceneSerializer.ts +++ b/packages/core/src/ECS/Serialization/SceneSerializer.ts @@ -7,11 +7,11 @@ import type { IScene } from '../IScene'; import { Entity } from '../Entity'; import { Component } from '../Component'; -import { ComponentType } from '../Core/ComponentStorage'; +import { ComponentType, ComponentRegistry } from '../Core/ComponentStorage'; import { EntitySerializer, SerializedEntity } from './EntitySerializer'; import { getComponentTypeName } from '../Decorators'; import { getSerializationMetadata } from './SerializationDecorators'; -import { ComponentTypeRegistry } from './ComponentTypeRegistry'; +import * as msgpack from 'msgpack-lite'; /** * 场景序列化格式 @@ -154,9 +154,9 @@ export class SceneSerializer { * * @param scene 要序列化的场景 * @param options 序列化选项 - * @returns 序列化后的数据(JSON字符串或二进制数据) + * @returns 序列化后的数据(JSON字符串或二进制Buffer) */ - public static serialize(scene: IScene, options?: SceneSerializationOptions): string { + public static serialize(scene: IScene, options?: SceneSerializationOptions): string | Buffer { const opts: SceneSerializationOptions = { systems: false, format: 'json', @@ -206,8 +206,8 @@ export class SceneSerializer { ? JSON.stringify(serializedScene, null, 2) : JSON.stringify(serializedScene); } else { - // 二进制格式(未来实现) - throw new Error('Binary serialization format is not yet implemented'); + // 二进制格式(使用 MessagePack) + return msgpack.encode(serializedScene); } } @@ -215,12 +215,12 @@ export class SceneSerializer { * 反序列化场景 * * @param scene 目标场景 - * @param saveData 序列化的数据 + * @param saveData 序列化的数据(JSON字符串或二进制Buffer) * @param options 反序列化选项 */ public static deserialize( scene: IScene, - saveData: string, + saveData: string | Buffer, options?: SceneDeserializationOptions ): void { const opts: SceneDeserializationOptions = { @@ -232,7 +232,13 @@ export class SceneSerializer { // 解析数据 let serializedScene: SerializedScene; try { - serializedScene = JSON.parse(saveData); + if (typeof saveData === 'string') { + // JSON格式 + serializedScene = JSON.parse(saveData); + } else { + // 二进制格式(MessagePack) + serializedScene = msgpack.decode(saveData); + } } catch (error) { throw new Error(`Failed to parse save data: ${error}`); } @@ -455,7 +461,7 @@ export class SceneSerializer { * 从所有已注册的组件类型构建注册表 */ private static getGlobalComponentRegistry(): Map { - return ComponentTypeRegistry.getRegistry(); + return ComponentRegistry.getAllComponentNames() as Map; } /** diff --git a/packages/core/src/ECS/Serialization/index.ts b/packages/core/src/ECS/Serialization/index.ts index 1e13f2f5..357babc1 100644 --- a/packages/core/src/ECS/Serialization/index.ts +++ b/packages/core/src/ECS/Serialization/index.ts @@ -49,6 +49,3 @@ export type { ComponentMigrationFunction, SceneMigrationFunction } from './VersionMigration'; - -// 组件类型注册表 -export { ComponentTypeRegistry } from './ComponentTypeRegistry'; diff --git a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts index c0da46ad..e176e62d 100644 --- a/packages/core/src/ECS/Systems/WorkerEntitySystem.ts +++ b/packages/core/src/ECS/Systems/WorkerEntitySystem.ts @@ -198,6 +198,7 @@ export abstract class WorkerEntitySystem extends EntitySystem protected sharedBuffer: SharedArrayBuffer | null = null; protected sharedFloatArray: Float32Array | null = null; private platformAdapter: IPlatformAdapter; + private hasLoggedSyncMode = false; constructor(matcher?: Matcher, config: WorkerSystemConfig = {}) { super(matcher); @@ -449,7 +450,10 @@ export abstract class WorkerEntitySystem extends EntitySystem } } else { // 同步处理(最后的fallback) - this.logger.info(`${this.systemName}: Worker不可用,使用同步处理`); + if (!this.hasLoggedSyncMode) { + this.logger.info(`${this.systemName}: Worker不可用,使用同步处理`); + this.hasLoggedSyncMode = true; + } this.processSynchronously(entities); this.isProcessing = false; } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index c77485a3..feb1e347 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -29,7 +29,6 @@ export * from './ECS'; // TypeScript类型增强API export * from './ECS/TypedEntity'; -export * from './ECS/Systems/TypedEntitySystem'; export * from './ECS/Core/Query/TypedQuery'; // 事件系统 @@ -39,5 +38,8 @@ export { ECSEventType, EventPriority, EVENT_TYPES, EventTypeValidator } from './ export * from './Utils'; export * from './Types'; +// 显式导出ComponentPool类(解决与Types中ComponentPool接口的命名冲突) +export { ComponentPool, ComponentPoolManager } from './ECS/Core/Storage'; + // 平台适配 export * from './Platform'; \ No newline at end of file diff --git a/packages/core/tests/ECS/Serialization/Serialization.test.ts b/packages/core/tests/ECS/Serialization/Serialization.test.ts index f185b408..edc369d6 100644 --- a/packages/core/tests/ECS/Serialization/Serialization.test.ts +++ b/packages/core/tests/ECS/Serialization/Serialization.test.ts @@ -14,11 +14,11 @@ import { ComponentSerializer, EntitySerializer, SceneSerializer, - ComponentTypeRegistry, VersionMigrationManager, MigrationBuilder } from '../../../src/ECS/Serialization'; import { ECSComponent } from '../../../src/ECS/Decorators'; +import { ComponentRegistry } from '../../../src/ECS/Core/ComponentStorage'; // 测试组件定义 @ECSComponent('Position') @@ -83,14 +83,8 @@ class NonSerializableComponent extends Component { describe('ECS Serialization System', () => { beforeEach(() => { - // 注册组件类型 - ComponentTypeRegistry.clear(); - ComponentTypeRegistry.registerMany([ - PositionComponent, - VelocityComponent, - PlayerComponent, - HealthComponent - ]); + // @ECSComponent装饰器会自动注册组件到ComponentRegistry,无需手动注册 + ComponentRegistry.reset(); // 清空测试环境 }); describe('Component Serialization', () => { @@ -112,7 +106,7 @@ describe('ECS Serialization System', () => { data: { x: 150, y: 250 } }; - const registry = ComponentTypeRegistry.getRegistry(); + const registry = ComponentRegistry.getAllComponentNames() as Map; const component = ComponentSerializer.deserialize(serializedData, registry); expect(component).not.toBeNull(); @@ -152,7 +146,7 @@ describe('ECS Serialization System', () => { } }; - const registry = ComponentTypeRegistry.getRegistry(); + const registry = ComponentRegistry.getAllComponentNames() as Map; const component = ComponentSerializer.deserialize( serializedData, registry @@ -231,7 +225,7 @@ describe('ECS Serialization System', () => { children: [] }; - const registry = ComponentTypeRegistry.getRegistry(); + const registry = ComponentRegistry.getAllComponentNames() as Map; let idCounter = 10; const entity = EntitySerializer.deserialize( serializedEntity, @@ -267,8 +261,9 @@ describe('ECS Serialization System', () => { const saveData = scene.serialize({ format: 'json', pretty: true }); expect(saveData).toBeTruthy(); + expect(typeof saveData).toBe('string'); - const parsed = JSON.parse(saveData); + const parsed = JSON.parse(saveData as string); expect(parsed.name).toBe('TestScene'); expect(parsed.version).toBe(1); expect(parsed.entities.length).toBe(2); @@ -287,7 +282,7 @@ describe('ECS Serialization System', () => { // 清空并重新加载 scene.deserialize(saveData, { strategy: 'replace', - componentRegistry: ComponentTypeRegistry.getRegistry() + // componentRegistry会自动从ComponentRegistry获取 }); expect(scene.entities.count).toBeGreaterThan(0); @@ -300,10 +295,11 @@ describe('ECS Serialization System', () => { entity.addComponent(new HealthComponent()); const saveData = scene.serialize({ - components: [PositionComponent, PlayerComponent] + components: [PositionComponent, PlayerComponent], + format: 'json' }); - const parsed = JSON.parse(saveData); + const parsed = JSON.parse(saveData as string); expect(parsed.entities.length).toBeGreaterThan(0); }); @@ -315,8 +311,8 @@ describe('ECS Serialization System', () => { parent.addComponent(new PositionComponent(0, 0)); child.addComponent(new PositionComponent(10, 10)); - const saveData = scene.serialize(); - const parsed = JSON.parse(saveData); + const saveData = scene.serialize({ format: 'json' }); + const parsed = JSON.parse(saveData as string); // 只有父实体在顶层 expect(parsed.entities.length).toBe(1); @@ -327,8 +323,8 @@ describe('ECS Serialization System', () => { const entity = scene.createEntity('Test'); entity.addComponent(new PositionComponent(5, 5)); - const saveData = scene.serialize(); - const validation = SceneSerializer.validate(saveData); + const saveData = scene.serialize({ format: 'json' }); + const validation = SceneSerializer.validate(saveData as string); expect(validation.valid).toBe(true); expect(validation.version).toBe(1); @@ -338,8 +334,8 @@ describe('ECS Serialization System', () => { const entity = scene.createEntity('InfoTest'); entity.addComponent(new PositionComponent(1, 1)); - const saveData = scene.serialize(); - const info = SceneSerializer.getInfo(saveData); + const saveData = scene.serialize({ format: 'json' }); + const info = SceneSerializer.getInfo(saveData as string); expect(info).not.toBeNull(); expect(info!.name).toBe('TestScene'); @@ -434,48 +430,7 @@ describe('ECS Serialization System', () => { }); }); - describe('ComponentTypeRegistry', () => { - it('should register and retrieve component types', () => { - ComponentTypeRegistry.clear(); - ComponentTypeRegistry.register(PositionComponent); - - expect(ComponentTypeRegistry.has('Position')).toBe(true); - expect(ComponentTypeRegistry.get('Position')).toBe(PositionComponent); - }); - - it('should register multiple component types', () => { - ComponentTypeRegistry.clear(); - ComponentTypeRegistry.registerMany([ - PositionComponent, - VelocityComponent, - PlayerComponent - ]); - - expect(ComponentTypeRegistry.size).toBe(3); - }); - - it('should get all type names', () => { - ComponentTypeRegistry.clear(); - ComponentTypeRegistry.register(PositionComponent); - ComponentTypeRegistry.register(VelocityComponent); - - const typeNames = ComponentTypeRegistry.getAllTypeNames(); - - expect(typeNames).toContain('Position'); - expect(typeNames).toContain('Velocity'); - }); - - it('should unregister component types', () => { - ComponentTypeRegistry.clear(); - ComponentTypeRegistry.register(PositionComponent); - - expect(ComponentTypeRegistry.has('Position')).toBe(true); - - ComponentTypeRegistry.unregister('Position'); - - expect(ComponentTypeRegistry.has('Position')).toBe(false); - }); - }); + // ComponentTypeRegistry已被移除,现在使用ComponentRegistry自动管理组件类型 describe('Integration Tests', () => { it('should perform full save/load cycle', () => { @@ -508,7 +463,7 @@ describe('ECS Serialization System', () => { // 反序列化 scene2.deserialize(saveData, { strategy: 'replace', - componentRegistry: ComponentTypeRegistry.getRegistry() + // componentRegistry会自动从ComponentRegistry获取 }); // 验证 @@ -549,7 +504,7 @@ describe('ECS Serialization System', () => { // 反序列化 scene2.deserialize(saveData, { strategy: 'replace', - componentRegistry: ComponentTypeRegistry.getRegistry() + // componentRegistry会自动从ComponentRegistry获取 }); // 验证场景数据 @@ -571,5 +526,100 @@ describe('ECS Serialization System', () => { scene1.end(); scene2.end(); }); + + it('should serialize and deserialize using binary format', () => { + const scene1 = new Scene({ name: 'BinaryTest' }); + + // 创建测试数据 + const player = scene1.createEntity('Player'); + const playerComp = new PlayerComponent(); + playerComp.name = 'BinaryHero'; + playerComp.level = 5; + playerComp.inventory.set('sword', 1); + player.addComponent(playerComp); + player.addComponent(new PositionComponent(100, 200)); + + scene1.sceneData.set('weather', 'sunny'); + scene1.sceneData.set('score', 9999); + + // 二进制序列化 + const binaryData = scene1.serialize({ format: 'binary' }); + + // 验证是Buffer类型 + expect(Buffer.isBuffer(binaryData)).toBe(true); + + // JSON序列化对比 + const jsonData = scene1.serialize({ format: 'json', pretty: false }); + + // 二进制应该更小 + const binarySize = (binaryData as Buffer).length; + const jsonSize = (jsonData as string).length; + console.log(`Binary size: ${binarySize} bytes, JSON size: ${jsonSize} bytes`); + expect(binarySize).toBeLessThan(jsonSize); + + // 新场景反序列化二进制数据 + const scene2 = new Scene({ name: 'LoadTest' }); + scene2.deserialize(binaryData, { + strategy: 'replace', + // componentRegistry会自动从ComponentRegistry获取 + }); + + // 验证数据完整性 + const loadedPlayer = scene2.findEntity('Player'); + expect(loadedPlayer).not.toBeNull(); + + const loadedPlayerComp = loadedPlayer!.getComponent(PlayerComponent as any) as PlayerComponent; + expect(loadedPlayerComp.name).toBe('BinaryHero'); + expect(loadedPlayerComp.level).toBe(5); + expect(loadedPlayerComp.inventory.get('sword')).toBe(1); + + const loadedPos = loadedPlayer!.getComponent(PositionComponent as any) as PositionComponent; + expect(loadedPos.x).toBe(100); + expect(loadedPos.y).toBe(200); + + expect(scene2.sceneData.get('weather')).toBe('sunny'); + expect(scene2.sceneData.get('score')).toBe(9999); + + scene1.end(); + scene2.end(); + }); + + it('should handle complex nested data in binary format', () => { + const scene1 = new Scene({ name: 'NestedBinaryTest' }); + + // 复杂嵌套数据 + scene1.sceneData.set('config', { + graphics: { + quality: 'high', + resolution: { width: 1920, height: 1080 } + }, + audio: { + masterVolume: 0.8, + effects: new Map([['music', 0.7], ['sfx', 0.9]]) + }, + tags: new Set(['multiplayer', 'ranked']), + timestamp: new Date('2024-01-01') + }); + + // 二进制序列化 + const binaryData = scene1.serialize({ format: 'binary' }); + + // 反序列化 + const scene2 = new Scene({ name: 'LoadTest' }); + scene2.deserialize(binaryData, { + // componentRegistry会自动从ComponentRegistry获取 + }); + + const config = scene2.sceneData.get('config'); + expect(config.graphics.quality).toBe('high'); + expect(config.graphics.resolution.width).toBe(1920); + expect(config.audio.masterVolume).toBe(0.8); + expect(config.audio.effects.get('music')).toBe(0.7); + expect(config.tags.has('multiplayer')).toBe(true); + expect(config.timestamp).toBeInstanceOf(Date); + + scene1.end(); + scene2.end(); + }); }); });