372 lines
10 KiB
TypeScript
372 lines
10 KiB
TypeScript
|
|
/**
|
|||
|
|
* 版本迁移系统
|
|||
|
|
*
|
|||
|
|
* 提供组件和场景数据的版本迁移支持
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
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
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|