# 序列化系统

序列化系统提供了完整的场景、实体和组件数据持久化方案，支持全量序列化和增量序列化两种模式，适用于游戏存档、网络同步、场景编辑器、时间回溯等场景。

## 基本概念

序列化系统分为两个层次：

- **全量序列化**：序列化完整的场景状态，包括所有实体、组件和场景数据
- **增量序列化**：只序列化相对于基础快照的变更部分，大幅减少数据量

### 支持的数据格式

- **JSON格式**：人类可读，便于调试和编辑
- **Binary格式**：使用MessagePack，体积更小，性能更高

> **📢 v2.2.2 重要变更**
>
> 从 v2.2.2 开始，二进制序列化格式返回 `Uint8Array` 而非 Node.js 的 `Buffer`，以确保浏览器兼容性：
> - `serialize({ format: 'binary' })` 返回 `string | Uint8Array`（原为 `string | Buffer`）
> - `deserialize(data)` 接收 `string | Uint8Array`（原为 `string | Buffer`）
> - `applyIncremental(data)` 接收 `IncrementalSnapshot | string | Uint8Array`（原为包含 `Buffer`）
>
> **迁移影响**：
> - ✅ **运行时兼容**：Node.js 的 `Buffer` 继承自 `Uint8Array`，现有代码可直接运行
> - ⚠️ **类型检查**：如果你的 TypeScript 代码中显式使用了 `Buffer` 类型，需要改为 `Uint8Array`
> - ✅ **浏览器支持**：`Uint8Array` 是标准 JavaScript 类型，所有现代浏览器都支持

## 全量序列化

### 基础用法

#### 1. 标记可序列化组件

使用 `@Serializable` 和 `@Serialize` 装饰器标记需要序列化的组件和字段：

```typescript
import { Component, ECSComponent, Serializable, Serialize } from '@esengine/ecs-framework';

@ECSComponent('Player')
@Serializable({ version: 1 })
class PlayerComponent extends Component {
  @Serialize()
  public name: string = '';

  @Serialize()
  public level: number = 1;

  @Serialize()
  public experience: number = 0;

  @Serialize()
  public position: { x: number; y: number } = { x: 0, y: 0 };

  // 不使用 @Serialize() 的字段不会被序列化
  private tempData: any = null;
}
```

#### 2. 序列化场景

```typescript
// JSON格式序列化
const jsonData = scene.serialize({
  format: 'json',
  pretty: true  // 美化输出
});

// 保存到本地存储
localStorage.setItem('gameSave', jsonData);

// Binary格式序列化（更小的体积）
const binaryData = scene.serialize({
  format: 'binary'
});

// 保存为文件（Node.js环境）
// 注意：binaryData 是 Uint8Array 类型，Node.js 的 fs 可以直接写入
fs.writeFileSync('save.bin', binaryData);
```

#### 3. 反序列化场景

```typescript
// 从JSON恢复
const saveData = localStorage.getItem('gameSave');
if (saveData) {
  scene.deserialize(saveData, {
    strategy: 'replace'  // 替换当前场景内容
  });
}

// 从Binary恢复
const binaryData = fs.readFileSync('save.bin');
scene.deserialize(binaryData, {
  strategy: 'merge'  // 合并到现有场景
});
```

### 序列化选项

#### SerializationOptions

```typescript
interface SceneSerializationOptions {
  // 指定要序列化的组件类型（可选）
  components?: ComponentType[];

  // 序列化格式：'json' 或 'binary'
  format?: 'json' | 'binary';

  // JSON美化输出
  pretty?: boolean;

  // 包含元数据
  includeMetadata?: boolean;
}
```

示例：

```typescript
// 只序列化特定组件类型
const saveData = scene.serialize({
  format: 'json',
  components: [PlayerComponent, InventoryComponent],
  pretty: true,
  includeMetadata: true
});
```

#### DeserializationOptions

```typescript
interface SceneDeserializationOptions {
  // 反序列化策略
  strategy?: 'merge' | 'replace';

  // 组件类型注册表（可选，默认使用全局注册表）
  componentRegistry?: Map<string, ComponentType>;
}
```

### 高级装饰器

#### 字段序列化选项

```typescript
@ECSComponent('Advanced')
@Serializable({ version: 1 })
class AdvancedComponent extends Component {
  // 使用别名
  @Serialize({ alias: 'playerName' })
  public name: string = '';

  // 自定义序列化器
  @Serialize({
    serializer: (value: Date) => value.toISOString(),
    deserializer: (value: string) => new Date(value)
  })
  public createdAt: Date = new Date();

  // 忽略序列化
  @IgnoreSerialization()
  public cachedData: any = null;
}
```

#### 集合类型序列化

```typescript
@ECSComponent('Collections')
@Serializable({ version: 1 })
class CollectionsComponent extends Component {
  // Map序列化
  @SerializeAsMap()
  public inventory: Map<string, number> = new Map();

  // Set序列化
  @SerializeAsSet()
  public acquiredSkills: Set<string> = new Set();

  constructor() {
    super();
    this.inventory.set('gold', 100);
    this.inventory.set('silver', 50);
    this.acquiredSkills.add('attack');
    this.acquiredSkills.add('defense');
  }
}
```

### 组件继承与序列化

框架完整支持组件类的继承，子类会自动继承父类的序列化字段，同时可以添加自己的字段。

#### 基础继承

```typescript
// 基类组件
@ECSComponent('Collider2DBase')
@Serializable({ version: 1, typeId: 'Collider2DBase' })
abstract class Collider2DBase extends Component {
  @Serialize()
  public friction: number = 0.5;

  @Serialize()
  public restitution: number = 0.0;

  @Serialize()
  public isTrigger: boolean = false;
}

// 子类组件 - 自动继承父类的序列化字段
@ECSComponent('BoxCollider2D')
@Serializable({ version: 1, typeId: 'BoxCollider2D' })
class BoxCollider2DComponent extends Collider2DBase {
  @Serialize()
  public width: number = 1.0;

  @Serialize()
  public height: number = 1.0;
}

// 另一个子类组件
@ECSComponent('CircleCollider2D')
@Serializable({ version: 1, typeId: 'CircleCollider2D' })
class CircleCollider2DComponent extends Collider2DBase {
  @Serialize()
  public radius: number = 0.5;
}
```

#### 继承规则

1. **字段继承**：子类自动继承父类所有被 `@Serialize()` 标记的字段
2. **独立元数据**：每个子类维护独立的序列化元数据，修改子类不会影响父类或其他子类
3. **typeId 区分**：使用 `typeId` 选项为每个类指定唯一标识，确保反序列化时能正确识别组件类型

#### 使用 typeId 的重要性

当使用组件继承时，**强烈建议**为每个类设置唯一的 `typeId`：

```typescript
// ✅ 推荐：明确指定 typeId
@Serializable({ version: 1, typeId: 'BoxCollider2D' })
class BoxCollider2DComponent extends Collider2DBase { }

@Serializable({ version: 1, typeId: 'CircleCollider2D' })
class CircleCollider2DComponent extends Collider2DBase { }

// ⚠️ 不推荐：依赖类名作为 typeId
// 在代码压缩后类名可能变化，导致反序列化失败
@Serializable({ version: 1 })
class BoxCollider2DComponent extends Collider2DBase { }
```

#### 子类覆盖父类字段

子类可以重新声明父类的字段以修改其序列化选项：

```typescript
@ECSComponent('SpecialCollider')
@Serializable({ version: 1, typeId: 'SpecialCollider' })
class SpecialColliderComponent extends Collider2DBase {
  // 覆盖父类字段，使用不同的别名
  @Serialize({ alias: 'fric' })
  public override friction: number = 0.8;

  @Serialize()
  public specialProperty: string = '';
}
```

#### 忽略继承的字段

使用 `@IgnoreSerialization()` 可以在子类中忽略从父类继承的字段：

```typescript
@ECSComponent('TriggerOnly')
@Serializable({ version: 1, typeId: 'TriggerOnly' })
class TriggerOnlyCollider extends Collider2DBase {
  // 忽略父类的 friction 和 restitution 字段
  // 因为 Trigger 不需要物理材质属性
  @IgnoreSerialization()
  public override friction: number = 0;

  @IgnoreSerialization()
  public override restitution: number = 0;
}
```

### 场景自定义数据

除了实体和组件，还可以序列化场景级别的配置数据：

```typescript
// 设置场景数据
scene.sceneData.set('weather', 'rainy');
scene.sceneData.set('difficulty', 'hard');
scene.sceneData.set('checkpoint', { x: 100, y: 200 });

// 序列化时会自动包含场景数据
const saveData = scene.serialize({ format: 'json' });

// 反序列化后场景数据会恢复
scene.deserialize(saveData);
console.log(scene.sceneData.get('weather')); // 'rainy'
```

## 增量序列化

增量序列化只保存场景的变更部分，适用于网络同步、撤销/重做、时间回溯等需要频繁保存状态的场景。

### 基础用法

#### 1. 创建基础快照

```typescript
// 在需要开始记录变更前创建基础快照
scene.createIncrementalSnapshot();
```

#### 2. 修改场景

```typescript
// 添加实体
const enemy = scene.createEntity('Enemy');
enemy.addComponent(new PositionComponent(100, 200));
enemy.addComponent(new HealthComponent(50));

// 修改组件
const player = scene.findEntity('Player');
const pos = player.getComponent(PositionComponent);
pos.x = 300;
pos.y = 400;

// 删除组件
player.removeComponentByType(BuffComponent);

// 删除实体
const oldEntity = scene.findEntity('ToDelete');
oldEntity.destroy();

// 修改场景数据
scene.sceneData.set('score', 1000);
```

#### 3. 获取增量变更

```typescript
// 获取相对于基础快照的所有变更
const incremental = scene.serializeIncremental();

// 查看变更统计
const stats = IncrementalSerializer.getIncrementalStats(incremental);
console.log('总变更数:', stats.totalChanges);
console.log('新增实体:', stats.addedEntities);
console.log('删除实体:', stats.removedEntities);
console.log('新增组件:', stats.addedComponents);
console.log('更新组件:', stats.updatedComponents);
```

#### 4. 序列化增量数据

```typescript
// JSON格式（默认）
const jsonData = IncrementalSerializer.serializeIncremental(incremental, {
  format: 'json'
});

// 二进制格式（更小的体积，更高性能）
const binaryData = IncrementalSerializer.serializeIncremental(incremental, {
  format: 'binary'
});

// 美化JSON输出（便于调试）
const prettyJson = IncrementalSerializer.serializeIncremental(incremental, {
  format: 'json',
  pretty: true
});

// 发送或保存
socket.send(binaryData);  // 网络传输使用二进制
localStorage.setItem('changes', jsonData);  // 本地存储可用JSON
```

#### 5. 应用增量变更

```typescript
// 在另一个场景应用变更
const otherScene = new Scene();

// 直接应用增量对象
otherScene.applyIncremental(incremental);

// 从JSON字符串应用
const jsonData = IncrementalSerializer.serializeIncremental(incremental, { format: 'json' });
otherScene.applyIncremental(jsonData);

// 从二进制Uint8Array应用
const binaryData = IncrementalSerializer.serializeIncremental(incremental, { format: 'binary' });
otherScene.applyIncremental(binaryData);
```

### 增量快照管理

#### 更新快照基准

在应用增量变更后，可以更新快照基准：

```typescript
// 创建初始快照
scene.createIncrementalSnapshot();

// 第一次修改
entity.addComponent(new VelocityComponent(5, 0));
const incremental1 = scene.serializeIncremental();

// 更新基准（将当前状态设为新的基准）
scene.updateIncrementalSnapshot();

// 第二次修改（增量将基于更新后的基准）
entity.getComponent(VelocityComponent).dx = 10;
const incremental2 = scene.serializeIncremental();
```

#### 清除快照

```typescript
// 释放快照占用的内存
scene.clearIncrementalSnapshot();

// 检查是否有快照
if (scene.hasIncrementalSnapshot()) {
  console.log('存在增量快照');
}
```

### 增量序列化选项

```typescript
interface IncrementalSerializationOptions {
  // 是否进行组件数据的深度对比
  // 默认true，设为false可提升性能但可能漏掉组件内部字段变更
  deepComponentComparison?: boolean;

  // 是否跟踪场景数据变更
  // 默认true
  trackSceneData?: boolean;

  // 是否压缩快照（使用JSON序列化）
  // 默认false
  compressSnapshot?: boolean;

  // 序列化格式
  // 'json': JSON格式（可读性好，方便调试）
  // 'binary': MessagePack二进制格式（体积小，性能高）
  // 默认 'json'
  format?: 'json' | 'binary';

  // 是否美化JSON输出（仅在format='json'时有效）
  // 默认false
  pretty?: boolean;
}

// 使用选项
scene.createIncrementalSnapshot({
  deepComponentComparison: true,
  trackSceneData: true
});
```

### 增量数据结构

增量快照包含以下变更类型：

```typescript
interface IncrementalSnapshot {
  version: number;           // 快照版本号
  timestamp: number;         // 时间戳
  sceneName: string;         // 场景名称
  baseVersion: number;       // 基础版本号
  entityChanges: EntityChange[];      // 实体变更
  componentChanges: ComponentChange[]; // 组件变更
  sceneDataChanges: SceneDataChange[]; // 场景数据变更
}

// 变更操作类型
enum ChangeOperation {
  EntityAdded = 'entity_added',
  EntityRemoved = 'entity_removed',
  EntityUpdated = 'entity_updated',
  ComponentAdded = 'component_added',
  ComponentRemoved = 'component_removed',
  ComponentUpdated = 'component_updated',
  SceneDataUpdated = 'scene_data_updated'
}
```

## 版本迁移

当组件结构发生变化时，版本迁移系统可以自动升级旧版本的存档数据。

### 注册迁移函数

```typescript
import { VersionMigrationManager } from '@esengine/ecs-framework';

// 假设 PlayerComponent v1 有 hp 字段
// v2 改为 health 和 maxHealth 字段

// 注册从版本1到版本2的迁移
VersionMigrationManager.registerComponentMigration(
  'Player',
  1,  // 从版本
  2,  // 到版本
  (data) => {
    // 迁移逻辑
    const newData = {
      ...data,
      health: data.hp,
      maxHealth: data.hp,
    };
    delete newData.hp;
    return newData;
  }
);
```

### 使用迁移构建器

```typescript
import { MigrationBuilder } from '@esengine/ecs-framework';

new MigrationBuilder()
  .forComponent('Player')
  .fromVersionToVersion(2, 3)
  .migrate((data) => {
    // 从版本2迁移到版本3
    data.experience = data.exp || 0;
    delete data.exp;
    return data;
  });
```

### 场景级迁移

```typescript
// 注册场景级迁移
VersionMigrationManager.registerSceneMigration(
  1,  // 从版本
  2,  // 到版本
  (scene) => {
    // 迁移场景结构
    scene.metadata = {
      ...scene.metadata,
      migratedFrom: 1
    };
    return scene;
  }
);
```

### 检查迁移路径

```typescript
// 检查是否可以迁移
const canMigrate = VersionMigrationManager.canMigrateComponent(
  'Player',
  1,  // 从版本
  3   // 到版本
);

if (canMigrate) {
  // 可以安全迁移
  scene.deserialize(oldSaveData);
}

// 获取迁移路径
const path = VersionMigrationManager.getComponentMigrationPath('Player');
console.log('可用迁移版本:', path); // [1, 2, 3]
```

## 使用场景

### 游戏存档系统

```typescript
class SaveSystem {
  private static SAVE_KEY = 'game_save';

  // 保存游戏
  public static saveGame(scene: Scene): void {
    const saveData = scene.serialize({
      format: 'json',
      pretty: false
    });

    localStorage.setItem(this.SAVE_KEY, saveData);
    console.log('游戏已保存');
  }

  // 加载游戏
  public static loadGame(scene: Scene): boolean {
    const saveData = localStorage.getItem(this.SAVE_KEY);
    if (saveData) {
      scene.deserialize(saveData, {
        strategy: 'replace'
      });
      console.log('游戏已加载');
      return true;
    }
    return false;
  }

  // 检查是否有存档
  public static hasSave(): boolean {
    return localStorage.getItem(this.SAVE_KEY) !== null;
  }
}
```

### 网络同步

```typescript
class NetworkSync {
  private baseSnapshot?: any;
  private syncInterval: number = 100; // 100ms同步一次

  constructor(private scene: Scene, private socket: WebSocket) {
    this.setupSync();
  }

  private setupSync(): void {
    // 创建基础快照
    this.scene.createIncrementalSnapshot();

    // 定期发送增量
    setInterval(() => {
      this.sendIncremental();
    }, this.syncInterval);

    // 接收远程增量
    this.socket.onmessage = (event) => {
      this.receiveIncremental(event.data);
    };
  }

  private sendIncremental(): void {
    const incremental = this.scene.serializeIncremental();
    const stats = IncrementalSerializer.getIncrementalStats(incremental);

    // 只在有变更时发送
    if (stats.totalChanges > 0) {
      // 使用二进制格式减少网络传输量
      const binaryData = IncrementalSerializer.serializeIncremental(incremental, {
        format: 'binary'
      });
      this.socket.send(binaryData);

      // 更新基准
      this.scene.updateIncrementalSnapshot();
    }
  }

  private receiveIncremental(data: ArrayBuffer): void {
    // 直接应用二进制数据（ArrayBuffer 转 Uint8Array）
    const uint8Array = new Uint8Array(data);
    this.scene.applyIncremental(uint8Array);
  }
}
```

### 撤销/重做系统

```typescript
class UndoRedoSystem {
  private history: IncrementalSnapshot[] = [];
  private currentIndex: number = -1;
  private maxHistory: number = 50;

  constructor(private scene: Scene) {
    // 创建初始快照
    this.scene.createIncrementalSnapshot();
    this.saveState('Initial');
  }

  // 保存当前状态
  public saveState(label: string): void {
    const incremental = this.scene.serializeIncremental();

    // 删除当前位置之后的历史
    this.history = this.history.slice(0, this.currentIndex + 1);

    // 添加新状态
    this.history.push(incremental);
    this.currentIndex++;

    // 限制历史记录数量
    if (this.history.length > this.maxHistory) {
      this.history.shift();
      this.currentIndex--;
    }

    // 更新快照基准
    this.scene.updateIncrementalSnapshot();
  }

  // 撤销
  public undo(): boolean {
    if (this.currentIndex > 0) {
      this.currentIndex--;
      const incremental = this.history[this.currentIndex];
      this.scene.applyIncremental(incremental);
      return true;
    }
    return false;
  }

  // 重做
  public redo(): boolean {
    if (this.currentIndex < this.history.length - 1) {
      this.currentIndex++;
      const incremental = this.history[this.currentIndex];
      this.scene.applyIncremental(incremental);
      return true;
    }
    return false;
  }

  public canUndo(): boolean {
    return this.currentIndex > 0;
  }

  public canRedo(): boolean {
    return this.currentIndex < this.history.length - 1;
  }
}
```

### 关卡编辑器

```typescript
class LevelEditor {
  // 导出关卡
  public exportLevel(scene: Scene, filename: string): void {
    const levelData = scene.serialize({
      format: 'json',
      pretty: true,
      includeMetadata: true
    });

    // 浏览器环境
    const blob = new Blob([levelData], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    a.click();
    URL.revokeObjectURL(url);
  }

  // 导入关卡
  public importLevel(scene: Scene, fileContent: string): void {
    scene.deserialize(fileContent, {
      strategy: 'replace'
    });
  }

  // 验证关卡数据
  public validateLevel(saveData: string): boolean {
    const validation = SceneSerializer.validate(saveData);
    if (!validation.valid) {
      console.error('关卡数据无效:', validation.errors);
      return false;
    }
    return true;
  }

  // 获取关卡信息（不完全反序列化）
  public getLevelInfo(saveData: string): any {
    const info = SceneSerializer.getInfo(saveData);
    return info;
  }
}
```

## 性能优化建议

### 1. 选择合适的格式

- **开发阶段**：使用JSON格式，便于调试和查看
- **生产环境**：使用Binary格式，减少30-50%的数据大小

### 2. 按需序列化

```typescript
// 只序列化需要持久化的组件
const saveData = scene.serialize({
  format: 'binary',
  components: [PlayerComponent, InventoryComponent, QuestComponent]
});
```

### 3. 增量序列化优化

```typescript
// 对于高频同步，关闭深度对比以提升性能
scene.createIncrementalSnapshot({
  deepComponentComparison: false  // 只检测组件的添加/删除
});
```

### 4. 批量操作

```typescript
// 批量修改后再序列化
scene.entities.buffer.forEach(entity => {
  // 批量修改
});

// 一次性序列化所有变更
const incremental = scene.serializeIncremental();
```

## 最佳实践

### 1. 明确序列化字段

```typescript
// 明确标记需要序列化的字段
@ECSComponent('Player')
@Serializable({ version: 1 })
class PlayerComponent extends Component {
  @Serialize()
  public name: string = '';

  @Serialize()
  public level: number = 1;

  // 运行时数据不序列化
  private _cachedSprite: any = null;
}
```

### 2. 使用版本控制

```typescript
// 为组件指定版本
@Serializable({ version: 2 })
class PlayerComponent extends Component {
  // 版本2的字段
}

// 注册迁移函数确保兼容性
VersionMigrationManager.registerComponentMigration('Player', 1, 2, migrateV1ToV2);
```

### 3. 避免循环引用

```typescript
// 不要在组件中直接引用其他实体
@ECSComponent('Follower')
@Serializable({ version: 1 })
class FollowerComponent extends Component {
  // 存储实体ID而不是实体引用
  @Serialize()
  public targetId: number = 0;

  // 通过场景查找目标实体
  public getTarget(scene: Scene): Entity | null {
    return scene.entities.findEntityById(this.targetId);
  }
}
```

### 4. 压缩大数据

```typescript
// 对于大型数据结构，使用自定义序列化
@ECSComponent('LargeData')
@Serializable({ version: 1 })
class LargeDataComponent extends Component {
  @Serialize({
    serializer: (data: LargeObject) => compressData(data),
    deserializer: (data: CompressedData) => decompressData(data)
  })
  public data: LargeObject;
}
```

## API参考

### 全量序列化API

- [`Scene.serialize()`](/api/classes/Scene#serialize) - 序列化场景
- [`Scene.deserialize()`](/api/classes/Scene#deserialize) - 反序列化场景
- [`SceneSerializer`](/api/classes/SceneSerializer) - 场景序列化器
- [`ComponentSerializer`](/api/classes/ComponentSerializer) - 组件序列化器

### 增量序列化API

- [`Scene.createIncrementalSnapshot()`](/api/classes/Scene#createincrementalsnapshot) - 创建基础快照
- [`Scene.serializeIncremental()`](/api/classes/Scene#serializeincremental) - 获取增量变更
- [`Scene.applyIncremental()`](/api/classes/Scene#applyincremental) - 应用增量变更（支持IncrementalSnapshot对象、JSON字符串或二进制Uint8Array）
- [`Scene.updateIncrementalSnapshot()`](/api/classes/Scene#updateincrementalsnapshot) - 更新快照基准
- [`Scene.clearIncrementalSnapshot()`](/api/classes/Scene#clearincrementalsnapshot) - 清除快照
- [`Scene.hasIncrementalSnapshot()`](/api/classes/Scene#hasincrementalsnapshot) - 检查是否有快照
- [`IncrementalSerializer`](/api/classes/IncrementalSerializer) - 增量序列化器
- [`IncrementalSnapshot`](/api/interfaces/IncrementalSnapshot) - 增量快照接口
- [`IncrementalSerializationOptions`](/api/interfaces/IncrementalSerializationOptions) - 增量序列化选项
- [`IncrementalSerializationFormat`](/api/type-aliases/IncrementalSerializationFormat) - 序列化格式类型

### 版本迁移API

- [`VersionMigrationManager`](/api/classes/VersionMigrationManager) - 版本迁移管理器
- `VersionMigrationManager.registerComponentMigration()` - 注册组件迁移
- `VersionMigrationManager.registerSceneMigration()` - 注册场景迁移
- `VersionMigrationManager.canMigrateComponent()` - 检查是否可以迁移
- `VersionMigrationManager.getComponentMigrationPath()` - 获取迁移路径

序列化系统是构建完整游戏的重要基础设施，合理使用可以实现强大的功能，如存档系统、网络同步、关卡编辑器等。
