Files
esengine/src/Utils/Serialization/ProtobufSerializer.ts

381 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Protobuf序列化器
*
* 处理组件的protobuf序列化和反序列化
*/
import { Component } from '../../ECS/Component';
import {
ProtobufRegistry,
ProtoComponentDefinition,
ProtoFieldDefinition,
ProtoFieldType,
isProtoSerializable,
getProtoName
} from './ProtobufDecorators';
import { SerializedData } from './SerializationTypes';
/**
* Protobuf序列化器
*/
export class ProtobufSerializer {
private registry: ProtobufRegistry;
private static instance: ProtobufSerializer;
/** protobuf.js库实例 */
private protobuf: any = null;
private root: any = null;
/** MessageType缓存映射表 */
private messageTypeCache: Map<string, any> = new Map();
/** 是否启用数据验证 */
private enableValidation: boolean = process.env.NODE_ENV === 'development';
private constructor() {
this.registry = ProtobufRegistry.getInstance();
this.initializeProtobuf();
}
/**
* 设置性能选项
*/
public setPerformanceOptions(options: {
enableValidation?: boolean;
clearCache?: boolean;
}): void {
if (options.enableValidation !== undefined) {
this.enableValidation = options.enableValidation;
}
if (options.clearCache) {
this.messageTypeCache.clear();
}
}
/**
* 自动初始化protobuf支持
*/
private async initializeProtobuf(): Promise<void> {
try {
// 动态导入protobufjs
this.protobuf = await import('protobufjs');
this.buildProtoDefinitions();
console.log('[ProtobufSerializer] Protobuf支持已启用');
} catch (error) {
throw new Error('[ProtobufSerializer] 无法加载protobufjs: ' + error);
}
}
public static getInstance(): ProtobufSerializer {
if (!ProtobufSerializer.instance) {
ProtobufSerializer.instance = new ProtobufSerializer();
}
return ProtobufSerializer.instance;
}
/**
* 手动初始化protobuf.js库
*
* @param protobufJs protobuf.js库实例
*/
public initialize(protobufJs: any): void {
this.protobuf = protobufJs;
this.buildProtoDefinitions();
console.log('[ProtobufSerializer] Protobuf支持已手动启用');
}
/**
* 序列化组件
*
* @param component 要序列化的组件
* @returns 序列化数据
* @throws Error 如果组件不支持protobuf序列化
*/
public serialize(component: Component): SerializedData {
const componentType = component.constructor.name;
// 检查是否支持protobuf序列化
if (!isProtoSerializable(component)) {
throw new Error(`[ProtobufSerializer] 组件 ${componentType} 不支持protobuf序列化请添加@ProtoSerializable装饰器`);
}
const protoName = getProtoName(component);
if (!protoName) {
throw new Error(`[ProtobufSerializer] 组件 ${componentType} 未设置protobuf名称`);
}
const definition = this.registry.getComponentDefinition(protoName);
if (!definition) {
throw new Error(`[ProtobufSerializer] 未找到组件定义: ${protoName}`);
}
// 获取protobuf消息类型
const MessageType = this.getMessageType(protoName);
if (!MessageType) {
throw new Error(`[ProtobufSerializer] 未找到消息类型: ${protoName}`);
}
try {
// 构建protobuf数据对象
const protoData = this.buildProtoData(component, definition);
// 数据验证(仅在开发环境)
if (this.enableValidation) {
const error = MessageType.verify(protoData);
if (error) {
throw new Error(`[ProtobufSerializer] 数据验证失败: ${error}`);
}
}
// 创建消息并编码
const message = MessageType.create(protoData);
const buffer = MessageType.encode(message).finish();
return {
type: 'protobuf',
componentType: componentType,
data: buffer,
size: buffer.length
};
} catch (error) {
throw new Error(`[ProtobufSerializer] 序列化失败: ${componentType} - ${error}`);
}
}
/**
* 反序列化组件
*
* @param component 目标组件实例
* @param serializedData 序列化数据
* @throws Error 如果反序列化失败
*/
public deserialize(component: Component, serializedData: SerializedData): void {
const protoName = getProtoName(component);
if (!protoName) {
throw new Error(`[ProtobufSerializer] 组件 ${component.constructor.name} 未设置protobuf名称`);
}
const MessageType = this.getMessageType(protoName);
if (!MessageType) {
throw new Error(`[ProtobufSerializer] 未找到消息类型: ${protoName}`);
}
try {
// 解码消息
const message = MessageType.decode(serializedData.data);
const data = MessageType.toObject(message);
// 应用数据到组件
this.applyDataToComponent(component, data);
} catch (error) {
throw new Error(`[ProtobufSerializer] 反序列化失败: ${component.constructor.name} - ${error}`);
}
}
/**
* 检查组件是否支持protobuf序列化
*/
public canSerialize(component: Component): boolean {
if (!this.protobuf) return false;
return isProtoSerializable(component);
}
/**
* 批量序列化组件
*
* @param components 要序列化的组件数组
* @returns 序列化结果数组
*/
public serializeBatch(components: Component[]): SerializedData[] {
const results: SerializedData[] = [];
// 按组件类型分组,减少重复查找
const componentGroups = new Map<string, Component[]>();
for (const component of components) {
if (!isProtoSerializable(component)) {
throw new Error(`[ProtobufSerializer] 组件 ${component.constructor.name} 不支持protobuf序列化`);
}
const protoName = getProtoName(component);
if (!protoName) {
throw new Error(`[ProtobufSerializer] 组件 ${component.constructor.name} 未设置protobuf名称`);
}
if (!componentGroups.has(protoName)) {
componentGroups.set(protoName, []);
}
componentGroups.get(protoName)!.push(component);
}
// 按组分别序列化
for (const [protoName, groupComponents] of componentGroups) {
const definition = this.registry.getComponentDefinition(protoName);
const MessageType = this.getMessageType(protoName);
if (!definition || !MessageType) {
throw new Error(`[ProtobufSerializer] 组件类型 ${protoName} 未正确注册`);
}
for (const component of groupComponents) {
try {
const protoData = this.buildProtoData(component, definition);
// 数据验证(仅在开发环境)
if (this.enableValidation) {
const error = MessageType.verify(protoData);
if (error) {
throw new Error(`[ProtobufSerializer] 数据验证失败: ${error}`);
}
}
const message = MessageType.create(protoData);
const buffer = MessageType.encode(message).finish();
results.push({
type: 'protobuf',
componentType: component.constructor.name,
data: buffer,
size: buffer.length
});
} catch (error) {
throw new Error(`[ProtobufSerializer] 批量序列化失败: ${component.constructor.name} - ${error}`);
}
}
}
return results;
}
/**
* 获取序列化统计信息
*/
public getStats(): {
registeredComponents: number;
protobufAvailable: boolean;
cacheSize: number;
} {
return {
registeredComponents: this.registry.getAllComponents().size,
protobufAvailable: !!this.protobuf,
cacheSize: this.messageTypeCache.size
};
}
/**
* 构建protobuf数据对象
*/
private buildProtoData(component: Component, definition: ProtoComponentDefinition): any {
const data: any = {};
for (const [propertyName, fieldDef] of definition.fields) {
const value = (component as any)[propertyName];
if (value !== undefined && value !== null) {
data[fieldDef.name] = this.convertValueToProtoType(value, fieldDef);
}
}
return data;
}
/**
* 转换值到protobuf类型
*/
private convertValueToProtoType(value: any, fieldDef: ProtoFieldDefinition): any {
if (fieldDef.repeated && Array.isArray(value)) {
return value.map(v => this.convertSingleValue(v, fieldDef.type));
}
return this.convertSingleValue(value, fieldDef.type);
}
/**
* 转换单个值为protobuf类型
*/
private convertSingleValue(value: any, type: ProtoFieldType): any {
switch (type) {
case ProtoFieldType.INT32:
case ProtoFieldType.UINT32:
case ProtoFieldType.SINT32:
case ProtoFieldType.FIXED32:
case ProtoFieldType.SFIXED32:
return typeof value === 'number' ? (value | 0) : (parseInt(value) || 0);
case ProtoFieldType.FLOAT:
case ProtoFieldType.DOUBLE:
return typeof value === 'number' ? value : (parseFloat(value) || 0);
case ProtoFieldType.BOOL:
return typeof value === 'boolean' ? value : Boolean(value);
case ProtoFieldType.STRING:
return typeof value === 'string' ? value : String(value);
default:
return value;
}
}
/**
* 应用数据到组件
*/
private applyDataToComponent(component: Component, data: any): void {
const protoName = getProtoName(component);
if (!protoName) return;
const definition = this.registry.getComponentDefinition(protoName);
if (!definition) return;
for (const [propertyName, fieldDef] of definition.fields) {
const value = data[fieldDef.name];
if (value !== undefined) {
(component as any)[propertyName] = value;
}
}
}
/**
* 构建protobuf定义
*/
private buildProtoDefinitions(): void {
if (!this.protobuf) return;
try {
const protoDefinition = this.registry.generateProtoDefinition();
this.root = this.protobuf.parse(protoDefinition).root;
// 清空缓存schema已更新
this.messageTypeCache.clear();
} catch (error) {
console.error('[ProtobufSerializer] 构建protobuf定义失败:', error);
}
}
/**
* 获取消息类型并缓存结果
*/
private getMessageType(typeName: string): any {
if (!this.root) return null;
// 检查缓存
const fullTypeName = `ecs.${typeName}`;
if (this.messageTypeCache.has(fullTypeName)) {
return this.messageTypeCache.get(fullTypeName);
}
try {
const messageType = this.root.lookupType(fullTypeName);
// 缓存结果
this.messageTypeCache.set(fullTypeName, messageType);
return messageType;
} catch (error) {
console.warn(`[ProtobufSerializer] 未找到消息类型: ${fullTypeName}`);
// 缓存null结果以避免重复查找
this.messageTypeCache.set(fullTypeName, null);
return null;
}
}
}