场景自定义序列化支持
This commit is contained in:
@@ -9,7 +9,7 @@ import { TypeSafeEventSystem } from './Core/EventSystem';
|
||||
|
||||
/**
|
||||
* 场景接口定义
|
||||
*
|
||||
*
|
||||
* 定义场景应该实现的核心功能和属性,使用接口而非继承提供更灵活的实现方式。
|
||||
*/
|
||||
export interface IScene {
|
||||
@@ -18,6 +18,25 @@ export interface IScene {
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* 场景自定义数据
|
||||
*
|
||||
* 用于存储场景级别的配置和状态数据,例如:
|
||||
* - 天气状态
|
||||
* - 时间设置
|
||||
* - 游戏难度
|
||||
* - 音频配置
|
||||
* - 关卡检查点
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* scene.sceneData.set('weather', 'rainy');
|
||||
* scene.sceneData.set('timeOfDay', 14.5);
|
||||
* scene.sceneData.set('checkpoint', { x: 100, y: 200 });
|
||||
* ```
|
||||
*/
|
||||
readonly sceneData: Map<string, any>;
|
||||
|
||||
/**
|
||||
* 场景中的实体集合
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,7 @@ import { EventBus } from './Core/EventBus';
|
||||
import { IScene, ISceneConfig } from './IScene';
|
||||
import { getComponentInstanceTypeName, getSystemInstanceTypeName } from './Decorators';
|
||||
import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||
import { SceneSerializer, SceneSerializationOptions, SceneDeserializationOptions } from './Serialization/SceneSerializer';
|
||||
|
||||
/**
|
||||
* 游戏场景默认实现类
|
||||
@@ -20,14 +21,21 @@ import { TypedQueryBuilder } from './Core/Query/TypedQuery';
|
||||
export class Scene implements IScene {
|
||||
/**
|
||||
* 场景名称
|
||||
*
|
||||
*
|
||||
* 用于标识和调试的友好名称。
|
||||
*/
|
||||
public name: string = "";
|
||||
|
||||
/**
|
||||
* 场景自定义数据
|
||||
*
|
||||
* 用于存储场景级别的配置和状态数据。
|
||||
*/
|
||||
public readonly sceneData: Map<string, any> = new Map();
|
||||
|
||||
/**
|
||||
* 场景中的实体集合
|
||||
*
|
||||
*
|
||||
* 管理场景内所有实体的生命周期。
|
||||
*/
|
||||
public readonly entities: EntityList;
|
||||
@@ -489,4 +497,46 @@ export class Scene implements IScene {
|
||||
componentStats: this.componentStorageManager.getAllStats()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化场景
|
||||
*
|
||||
* 将场景及其所有实体、组件序列化为JSON字符串
|
||||
*
|
||||
* @param options 序列化选项
|
||||
* @returns 序列化后的JSON字符串
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const saveData = scene.serialize({
|
||||
* components: [PlayerComponent, PositionComponent],
|
||||
* format: 'json',
|
||||
* pretty: true
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public serialize(options?: SceneSerializationOptions): string {
|
||||
return SceneSerializer.serialize(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化场景
|
||||
*
|
||||
* 从序列化数据恢复场景状态
|
||||
*
|
||||
* @param saveData 序列化的数据
|
||||
* @param options 反序列化选项
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* scene.deserialize(saveData, {
|
||||
* strategy: 'replace',
|
||||
* preserveIds: false,
|
||||
* componentRegistry: ComponentTypeRegistry.getRegistry()
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
public deserialize(saveData: string, options?: SceneDeserializationOptions): void {
|
||||
SceneSerializer.deserialize(this, saveData, options);
|
||||
}
|
||||
}
|
||||
336
packages/core/src/ECS/Serialization/ComponentSerializer.ts
Normal file
336
packages/core/src/ECS/Serialization/ComponentSerializer.ts
Normal file
@@ -0,0 +1,336 @@
|
||||
/**
|
||||
* 组件序列化器
|
||||
*
|
||||
* 负责组件的序列化和反序列化操作
|
||||
*/
|
||||
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
import {
|
||||
getSerializationMetadata,
|
||||
isSerializable,
|
||||
SerializationMetadata
|
||||
} from './SerializationDecorators';
|
||||
|
||||
/**
|
||||
* 序列化后的组件数据
|
||||
*/
|
||||
export interface SerializedComponent {
|
||||
/**
|
||||
* 组件类型名称
|
||||
*/
|
||||
type: string;
|
||||
|
||||
/**
|
||||
* 序列化版本
|
||||
*/
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* 组件数据
|
||||
*/
|
||||
data: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件序列化器类
|
||||
*/
|
||||
export class ComponentSerializer {
|
||||
/**
|
||||
* 序列化单个组件
|
||||
*
|
||||
* @param component 要序列化的组件实例
|
||||
* @returns 序列化后的组件数据,如果组件不可序列化则返回null
|
||||
*/
|
||||
public static serialize(component: Component): SerializedComponent | null {
|
||||
const metadata = getSerializationMetadata(component);
|
||||
|
||||
if (!metadata) {
|
||||
// 组件没有使用@Serializable装饰器,不可序列化
|
||||
return null;
|
||||
}
|
||||
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const typeName = metadata.options.typeId || getComponentTypeName(componentType);
|
||||
const data: Record<string, any> = {};
|
||||
|
||||
// 序列化标记的字段
|
||||
for (const [fieldName, options] of metadata.fields) {
|
||||
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
|
||||
const value = (component as any)[fieldName];
|
||||
|
||||
// 跳过忽略的字段
|
||||
if (metadata.ignoredFields.has(fieldName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 使用自定义序列化器或默认序列化
|
||||
const serializedValue = options.serializer
|
||||
? options.serializer(value)
|
||||
: this.serializeValue(value);
|
||||
|
||||
// 使用别名或原始字段名
|
||||
const key = options.alias || fieldKey;
|
||||
data[key] = serializedValue;
|
||||
}
|
||||
|
||||
return {
|
||||
type: typeName,
|
||||
version: metadata.options.version,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化组件
|
||||
*
|
||||
* @param serializedData 序列化的组件数据
|
||||
* @param componentRegistry 组件类型注册表 (类型名 -> 构造函数)
|
||||
* @returns 反序列化后的组件实例,如果失败则返回null
|
||||
*/
|
||||
public static deserialize(
|
||||
serializedData: SerializedComponent,
|
||||
componentRegistry: Map<string, ComponentType>
|
||||
): Component | null {
|
||||
const componentClass = componentRegistry.get(serializedData.type);
|
||||
|
||||
if (!componentClass) {
|
||||
console.warn(`未找到组件类型: ${serializedData.type}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadata = getSerializationMetadata(componentClass);
|
||||
|
||||
if (!metadata) {
|
||||
console.warn(`组件 ${serializedData.type} 不可序列化`);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 创建组件实例
|
||||
const component = new componentClass();
|
||||
|
||||
// 反序列化字段
|
||||
for (const [fieldName, options] of metadata.fields) {
|
||||
const fieldKey = typeof fieldName === 'symbol' ? fieldName.toString() : fieldName;
|
||||
const key = options.alias || fieldKey;
|
||||
const serializedValue = serializedData.data[key];
|
||||
|
||||
if (serializedValue === undefined) {
|
||||
continue; // 字段不存在于序列化数据中
|
||||
}
|
||||
|
||||
// 使用自定义反序列化器或默认反序列化
|
||||
const value = options.deserializer
|
||||
? options.deserializer(serializedValue)
|
||||
: this.deserializeValue(serializedValue);
|
||||
|
||||
(component as any)[fieldName] = value;
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量序列化组件
|
||||
*
|
||||
* @param components 组件数组
|
||||
* @returns 序列化后的组件数据数组
|
||||
*/
|
||||
public static serializeComponents(components: Component[]): SerializedComponent[] {
|
||||
const result: SerializedComponent[] = [];
|
||||
|
||||
for (const component of components) {
|
||||
const serialized = this.serialize(component);
|
||||
if (serialized) {
|
||||
result.push(serialized);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量反序列化组件
|
||||
*
|
||||
* @param serializedComponents 序列化的组件数据数组
|
||||
* @param componentRegistry 组件类型注册表
|
||||
* @returns 反序列化后的组件数组
|
||||
*/
|
||||
public static deserializeComponents(
|
||||
serializedComponents: SerializedComponent[],
|
||||
componentRegistry: Map<string, ComponentType>
|
||||
): Component[] {
|
||||
const result: Component[] = [];
|
||||
|
||||
for (const serialized of serializedComponents) {
|
||||
const component = this.deserialize(serialized, componentRegistry);
|
||||
if (component) {
|
||||
result.push(component);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认值序列化
|
||||
*
|
||||
* 处理基本类型、数组、对象等的序列化
|
||||
*/
|
||||
private static serializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 日期
|
||||
if (value instanceof Date) {
|
||||
return {
|
||||
__type: 'Date',
|
||||
value: value.toISOString()
|
||||
};
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// Map (如果没有使用@SerializeMap装饰器)
|
||||
if (value instanceof Map) {
|
||||
return {
|
||||
__type: 'Map',
|
||||
value: Array.from(value.entries())
|
||||
};
|
||||
}
|
||||
|
||||
// Set
|
||||
if (value instanceof Set) {
|
||||
return {
|
||||
__type: 'Set',
|
||||
value: Array.from(value)
|
||||
};
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.serializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 其他类型(函数等)不序列化
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认值反序列化
|
||||
*/
|
||||
private static deserializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型直接返回
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 处理特殊类型标记
|
||||
if (type === 'object' && value.__type) {
|
||||
switch (value.__type) {
|
||||
case 'Date':
|
||||
return new Date(value.value);
|
||||
case 'Map':
|
||||
return new Map(value.value);
|
||||
case 'Set':
|
||||
return new Set(value.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.deserializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证序列化数据的版本
|
||||
*
|
||||
* @param serializedData 序列化数据
|
||||
* @param expectedVersion 期望的版本号
|
||||
* @returns 版本是否匹配
|
||||
*/
|
||||
public static validateVersion(
|
||||
serializedData: SerializedComponent,
|
||||
expectedVersion: number
|
||||
): boolean {
|
||||
return serializedData.version === expectedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的序列化信息
|
||||
*
|
||||
* @param component 组件实例或组件类
|
||||
* @returns 序列化信息对象,包含类型名、版本、可序列化字段列表
|
||||
*/
|
||||
public static getSerializationInfo(component: Component | ComponentType): {
|
||||
type: string;
|
||||
version: number;
|
||||
fields: string[];
|
||||
ignoredFields: string[];
|
||||
isSerializable: boolean;
|
||||
} | null {
|
||||
const metadata = getSerializationMetadata(component);
|
||||
|
||||
if (!metadata) {
|
||||
return {
|
||||
type: 'unknown',
|
||||
version: 0,
|
||||
fields: [],
|
||||
ignoredFields: [],
|
||||
isSerializable: false
|
||||
};
|
||||
}
|
||||
|
||||
const componentType = typeof component === 'function'
|
||||
? component
|
||||
: (component.constructor as ComponentType);
|
||||
|
||||
return {
|
||||
type: metadata.options.typeId || getComponentTypeName(componentType),
|
||||
version: metadata.options.version,
|
||||
fields: Array.from(metadata.fields.keys()).map(k =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
ignoredFields: Array.from(metadata.ignoredFields).map(k =>
|
||||
typeof k === 'symbol' ? k.toString() : k
|
||||
),
|
||||
isSerializable: true
|
||||
};
|
||||
}
|
||||
}
|
||||
194
packages/core/src/ECS/Serialization/ComponentTypeRegistry.ts
Normal file
194
packages/core/src/ECS/Serialization/ComponentTypeRegistry.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* 全局组件类型注册表
|
||||
*
|
||||
* 用于序列化系统的组件类型查找和管理
|
||||
*/
|
||||
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
|
||||
/**
|
||||
* 全局组件类型注册表
|
||||
*
|
||||
* 维护组件类型名称到构造函数的映射,用于序列化/反序列化
|
||||
*/
|
||||
export class ComponentTypeRegistry {
|
||||
/**
|
||||
* 组件类型映射表
|
||||
* Map<类型名称, 构造函数>
|
||||
*/
|
||||
private static registry = new Map<string, ComponentType>();
|
||||
|
||||
/**
|
||||
* 注册组件类型
|
||||
*
|
||||
* @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<string, ComponentType> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
223
packages/core/src/ECS/Serialization/EntitySerializer.ts
Normal file
223
packages/core/src/ECS/Serialization/EntitySerializer.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
/**
|
||||
* 实体序列化器
|
||||
*
|
||||
* 负责实体的序列化和反序列化操作
|
||||
*/
|
||||
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { ComponentSerializer, SerializedComponent } from './ComponentSerializer';
|
||||
|
||||
/**
|
||||
* 序列化后的实体数据
|
||||
*/
|
||||
export interface SerializedEntity {
|
||||
/**
|
||||
* 实体ID
|
||||
*/
|
||||
id: number;
|
||||
|
||||
/**
|
||||
* 实体名称
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* 实体标签
|
||||
*/
|
||||
tag: number;
|
||||
|
||||
/**
|
||||
* 激活状态
|
||||
*/
|
||||
active: boolean;
|
||||
|
||||
/**
|
||||
* 启用状态
|
||||
*/
|
||||
enabled: boolean;
|
||||
|
||||
/**
|
||||
* 更新顺序
|
||||
*/
|
||||
updateOrder: number;
|
||||
|
||||
/**
|
||||
* 组件列表
|
||||
*/
|
||||
components: SerializedComponent[];
|
||||
|
||||
/**
|
||||
* 子实体列表
|
||||
*/
|
||||
children: SerializedEntity[];
|
||||
|
||||
/**
|
||||
* 父实体ID(如果有)
|
||||
*/
|
||||
parentId?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 实体序列化器类
|
||||
*/
|
||||
export class EntitySerializer {
|
||||
/**
|
||||
* 序列化单个实体
|
||||
*
|
||||
* @param entity 要序列化的实体
|
||||
* @param includeChildren 是否包含子实体(默认true)
|
||||
* @returns 序列化后的实体数据
|
||||
*/
|
||||
public static serialize(entity: Entity, includeChildren: boolean = true): SerializedEntity {
|
||||
const serializedComponents = ComponentSerializer.serializeComponents(
|
||||
Array.from(entity.components)
|
||||
);
|
||||
|
||||
const serializedEntity: SerializedEntity = {
|
||||
id: entity.id,
|
||||
name: entity.name,
|
||||
tag: entity.tag,
|
||||
active: entity.active,
|
||||
enabled: entity.enabled,
|
||||
updateOrder: entity.updateOrder,
|
||||
components: serializedComponents,
|
||||
children: []
|
||||
};
|
||||
|
||||
// 序列化父实体引用
|
||||
if (entity.parent) {
|
||||
serializedEntity.parentId = entity.parent.id;
|
||||
}
|
||||
|
||||
// 序列化子实体
|
||||
if (includeChildren) {
|
||||
for (const child of entity.children) {
|
||||
serializedEntity.children.push(this.serialize(child, true));
|
||||
}
|
||||
}
|
||||
|
||||
return serializedEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化实体
|
||||
*
|
||||
* @param serializedEntity 序列化的实体数据
|
||||
* @param componentRegistry 组件类型注册表
|
||||
* @param idGenerator 实体ID生成器(用于生成新ID或保持原ID)
|
||||
* @param preserveIds 是否保持原始ID(默认false)
|
||||
* @returns 反序列化后的实体
|
||||
*/
|
||||
public static deserialize(
|
||||
serializedEntity: SerializedEntity,
|
||||
componentRegistry: Map<string, ComponentType>,
|
||||
idGenerator: () => number,
|
||||
preserveIds: boolean = false
|
||||
): Entity {
|
||||
// 创建实体(使用原始ID或新生成的ID)
|
||||
const entityId = preserveIds ? serializedEntity.id : idGenerator();
|
||||
const entity = new Entity(serializedEntity.name, entityId);
|
||||
|
||||
// 恢复实体属性
|
||||
entity.tag = serializedEntity.tag;
|
||||
entity.active = serializedEntity.active;
|
||||
entity.enabled = serializedEntity.enabled;
|
||||
entity.updateOrder = serializedEntity.updateOrder;
|
||||
|
||||
// 反序列化组件
|
||||
const components = ComponentSerializer.deserializeComponents(
|
||||
serializedEntity.components,
|
||||
componentRegistry
|
||||
);
|
||||
|
||||
for (const component of components) {
|
||||
entity.addComponent(component);
|
||||
}
|
||||
|
||||
// 反序列化子实体
|
||||
for (const childData of serializedEntity.children) {
|
||||
const childEntity = this.deserialize(
|
||||
childData,
|
||||
componentRegistry,
|
||||
idGenerator,
|
||||
preserveIds
|
||||
);
|
||||
entity.addChild(childEntity);
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量序列化实体
|
||||
*
|
||||
* @param entities 实体数组
|
||||
* @param includeChildren 是否包含子实体
|
||||
* @returns 序列化后的实体数据数组
|
||||
*/
|
||||
public static serializeEntities(
|
||||
entities: Entity[],
|
||||
includeChildren: boolean = true
|
||||
): SerializedEntity[] {
|
||||
const result: SerializedEntity[] = [];
|
||||
|
||||
for (const entity of entities) {
|
||||
// 只序列化顶层实体(没有父实体的实体)
|
||||
// 子实体会在父实体序列化时一并处理
|
||||
if (!entity.parent || !includeChildren) {
|
||||
result.push(this.serialize(entity, includeChildren));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量反序列化实体
|
||||
*
|
||||
* @param serializedEntities 序列化的实体数据数组
|
||||
* @param componentRegistry 组件类型注册表
|
||||
* @param idGenerator 实体ID生成器
|
||||
* @param preserveIds 是否保持原始ID
|
||||
* @returns 反序列化后的实体数组
|
||||
*/
|
||||
public static deserializeEntities(
|
||||
serializedEntities: SerializedEntity[],
|
||||
componentRegistry: Map<string, ComponentType>,
|
||||
idGenerator: () => number,
|
||||
preserveIds: boolean = false
|
||||
): Entity[] {
|
||||
const result: Entity[] = [];
|
||||
|
||||
for (const serialized of serializedEntities) {
|
||||
const entity = this.deserialize(
|
||||
serialized,
|
||||
componentRegistry,
|
||||
idGenerator,
|
||||
preserveIds
|
||||
);
|
||||
result.push(entity);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建实体的深拷贝
|
||||
*
|
||||
* @param entity 要拷贝的实体
|
||||
* @param componentRegistry 组件类型注册表
|
||||
* @param idGenerator ID生成器
|
||||
* @returns 拷贝后的新实体
|
||||
*/
|
||||
public static clone(
|
||||
entity: Entity,
|
||||
componentRegistry: Map<string, ComponentType>,
|
||||
idGenerator: () => number
|
||||
): Entity {
|
||||
const serialized = this.serialize(entity, true);
|
||||
return this.deserialize(serialized, componentRegistry, idGenerator, false);
|
||||
}
|
||||
}
|
||||
529
packages/core/src/ECS/Serialization/SceneSerializer.ts
Normal file
529
packages/core/src/ECS/Serialization/SceneSerializer.ts
Normal file
@@ -0,0 +1,529 @@
|
||||
/**
|
||||
* 场景序列化器
|
||||
*
|
||||
* 负责整个场景的序列化和反序列化,包括实体、组件等
|
||||
*/
|
||||
|
||||
import type { IScene } from '../IScene';
|
||||
import { Entity } from '../Entity';
|
||||
import { Component } from '../Component';
|
||||
import { ComponentType } from '../Core/ComponentStorage';
|
||||
import { EntitySerializer, SerializedEntity } from './EntitySerializer';
|
||||
import { getComponentTypeName } from '../Decorators';
|
||||
import { getSerializationMetadata } from './SerializationDecorators';
|
||||
import { ComponentTypeRegistry } from './ComponentTypeRegistry';
|
||||
|
||||
/**
|
||||
* 场景序列化格式
|
||||
*/
|
||||
export type SerializationFormat = 'json' | 'binary';
|
||||
|
||||
/**
|
||||
* 场景序列化策略
|
||||
*/
|
||||
export type DeserializationStrategy = 'merge' | 'replace';
|
||||
|
||||
/**
|
||||
* 版本迁移函数
|
||||
*/
|
||||
export type MigrationFunction = (
|
||||
oldVersion: number,
|
||||
newVersion: number,
|
||||
data: any
|
||||
) => any;
|
||||
|
||||
/**
|
||||
* 场景序列化选项
|
||||
*/
|
||||
export interface SceneSerializationOptions {
|
||||
/**
|
||||
* 要序列化的组件类型列表
|
||||
* 如果未指定,则序列化所有可序列化的组件
|
||||
*/
|
||||
components?: ComponentType[];
|
||||
|
||||
/**
|
||||
* 是否序列化系统状态(当前不支持)
|
||||
*/
|
||||
systems?: boolean;
|
||||
|
||||
/**
|
||||
* 序列化格式
|
||||
*/
|
||||
format?: SerializationFormat;
|
||||
|
||||
/**
|
||||
* 是否美化JSON输出(仅在format='json'时有效)
|
||||
*/
|
||||
pretty?: boolean;
|
||||
|
||||
/**
|
||||
* 是否包含元数据(如序列化时间、版本等)
|
||||
*/
|
||||
includeMetadata?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景反序列化选项
|
||||
*/
|
||||
export interface SceneDeserializationOptions {
|
||||
/**
|
||||
* 反序列化策略
|
||||
* - 'merge': 合并到现有场景
|
||||
* - 'replace': 替换现有场景内容
|
||||
*/
|
||||
strategy?: DeserializationStrategy;
|
||||
|
||||
/**
|
||||
* 版本迁移函数
|
||||
*/
|
||||
migration?: MigrationFunction;
|
||||
|
||||
/**
|
||||
* 是否保持原始实体ID
|
||||
*/
|
||||
preserveIds?: boolean;
|
||||
|
||||
/**
|
||||
* 组件类型注册表
|
||||
* 如果未提供,将尝试从全局注册表获取
|
||||
*/
|
||||
componentRegistry?: Map<string, ComponentType>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化后的场景数据
|
||||
*/
|
||||
export interface SerializedScene {
|
||||
/**
|
||||
* 场景名称
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* 序列化版本
|
||||
*/
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* 序列化时间戳
|
||||
*/
|
||||
timestamp?: number;
|
||||
|
||||
/**
|
||||
* 场景自定义数据
|
||||
*
|
||||
* 存储场景级别的配置和状态
|
||||
*/
|
||||
sceneData?: Record<string, any>;
|
||||
|
||||
/**
|
||||
* 实体列表
|
||||
*/
|
||||
entities: SerializedEntity[];
|
||||
|
||||
/**
|
||||
* 元数据
|
||||
*/
|
||||
metadata?: {
|
||||
entityCount: number;
|
||||
componentTypeCount: number;
|
||||
serializationOptions?: SceneSerializationOptions;
|
||||
};
|
||||
|
||||
/**
|
||||
* 组件类型注册信息
|
||||
*/
|
||||
componentTypeRegistry: Array<{
|
||||
typeName: string;
|
||||
version: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景序列化器类
|
||||
*/
|
||||
export class SceneSerializer {
|
||||
/**
|
||||
* 当前序列化版本
|
||||
*/
|
||||
private static readonly SERIALIZATION_VERSION = 1;
|
||||
|
||||
/**
|
||||
* 序列化场景
|
||||
*
|
||||
* @param scene 要序列化的场景
|
||||
* @param options 序列化选项
|
||||
* @returns 序列化后的数据(JSON字符串或二进制数据)
|
||||
*/
|
||||
public static serialize(scene: IScene, options?: SceneSerializationOptions): string {
|
||||
const opts: SceneSerializationOptions = {
|
||||
systems: false,
|
||||
format: 'json',
|
||||
pretty: true,
|
||||
includeMetadata: true,
|
||||
...options
|
||||
};
|
||||
|
||||
// 过滤实体和组件
|
||||
const entities = this.filterEntities(scene, opts);
|
||||
|
||||
// 序列化实体
|
||||
const serializedEntities = EntitySerializer.serializeEntities(entities, true);
|
||||
|
||||
// 收集组件类型信息
|
||||
const componentTypeRegistry = this.buildComponentTypeRegistry(entities);
|
||||
|
||||
// 序列化场景自定义数据
|
||||
const sceneData = this.serializeSceneData(scene.sceneData);
|
||||
|
||||
// 构建序列化数据
|
||||
const serializedScene: SerializedScene = {
|
||||
name: scene.name,
|
||||
version: this.SERIALIZATION_VERSION,
|
||||
entities: serializedEntities,
|
||||
componentTypeRegistry
|
||||
};
|
||||
|
||||
// 添加场景数据(如果有)
|
||||
if (sceneData && Object.keys(sceneData).length > 0) {
|
||||
serializedScene.sceneData = sceneData;
|
||||
}
|
||||
|
||||
// 添加元数据
|
||||
if (opts.includeMetadata) {
|
||||
serializedScene.timestamp = Date.now();
|
||||
serializedScene.metadata = {
|
||||
entityCount: serializedEntities.length,
|
||||
componentTypeCount: componentTypeRegistry.length,
|
||||
serializationOptions: opts
|
||||
};
|
||||
}
|
||||
|
||||
// 根据格式返回数据
|
||||
if (opts.format === 'json') {
|
||||
return opts.pretty
|
||||
? JSON.stringify(serializedScene, null, 2)
|
||||
: JSON.stringify(serializedScene);
|
||||
} else {
|
||||
// 二进制格式(未来实现)
|
||||
throw new Error('Binary serialization format is not yet implemented');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化场景
|
||||
*
|
||||
* @param scene 目标场景
|
||||
* @param saveData 序列化的数据
|
||||
* @param options 反序列化选项
|
||||
*/
|
||||
public static deserialize(
|
||||
scene: IScene,
|
||||
saveData: string,
|
||||
options?: SceneDeserializationOptions
|
||||
): void {
|
||||
const opts: SceneDeserializationOptions = {
|
||||
strategy: 'replace',
|
||||
preserveIds: false,
|
||||
...options
|
||||
};
|
||||
|
||||
// 解析数据
|
||||
let serializedScene: SerializedScene;
|
||||
try {
|
||||
serializedScene = JSON.parse(saveData);
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse save data: ${error}`);
|
||||
}
|
||||
|
||||
// 版本迁移
|
||||
if (opts.migration && serializedScene.version !== this.SERIALIZATION_VERSION) {
|
||||
serializedScene = opts.migration(
|
||||
serializedScene.version,
|
||||
this.SERIALIZATION_VERSION,
|
||||
serializedScene
|
||||
);
|
||||
}
|
||||
|
||||
// 构建组件注册表
|
||||
const componentRegistry = opts.componentRegistry || this.getGlobalComponentRegistry();
|
||||
|
||||
// 根据策略处理场景
|
||||
if (opts.strategy === 'replace') {
|
||||
// 清空场景
|
||||
scene.destroyAllEntities();
|
||||
}
|
||||
|
||||
// ID生成器
|
||||
const idGenerator = () => scene.identifierPool.checkOut();
|
||||
|
||||
// 反序列化实体
|
||||
const entities = EntitySerializer.deserializeEntities(
|
||||
serializedScene.entities,
|
||||
componentRegistry,
|
||||
idGenerator,
|
||||
opts.preserveIds || false
|
||||
);
|
||||
|
||||
// 将实体添加到场景
|
||||
for (const entity of entities) {
|
||||
scene.addEntity(entity);
|
||||
}
|
||||
|
||||
// 反序列化场景自定义数据
|
||||
if (serializedScene.sceneData) {
|
||||
this.deserializeSceneData(serializedScene.sceneData, scene.sceneData);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化场景自定义数据
|
||||
*
|
||||
* 将 Map<string, any> 转换为普通对象
|
||||
*/
|
||||
private static serializeSceneData(sceneData: Map<string, any>): Record<string, any> {
|
||||
const result: Record<string, any> = {};
|
||||
|
||||
for (const [key, value] of sceneData) {
|
||||
result[key] = this.serializeValue(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化场景自定义数据
|
||||
*
|
||||
* 将普通对象还原为 Map<string, any>
|
||||
*/
|
||||
private static deserializeSceneData(
|
||||
data: Record<string, any>,
|
||||
targetMap: Map<string, any>
|
||||
): void {
|
||||
targetMap.clear();
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
targetMap.set(key, this.deserializeValue(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化单个值
|
||||
*/
|
||||
private static serializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// Date
|
||||
if (value instanceof Date) {
|
||||
return { __type: 'Date', value: value.toISOString() };
|
||||
}
|
||||
|
||||
// Map
|
||||
if (value instanceof Map) {
|
||||
return { __type: 'Map', value: Array.from(value.entries()) };
|
||||
}
|
||||
|
||||
// Set
|
||||
if (value instanceof Set) {
|
||||
return { __type: 'Set', value: Array.from(value) };
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.serializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.serializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// 其他类型不序列化
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化单个值
|
||||
*/
|
||||
private static deserializeValue(value: any): any {
|
||||
if (value === null || value === undefined) {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 基本类型
|
||||
const type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean') {
|
||||
return value;
|
||||
}
|
||||
|
||||
// 处理特殊类型标记
|
||||
if (type === 'object' && value.__type) {
|
||||
switch (value.__type) {
|
||||
case 'Date':
|
||||
return new Date(value.value);
|
||||
case 'Map':
|
||||
return new Map(value.value);
|
||||
case 'Set':
|
||||
return new Set(value.value);
|
||||
}
|
||||
}
|
||||
|
||||
// 数组
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(item => this.deserializeValue(item));
|
||||
}
|
||||
|
||||
// 普通对象
|
||||
if (type === 'object') {
|
||||
const result: Record<string, any> = {};
|
||||
for (const key in value) {
|
||||
if (value.hasOwnProperty(key)) {
|
||||
result[key] = this.deserializeValue(value[key]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤要序列化的实体和组件
|
||||
*/
|
||||
private static filterEntities(scene: IScene, options: SceneSerializationOptions): Entity[] {
|
||||
const entities = Array.from(scene.entities.buffer);
|
||||
|
||||
// 如果指定了组件类型过滤
|
||||
if (options.components && options.components.length > 0) {
|
||||
const componentTypeSet = new Set(options.components);
|
||||
|
||||
// 只返回拥有指定组件的实体
|
||||
return entities.filter(entity => {
|
||||
return Array.from(entity.components).some(component =>
|
||||
componentTypeSet.has(component.constructor as ComponentType)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return entities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建组件类型注册表
|
||||
*/
|
||||
private static buildComponentTypeRegistry(
|
||||
entities: Entity[]
|
||||
): Array<{ typeName: string; version: number }> {
|
||||
const registry = new Map<string, number>();
|
||||
|
||||
for (const entity of entities) {
|
||||
for (const component of entity.components) {
|
||||
const componentType = component.constructor as ComponentType;
|
||||
const typeName = getComponentTypeName(componentType);
|
||||
const metadata = getSerializationMetadata(component);
|
||||
|
||||
if (metadata && !registry.has(typeName)) {
|
||||
registry.set(typeName, metadata.options.version);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(registry.entries()).map(([typeName, version]) => ({
|
||||
typeName,
|
||||
version
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全局组件注册表
|
||||
*
|
||||
* 从所有已注册的组件类型构建注册表
|
||||
*/
|
||||
private static getGlobalComponentRegistry(): Map<string, ComponentType> {
|
||||
return ComponentTypeRegistry.getRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证保存数据的有效性
|
||||
*
|
||||
* @param saveData 序列化的数据
|
||||
* @returns 验证结果
|
||||
*/
|
||||
public static validate(saveData: string): {
|
||||
valid: boolean;
|
||||
version?: number;
|
||||
errors?: string[];
|
||||
} {
|
||||
const errors: string[] = [];
|
||||
|
||||
try {
|
||||
const data = JSON.parse(saveData);
|
||||
|
||||
if (!data.version) {
|
||||
errors.push('Missing version field');
|
||||
}
|
||||
|
||||
if (!data.entities || !Array.isArray(data.entities)) {
|
||||
errors.push('Missing or invalid entities field');
|
||||
}
|
||||
|
||||
if (!data.componentTypeRegistry || !Array.isArray(data.componentTypeRegistry)) {
|
||||
errors.push('Missing or invalid componentTypeRegistry field');
|
||||
}
|
||||
|
||||
return {
|
||||
valid: errors.length === 0,
|
||||
version: data.version,
|
||||
errors: errors.length > 0 ? errors : undefined
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
valid: false,
|
||||
errors: [`JSON parse error: ${error}`]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取保存数据的信息(不完全反序列化)
|
||||
*
|
||||
* @param saveData 序列化的数据
|
||||
* @returns 保存数据的元信息
|
||||
*/
|
||||
public static getInfo(saveData: string): {
|
||||
name: string;
|
||||
version: number;
|
||||
timestamp?: number;
|
||||
entityCount: number;
|
||||
componentTypeCount: number;
|
||||
} | null {
|
||||
try {
|
||||
const data: SerializedScene = JSON.parse(saveData);
|
||||
|
||||
return {
|
||||
name: data.name,
|
||||
version: data.version,
|
||||
timestamp: data.timestamp,
|
||||
entityCount: data.metadata?.entityCount || data.entities.length,
|
||||
componentTypeCount: data.componentTypeRegistry.length
|
||||
};
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
254
packages/core/src/ECS/Serialization/SerializationDecorators.ts
Normal file
254
packages/core/src/ECS/Serialization/SerializationDecorators.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* 序列化装饰器
|
||||
*
|
||||
* 提供组件级别的序列化支持,包括字段级装饰器和类级装饰器
|
||||
*/
|
||||
|
||||
import { Component } from '../Component';
|
||||
|
||||
/**
|
||||
* 序列化元数据的Symbol键
|
||||
*/
|
||||
export const SERIALIZABLE_METADATA = Symbol('SerializableMetadata');
|
||||
export const SERIALIZE_FIELD = Symbol('SerializeField');
|
||||
export const SERIALIZE_OPTIONS = Symbol('SerializeOptions');
|
||||
|
||||
/**
|
||||
* 可序列化配置选项
|
||||
*/
|
||||
export interface SerializableOptions {
|
||||
/**
|
||||
* 序列化版本号,用于数据迁移
|
||||
*/
|
||||
version: number;
|
||||
|
||||
/**
|
||||
* 组件类型标识符(可选,默认使用类名)
|
||||
*/
|
||||
typeId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段序列化配置
|
||||
*/
|
||||
export interface FieldSerializeOptions {
|
||||
/**
|
||||
* 自定义序列化器
|
||||
*/
|
||||
serializer?: (value: any) => any;
|
||||
|
||||
/**
|
||||
* 自定义反序列化器
|
||||
*/
|
||||
deserializer?: (value: any) => any;
|
||||
|
||||
/**
|
||||
* 字段别名(用于序列化后的键名)
|
||||
*/
|
||||
alias?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化元数据
|
||||
*/
|
||||
export interface SerializationMetadata {
|
||||
options: SerializableOptions;
|
||||
fields: Map<string | symbol, FieldSerializeOptions>;
|
||||
ignoredFields: Set<string | symbol>;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件可序列化装饰器
|
||||
*
|
||||
* 标记组件类为可序列化,必须与字段装饰器配合使用
|
||||
*
|
||||
* @param options 序列化配置选项
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @ECSComponent('Player')
|
||||
* @Serializable({ version: 1 })
|
||||
* class PlayerComponent extends Component {
|
||||
* @Serialize() name: string = 'Player';
|
||||
* @Serialize() level: number = 1;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function Serializable(options: SerializableOptions) {
|
||||
return function <T extends new (...args: any[]) => Component>(target: T): T {
|
||||
if (!options || typeof options.version !== 'number') {
|
||||
throw new Error('Serializable装饰器必须提供有效的版本号');
|
||||
}
|
||||
|
||||
// 初始化或获取现有元数据
|
||||
let metadata: SerializationMetadata = (target as any)[SERIALIZABLE_METADATA];
|
||||
if (!metadata) {
|
||||
metadata = {
|
||||
options,
|
||||
fields: new Map(),
|
||||
ignoredFields: new Set()
|
||||
};
|
||||
(target as any)[SERIALIZABLE_METADATA] = metadata;
|
||||
} else {
|
||||
metadata.options = options;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 字段序列化装饰器
|
||||
*
|
||||
* 标记字段为可序列化
|
||||
*
|
||||
* @param options 字段序列化选项(可选)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @Serialize()
|
||||
* name: string = 'Player';
|
||||
*
|
||||
* @Serialize({ alias: 'hp' })
|
||||
* health: number = 100;
|
||||
* ```
|
||||
*/
|
||||
export function Serialize(options?: FieldSerializeOptions) {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
const constructor = target.constructor;
|
||||
|
||||
// 获取或创建元数据
|
||||
let metadata: SerializationMetadata = constructor[SERIALIZABLE_METADATA];
|
||||
if (!metadata) {
|
||||
metadata = {
|
||||
options: { version: 1 }, // 默认版本
|
||||
fields: new Map(),
|
||||
ignoredFields: new Set()
|
||||
};
|
||||
constructor[SERIALIZABLE_METADATA] = metadata;
|
||||
}
|
||||
|
||||
// 记录字段
|
||||
metadata.fields.set(propertyKey, options || {});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map序列化装饰器
|
||||
*
|
||||
* 专门用于序列化Map类型字段
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @SerializeAsMap()
|
||||
* inventory: Map<string, number> = new Map();
|
||||
* ```
|
||||
*/
|
||||
export function SerializeAsMap() {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
Serialize({
|
||||
serializer: (value: Map<any, any>) => {
|
||||
if (!(value instanceof Map)) {
|
||||
return null;
|
||||
}
|
||||
return Array.from(value.entries());
|
||||
},
|
||||
deserializer: (value: any) => {
|
||||
if (!Array.isArray(value)) {
|
||||
return new Map();
|
||||
}
|
||||
return new Map(value);
|
||||
}
|
||||
})(target, propertyKey);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set序列化装饰器
|
||||
*
|
||||
* 专门用于序列化Set类型字段
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @SerializeAsSet()
|
||||
* tags: Set<string> = new Set();
|
||||
* ```
|
||||
*/
|
||||
export function SerializeAsSet() {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
Serialize({
|
||||
serializer: (value: Set<any>) => {
|
||||
if (!(value instanceof Set)) {
|
||||
return null;
|
||||
}
|
||||
return Array.from(value);
|
||||
},
|
||||
deserializer: (value: any) => {
|
||||
if (!Array.isArray(value)) {
|
||||
return new Set();
|
||||
}
|
||||
return new Set(value);
|
||||
}
|
||||
})(target, propertyKey);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 忽略序列化装饰器
|
||||
*
|
||||
* 标记字段不参与序列化
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* @IgnoreSerialization()
|
||||
* tempCache: any = null;
|
||||
* ```
|
||||
*/
|
||||
export function IgnoreSerialization() {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
const constructor = target.constructor;
|
||||
|
||||
// 获取或创建元数据
|
||||
let metadata: SerializationMetadata = constructor[SERIALIZABLE_METADATA];
|
||||
if (!metadata) {
|
||||
metadata = {
|
||||
options: { version: 1 },
|
||||
fields: new Map(),
|
||||
ignoredFields: new Set()
|
||||
};
|
||||
constructor[SERIALIZABLE_METADATA] = metadata;
|
||||
}
|
||||
|
||||
// 记录忽略字段
|
||||
metadata.ignoredFields.add(propertyKey);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的序列化元数据
|
||||
*
|
||||
* @param componentClass 组件类或组件实例
|
||||
* @returns 序列化元数据,如果组件不可序列化则返回null
|
||||
*/
|
||||
export function getSerializationMetadata(componentClass: any): SerializationMetadata | null {
|
||||
if (!componentClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 如果是实例,获取其构造函数
|
||||
const constructor = typeof componentClass === 'function'
|
||||
? componentClass
|
||||
: componentClass.constructor;
|
||||
|
||||
return constructor[SERIALIZABLE_METADATA] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件是否可序列化
|
||||
*
|
||||
* @param component 组件类或组件实例
|
||||
* @returns 如果组件可序列化返回true
|
||||
*/
|
||||
export function isSerializable(component: any): boolean {
|
||||
return getSerializationMetadata(component) !== null;
|
||||
}
|
||||
371
packages/core/src/ECS/Serialization/VersionMigration.ts
Normal file
371
packages/core/src/ECS/Serialization/VersionMigration.ts
Normal file
@@ -0,0 +1,371 @@
|
||||
/**
|
||||
* 版本迁移系统
|
||||
*
|
||||
* 提供组件和场景数据的版本迁移支持
|
||||
*/
|
||||
|
||||
import { SerializedComponent } from './ComponentSerializer';
|
||||
import { SerializedScene } from './SceneSerializer';
|
||||
|
||||
/**
|
||||
* 组件迁移函数
|
||||
*/
|
||||
export type ComponentMigrationFunction = (data: any, fromVersion: number, toVersion: number) => any;
|
||||
|
||||
/**
|
||||
* 场景迁移函数
|
||||
*/
|
||||
export type SceneMigrationFunction = (
|
||||
scene: SerializedScene,
|
||||
fromVersion: number,
|
||||
toVersion: number
|
||||
) => SerializedScene;
|
||||
|
||||
/**
|
||||
* 版本迁移管理器
|
||||
*/
|
||||
export class VersionMigrationManager {
|
||||
/**
|
||||
* 组件迁移函数注册表
|
||||
* Map<组件类型名, Map<版本号, 迁移函数>>
|
||||
*/
|
||||
private static componentMigrations = new Map<string, Map<number, ComponentMigrationFunction>>();
|
||||
|
||||
/**
|
||||
* 场景迁移函数注册表
|
||||
* Map<版本号, 迁移函数>
|
||||
*/
|
||||
private static sceneMigrations = new Map<number, SceneMigrationFunction>();
|
||||
|
||||
/**
|
||||
* 注册组件迁移函数
|
||||
*
|
||||
* @param componentType 组件类型名称
|
||||
* @param fromVersion 源版本号
|
||||
* @param toVersion 目标版本号
|
||||
* @param migration 迁移函数
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // 从版本1迁移到版本2
|
||||
* VersionMigrationManager.registerComponentMigration(
|
||||
* 'PlayerComponent',
|
||||
* 1,
|
||||
* 2,
|
||||
* (data) => {
|
||||
* // 添加新字段
|
||||
* data.experience = 0;
|
||||
* return data;
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
public static registerComponentMigration(
|
||||
componentType: string,
|
||||
fromVersion: number,
|
||||
toVersion: number,
|
||||
migration: ComponentMigrationFunction
|
||||
): void {
|
||||
if (!this.componentMigrations.has(componentType)) {
|
||||
this.componentMigrations.set(componentType, new Map());
|
||||
}
|
||||
|
||||
const versionMap = this.componentMigrations.get(componentType)!;
|
||||
|
||||
// 使用fromVersion作为key,表示"从这个版本迁移"
|
||||
versionMap.set(fromVersion, migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册场景迁移函数
|
||||
*
|
||||
* @param fromVersion 源版本号
|
||||
* @param toVersion 目标版本号
|
||||
* @param migration 迁移函数
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* VersionMigrationManager.registerSceneMigration(
|
||||
* 1,
|
||||
* 2,
|
||||
* (scene) => {
|
||||
* // 迁移场景结构
|
||||
* scene.metadata = { ...scene.metadata, migratedFrom: 1 };
|
||||
* return scene;
|
||||
* }
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
public static registerSceneMigration(
|
||||
fromVersion: number,
|
||||
toVersion: number,
|
||||
migration: SceneMigrationFunction
|
||||
): void {
|
||||
this.sceneMigrations.set(fromVersion, migration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 迁移组件数据
|
||||
*
|
||||
* @param component 序列化的组件数据
|
||||
* @param targetVersion 目标版本号
|
||||
* @returns 迁移后的组件数据
|
||||
*/
|
||||
public static migrateComponent(
|
||||
component: SerializedComponent,
|
||||
targetVersion: number
|
||||
): SerializedComponent {
|
||||
const currentVersion = component.version;
|
||||
|
||||
if (currentVersion === targetVersion) {
|
||||
return component; // 版本相同,无需迁移
|
||||
}
|
||||
|
||||
const migrations = this.componentMigrations.get(component.type);
|
||||
if (!migrations) {
|
||||
console.warn(`No migration path found for component ${component.type}`);
|
||||
return component;
|
||||
}
|
||||
|
||||
let migratedData = { ...component };
|
||||
let version = currentVersion;
|
||||
|
||||
// 执行迁移链
|
||||
while (version < targetVersion) {
|
||||
const migration = migrations.get(version);
|
||||
|
||||
if (!migration) {
|
||||
console.warn(
|
||||
`Missing migration from version ${version} to ${version + 1} for ${component.type}`
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
migratedData.data = migration(migratedData.data, version, version + 1);
|
||||
version++;
|
||||
}
|
||||
|
||||
migratedData.version = version;
|
||||
return migratedData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迁移场景数据
|
||||
*
|
||||
* @param scene 序列化的场景数据
|
||||
* @param targetVersion 目标版本号
|
||||
* @returns 迁移后的场景数据
|
||||
*/
|
||||
public static migrateScene(scene: SerializedScene, targetVersion: number): SerializedScene {
|
||||
const currentVersion = scene.version;
|
||||
|
||||
if (currentVersion === targetVersion) {
|
||||
return scene; // 版本相同,无需迁移
|
||||
}
|
||||
|
||||
let migratedScene = { ...scene };
|
||||
let version = currentVersion;
|
||||
|
||||
// 执行场景级迁移
|
||||
while (version < targetVersion) {
|
||||
const migration = this.sceneMigrations.get(version);
|
||||
|
||||
if (!migration) {
|
||||
console.warn(`Missing scene migration from version ${version} to ${version + 1}`);
|
||||
break;
|
||||
}
|
||||
|
||||
migratedScene = migration(migratedScene, version, version + 1);
|
||||
version++;
|
||||
}
|
||||
|
||||
migratedScene.version = version;
|
||||
|
||||
// 迁移所有组件
|
||||
migratedScene = this.migrateSceneComponents(migratedScene);
|
||||
|
||||
return migratedScene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 迁移场景中所有组件的版本
|
||||
*/
|
||||
private static migrateSceneComponents(scene: SerializedScene): SerializedScene {
|
||||
const migratedScene = { ...scene };
|
||||
|
||||
migratedScene.entities = scene.entities.map(entity => ({
|
||||
...entity,
|
||||
components: entity.components.map(component => {
|
||||
// 查找组件的目标版本
|
||||
const typeInfo = scene.componentTypeRegistry.find(
|
||||
t => t.typeName === component.type
|
||||
);
|
||||
|
||||
if (typeInfo && typeInfo.version !== component.version) {
|
||||
return this.migrateComponent(component, typeInfo.version);
|
||||
}
|
||||
|
||||
return component;
|
||||
}),
|
||||
children: this.migrateEntitiesComponents(entity.children, scene.componentTypeRegistry)
|
||||
}));
|
||||
|
||||
return migratedScene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归迁移实体的组件
|
||||
*/
|
||||
private static migrateEntitiesComponents(
|
||||
entities: any[],
|
||||
typeRegistry: Array<{ typeName: string; version: number }>
|
||||
): any[] {
|
||||
return entities.map(entity => ({
|
||||
...entity,
|
||||
components: entity.components.map((component: SerializedComponent) => {
|
||||
const typeInfo = typeRegistry.find(t => t.typeName === component.type);
|
||||
|
||||
if (typeInfo && typeInfo.version !== component.version) {
|
||||
return this.migrateComponent(component, typeInfo.version);
|
||||
}
|
||||
|
||||
return component;
|
||||
}),
|
||||
children: this.migrateEntitiesComponents(entity.children, typeRegistry)
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有迁移函数
|
||||
*/
|
||||
public static clearMigrations(): void {
|
||||
this.componentMigrations.clear();
|
||||
this.sceneMigrations.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的迁移路径
|
||||
*
|
||||
* @param componentType 组件类型名称
|
||||
* @returns 可用的迁移版本列表
|
||||
*/
|
||||
public static getComponentMigrationPath(componentType: string): number[] {
|
||||
const migrations = this.componentMigrations.get(componentType);
|
||||
if (!migrations) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(migrations.keys()).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取场景的迁移路径
|
||||
*
|
||||
* @returns 可用的场景迁移版本列表
|
||||
*/
|
||||
public static getSceneMigrationPath(): number[] {
|
||||
return Array.from(this.sceneMigrations.keys()).sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以迁移组件
|
||||
*
|
||||
* @param componentType 组件类型名称
|
||||
* @param fromVersion 源版本
|
||||
* @param toVersion 目标版本
|
||||
* @returns 是否存在完整的迁移路径
|
||||
*/
|
||||
public static canMigrateComponent(
|
||||
componentType: string,
|
||||
fromVersion: number,
|
||||
toVersion: number
|
||||
): boolean {
|
||||
if (fromVersion === toVersion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const migrations = this.componentMigrations.get(componentType);
|
||||
if (!migrations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否存在完整的迁移路径
|
||||
for (let v = fromVersion; v < toVersion; v++) {
|
||||
if (!migrations.has(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以迁移场景
|
||||
*
|
||||
* @param fromVersion 源版本
|
||||
* @param toVersion 目标版本
|
||||
* @returns 是否存在完整的迁移路径
|
||||
*/
|
||||
public static canMigrateScene(fromVersion: number, toVersion: number): boolean {
|
||||
if (fromVersion === toVersion) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查是否存在完整的场景迁移路径
|
||||
for (let v = fromVersion; v < toVersion; v++) {
|
||||
if (!this.sceneMigrations.has(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷的迁移构建器
|
||||
*
|
||||
* 提供链式API来定义迁移
|
||||
*/
|
||||
export class MigrationBuilder {
|
||||
private componentType?: string;
|
||||
private fromVersion: number = 1;
|
||||
private toVersion: number = 2;
|
||||
|
||||
/**
|
||||
* 设置组件类型
|
||||
*/
|
||||
public forComponent(componentType: string): this {
|
||||
this.componentType = componentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置版本范围
|
||||
*/
|
||||
public fromVersionToVersion(from: number, to: number): this {
|
||||
this.fromVersion = from;
|
||||
this.toVersion = to;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册迁移函数
|
||||
*/
|
||||
public migrate(migration: ComponentMigrationFunction | SceneMigrationFunction): void {
|
||||
if (this.componentType) {
|
||||
VersionMigrationManager.registerComponentMigration(
|
||||
this.componentType,
|
||||
this.fromVersion,
|
||||
this.toVersion,
|
||||
migration as ComponentMigrationFunction
|
||||
);
|
||||
} else {
|
||||
VersionMigrationManager.registerSceneMigration(
|
||||
this.fromVersion,
|
||||
this.toVersion,
|
||||
migration as SceneMigrationFunction
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
packages/core/src/ECS/Serialization/index.ts
Normal file
54
packages/core/src/ECS/Serialization/index.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* ECS序列化系统
|
||||
*
|
||||
* 提供完整的场景、实体和组件序列化支持
|
||||
*/
|
||||
|
||||
// 装饰器
|
||||
export {
|
||||
Serializable,
|
||||
Serialize,
|
||||
SerializeAsMap,
|
||||
SerializeAsSet,
|
||||
IgnoreSerialization,
|
||||
getSerializationMetadata,
|
||||
isSerializable,
|
||||
SERIALIZABLE_METADATA,
|
||||
SERIALIZE_FIELD,
|
||||
SERIALIZE_OPTIONS
|
||||
} from './SerializationDecorators';
|
||||
|
||||
export type {
|
||||
SerializableOptions,
|
||||
FieldSerializeOptions,
|
||||
SerializationMetadata
|
||||
} from './SerializationDecorators';
|
||||
|
||||
// 组件序列化器
|
||||
export { ComponentSerializer } from './ComponentSerializer';
|
||||
export type { SerializedComponent } from './ComponentSerializer';
|
||||
|
||||
// 实体序列化器
|
||||
export { EntitySerializer } from './EntitySerializer';
|
||||
export type { SerializedEntity } from './EntitySerializer';
|
||||
|
||||
// 场景序列化器
|
||||
export { SceneSerializer } from './SceneSerializer';
|
||||
export type {
|
||||
SerializedScene,
|
||||
SerializationFormat,
|
||||
DeserializationStrategy,
|
||||
MigrationFunction,
|
||||
SceneSerializationOptions,
|
||||
SceneDeserializationOptions
|
||||
} from './SceneSerializer';
|
||||
|
||||
// 版本迁移
|
||||
export { VersionMigrationManager, MigrationBuilder } from './VersionMigration';
|
||||
export type {
|
||||
ComponentMigrationFunction,
|
||||
SceneMigrationFunction
|
||||
} from './VersionMigration';
|
||||
|
||||
// 组件类型注册表
|
||||
export { ComponentTypeRegistry } from './ComponentTypeRegistry';
|
||||
@@ -11,4 +11,5 @@ export { WorldManager, IWorldManagerConfig } from './WorldManager';
|
||||
export * from './Core/Events';
|
||||
export * from './Core/Query';
|
||||
export * from './Core/Storage';
|
||||
export * from './Core/StorageDecorators';
|
||||
export * from './Core/StorageDecorators';
|
||||
export * from './Serialization';
|
||||
Reference in New Issue
Block a user