From 171d03c006298616f4918f2c85e2d140a4dac0b7 Mon Sep 17 00:00:00 2001 From: YHH <359807859@qq.com> Date: Mon, 7 Jul 2025 09:45:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Esnapshot=E5=BF=AB=E7=85=A7?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Utils/Snapshot/ISnapshotable.ts | 92 ++++ src/Utils/Snapshot/SnapshotExtension.ts | 255 ++++++++++ src/Utils/Snapshot/SnapshotManager.ts | 650 ++++++++++++++++++++++++ src/Utils/Snapshot/index.ts | 19 + 4 files changed, 1016 insertions(+) create mode 100644 src/Utils/Snapshot/ISnapshotable.ts create mode 100644 src/Utils/Snapshot/SnapshotExtension.ts create mode 100644 src/Utils/Snapshot/SnapshotManager.ts create mode 100644 src/Utils/Snapshot/index.ts diff --git a/src/Utils/Snapshot/ISnapshotable.ts b/src/Utils/Snapshot/ISnapshotable.ts new file mode 100644 index 00000000..382e03c6 --- /dev/null +++ b/src/Utils/Snapshot/ISnapshotable.ts @@ -0,0 +1,92 @@ +/** + * 可序列化接口 + * + * 实现此接口的类可以被快照系统序列化和反序列化 + */ +export interface ISnapshotable { + /** + * 序列化对象到可传输的数据格式 + * + * @returns 序列化后的数据 + */ + serialize(): any; + + /** + * 从序列化数据恢复对象状态 + * + * @param data - 序列化数据 + */ + deserialize(data: any): void; +} + +/** + * 快照配置接口 + */ +export interface SnapshotConfig { + /** 是否包含在快照中 */ + includeInSnapshot: boolean; + /** 压缩级别 (0-9) */ + compressionLevel: number; + /** 同步优先级 (数字越大优先级越高) */ + syncPriority: number; + /** 是否启用增量快照 */ + enableIncremental: boolean; +} + +/** + * 组件快照数据 + */ +export interface ComponentSnapshot { + /** 组件类型名称 */ + type: string; + /** 组件ID */ + id: number; + /** 序列化数据 */ + data: any; + /** 是否启用 */ + enabled: boolean; + /** 快照配置 */ + config?: SnapshotConfig; +} + +/** + * 实体快照数据 + */ +export interface EntitySnapshot { + /** 实体ID */ + id: number; + /** 实体名称 */ + name: string; + /** 是否启用 */ + enabled: boolean; + /** 是否激活 */ + active: boolean; + /** 标签 */ + tag: number; + /** 更新顺序 */ + updateOrder: number; + /** 组件快照列表 */ + components: ComponentSnapshot[]; + /** 子实体ID列表 */ + children: number[]; + /** 父实体ID */ + parent?: number; + /** 快照时间戳 */ + timestamp: number; +} + +/** + * 场景快照数据 + */ +export interface SceneSnapshot { + /** 实体快照列表 */ + entities: EntitySnapshot[]; + /** 快照时间戳 */ + timestamp: number; + /** 框架版本 */ + version: string; + /** 快照类型 */ + type: 'full' | 'incremental'; + /** 基础快照ID(增量快照使用) */ + baseSnapshotId?: string; +} \ No newline at end of file diff --git a/src/Utils/Snapshot/SnapshotExtension.ts b/src/Utils/Snapshot/SnapshotExtension.ts new file mode 100644 index 00000000..c3627200 --- /dev/null +++ b/src/Utils/Snapshot/SnapshotExtension.ts @@ -0,0 +1,255 @@ +import { Component } from '../../ECS/Component'; +import { ISnapshotable, SnapshotConfig } from './ISnapshotable'; + +/** + * 快照扩展接口 + * + * 为Component基类提供快照功能的扩展接口 + */ +export interface ISnapshotExtension { + /** 快照配置 */ + snapshotConfig?: SnapshotConfig; + + /** 序列化方法 */ + serialize?(): any; + + /** 反序列化方法 */ + deserialize?(data: any): void; + + /** 变化检测方法 */ + hasChanged?(baseData: any): boolean; +} + +/** + * 快照装饰器 + * + * 用于标记组件属性为可序列化 + */ +export function Serializable(config?: Partial) { + return function (target: any, propertyKey: string) { + // 确保组件有快照配置 + if (!target.snapshotConfig) { + target.snapshotConfig = { + includeInSnapshot: true, + compressionLevel: 0, + syncPriority: 5, + enableIncremental: true + }; + } + + // 标记属性为可序列化 + if (!target._serializableProperties) { + target._serializableProperties = new Set(); + } + target._serializableProperties.add(propertyKey); + + // 应用配置 + if (config) { + Object.assign(target.snapshotConfig, config); + } + }; +} + +/** + * 快照配置装饰器 + * + * 用于配置组件的快照行为 + */ +export function SnapshotConfig(config: SnapshotConfig) { + return function (target: any) { + target.prototype.snapshotConfig = config; + }; +} + +/** + * 快照扩展工具类 + */ +export class SnapshotExtension { + /** + * 为组件添加快照支持 + * + * @param component - 目标组件 + * @param config - 快照配置 + */ + public static enableSnapshot(component: Component, config?: Partial): void { + const defaultConfig: SnapshotConfig = { + includeInSnapshot: true, + compressionLevel: 0, + syncPriority: 5, + enableIncremental: true + }; + + (component as any).snapshotConfig = { ...defaultConfig, ...config }; + } + + /** + * 禁用组件的快照功能 + * + * @param component - 目标组件 + */ + public static disableSnapshot(component: Component): void { + if ((component as any).snapshotConfig) { + (component as any).snapshotConfig.includeInSnapshot = false; + } + } + + /** + * 检查组件是否支持快照 + * + * @param component - 目标组件 + * @returns 是否支持快照 + */ + public static isSnapshotable(component: Component): boolean { + const config = (component as any).snapshotConfig; + return config && config.includeInSnapshot; + } + + /** + * 获取组件的可序列化属性 + * + * @param component - 目标组件 + * @returns 可序列化属性列表 + */ + public static getSerializableProperties(component: Component): string[] { + const properties = (component as any)._serializableProperties; + if (properties) { + return Array.from(properties); + } + + // 如果没有标记,返回所有公共属性 + const publicProperties: string[] = []; + for (const key in component) { + if (component.hasOwnProperty(key) && + typeof (component as any)[key] !== 'function' && + key !== 'id' && + key !== 'entity' && + key !== '_enabled' && + key !== '_updateOrder') { + publicProperties.push(key); + } + } + + return publicProperties; + } + + /** + * 创建组件的默认序列化方法 + * + * @param component - 目标组件 + * @returns 序列化数据 + */ + public static createDefaultSerializer(component: Component): () => any { + return function() { + const data: any = {}; + const properties = SnapshotExtension.getSerializableProperties(component); + + for (const prop of properties) { + const value = (component as any)[prop]; + if (value !== undefined && value !== null) { + data[prop] = value; + } + } + + return data; + }; + } + + /** + * 创建组件的默认反序列化方法 + * + * @param component - 目标组件 + * @returns 反序列化函数 + */ + public static createDefaultDeserializer(component: Component): (data: any) => void { + return function(data: any) { + const properties = SnapshotExtension.getSerializableProperties(component); + + for (const prop of properties) { + if (data.hasOwnProperty(prop)) { + (component as any)[prop] = data[prop]; + } + } + }; + } + + /** + * 创建简单的变化检测方法 + * + * @param component - 目标组件 + * @returns 变化检测函数 + */ + public static createSimpleChangeDetector(component: Component): (baseData: any) => boolean { + return function(baseData: any) { + const properties = SnapshotExtension.getSerializableProperties(component); + + for (const prop of properties) { + const currentValue = (component as any)[prop]; + const baseValue = baseData[prop]; + + if (currentValue !== baseValue) { + return true; + } + } + + return false; + }; + } + + /** + * 创建深度变化检测方法 + * + * @param component - 目标组件 + * @returns 变化检测函数 + */ + public static createDeepChangeDetector(component: Component): (baseData: any) => boolean { + return function(baseData: any) { + const properties = SnapshotExtension.getSerializableProperties(component); + + for (const prop of properties) { + const currentValue = (component as any)[prop]; + const baseValue = baseData[prop]; + + if (SnapshotExtension.deepCompare(currentValue, baseValue)) { + return true; + } + } + + return false; + }; + } + + /** + * 深度比较两个值 + */ + private static deepCompare(value1: any, value2: any): boolean { + if (value1 === value2) return false; + + if (typeof value1 !== typeof value2) return true; + + if (value1 === null || value2 === null) return value1 !== value2; + + if (typeof value1 !== 'object') return value1 !== value2; + + if (Array.isArray(value1) !== Array.isArray(value2)) return true; + + if (Array.isArray(value1)) { + if (value1.length !== value2.length) return true; + for (let i = 0; i < value1.length; i++) { + if (this.deepCompare(value1[i], value2[i])) return true; + } + return false; + } + + const keys1 = Object.keys(value1); + const keys2 = Object.keys(value2); + + if (keys1.length !== keys2.length) return true; + + for (const key of keys1) { + if (!keys2.includes(key)) return true; + if (this.deepCompare(value1[key], value2[key])) return true; + } + + return false; + } +} \ No newline at end of file diff --git a/src/Utils/Snapshot/SnapshotManager.ts b/src/Utils/Snapshot/SnapshotManager.ts new file mode 100644 index 00000000..a7f1f5cc --- /dev/null +++ b/src/Utils/Snapshot/SnapshotManager.ts @@ -0,0 +1,650 @@ +import { Entity } from '../../ECS/Entity'; +import { Component } from '../../ECS/Component'; +import { ISnapshotable, SceneSnapshot, EntitySnapshot, ComponentSnapshot, SnapshotConfig } from './ISnapshotable'; + +/** + * 快照管理器 + * + * 负责创建和管理ECS系统的快照,支持完整快照和增量快照 + */ +export class SnapshotManager { + /** 默认快照配置 */ + private static readonly DEFAULT_CONFIG: SnapshotConfig = { + includeInSnapshot: true, + compressionLevel: 0, + syncPriority: 5, + enableIncremental: true + }; + + /** 框架版本 */ + private readonly version: string = '1.0.0'; + + /** 最后快照时间戳 */ + private lastSnapshotTime: number = 0; + + /** 快照缓存 */ + private snapshotCache = new Map(); + + /** 最大缓存数量 */ + private maxCacheSize: number = 10; + + + + /** + * 创建场景快照 + * + * @param entities - 实体列表 + * @param type - 快照类型 + * @returns 场景快照 + */ + public createSceneSnapshot(entities: Entity[], type: 'full' | 'incremental' = 'full'): SceneSnapshot { + const entitySnapshots: EntitySnapshot[] = []; + + const sortedEntities = entities.sort((a, b) => a.id - b.id); + + for (const entity of sortedEntities) { + if (entity.isDestroyed) continue; + + const entitySnapshot = this.createEntitySnapshot(entity); + if (entitySnapshot) { + entitySnapshots.push(entitySnapshot); + } + } + + const snapshot: SceneSnapshot = { + entities: entitySnapshots, + timestamp: Date.now(), + version: this.version, + type: type + }; + + this.lastSnapshotTime = snapshot.timestamp; + + return snapshot; + } + + /** + * 从快照恢复场景 + * + * @param snapshot - 场景快照 + * @param targetEntities - 目标实体列表(用于增量恢复) + * @param createMissingEntities - 是否创建缺失的实体 + */ + public restoreFromSnapshot(snapshot: SceneSnapshot, targetEntities?: Entity[], createMissingEntities: boolean = false): Entity[] { + if (snapshot.type === 'incremental' && targetEntities) { + return this.restoreIncrementalSnapshot(snapshot, targetEntities); + } else { + return this.restoreFullSnapshot(snapshot, targetEntities, createMissingEntities); + } + } + + /** + * 从快照恢复实体列表 + * + * @param snapshot - 场景快照 + * @param targetEntities - 目标实体列表 + * @param createMissingEntities - 是否创建缺失的实体 + */ + public restoreEntitiesFromSnapshot(snapshot: SceneSnapshot, targetEntities: Entity[], createMissingEntities: boolean = false): Entity[] { + const restoredEntities: Entity[] = []; + const targetEntityMap = new Map(); + + for (const entity of targetEntities) { + targetEntityMap.set(entity.id, entity); + } + + for (const entitySnapshot of snapshot.entities) { + let targetEntity = targetEntityMap.get(entitySnapshot.id); + + if (!targetEntity && createMissingEntities) { + // 创建缺失的实体 + const newEntity = this.createEntityFromSnapshot(entitySnapshot); + if (newEntity) { + restoredEntities.push(newEntity); + } + } else if (targetEntity) { + // 恢复现有实体 + this.restoreEntityFromSnapshot(targetEntity, entitySnapshot); + restoredEntities.push(targetEntity); + } + } + + return restoredEntities; + } + + /** + * 从快照创建实体 + */ + private createEntityFromSnapshot(entitySnapshot: EntitySnapshot): Entity | null { + try { + const entity = new Entity(entitySnapshot.name, entitySnapshot.id); + + // 设置基本属性 + entity.enabled = entitySnapshot.enabled; + entity.active = entitySnapshot.active; + entity.tag = entitySnapshot.tag; + entity.updateOrder = entitySnapshot.updateOrder; + + // 创建组件 + for (const componentSnapshot of entitySnapshot.components) { + this.createComponentFromSnapshot(entity, componentSnapshot); + } + + return entity; + } catch (error) { + console.error(`[SnapshotManager] 创建实体失败: ${entitySnapshot.name}`, error); + return null; + } + } + + /** + * 从快照创建组件 + */ + private createComponentFromSnapshot(entity: Entity, componentSnapshot: ComponentSnapshot): void { + try { + // 尝试获取组件构造函数 + const componentType = this.getComponentType(componentSnapshot.type); + if (!componentType) { + console.warn(`[SnapshotManager] 未知组件类型: ${componentSnapshot.type}`); + return; + } + + // 创建组件实例 + const component = entity.createComponent(componentType); + + // 恢复组件启用状态 + component.enabled = componentSnapshot.enabled; + + // 恢复组件数据 + if (this.hasSerializeMethod(component)) { + try { + (component as any).deserialize(componentSnapshot.data); + } catch (error) { + console.warn(`[SnapshotManager] 组件 ${componentSnapshot.type} 反序列化失败:`, error); + } + } else { + this.defaultDeserializeComponent(component, componentSnapshot.data); + } + } catch (error) { + console.error(`[SnapshotManager] 创建组件失败: ${componentSnapshot.type}`, error); + } + } + + /** + * 获取组件类型 + */ + private getComponentType(typeName: string): any { + // 这里需要与组件注册系统集成 + // 暂时返回null,实际实现需要组件类型管理器 + return null; + } + + /** + * 创建快速快照(跳过变化检测) + * + * @param entities - 实体列表 + * @returns 场景快照 + */ + public createQuickSnapshot(entities: Entity[]): SceneSnapshot { + return this.createSceneSnapshot(entities, 'full'); + } + + /** + * 创建增量快照 + * + * @param entities - 实体列表 + * @param baseSnapshot - 基础快照 + * @param enableChangeDetection - 是否启用变化检测 + * @returns 增量快照 + */ + public createIncrementalSnapshot(entities: Entity[], baseSnapshot: SceneSnapshot, enableChangeDetection: boolean = true): SceneSnapshot { + const incrementalEntities: EntitySnapshot[] = []; + + const baseEntityMap = new Map(); + for (const entity of baseSnapshot.entities) { + baseEntityMap.set(entity.id, entity); + } + + for (const entity of entities) { + if (entity.isDestroyed) continue; + + const baseEntity = baseEntityMap.get(entity.id); + if (!baseEntity) { + const entitySnapshot = this.createEntitySnapshot(entity); + if (entitySnapshot) { + incrementalEntities.push(entitySnapshot); + } + } else if (enableChangeDetection) { + const changedComponents = this.getChangedComponents(entity, baseEntity); + if (this.hasEntityStructureChanged(entity, baseEntity) || changedComponents.length > 0) { + const incrementalEntitySnapshot = this.createIncrementalEntitySnapshot(entity, baseEntity, changedComponents); + if (incrementalEntitySnapshot) { + incrementalEntities.push(incrementalEntitySnapshot); + } + } + } + } + + return { + entities: incrementalEntities, + timestamp: Date.now(), + version: this.version, + type: 'incremental', + baseSnapshotId: this.generateSnapshotId(baseSnapshot) + }; + } + + /** + * 缓存快照 + * + * @param id - 快照ID + * @param snapshot - 快照数据 + */ + public cacheSnapshot(id: string, snapshot: SceneSnapshot): void { + // 清理过期缓存 + if (this.snapshotCache.size >= this.maxCacheSize) { + const oldestKey = this.snapshotCache.keys().next().value; + if (oldestKey) { + this.snapshotCache.delete(oldestKey); + } + } + + this.snapshotCache.set(id, snapshot); + } + + /** + * 获取缓存的快照 + * + * @param id - 快照ID + * @returns 快照数据或undefined + */ + public getCachedSnapshot(id: string): SceneSnapshot | undefined { + return this.snapshotCache.get(id); + } + + /** + * 清空快照缓存 + */ + public clearCache(): void { + this.snapshotCache.clear(); + } + + /** + * 清空所有缓存 + */ + public clearAllCaches(): void { + this.snapshotCache.clear(); + } + + /** + * 获取缓存统计信息 + */ + public getCacheStats(): { + snapshotCacheSize: number; + } { + return { + snapshotCacheSize: this.snapshotCache.size + }; + } + + /** + * 创建实体快照 + */ + private createEntitySnapshot(entity: Entity): EntitySnapshot | null { + const componentSnapshots: ComponentSnapshot[] = []; + + for (const component of entity.components) { + const componentSnapshot = this.createComponentSnapshot(component); + if (componentSnapshot) { + componentSnapshots.push(componentSnapshot); + } + } + + return { + id: entity.id, + name: entity.name, + enabled: entity.enabled, + active: entity.active, + tag: entity.tag, + updateOrder: entity.updateOrder, + components: componentSnapshots, + children: entity.children.map(child => child.id), + parent: entity.parent?.id || undefined, + timestamp: Date.now() + }; + } + + /** + * 创建组件快照 + */ + private createComponentSnapshot(component: Component): ComponentSnapshot | null { + if (!this.isComponentSnapshotable(component)) { + return null; + } + + let data: any; + + if (this.hasSerializeMethod(component)) { + try { + data = (component as any).serialize(); + } catch (error) { + console.warn(`[SnapshotManager] 组件序列化失败: ${component.constructor.name}`, error); + return null; + } + } else { + data = this.defaultSerializeComponent(component); + } + + return { + type: component.constructor.name, + id: component.id, + data: data, + enabled: component.enabled, + config: this.getComponentSnapshotConfig(component) + }; + } + + /** + * 默认组件序列化 + */ + private defaultSerializeComponent(component: Component): any { + const data: any = {}; + + // 只序列化公共属性 + for (const key in component) { + if (component.hasOwnProperty(key) && + typeof (component as any)[key] !== 'function' && + key !== 'id' && + key !== 'entity' && + key !== '_enabled' && + key !== '_updateOrder') { + + const value = (component as any)[key]; + if (this.isSerializableValue(value)) { + data[key] = value; + } + } + } + + return data; + } + + /** + * 检查值是否可序列化 + */ + private isSerializableValue(value: any): boolean { + if (value === null || value === undefined) return true; + if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') return true; + if (Array.isArray(value)) return value.every(v => this.isSerializableValue(v)); + if (typeof value === 'object') { + // 简单对象检查,避免循环引用 + try { + JSON.stringify(value); + return true; + } catch { + return false; + } + } + return false; + } + + /** + * 检查组件是否支持快照 + */ + private isComponentSnapshotable(component: Component): boolean { + // 检查是否有快照配置 + const config = this.getComponentSnapshotConfig(component); + return config.includeInSnapshot; + } + + /** + * 获取组件快照配置 + */ + private getComponentSnapshotConfig(component: Component): SnapshotConfig { + // 检查组件是否有自定义配置 + if ((component as any).snapshotConfig) { + return { ...SnapshotManager.DEFAULT_CONFIG, ...(component as any).snapshotConfig }; + } + + return SnapshotManager.DEFAULT_CONFIG; + } + + /** + * 检查组件是否有序列化方法 + */ + private hasSerializeMethod(component: Component): boolean { + return typeof (component as any).serialize === 'function'; + } + + /** + * 恢复完整快照 + */ + private restoreFullSnapshot(snapshot: SceneSnapshot, targetEntities?: Entity[], createMissingEntities: boolean = false): Entity[] { + if (targetEntities && createMissingEntities) { + return this.restoreEntitiesFromSnapshot(snapshot, targetEntities, true); + } else if (targetEntities) { + return this.restoreEntitiesFromSnapshot(snapshot, targetEntities, false); + } else { + const restoredEntities: Entity[] = []; + for (const entitySnapshot of snapshot.entities) { + const entity = this.createEntityFromSnapshot(entitySnapshot); + if (entity) { + restoredEntities.push(entity); + } + } + return restoredEntities; + } + } + + /** + * 恢复增量快照 + */ + private restoreIncrementalSnapshot(snapshot: SceneSnapshot, targetEntities: Entity[]): Entity[] { + const restoredEntities: Entity[] = []; + const targetEntityMap = new Map(); + + for (const entity of targetEntities) { + targetEntityMap.set(entity.id, entity); + } + + for (const entitySnapshot of snapshot.entities) { + const targetEntity = targetEntityMap.get(entitySnapshot.id); + if (targetEntity) { + this.restoreEntityFromSnapshot(targetEntity, entitySnapshot); + restoredEntities.push(targetEntity); + } + } + + return restoredEntities; + } + + /** + * 从快照恢复实体 + */ + private restoreEntityFromSnapshot(entity: Entity, entitySnapshot: EntitySnapshot): void { + // 恢复实体基本属性 + entity.enabled = entitySnapshot.enabled; + entity.active = entitySnapshot.active; + entity.tag = entitySnapshot.tag; + entity.updateOrder = entitySnapshot.updateOrder; + + // 恢复组件 + for (const componentSnapshot of entitySnapshot.components) { + this.restoreComponentFromSnapshot(entity, componentSnapshot); + } + } + + /** + * 从快照恢复组件 + */ + private restoreComponentFromSnapshot(entity: Entity, componentSnapshot: ComponentSnapshot): void { + // 查找现有组件 + let component = entity.getComponent(componentSnapshot.type as any); + + if (!component) { + // 组件不存在,需要创建 + console.warn(`[SnapshotManager] 组件 ${componentSnapshot.type} 不存在于实体 ${entity.name},无法恢复`); + return; + } + + // 恢复组件启用状态 + component.enabled = componentSnapshot.enabled; + + // 恢复组件数据 + if (this.hasSerializeMethod(component)) { + try { + (component as any).deserialize(componentSnapshot.data); + } catch (error) { + console.warn(`[SnapshotManager] 组件 ${componentSnapshot.type} 反序列化失败:`, error); + } + } else { + // 使用默认反序列化 + this.defaultDeserializeComponent(component, componentSnapshot.data); + } + } + + /** + * 默认组件反序列化 + */ + private defaultDeserializeComponent(component: Component, data: any): void { + for (const key in data) { + if (component.hasOwnProperty(key) && + typeof (component as any)[key] !== 'function' && + key !== 'id' && + key !== 'entity' && + key !== '_enabled' && + key !== '_updateOrder') { + + (component as any)[key] = data[key]; + } + } + } + + /** + * 检查实体结构是否发生变化(组件数量、类型等) + */ + private hasEntityStructureChanged(entity: Entity, baseSnapshot: EntitySnapshot): boolean { + // 检查基本属性变化 + if (entity.enabled !== baseSnapshot.enabled || + entity.active !== baseSnapshot.active || + entity.tag !== baseSnapshot.tag || + entity.updateOrder !== baseSnapshot.updateOrder) { + return true; + } + + // 检查组件数量变化 + if (entity.components.length !== baseSnapshot.components.length) { + return true; + } + + // 检查组件类型变化 + const currentComponentTypes = new Set(entity.components.map(c => c.constructor.name)); + const baseComponentTypes = new Set(baseSnapshot.components.map(c => c.type)); + + if (currentComponentTypes.size !== baseComponentTypes.size) { + return true; + } + + for (const type of currentComponentTypes) { + if (!baseComponentTypes.has(type)) { + return true; + } + } + + return false; + } + + /** + * 获取发生变化的组件列表 + */ + private getChangedComponents(entity: Entity, baseSnapshot: EntitySnapshot): ComponentSnapshot[] { + const changedComponents: ComponentSnapshot[] = []; + + const baseComponentMap = new Map(); + for (const comp of baseSnapshot.components) { + baseComponentMap.set(comp.type, comp); + } + + for (const component of entity.components) { + const baseComponent = baseComponentMap.get(component.constructor.name); + + if (!baseComponent) { + const componentSnapshot = this.createComponentSnapshot(component); + if (componentSnapshot) { + changedComponents.push(componentSnapshot); + } + } else { + if (this.hasComponentDataChanged(component, baseComponent)) { + const componentSnapshot = this.createComponentSnapshot(component); + if (componentSnapshot) { + changedComponents.push(componentSnapshot); + } + } + } + } + + return changedComponents; + } + + /** + * 检查组件数据是否发生变化 + */ + private hasComponentDataChanged(component: Component, baseComponent: ComponentSnapshot): boolean { + if (component.enabled !== baseComponent.enabled) { + return true; + } + + if (this.hasChangeDetectionMethod(component)) { + try { + return (component as any).hasChanged(baseComponent.data); + } catch { + return true; + } + } + + return true; + } + + /** + * 检查组件是否有变化检测方法 + */ + private hasChangeDetectionMethod(component: Component): boolean { + return typeof (component as any).hasChanged === 'function'; + } + + /** + * 创建增量实体快照(只包含变化的组件) + */ + private createIncrementalEntitySnapshot(entity: Entity, baseSnapshot: EntitySnapshot, changedComponents: ComponentSnapshot[]): EntitySnapshot | null { + // 检查实体基本属性是否变化 + const hasBasicChanges = entity.enabled !== baseSnapshot.enabled || + entity.active !== baseSnapshot.active || + entity.tag !== baseSnapshot.tag || + entity.updateOrder !== baseSnapshot.updateOrder; + + // 如果没有基本变化且没有组件变化,返回null + if (!hasBasicChanges && changedComponents.length === 0) { + return null; + } + + return { + id: entity.id, + name: entity.name, + enabled: entity.enabled, + active: entity.active, + tag: entity.tag, + updateOrder: entity.updateOrder, + components: changedComponents, // 只包含变化的组件 + children: entity.children.map(child => child.id), + parent: entity.parent?.id || undefined, + timestamp: Date.now() + }; + } + + /** + * 生成快照ID + */ + private generateSnapshotId(snapshot: SceneSnapshot): string { + return `${snapshot.timestamp}_${snapshot.entities.length}`; + } +} \ No newline at end of file diff --git a/src/Utils/Snapshot/index.ts b/src/Utils/Snapshot/index.ts new file mode 100644 index 00000000..5bf5ac05 --- /dev/null +++ b/src/Utils/Snapshot/index.ts @@ -0,0 +1,19 @@ +/** + * 快照系统模块 + * + * 提供ECS系统的快照功能,支持实体和组件的序列化与反序列化 + */ + +// 核心接口 +export * from './ISnapshotable'; + +// 快照管理器 +export { SnapshotManager } from './SnapshotManager'; + +// 快照扩展 +export { + ISnapshotExtension, + Serializable, + SnapshotConfig as SnapshotConfigDecorator, + SnapshotExtension +} from './SnapshotExtension'; \ No newline at end of file