新增syncvar高级特性,使用protobuf定义
This commit is contained in:
271
packages/network/src/SyncVar/SyncVarDecorator.ts
Normal file
271
packages/network/src/SyncVar/SyncVarDecorator.ts
Normal file
@@ -0,0 +1,271 @@
|
||||
import 'reflect-metadata';
|
||||
|
||||
/**
|
||||
* SyncVar配置选项
|
||||
*/
|
||||
export interface SyncVarOptions {
|
||||
/**
|
||||
* 值变化时的回调函数名
|
||||
*
|
||||
* 回调函数签名: (oldValue: T, newValue: T) => void
|
||||
*/
|
||||
hook?: string;
|
||||
|
||||
/**
|
||||
* 是否只有拥有权限的客户端才能修改
|
||||
*
|
||||
* 默认为false,任何客户端都可以修改
|
||||
*/
|
||||
authorityOnly?: boolean;
|
||||
|
||||
/**
|
||||
* 自定义序列化函数
|
||||
*
|
||||
* 如果不提供,将使用默认的类型序列化
|
||||
*/
|
||||
serializer?: (value: any) => Uint8Array;
|
||||
|
||||
/**
|
||||
* 自定义反序列化函数
|
||||
*/
|
||||
deserializer?: (data: Uint8Array) => any;
|
||||
|
||||
/**
|
||||
* 同步频率限制(毫秒)
|
||||
*
|
||||
* 防止过于频繁的网络同步,默认为0(不限制)
|
||||
*/
|
||||
throttleMs?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar元数据信息
|
||||
*/
|
||||
export interface SyncVarMetadata {
|
||||
/**
|
||||
* 属性名称
|
||||
*/
|
||||
propertyKey: string;
|
||||
|
||||
/**
|
||||
* 字段编号(用于protobuf序列化)
|
||||
*/
|
||||
fieldNumber: number;
|
||||
|
||||
/**
|
||||
* 配置选项
|
||||
*/
|
||||
options: SyncVarOptions;
|
||||
|
||||
/**
|
||||
* 属性类型
|
||||
*/
|
||||
type: Function;
|
||||
|
||||
/**
|
||||
* 最后同步时间(用于频率限制)
|
||||
*/
|
||||
lastSyncTime: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar元数据存储
|
||||
*/
|
||||
const SYNCVAR_METADATA_KEY = Symbol('syncvar:metadata');
|
||||
const SYNCVAR_FIELD_COUNTER = Symbol('syncvar:field_counter');
|
||||
|
||||
/**
|
||||
* 获取类的SyncVar元数据
|
||||
*
|
||||
* @param target - 目标类
|
||||
* @returns SyncVar元数据数组
|
||||
*/
|
||||
export function getSyncVarMetadata(target: any): SyncVarMetadata[] {
|
||||
return Reflect.getMetadata(SYNCVAR_METADATA_KEY, target) || [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置类的SyncVar元数据
|
||||
*
|
||||
* @param target - 目标类
|
||||
* @param metadata - 元数据数组
|
||||
*/
|
||||
export function setSyncVarMetadata(target: any, metadata: SyncVarMetadata[]): void {
|
||||
Reflect.defineMetadata(SYNCVAR_METADATA_KEY, metadata, target);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下一个可用的字段编号
|
||||
*
|
||||
* @param target - 目标类
|
||||
* @returns 字段编号
|
||||
*/
|
||||
function getNextFieldNumber(target: any): number {
|
||||
let counter = Reflect.getMetadata(SYNCVAR_FIELD_COUNTER, target) || 1;
|
||||
const nextNumber = counter;
|
||||
Reflect.defineMetadata(SYNCVAR_FIELD_COUNTER, counter + 1, target);
|
||||
return nextNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查属性是否为SyncVar
|
||||
*
|
||||
* @param target - 目标对象
|
||||
* @param propertyKey - 属性名
|
||||
* @returns 是否为SyncVar
|
||||
*/
|
||||
export function isSyncVar(target: any, propertyKey: string): boolean {
|
||||
const metadata = getSyncVarMetadata(target.constructor);
|
||||
return metadata.some(m => m.propertyKey === propertyKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定属性的SyncVar元数据
|
||||
*
|
||||
* @param target - 目标对象
|
||||
* @param propertyKey - 属性名
|
||||
* @returns SyncVar元数据
|
||||
*/
|
||||
export function getSyncVarMetadataForProperty(target: any, propertyKey: string): SyncVarMetadata | undefined {
|
||||
const metadata = getSyncVarMetadata(target.constructor);
|
||||
return metadata.find(m => m.propertyKey === propertyKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar装饰器
|
||||
*
|
||||
* 标记字段为自动同步变量,当值改变时会自动发送给其他客户端
|
||||
*
|
||||
* @param options - 配置选项
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* class PlayerComponent extends NetworkComponent {
|
||||
* @SyncVar()
|
||||
* public health: number = 100;
|
||||
*
|
||||
* @SyncVar({ hook: 'onNameChanged' })
|
||||
* public playerName: string = 'Player';
|
||||
*
|
||||
* @SyncVar({ authorityOnly: true })
|
||||
* public isReady: boolean = false;
|
||||
*
|
||||
* onNameChanged(oldName: string, newName: string) {
|
||||
* console.log(`Name changed: ${oldName} -> ${newName}`);
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
export function SyncVar(options: SyncVarOptions = {}): PropertyDecorator {
|
||||
return function (target: any, propertyKey: string | symbol) {
|
||||
if (typeof propertyKey !== 'string') {
|
||||
throw new Error('SyncVar装饰器只能用于字符串属性名');
|
||||
}
|
||||
|
||||
// 获取属性类型
|
||||
const type = Reflect.getMetadata('design:type', target, propertyKey);
|
||||
if (!type) {
|
||||
console.warn(`[SyncVar] 无法获取属性 ${propertyKey} 的类型信息`);
|
||||
}
|
||||
|
||||
// 获取现有元数据
|
||||
const existingMetadata = getSyncVarMetadata(target.constructor);
|
||||
|
||||
// 检查是否已经存在
|
||||
const existingIndex = existingMetadata.findIndex(m => m.propertyKey === propertyKey);
|
||||
if (existingIndex !== -1) {
|
||||
console.warn(`[SyncVar] 属性 ${propertyKey} 已经被标记为SyncVar,将覆盖配置`);
|
||||
existingMetadata[existingIndex].options = options;
|
||||
existingMetadata[existingIndex].type = type;
|
||||
} else {
|
||||
// 添加新的元数据
|
||||
const fieldNumber = getNextFieldNumber(target.constructor);
|
||||
const metadata: SyncVarMetadata = {
|
||||
propertyKey,
|
||||
fieldNumber,
|
||||
options,
|
||||
type,
|
||||
lastSyncTime: 0
|
||||
};
|
||||
|
||||
existingMetadata.push(metadata);
|
||||
}
|
||||
|
||||
// 保存元数据
|
||||
setSyncVarMetadata(target.constructor, existingMetadata);
|
||||
|
||||
console.log(`[SyncVar] 注册同步变量: ${target.constructor.name}.${propertyKey}, 字段编号: ${existingMetadata.find(m => m.propertyKey === propertyKey)?.fieldNumber}`);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证SyncVar配置的有效性
|
||||
*
|
||||
* @param target - 目标类实例
|
||||
* @param metadata - SyncVar元数据
|
||||
* @returns 验证结果
|
||||
*/
|
||||
export function validateSyncVarMetadata(target: any, metadata: SyncVarMetadata): {
|
||||
isValid: boolean;
|
||||
errors: string[];
|
||||
} {
|
||||
const errors: string[] = [];
|
||||
|
||||
// 检查属性是否存在
|
||||
if (!(metadata.propertyKey in target)) {
|
||||
errors.push(`属性 ${metadata.propertyKey} 不存在于类 ${target.constructor.name} 中`);
|
||||
}
|
||||
|
||||
// 检查hook函数是否存在
|
||||
if (metadata.options.hook) {
|
||||
if (typeof target[metadata.options.hook] !== 'function') {
|
||||
errors.push(`Hook函数 ${metadata.options.hook} 不存在或不是函数`);
|
||||
}
|
||||
}
|
||||
|
||||
// 检查自定义序列化函数
|
||||
if (metadata.options.serializer && typeof metadata.options.serializer !== 'function') {
|
||||
errors.push(`自定义序列化函数必须是function类型`);
|
||||
}
|
||||
|
||||
if (metadata.options.deserializer && typeof metadata.options.deserializer !== 'function') {
|
||||
errors.push(`自定义反序列化函数必须是function类型`);
|
||||
}
|
||||
|
||||
// 检查频率限制
|
||||
if (metadata.options.throttleMs !== undefined &&
|
||||
(typeof metadata.options.throttleMs !== 'number' || metadata.options.throttleMs < 0)) {
|
||||
errors.push(`throttleMs必须是非负数`);
|
||||
}
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类的所有SyncVar统计信息
|
||||
*
|
||||
* @param target - 目标类
|
||||
* @returns 统计信息
|
||||
*/
|
||||
export function getSyncVarStats(target: any): {
|
||||
totalCount: number;
|
||||
withHooks: number;
|
||||
authorityOnly: number;
|
||||
customSerialized: number;
|
||||
throttled: number;
|
||||
fieldNumbers: number[];
|
||||
} {
|
||||
const metadata = getSyncVarMetadata(target);
|
||||
|
||||
return {
|
||||
totalCount: metadata.length,
|
||||
withHooks: metadata.filter(m => m.options.hook).length,
|
||||
authorityOnly: metadata.filter(m => m.options.authorityOnly).length,
|
||||
customSerialized: metadata.filter(m => m.options.serializer || m.options.deserializer).length,
|
||||
throttled: metadata.filter(m => m.options.throttleMs !== undefined && m.options.throttleMs > 0).length,
|
||||
fieldNumbers: metadata.map(m => m.fieldNumber).sort((a, b) => a - b)
|
||||
};
|
||||
}
|
||||
81
packages/network/src/SyncVar/SyncVarFactory.ts
Normal file
81
packages/network/src/SyncVar/SyncVarFactory.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { createSyncVarProxy } from './SyncVarProxy';
|
||||
import { getSyncVarMetadata } from './SyncVarDecorator';
|
||||
import { INetworkSyncable } from '../types/NetworkTypes';
|
||||
|
||||
/**
|
||||
* SyncVar工厂函数
|
||||
*
|
||||
* 为NetworkComponent创建带有SyncVar代理的实例
|
||||
* 这是必需的,因为TypeScript类构造函数不能直接返回代理对象
|
||||
*/
|
||||
|
||||
/**
|
||||
* 创建带SyncVar支持的NetworkComponent实例
|
||||
*
|
||||
* @param ComponentClass - 组件类构造函数
|
||||
* @param args - 构造函数参数
|
||||
* @returns 带代理的组件实例
|
||||
*/
|
||||
export function createNetworkComponent<T extends INetworkSyncable>(
|
||||
ComponentClass: new (...args: any[]) => T,
|
||||
...args: any[]
|
||||
): T {
|
||||
// 创建组件实例
|
||||
const instance = new ComponentClass(...args);
|
||||
|
||||
// 检查是否有SyncVar字段
|
||||
const metadata = getSyncVarMetadata(ComponentClass);
|
||||
|
||||
if (metadata.length === 0) {
|
||||
// 没有SyncVar,直接返回原实例
|
||||
return instance;
|
||||
}
|
||||
|
||||
// 创建代理包装实例
|
||||
const proxy = createSyncVarProxy(instance, {
|
||||
debugLog: false // 可以根据需要启用调试
|
||||
});
|
||||
|
||||
console.log(`[SyncVarFactory] 为 ${ComponentClass.name} 创建了SyncVar代理,包含 ${metadata.length} 个同步字段`);
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar组件装饰器
|
||||
*
|
||||
* 装饰器版本的工厂函数,自动为类添加SyncVar支持
|
||||
* 注意:由于TypeScript装饰器的限制,这个方法有一些局限性
|
||||
*
|
||||
* @param options - 配置选项
|
||||
*/
|
||||
export function NetworkComponentWithSyncVar(options: { debugLog?: boolean } = {}) {
|
||||
return function <T extends new (...args: any[]) => INetworkSyncable>(constructor: T) {
|
||||
return class extends constructor {
|
||||
constructor(...args: any[]) {
|
||||
super(...args);
|
||||
|
||||
// 检查是否需要创建代理
|
||||
const metadata = getSyncVarMetadata(constructor);
|
||||
if (metadata.length > 0) {
|
||||
// 返回代理实例
|
||||
return createSyncVarProxy(this as INetworkSyncable, {
|
||||
debugLog: options.debugLog || false
|
||||
}) as this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
} as T;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 便捷方法:检查实例是否使用了SyncVar工厂创建
|
||||
*
|
||||
* @param instance - 组件实例
|
||||
* @returns 是否使用了SyncVar工厂
|
||||
*/
|
||||
export function isNetworkComponentWithSyncVar(instance: any): boolean {
|
||||
return instance && (instance._syncVarProxied === true || instance.hasSyncVars?.() === true);
|
||||
}
|
||||
827
packages/network/src/SyncVar/SyncVarManager.ts
Normal file
827
packages/network/src/SyncVar/SyncVarManager.ts
Normal file
@@ -0,0 +1,827 @@
|
||||
import {
|
||||
SyncVarMetadata,
|
||||
getSyncVarMetadata,
|
||||
validateSyncVarMetadata,
|
||||
getSyncVarMetadataForProperty
|
||||
} from './SyncVarDecorator';
|
||||
import { NetworkEnvironment } from '../Core/NetworkEnvironment';
|
||||
import { SyncVarUpdateMessage, SyncVarFieldUpdate } from '../Messaging/MessageTypes';
|
||||
import {
|
||||
SyncVarValue,
|
||||
INetworkSyncable,
|
||||
NetworkComponentType,
|
||||
TypeGuards
|
||||
} from '../types/NetworkTypes';
|
||||
|
||||
/**
|
||||
* SyncVar变化记录
|
||||
*/
|
||||
export interface SyncVarChange {
|
||||
/**
|
||||
* 属性名
|
||||
*/
|
||||
propertyKey: string;
|
||||
|
||||
/**
|
||||
* 字段编号
|
||||
*/
|
||||
fieldNumber: number;
|
||||
|
||||
/**
|
||||
* 旧值
|
||||
*/
|
||||
oldValue: SyncVarValue;
|
||||
|
||||
/**
|
||||
* 新值
|
||||
*/
|
||||
newValue: SyncVarValue;
|
||||
|
||||
/**
|
||||
* 变化时间戳
|
||||
*/
|
||||
timestamp: number;
|
||||
|
||||
/**
|
||||
* 是否需要网络同步
|
||||
*/
|
||||
needsSync: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar同步数据
|
||||
*/
|
||||
export interface SyncVarSyncData {
|
||||
/**
|
||||
* 组件类名
|
||||
*/
|
||||
componentType: string;
|
||||
|
||||
/**
|
||||
* 网络对象ID(将来实现)
|
||||
*/
|
||||
networkId?: string;
|
||||
|
||||
/**
|
||||
* 字段更新数据
|
||||
*/
|
||||
fieldUpdates: Array<{
|
||||
fieldNumber: number;
|
||||
data: Uint8Array;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* 时间戳
|
||||
*/
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar管理器
|
||||
*
|
||||
* 负责管理组件的SyncVar变量,检测变化,处理序列化和同步
|
||||
*/
|
||||
export class SyncVarManager {
|
||||
private static _instance: SyncVarManager | null = null;
|
||||
|
||||
/**
|
||||
* 组件实例的SyncVar变化监听器
|
||||
* Key: 组件实例的唯一ID
|
||||
* Value: 变化记录数组
|
||||
*/
|
||||
private _componentChanges: Map<string, SyncVarChange[]> = new Map();
|
||||
|
||||
/**
|
||||
* 组件实例的最后同步时间
|
||||
*/
|
||||
private _lastSyncTimes: Map<string, Map<string, number>> = new Map();
|
||||
|
||||
/**
|
||||
* 获取SyncVarManager单例
|
||||
*/
|
||||
public static get Instance(): SyncVarManager {
|
||||
if (!SyncVarManager._instance) {
|
||||
SyncVarManager._instance = new SyncVarManager();
|
||||
}
|
||||
return SyncVarManager._instance;
|
||||
}
|
||||
|
||||
private constructor() {}
|
||||
|
||||
/**
|
||||
* 初始化组件的SyncVar系统
|
||||
*
|
||||
* @param component - 网络组件实例
|
||||
* @returns 是否成功初始化
|
||||
*/
|
||||
public initializeComponent(component: INetworkSyncable): boolean {
|
||||
const componentId = this.getComponentId(component);
|
||||
const metadata = getSyncVarMetadata(component.constructor as NetworkComponentType);
|
||||
|
||||
if (metadata.length === 0) {
|
||||
// 没有SyncVar,无需初始化
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证所有SyncVar配置
|
||||
const validationErrors: string[] = [];
|
||||
for (const meta of metadata) {
|
||||
const validation = validateSyncVarMetadata(component, meta);
|
||||
if (!validation.isValid) {
|
||||
validationErrors.push(...validation.errors);
|
||||
}
|
||||
}
|
||||
|
||||
if (validationErrors.length > 0) {
|
||||
console.error(`[SyncVarManager] 组件 ${component.constructor.name} 的SyncVar配置错误:`, validationErrors);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化变化记录
|
||||
this._componentChanges.set(componentId, []);
|
||||
this._lastSyncTimes.set(componentId, new Map());
|
||||
|
||||
console.log(`[SyncVarManager] 初始化组件 ${component.constructor.name} 的SyncVar系统,共 ${metadata.length} 个同步变量`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理组件的SyncVar系统
|
||||
*
|
||||
* @param component - 网络组件实例
|
||||
*/
|
||||
public cleanupComponent(component: INetworkSyncable): void {
|
||||
const componentId = this.getComponentId(component);
|
||||
this._componentChanges.delete(componentId);
|
||||
this._lastSyncTimes.delete(componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录SyncVar字段的变化
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param propertyKey - 属性名
|
||||
* @param oldValue - 旧值
|
||||
* @param newValue - 新值
|
||||
*/
|
||||
public recordChange(
|
||||
component: INetworkSyncable,
|
||||
propertyKey: string,
|
||||
oldValue: SyncVarValue,
|
||||
newValue: SyncVarValue
|
||||
): void {
|
||||
const componentId = this.getComponentId(component);
|
||||
const metadata = getSyncVarMetadataForProperty(component, propertyKey);
|
||||
|
||||
if (!metadata) {
|
||||
console.warn(`[SyncVarManager] 属性 ${propertyKey} 不是SyncVar`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查值是否真的发生了变化
|
||||
if (!TypeGuards.isSyncVarValue(oldValue) || !TypeGuards.isSyncVarValue(newValue)) {
|
||||
console.warn(`[SyncVarManager] 无效的SyncVar值类型: ${typeof oldValue}, ${typeof newValue}`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isValueEqual(oldValue, newValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查频率限制
|
||||
const now = Date.now();
|
||||
const lastSyncTimes = this._lastSyncTimes.get(componentId);
|
||||
const lastSyncTime = lastSyncTimes?.get(propertyKey) || 0;
|
||||
|
||||
if (metadata.options.throttleMs && metadata.options.throttleMs > 0) {
|
||||
if (now - lastSyncTime < metadata.options.throttleMs) {
|
||||
console.log(`[SyncVarManager] 属性 ${propertyKey} 变化过于频繁,跳过同步`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 检查权限
|
||||
if (metadata.options.authorityOnly && !this.hasAuthority(component)) {
|
||||
console.warn(`[SyncVarManager] 属性 ${propertyKey} 需要权限才能修改,但当前没有权限`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录变化
|
||||
const change: SyncVarChange = {
|
||||
propertyKey,
|
||||
fieldNumber: metadata.fieldNumber,
|
||||
oldValue,
|
||||
newValue,
|
||||
timestamp: now,
|
||||
needsSync: this.shouldSync(component, metadata)
|
||||
};
|
||||
|
||||
let changes = this._componentChanges.get(componentId);
|
||||
if (!changes) {
|
||||
changes = [];
|
||||
this._componentChanges.set(componentId, changes);
|
||||
}
|
||||
|
||||
changes.push(change);
|
||||
|
||||
// 更新最后同步时间
|
||||
if (lastSyncTimes) {
|
||||
lastSyncTimes.set(propertyKey, now);
|
||||
}
|
||||
|
||||
console.log(`[SyncVarManager] 记录变化: ${component.constructor.name}.${propertyKey} = ${newValue} (was ${oldValue})`);
|
||||
|
||||
// 触发hook回调
|
||||
this.triggerHook(component, metadata, oldValue, newValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的待同步变化
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @returns 待同步的变化数组
|
||||
*/
|
||||
public getPendingChanges(component: any): SyncVarChange[] {
|
||||
const componentId = this.getComponentId(component);
|
||||
const changes = this._componentChanges.get(componentId) || [];
|
||||
return changes.filter(change => change.needsSync);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除组件的变化记录
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param propertyKeys - 要清除的属性名数组,如果不提供则清除所有
|
||||
*/
|
||||
public clearChanges(component: any, propertyKeys?: string[]): void {
|
||||
const componentId = this.getComponentId(component);
|
||||
const changes = this._componentChanges.get(componentId);
|
||||
|
||||
if (!changes) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (propertyKeys) {
|
||||
// 清除指定属性的变化
|
||||
const filteredChanges = changes.filter(change => !propertyKeys.includes(change.propertyKey));
|
||||
this._componentChanges.set(componentId, filteredChanges);
|
||||
} else {
|
||||
// 清除所有变化
|
||||
this._componentChanges.set(componentId, []);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建同步数据
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @returns 同步数据
|
||||
*/
|
||||
public createSyncData(component: any): SyncVarSyncData | null {
|
||||
const pendingChanges = this.getPendingChanges(component);
|
||||
|
||||
if (pendingChanges.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fieldUpdates: Array<{ fieldNumber: number; data: Uint8Array }> = [];
|
||||
|
||||
for (const change of pendingChanges) {
|
||||
try {
|
||||
const serializedData = this.serializeValue(component, change.propertyKey, change.newValue);
|
||||
fieldUpdates.push({
|
||||
fieldNumber: change.fieldNumber,
|
||||
data: serializedData
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarManager] 序列化失败 ${change.propertyKey}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldUpdates.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
componentType: component.constructor.name,
|
||||
fieldUpdates,
|
||||
timestamp: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用同步数据
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param syncData - 同步数据
|
||||
*/
|
||||
public applySyncData(component: any, syncData: SyncVarSyncData): void {
|
||||
const metadata = getSyncVarMetadata(component.constructor);
|
||||
const metadataMap = new Map(metadata.map(m => [m.fieldNumber, m]));
|
||||
|
||||
for (const update of syncData.fieldUpdates) {
|
||||
const meta = metadataMap.get(update.fieldNumber);
|
||||
if (!meta) {
|
||||
console.warn(`[SyncVarManager] 未找到字段编号 ${update.fieldNumber} 的元数据`);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const newValue = this.deserializeValue(component, meta.propertyKey, update.data);
|
||||
const oldValue = component[meta.propertyKey];
|
||||
|
||||
// 直接设置值,不通过代理(避免循环触发)
|
||||
this.setValueDirectly(component, meta.propertyKey, newValue);
|
||||
|
||||
// 触发hook回调
|
||||
this.triggerHook(component, meta, oldValue, newValue);
|
||||
|
||||
console.log(`[SyncVarManager] 应用同步: ${component.constructor.name}.${meta.propertyKey} = ${newValue}`);
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarManager] 反序列化失败 ${meta.propertyKey}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成组件的唯一ID
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @returns 唯一ID
|
||||
*/
|
||||
private getComponentId(component: any): string {
|
||||
// 简单实现,将来可以集成网络ID系统
|
||||
if (!component._syncVarId) {
|
||||
component._syncVarId = `${component.constructor.name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||
}
|
||||
return component._syncVarId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查两个值是否相等
|
||||
*
|
||||
* @param a - 值A
|
||||
* @param b - 值B
|
||||
* @returns 是否相等
|
||||
*/
|
||||
private isValueEqual(a: any, b: any): boolean {
|
||||
// 基础类型比较
|
||||
if (typeof a !== typeof b) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 对象比较(浅比较)
|
||||
if (typeof a === 'object' && a !== null && b !== null) {
|
||||
const keysA = Object.keys(a);
|
||||
const keysB = Object.keys(b);
|
||||
|
||||
if (keysA.length !== keysB.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return keysA.every(key => a[key] === b[key]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件是否有修改权限
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @returns 是否有权限
|
||||
*/
|
||||
private hasAuthority(component: any): boolean {
|
||||
// 简单实现:服务端始终有权限
|
||||
if (NetworkEnvironment.isServer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 客户端检查组件的权限设置
|
||||
// 如果组件有hasAuthority方法,使用它;否则默认客户端没有权限
|
||||
if (typeof component.hasAuthority === 'function') {
|
||||
return component.hasAuthority();
|
||||
}
|
||||
|
||||
// 默认情况下,客户端对权威字段没有权限
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该同步
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param metadata - SyncVar元数据
|
||||
* @returns 是否应该同步
|
||||
*/
|
||||
private shouldSync(component: any, metadata: SyncVarMetadata): boolean {
|
||||
// 权限检查:权威字段只有在有权限时才同步
|
||||
if (metadata.options.authorityOnly && !this.hasAuthority(component)) {
|
||||
console.log(`[SyncVarManager] 字段 ${metadata.propertyKey} 是权威字段,但当前没有权限,跳过同步`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 环境检查:服务端可以同步所有字段
|
||||
if (NetworkEnvironment.isServer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 客户端:非权威字段可以同步,权威字段需要检查权限
|
||||
if (metadata.options.authorityOnly) {
|
||||
return this.hasAuthority(component);
|
||||
}
|
||||
|
||||
// 普通字段客户端也可以同步
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发hook回调
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param metadata - SyncVar元数据
|
||||
* @param oldValue - 旧值
|
||||
* @param newValue - 新值
|
||||
*/
|
||||
private triggerHook(component: any, metadata: SyncVarMetadata, oldValue: any, newValue: any): void {
|
||||
if (!metadata.options.hook) {
|
||||
return;
|
||||
}
|
||||
|
||||
const hookFunction = component[metadata.options.hook];
|
||||
if (typeof hookFunction === 'function') {
|
||||
try {
|
||||
hookFunction.call(component, oldValue, newValue);
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarManager] Hook函数执行失败 ${metadata.options.hook}:`, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化值
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param propertyKey - 属性名
|
||||
* @param value - 值
|
||||
* @returns 序列化数据
|
||||
*/
|
||||
private serializeValue(component: any, propertyKey: string, value: any): Uint8Array {
|
||||
const metadata = getSyncVarMetadataForProperty(component, propertyKey);
|
||||
|
||||
if (metadata?.options.serializer) {
|
||||
return metadata.options.serializer(value);
|
||||
}
|
||||
|
||||
return this.serializeValueToBinary(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化值
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param propertyKey - 属性名
|
||||
* @param data - 序列化数据
|
||||
* @returns 反序列化的值
|
||||
*/
|
||||
private deserializeValue(component: any, propertyKey: string, data: Uint8Array): any {
|
||||
const metadata = getSyncVarMetadataForProperty(component, propertyKey);
|
||||
|
||||
if (metadata?.options.deserializer) {
|
||||
return metadata.options.deserializer(data);
|
||||
}
|
||||
|
||||
return this.deserializeValueFromBinary(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将值序列化为二进制数据
|
||||
*/
|
||||
private serializeValueToBinary(value: any): Uint8Array {
|
||||
if (value === null || value === undefined) {
|
||||
return new Uint8Array([0]);
|
||||
}
|
||||
|
||||
if (typeof value === 'boolean') {
|
||||
return new Uint8Array([1, value ? 1 : 0]);
|
||||
}
|
||||
|
||||
if (typeof value === 'number') {
|
||||
const buffer = new ArrayBuffer(9);
|
||||
const view = new DataView(buffer);
|
||||
view.setUint8(0, 2);
|
||||
view.setFloat64(1, value, true);
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
if (typeof value === 'string') {
|
||||
const encoded = new TextEncoder().encode(value);
|
||||
const buffer = new Uint8Array(5 + encoded.length);
|
||||
const view = new DataView(buffer.buffer);
|
||||
view.setUint8(0, 3);
|
||||
view.setUint32(1, encoded.length, true);
|
||||
buffer.set(encoded, 5);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
const jsonString = JSON.stringify(value);
|
||||
const encoded = new TextEncoder().encode(jsonString);
|
||||
const buffer = new Uint8Array(5 + encoded.length);
|
||||
const view = new DataView(buffer.buffer);
|
||||
view.setUint8(0, 4);
|
||||
view.setUint32(1, encoded.length, true);
|
||||
buffer.set(encoded, 5);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从二进制数据反序列化值
|
||||
*/
|
||||
private deserializeValueFromBinary(data: Uint8Array): any {
|
||||
if (data.length === 0) return null;
|
||||
|
||||
const view = new DataView(data.buffer, data.byteOffset);
|
||||
const type = view.getUint8(0);
|
||||
|
||||
switch (type) {
|
||||
case 0: return null;
|
||||
case 1: return view.getUint8(1) === 1;
|
||||
case 2: return view.getFloat64(1, true);
|
||||
case 3: {
|
||||
const length = view.getUint32(1, true);
|
||||
return new TextDecoder().decode(data.subarray(5, 5 + length));
|
||||
}
|
||||
case 4: {
|
||||
const length = view.getUint32(1, true);
|
||||
const jsonString = new TextDecoder().decode(data.subarray(5, 5 + length));
|
||||
return JSON.parse(jsonString);
|
||||
}
|
||||
default:
|
||||
throw new Error(`未知的序列化类型: ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置值(绕过代理)
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param propertyKey - 属性名
|
||||
* @param value - 值
|
||||
*/
|
||||
private setValueDirectly(component: any, propertyKey: string, value: any): void {
|
||||
// 临时禁用代理监听
|
||||
const originalValue = component._syncVarDisabled;
|
||||
component._syncVarDisabled = true;
|
||||
|
||||
try {
|
||||
component[propertyKey] = value;
|
||||
} finally {
|
||||
component._syncVarDisabled = originalValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SyncVar更新消息
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param networkId - 网络对象ID
|
||||
* @param senderId - 发送者ID
|
||||
* @param syncSequence - 同步序号
|
||||
* @param isFullSync - 是否是完整同步
|
||||
* @returns SyncVar更新消息,如果没有待同步的变化则返回null
|
||||
*/
|
||||
public createSyncVarUpdateMessage(
|
||||
component: any,
|
||||
networkId: string = '',
|
||||
senderId: string = '',
|
||||
syncSequence: number = 0,
|
||||
isFullSync: boolean = false
|
||||
): SyncVarUpdateMessage | null {
|
||||
const pendingChanges = this.getPendingChanges(component);
|
||||
|
||||
if (pendingChanges.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 转换变化记录为消息格式
|
||||
const fieldUpdates: SyncVarFieldUpdate[] = [];
|
||||
|
||||
for (const change of pendingChanges) {
|
||||
const metadata = getSyncVarMetadataForProperty(component, change.propertyKey);
|
||||
if (!metadata) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fieldUpdate: SyncVarFieldUpdate = {
|
||||
fieldNumber: change.fieldNumber,
|
||||
propertyKey: change.propertyKey,
|
||||
newValue: change.newValue as any,
|
||||
oldValue: change.oldValue as any,
|
||||
timestamp: change.timestamp,
|
||||
authorityOnly: metadata.options.authorityOnly
|
||||
};
|
||||
|
||||
fieldUpdates.push(fieldUpdate);
|
||||
}
|
||||
|
||||
if (fieldUpdates.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const message = new SyncVarUpdateMessage(
|
||||
networkId,
|
||||
component.constructor.name,
|
||||
fieldUpdates,
|
||||
isFullSync,
|
||||
senderId,
|
||||
syncSequence
|
||||
);
|
||||
|
||||
console.log(`[SyncVarManager] 创建SyncVar更新消息: ${component.constructor.name}, ${fieldUpdates.length} 个字段`);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用SyncVar更新消息
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @param message - SyncVar更新消息
|
||||
*/
|
||||
public applySyncVarUpdateMessage(component: any, message: SyncVarUpdateMessage): void {
|
||||
if (message.componentType !== component.constructor.name) {
|
||||
console.warn(`[SyncVarManager] 组件类型不匹配: 期望 ${component.constructor.name}, 收到 ${message.componentType}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const metadata = getSyncVarMetadata(component.constructor);
|
||||
const metadataMap = new Map(metadata.map(m => [m.fieldNumber, m]));
|
||||
|
||||
for (const fieldUpdate of message.fieldUpdates) {
|
||||
const meta = metadataMap.get(fieldUpdate.fieldNumber);
|
||||
if (!meta) {
|
||||
console.warn(`[SyncVarManager] 未找到字段编号 ${fieldUpdate.fieldNumber} 的元数据`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 权限检查:权威字段在客户端通常应该接受来自服务端的更新
|
||||
// 只有当客户端试图应用自己产生的权威字段更新时才拒绝
|
||||
if (fieldUpdate.authorityOnly && NetworkEnvironment.isClient && !this.hasAuthority(component)) {
|
||||
// 如果这是来自服务端的更新,则允许应用
|
||||
// 这里简单实现:客户端接受所有权威字段的更新
|
||||
console.log(`[SyncVarManager] 客户端接受权威字段更新: ${fieldUpdate.propertyKey}`);
|
||||
}
|
||||
|
||||
try {
|
||||
const oldValue = component[meta.propertyKey];
|
||||
|
||||
// 直接设置值,不通过代理(避免循环触发)
|
||||
this.setValueDirectly(component, meta.propertyKey, fieldUpdate.newValue);
|
||||
|
||||
// 触发hook回调
|
||||
this.triggerHook(component, meta, oldValue, fieldUpdate.newValue);
|
||||
|
||||
console.log(`[SyncVarManager] 应用SyncVar消息更新: ${component.constructor.name}.${meta.propertyKey} = ${fieldUpdate.newValue}`);
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarManager] 应用SyncVar更新失败 ${meta.propertyKey}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 清除对应的变化记录(已经同步完成)
|
||||
this.clearChanges(component, message.fieldUpdates.map(u => u.propertyKey));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量创建多个组件的SyncVar更新消息
|
||||
*
|
||||
* @param components - 组件实例数组
|
||||
* @param networkIds - 对应的网络对象ID数组
|
||||
* @param senderId - 发送者ID
|
||||
* @param syncSequence - 同步序号
|
||||
* @returns SyncVar更新消息数组
|
||||
*/
|
||||
public createBatchSyncVarUpdateMessages(
|
||||
components: any[],
|
||||
networkIds: string[] = [],
|
||||
senderId: string = '',
|
||||
syncSequence: number = 0
|
||||
): SyncVarUpdateMessage[] {
|
||||
const messages: SyncVarUpdateMessage[] = [];
|
||||
|
||||
for (let i = 0; i < components.length; i++) {
|
||||
const component = components[i];
|
||||
const networkId = networkIds[i] || '';
|
||||
|
||||
const message = this.createSyncVarUpdateMessage(
|
||||
component,
|
||||
networkId,
|
||||
senderId,
|
||||
syncSequence + i
|
||||
);
|
||||
|
||||
if (message) {
|
||||
messages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤需要同步的组件
|
||||
*
|
||||
* @param components - 组件数组
|
||||
* @returns 有待同步变化的组件数组
|
||||
*/
|
||||
public filterComponentsWithChanges(components: any[]): any[] {
|
||||
return components.filter(component => {
|
||||
const pendingChanges = this.getPendingChanges(component);
|
||||
return pendingChanges.length > 0;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取组件的变化统计
|
||||
*
|
||||
* @param component - 组件实例
|
||||
* @returns 变化统计信息
|
||||
*/
|
||||
public getComponentChangeStats(component: any): {
|
||||
totalChanges: number;
|
||||
pendingChanges: number;
|
||||
lastChangeTime: number;
|
||||
fieldChangeCounts: Map<string, number>;
|
||||
hasAuthorityOnlyChanges: boolean;
|
||||
} {
|
||||
const componentId = this.getComponentId(component);
|
||||
const changes = this._componentChanges.get(componentId) || [];
|
||||
const pendingChanges = changes.filter(c => c.needsSync);
|
||||
|
||||
const fieldChangeCounts = new Map<string, number>();
|
||||
let lastChangeTime = 0;
|
||||
let hasAuthorityOnlyChanges = false;
|
||||
|
||||
for (const change of changes) {
|
||||
const count = fieldChangeCounts.get(change.propertyKey) || 0;
|
||||
fieldChangeCounts.set(change.propertyKey, count + 1);
|
||||
lastChangeTime = Math.max(lastChangeTime, change.timestamp);
|
||||
|
||||
if (!hasAuthorityOnlyChanges) {
|
||||
const metadata = getSyncVarMetadataForProperty(component, change.propertyKey);
|
||||
if (metadata?.options.authorityOnly) {
|
||||
hasAuthorityOnlyChanges = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
totalChanges: changes.length,
|
||||
pendingChanges: pendingChanges.length,
|
||||
lastChangeTime,
|
||||
fieldChangeCounts,
|
||||
hasAuthorityOnlyChanges
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取管理器统计信息
|
||||
*
|
||||
* @returns 统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
totalComponents: number;
|
||||
totalChanges: number;
|
||||
pendingChanges: number;
|
||||
components: Array<{
|
||||
id: string;
|
||||
changes: number;
|
||||
pending: number;
|
||||
}>;
|
||||
} {
|
||||
let totalChanges = 0;
|
||||
let pendingChanges = 0;
|
||||
const components: Array<{ id: string; changes: number; pending: number }> = [];
|
||||
|
||||
for (const [componentId, changes] of this._componentChanges) {
|
||||
const pendingCount = changes.filter(c => c.needsSync).length;
|
||||
totalChanges += changes.length;
|
||||
pendingChanges += pendingCount;
|
||||
|
||||
components.push({
|
||||
id: componentId,
|
||||
changes: changes.length,
|
||||
pending: pendingCount
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
totalComponents: this._componentChanges.size,
|
||||
totalChanges,
|
||||
pendingChanges,
|
||||
components
|
||||
};
|
||||
}
|
||||
}
|
||||
285
packages/network/src/SyncVar/SyncVarMessageHandler.ts
Normal file
285
packages/network/src/SyncVar/SyncVarMessageHandler.ts
Normal file
@@ -0,0 +1,285 @@
|
||||
import { IMessageHandler } from '../Messaging/MessageHandler';
|
||||
import { SyncVarUpdateMessage } from '../Messaging/MessageTypes';
|
||||
import { NetworkConnection } from '../Core/NetworkConnection';
|
||||
import { NetworkIdentityRegistry } from '../Core/NetworkIdentity';
|
||||
import { SyncVarManager } from './SyncVarManager';
|
||||
import { NetworkEnvironment } from '../Core/NetworkEnvironment';
|
||||
import { ComponentRegistry } from '@esengine/ecs-framework';
|
||||
import { NetworkManager } from '../Core/NetworkManager';
|
||||
|
||||
/**
|
||||
* SyncVar更新消息处理器
|
||||
*
|
||||
* 处理接收到的SyncVar更新消息,自动查找目标网络对象并应用更新
|
||||
*/
|
||||
export class SyncVarMessageHandler implements IMessageHandler<SyncVarUpdateMessage> {
|
||||
private _processedMessages: Set<string> = new Set();
|
||||
private _maxProcessedCache: number = 1000;
|
||||
|
||||
/**
|
||||
* 处理SyncVar更新消息
|
||||
*
|
||||
* @param message - SyncVar更新消息
|
||||
* @param connection - 发送消息的连接(服务端有效)
|
||||
*/
|
||||
public async handle(message: SyncVarUpdateMessage, connection?: NetworkConnection): Promise<void> {
|
||||
try {
|
||||
// 生成消息唯一标识符用于去重
|
||||
const messageKey = this.generateMessageKey(message);
|
||||
if (this._processedMessages.has(messageKey)) {
|
||||
console.log(`[SyncVarMessageHandler] 跳过重复消息: ${messageKey}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 添加到已处理缓存
|
||||
this.addToProcessedCache(messageKey);
|
||||
|
||||
// 验证消息基本有效性
|
||||
if (!this.validateMessage(message)) {
|
||||
console.error('[SyncVarMessageHandler] 消息验证失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找目标网络对象
|
||||
const targetIdentity = NetworkIdentityRegistry.Instance.find(message.networkId);
|
||||
if (!targetIdentity) {
|
||||
console.warn(`[SyncVarMessageHandler] 未找到网络对象: ${message.networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 权限检查
|
||||
if (!this.checkAuthority(message, connection, targetIdentity)) {
|
||||
console.warn(`[SyncVarMessageHandler] 权限检查失败: ${message.networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找目标组件
|
||||
const targetComponent = this.findTargetComponent(targetIdentity, message.componentType);
|
||||
if (!targetComponent) {
|
||||
console.warn(`[SyncVarMessageHandler] 未找到目标组件: ${message.componentType} on ${message.networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 应用SyncVar更新
|
||||
this.applySyncVarUpdates(targetComponent, message);
|
||||
|
||||
// 更新网络对象的同步信息
|
||||
targetIdentity.updateSyncTime();
|
||||
if (message.syncSequence > targetIdentity.syncSequence) {
|
||||
targetIdentity.syncSequence = message.syncSequence;
|
||||
}
|
||||
|
||||
// 如果是服务端接收的消息,需要转发给其他客户端
|
||||
if (NetworkEnvironment.isServer && connection) {
|
||||
await this.forwardToOtherClients(message, connection);
|
||||
}
|
||||
|
||||
console.log(`[SyncVarMessageHandler] 成功处理SyncVar更新: ${message.networkId}.${message.componentType}, ${message.fieldUpdates.length}个字段`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('[SyncVarMessageHandler] 处理SyncVar更新失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成消息唯一标识符
|
||||
*/
|
||||
private generateMessageKey(message: SyncVarUpdateMessage): string {
|
||||
return `${message.networkId}_${message.componentType}_${message.syncSequence}_${message.timestamp}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加到已处理消息缓存
|
||||
*/
|
||||
private addToProcessedCache(messageKey: string): void {
|
||||
this._processedMessages.add(messageKey);
|
||||
|
||||
// 限制缓存大小
|
||||
if (this._processedMessages.size > this._maxProcessedCache) {
|
||||
const toDelete = Array.from(this._processedMessages).slice(0, this._maxProcessedCache / 2);
|
||||
toDelete.forEach(key => this._processedMessages.delete(key));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证消息基本有效性
|
||||
*/
|
||||
private validateMessage(message: SyncVarUpdateMessage): boolean {
|
||||
if (!message.networkId || !message.componentType) {
|
||||
console.error('[SyncVarMessageHandler] 消息缺少必要字段');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!message.fieldUpdates || message.fieldUpdates.length === 0) {
|
||||
console.error('[SyncVarMessageHandler] 消息没有字段更新');
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查时间戳合理性(不能是未来的时间,不能太久以前)
|
||||
const now = Date.now();
|
||||
const maxAge = 60000; // 1分钟
|
||||
if (message.timestamp > now + 5000 || message.timestamp < now - maxAge) {
|
||||
console.warn(`[SyncVarMessageHandler] 消息时间戳异常: ${message.timestamp}, 当前: ${now}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查操作权限
|
||||
*/
|
||||
private checkAuthority(
|
||||
message: SyncVarUpdateMessage,
|
||||
connection: NetworkConnection | undefined,
|
||||
targetIdentity: any
|
||||
): boolean {
|
||||
// 服务端始终有权限处理消息
|
||||
if (NetworkEnvironment.isServer) {
|
||||
// 但需要检查客户端发送的消息是否有权限修改对象
|
||||
if (connection) {
|
||||
// 检查是否是对象拥有者
|
||||
if (targetIdentity.ownerId && targetIdentity.ownerId !== connection.connectionId) {
|
||||
// 非拥有者只能发送非权威字段更新
|
||||
const hasAuthorityOnlyUpdates = message.fieldUpdates.some(update => update.authorityOnly);
|
||||
if (hasAuthorityOnlyUpdates) {
|
||||
console.warn(`[SyncVarMessageHandler] 非拥有者 ${connection.connectionId} 尝试修改权威字段`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 客户端接收到的消息通常来自服务端,应该允许
|
||||
if (NetworkEnvironment.isClient) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找目标组件
|
||||
*/
|
||||
private findTargetComponent(targetIdentity: any, componentType: string): any {
|
||||
const entity = targetIdentity.entity;
|
||||
if (!entity || typeof entity.getComponent !== 'function') {
|
||||
console.error('[SyncVarMessageHandler] NetworkIdentity缺少有效的Entity引用');
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 获取组件类
|
||||
const ComponentClass = this.getComponentClassByName(componentType);
|
||||
if (!ComponentClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 使用Entity的getComponent方法查找组件
|
||||
const component = entity.getComponent(ComponentClass);
|
||||
if (!component) {
|
||||
console.warn(`[SyncVarMessageHandler] Entity ${entity.id} 上未找到组件: ${componentType}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return component;
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarMessageHandler] 查找组件失败: ${componentType}`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据组件名称获取组件类
|
||||
*/
|
||||
private getComponentClassByName(componentType: string): any {
|
||||
const componentClass = ComponentRegistry.getComponentType(componentType);
|
||||
|
||||
if (!componentClass) {
|
||||
console.warn(`[SyncVarMessageHandler] 未找到组件类型: ${componentType}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return componentClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用SyncVar更新到组件
|
||||
*/
|
||||
private applySyncVarUpdates(targetComponent: any, message: SyncVarUpdateMessage): void {
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
|
||||
try {
|
||||
syncVarManager.applySyncVarUpdateMessage(targetComponent, message);
|
||||
} catch (error) {
|
||||
console.error('[SyncVarMessageHandler] 应用SyncVar更新失败:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 转发消息给其他客户端(服务端专用)
|
||||
*/
|
||||
private async forwardToOtherClients(
|
||||
message: SyncVarUpdateMessage,
|
||||
senderConnection: NetworkConnection
|
||||
): Promise<void> {
|
||||
try {
|
||||
// 获取NetworkServer实例
|
||||
const server = NetworkManager.GetServer();
|
||||
|
||||
if (!server || !server.isRunning) {
|
||||
console.warn('[SyncVarMessageHandler] NetworkServer未运行,无法转发消息');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用NetworkServer的broadcastSyncVarMessageExcept方法排除发送者
|
||||
const successCount = await server.broadcastSyncVarMessageExcept(message, senderConnection.connectionId);
|
||||
|
||||
if (successCount > 0) {
|
||||
console.log(`[SyncVarMessageHandler] 成功转发消息给 ${successCount} 个其他客户端 (发送者: ${senderConnection.connectionId})`);
|
||||
} else {
|
||||
console.log(`[SyncVarMessageHandler] 没有其他客户端需要转发消息 (发送者: ${senderConnection.connectionId})`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarMessageHandler] 转发消息失败 (发送者: ${senderConnection.connectionId}):`, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取处理器统计信息
|
||||
*/
|
||||
public getStats(): {
|
||||
processedMessages: number;
|
||||
cacheSize: number;
|
||||
maxCacheSize: number;
|
||||
} {
|
||||
return {
|
||||
processedMessages: this._processedMessages.size,
|
||||
cacheSize: this._processedMessages.size,
|
||||
maxCacheSize: this._maxProcessedCache
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理已处理消息缓存
|
||||
*/
|
||||
public clearProcessedCache(): void {
|
||||
this._processedMessages.clear();
|
||||
console.log('[SyncVarMessageHandler] 已清理消息处理缓存');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置最大缓存大小
|
||||
*/
|
||||
public setMaxCacheSize(maxSize: number): void {
|
||||
this._maxProcessedCache = Math.max(100, maxSize);
|
||||
|
||||
// 如果当前缓存超过新的最大值,进行清理
|
||||
if (this._processedMessages.size > this._maxProcessedCache) {
|
||||
const toDelete = Array.from(this._processedMessages).slice(0, this._processedMessages.size - this._maxProcessedCache);
|
||||
toDelete.forEach(key => this._processedMessages.delete(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
476
packages/network/src/SyncVar/SyncVarOptimizer.ts
Normal file
476
packages/network/src/SyncVar/SyncVarOptimizer.ts
Normal file
@@ -0,0 +1,476 @@
|
||||
import { SyncVarUpdateMessage, SyncVarFieldUpdate } from '../Messaging/MessageTypes';
|
||||
|
||||
/**
|
||||
* SyncVar优化配置
|
||||
*/
|
||||
export interface SyncVarOptimizationConfig {
|
||||
/** 是否启用消息合并 */
|
||||
enableMessageMerging: boolean;
|
||||
/** 最大合并时间窗口(毫秒) */
|
||||
mergeTimeWindow: number;
|
||||
/** 是否启用Delta压缩 */
|
||||
enableDeltaCompression: boolean;
|
||||
/** 是否启用批量发送 */
|
||||
enableBatchSending: boolean;
|
||||
/** 批量大小限制 */
|
||||
batchSizeLimit: number;
|
||||
/** 是否启用优先级队列 */
|
||||
enablePriorityQueue: boolean;
|
||||
/** 是否启用距离剔除 */
|
||||
enableDistanceCulling: boolean;
|
||||
/** 距离剔除半径 */
|
||||
cullingDistance: number;
|
||||
/** 是否启用频率限制 */
|
||||
enableRateLimit: boolean;
|
||||
/** 每秒最大消息数 */
|
||||
maxMessagesPerSecond: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息合并器
|
||||
*/
|
||||
export class SyncVarMessageMerger {
|
||||
private _pendingMessages: Map<string, SyncVarUpdateMessage[]> = new Map();
|
||||
private _mergeTimers: Map<string, NodeJS.Timeout> = new Map();
|
||||
private _config: SyncVarOptimizationConfig;
|
||||
|
||||
constructor(config: SyncVarOptimizationConfig) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加消息到合并队列
|
||||
*
|
||||
* @param message - SyncVar更新消息
|
||||
* @param onMerged - 合并完成回调
|
||||
*/
|
||||
public addMessage(
|
||||
message: SyncVarUpdateMessage,
|
||||
onMerged: (mergedMessage: SyncVarUpdateMessage) => void
|
||||
): void {
|
||||
if (!this._config.enableMessageMerging) {
|
||||
onMerged(message);
|
||||
return;
|
||||
}
|
||||
|
||||
const key = `${message.networkId}_${message.componentType}`;
|
||||
|
||||
// 获取或创建消息列表
|
||||
let messages = this._pendingMessages.get(key);
|
||||
if (!messages) {
|
||||
messages = [];
|
||||
this._pendingMessages.set(key, messages);
|
||||
}
|
||||
|
||||
// 添加消息到列表
|
||||
messages.push(message);
|
||||
|
||||
// 清除现有计时器
|
||||
const existingTimer = this._mergeTimers.get(key);
|
||||
if (existingTimer) {
|
||||
clearTimeout(existingTimer);
|
||||
}
|
||||
|
||||
// 设置新的合并计时器
|
||||
const timer = setTimeout(() => {
|
||||
this.mergeAndSend(key, onMerged);
|
||||
}, this._config.mergeTimeWindow);
|
||||
|
||||
this._mergeTimers.set(key, timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并并发送消息
|
||||
*/
|
||||
private mergeAndSend(
|
||||
key: string,
|
||||
onMerged: (mergedMessage: SyncVarUpdateMessage) => void
|
||||
): void {
|
||||
const messages = this._pendingMessages.get(key);
|
||||
if (!messages || messages.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理
|
||||
this._pendingMessages.delete(key);
|
||||
this._mergeTimers.delete(key);
|
||||
|
||||
if (messages.length === 1) {
|
||||
// 只有一个消息,直接发送
|
||||
onMerged(messages[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
// 合并多个消息
|
||||
const mergedMessage = this.mergeMessages(messages);
|
||||
onMerged(mergedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并多个消息为单个消息
|
||||
*/
|
||||
private mergeMessages(messages: SyncVarUpdateMessage[]): SyncVarUpdateMessage {
|
||||
if (messages.length === 1) {
|
||||
return messages[0];
|
||||
}
|
||||
|
||||
const firstMessage = messages[0];
|
||||
const fieldUpdateMap = new Map<number, SyncVarFieldUpdate>();
|
||||
|
||||
// 合并字段更新(后面的覆盖前面的)
|
||||
for (const message of messages) {
|
||||
for (const fieldUpdate of message.fieldUpdates) {
|
||||
fieldUpdateMap.set(fieldUpdate.fieldNumber, fieldUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建合并后的消息
|
||||
const mergedFieldUpdates = Array.from(fieldUpdateMap.values());
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
|
||||
return new SyncVarUpdateMessage(
|
||||
firstMessage.networkId,
|
||||
firstMessage.componentType,
|
||||
mergedFieldUpdates,
|
||||
false, // 合并的消息总是增量同步
|
||||
firstMessage.senderId,
|
||||
lastMessage.syncSequence // 使用最新的序列号
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制刷新所有待合并的消息
|
||||
*/
|
||||
public flush(onMerged: (mergedMessage: SyncVarUpdateMessage) => void): void {
|
||||
for (const key of this._pendingMessages.keys()) {
|
||||
this.mergeAndSend(key, onMerged);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有待合并的消息
|
||||
*/
|
||||
public clear(): void {
|
||||
// 清除所有计时器
|
||||
for (const timer of this._mergeTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
this._pendingMessages.clear();
|
||||
this._mergeTimers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 频率限制器
|
||||
*/
|
||||
export class SyncVarRateLimiter {
|
||||
private _messageCount: Map<string, number> = new Map();
|
||||
private _resetTimers: Map<string, NodeJS.Timeout> = new Map();
|
||||
private _config: SyncVarOptimizationConfig;
|
||||
|
||||
constructor(config: SyncVarOptimizationConfig) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否允许发送消息
|
||||
*
|
||||
* @param networkId - 网络对象ID
|
||||
* @returns 是否允许发送
|
||||
*/
|
||||
public canSend(networkId: string): boolean {
|
||||
if (!this._config.enableRateLimit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const currentCount = this._messageCount.get(networkId) || 0;
|
||||
|
||||
if (currentCount >= this._config.maxMessagesPerSecond) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 增加计数
|
||||
this._messageCount.set(networkId, currentCount + 1);
|
||||
|
||||
// 如果这是第一个消息,设置重置计时器
|
||||
if (currentCount === 0) {
|
||||
const timer = setTimeout(() => {
|
||||
this._messageCount.delete(networkId);
|
||||
this._resetTimers.delete(networkId);
|
||||
}, 1000);
|
||||
|
||||
this._resetTimers.set(networkId, timer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置指定对象的频率限制
|
||||
*/
|
||||
public reset(networkId: string): void {
|
||||
this._messageCount.delete(networkId);
|
||||
|
||||
const timer = this._resetTimers.get(networkId);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
this._resetTimers.delete(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理所有频率限制
|
||||
*/
|
||||
public clear(): void {
|
||||
for (const timer of this._resetTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
this._messageCount.clear();
|
||||
this._resetTimers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 距离剔除器
|
||||
*/
|
||||
export class SyncVarDistanceCuller {
|
||||
private _config: SyncVarOptimizationConfig;
|
||||
private _positionCache: Map<string, { x: number; y: number; z?: number }> = new Map();
|
||||
|
||||
constructor(config: SyncVarOptimizationConfig) {
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对象位置
|
||||
*
|
||||
* @param networkId - 网络对象ID
|
||||
* @param position - 位置坐标
|
||||
*/
|
||||
public updatePosition(networkId: string, position: { x: number; y: number; z?: number }): void {
|
||||
this._positionCache.set(networkId, { ...position });
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该向指定观察者发送消息
|
||||
*
|
||||
* @param targetId - 目标对象ID
|
||||
* @param observerId - 观察者ID
|
||||
* @returns 是否应该发送
|
||||
*/
|
||||
public shouldSendTo(targetId: string, observerId: string): boolean {
|
||||
if (!this._config.enableDistanceCulling) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const targetPos = this._positionCache.get(targetId);
|
||||
const observerPos = this._positionCache.get(observerId);
|
||||
|
||||
if (!targetPos || !observerPos) {
|
||||
// 位置信息不完整,默认发送
|
||||
return true;
|
||||
}
|
||||
|
||||
const distance = this.calculateDistance(targetPos, observerPos);
|
||||
return distance <= this._config.cullingDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取在指定范围内的观察者列表
|
||||
*
|
||||
* @param targetId - 目标对象ID
|
||||
* @param observerIds - 观察者ID列表
|
||||
* @returns 在范围内的观察者ID列表
|
||||
*/
|
||||
public getObserversInRange(targetId: string, observerIds: string[]): string[] {
|
||||
if (!this._config.enableDistanceCulling) {
|
||||
return observerIds;
|
||||
}
|
||||
|
||||
return observerIds.filter(observerId => this.shouldSendTo(targetId, observerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算两点之间的距离
|
||||
*/
|
||||
private calculateDistance(
|
||||
pos1: { x: number; y: number; z?: number },
|
||||
pos2: { x: number; y: number; z?: number }
|
||||
): number {
|
||||
const dx = pos1.x - pos2.x;
|
||||
const dy = pos1.y - pos2.y;
|
||||
const dz = (pos1.z || 0) - (pos2.z || 0);
|
||||
|
||||
return Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理位置缓存
|
||||
*/
|
||||
public clear(): void {
|
||||
this._positionCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除指定对象的位置信息
|
||||
*/
|
||||
public remove(networkId: string): void {
|
||||
this._positionCache.delete(networkId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar性能优化器
|
||||
*/
|
||||
export class SyncVarOptimizer {
|
||||
private _config: SyncVarOptimizationConfig;
|
||||
private _messageMerger: SyncVarMessageMerger;
|
||||
private _rateLimiter: SyncVarRateLimiter;
|
||||
private _distanceCuller: SyncVarDistanceCuller;
|
||||
|
||||
// 统计信息
|
||||
private _stats = {
|
||||
messagesProcessed: 0,
|
||||
messagesBlocked: 0,
|
||||
messagesMerged: 0,
|
||||
bytesSaved: 0
|
||||
};
|
||||
|
||||
constructor(config?: Partial<SyncVarOptimizationConfig>) {
|
||||
// 默认配置
|
||||
this._config = {
|
||||
enableMessageMerging: true,
|
||||
mergeTimeWindow: 16, // 1帧时间
|
||||
enableDeltaCompression: true,
|
||||
enableBatchSending: true,
|
||||
batchSizeLimit: 10,
|
||||
enablePriorityQueue: true,
|
||||
enableDistanceCulling: false,
|
||||
cullingDistance: 100,
|
||||
enableRateLimit: true,
|
||||
maxMessagesPerSecond: 60,
|
||||
...config
|
||||
};
|
||||
|
||||
this._messageMerger = new SyncVarMessageMerger(this._config);
|
||||
this._rateLimiter = new SyncVarRateLimiter(this._config);
|
||||
this._distanceCuller = new SyncVarDistanceCuller(this._config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理SyncVar消息优化
|
||||
*
|
||||
* @param message - 原始消息
|
||||
* @param targetObservers - 目标观察者列表
|
||||
* @param onOptimized - 优化完成回调
|
||||
*/
|
||||
public optimizeMessage(
|
||||
message: SyncVarUpdateMessage,
|
||||
targetObservers: string[] = [],
|
||||
onOptimized: (optimizedMessages: SyncVarUpdateMessage[], observers: string[]) => void
|
||||
): void {
|
||||
this._stats.messagesProcessed++;
|
||||
|
||||
// 频率限制检查
|
||||
if (!this._rateLimiter.canSend(message.networkId)) {
|
||||
this._stats.messagesBlocked++;
|
||||
console.log(`[SyncVarOptimizer] 消息被频率限制阻止: ${message.networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 距离剔除
|
||||
const validObservers = this._distanceCuller.getObserversInRange(message.networkId, targetObservers);
|
||||
|
||||
if (validObservers.length === 0 && targetObservers.length > 0) {
|
||||
this._stats.messagesBlocked++;
|
||||
console.log(`[SyncVarOptimizer] 消息被距离剔除阻止: ${message.networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 消息合并
|
||||
this._messageMerger.addMessage(message, (mergedMessage) => {
|
||||
if (mergedMessage !== message) {
|
||||
this._stats.messagesMerged++;
|
||||
console.log(`[SyncVarOptimizer] 消息已合并: ${message.networkId}`);
|
||||
}
|
||||
|
||||
onOptimized([mergedMessage], validObservers);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新对象位置(用于距离剔除)
|
||||
*/
|
||||
public updateObjectPosition(networkId: string, position: { x: number; y: number; z?: number }): void {
|
||||
this._distanceCuller.updatePosition(networkId, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置优化器
|
||||
*/
|
||||
public configure(config: Partial<SyncVarOptimizationConfig>): void {
|
||||
this._config = { ...this._config, ...config };
|
||||
console.log('[SyncVarOptimizer] 配置已更新:', this._config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制刷新所有待处理的消息
|
||||
*/
|
||||
public flush(onOptimized?: (optimizedMessages: SyncVarUpdateMessage[], observers: string[]) => void): void {
|
||||
this._messageMerger.flush((mergedMessage) => {
|
||||
if (onOptimized) {
|
||||
onOptimized([mergedMessage], []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置指定对象的优化状态
|
||||
*/
|
||||
public resetObject(networkId: string): void {
|
||||
this._rateLimiter.reset(networkId);
|
||||
this._distanceCuller.remove(networkId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取优化统计信息
|
||||
*/
|
||||
public getStats(): typeof SyncVarOptimizer.prototype._stats & {
|
||||
config: SyncVarOptimizationConfig;
|
||||
optimizationRatio: number;
|
||||
} {
|
||||
const optimizationRatio = this._stats.messagesProcessed > 0
|
||||
? (this._stats.messagesBlocked + this._stats.messagesMerged) / this._stats.messagesProcessed
|
||||
: 0;
|
||||
|
||||
return {
|
||||
...this._stats,
|
||||
config: { ...this._config },
|
||||
optimizationRatio
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置统计信息
|
||||
*/
|
||||
public resetStats(): void {
|
||||
this._stats = {
|
||||
messagesProcessed: 0,
|
||||
messagesBlocked: 0,
|
||||
messagesMerged: 0,
|
||||
bytesSaved: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理优化器
|
||||
*/
|
||||
public cleanup(): void {
|
||||
this._messageMerger.clear();
|
||||
this._rateLimiter.clear();
|
||||
this._distanceCuller.clear();
|
||||
this.resetStats();
|
||||
}
|
||||
}
|
||||
296
packages/network/src/SyncVar/SyncVarProxy.ts
Normal file
296
packages/network/src/SyncVar/SyncVarProxy.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import { getSyncVarMetadata, isSyncVar } from './SyncVarDecorator';
|
||||
import { SyncVarManager } from './SyncVarManager';
|
||||
import { INetworkSyncable, SyncVarValue, TypeGuards } from '../types/NetworkTypes';
|
||||
|
||||
/**
|
||||
* SyncVar代理配置
|
||||
*/
|
||||
export interface SyncVarProxyOptions {
|
||||
/**
|
||||
* 是否启用调试日志
|
||||
*/
|
||||
debugLog?: boolean;
|
||||
|
||||
/**
|
||||
* 自定义属性过滤器
|
||||
*/
|
||||
propertyFilter?: (propertyKey: string) => boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建SyncVar代理
|
||||
*
|
||||
* 为组件实例创建Proxy,拦截SyncVar字段的读写操作,
|
||||
* 当字段值发生变化时自动触发同步逻辑
|
||||
*
|
||||
* @param target - 目标组件实例
|
||||
* @param options - 代理配置选项
|
||||
* @returns 代理包装的组件实例
|
||||
*/
|
||||
export function createSyncVarProxy<T extends INetworkSyncable>(
|
||||
target: T,
|
||||
options: SyncVarProxyOptions = {}
|
||||
): T {
|
||||
const { debugLog = false, propertyFilter } = options;
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
|
||||
// 检查目标是否有SyncVar
|
||||
const metadata = getSyncVarMetadata(target.constructor);
|
||||
if (metadata.length === 0) {
|
||||
if (debugLog) {
|
||||
console.log(`[SyncVarProxy] 对象 ${target.constructor.name} 没有SyncVar,返回原对象`);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
// 初始化SyncVar管理器
|
||||
syncVarManager.initializeComponent(target);
|
||||
|
||||
if (debugLog) {
|
||||
console.log(`[SyncVarProxy] 为 ${target.constructor.name} 创建代理,SyncVar字段:`,
|
||||
metadata.map(m => m.propertyKey));
|
||||
}
|
||||
|
||||
// 存储原始值的副本,用于比较变化
|
||||
const originalValues = new Map<string, unknown>();
|
||||
|
||||
// 初始化原始值
|
||||
for (const meta of metadata) {
|
||||
if (meta.propertyKey in target) {
|
||||
originalValues.set(meta.propertyKey, (target as Record<string, unknown>)[meta.propertyKey]);
|
||||
}
|
||||
}
|
||||
|
||||
const proxy = new Proxy(target, {
|
||||
/**
|
||||
* 拦截属性读取
|
||||
*/
|
||||
get(obj: T, prop: string | symbol): unknown {
|
||||
// 内部属性和方法直接返回
|
||||
if (typeof prop === 'symbol' || prop.startsWith('_') || prop.startsWith('$')) {
|
||||
return Reflect.get(obj, prop);
|
||||
}
|
||||
|
||||
const propertyKey = prop as string;
|
||||
|
||||
// 如果有自定义过滤器且不通过,直接返回
|
||||
if (propertyFilter && !propertyFilter(propertyKey)) {
|
||||
return Reflect.get(obj, prop);
|
||||
}
|
||||
|
||||
const value = Reflect.get(obj, prop);
|
||||
|
||||
if (debugLog && isSyncVar(obj, propertyKey)) {
|
||||
console.log(`[SyncVarProxy] GET ${obj.constructor.name}.${propertyKey} = ${value}`);
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
|
||||
/**
|
||||
* 拦截属性设置
|
||||
*/
|
||||
set(obj: T, prop: string | symbol, newValue: unknown): boolean {
|
||||
// 内部属性和方法直接设置
|
||||
if (typeof prop === 'symbol' || prop.startsWith('_') || prop.startsWith('$')) {
|
||||
return Reflect.set(obj, prop, newValue);
|
||||
}
|
||||
|
||||
const propertyKey = prop as string;
|
||||
|
||||
// 如果有自定义过滤器且不通过,直接设置
|
||||
if (propertyFilter && !propertyFilter(propertyKey)) {
|
||||
return Reflect.set(obj, prop, newValue);
|
||||
}
|
||||
|
||||
// 检查是否被临时禁用(用于避免循环)
|
||||
if ((obj as any)._syncVarDisabled) {
|
||||
return Reflect.set(obj, prop, newValue);
|
||||
}
|
||||
|
||||
// 检查是否为SyncVar
|
||||
if (!isSyncVar(obj, propertyKey)) {
|
||||
return Reflect.set(obj, prop, newValue);
|
||||
}
|
||||
|
||||
// 获取旧值
|
||||
const oldValue = originalValues.get(propertyKey);
|
||||
|
||||
if (debugLog) {
|
||||
console.log(`[SyncVarProxy] SET ${obj.constructor.name}.${propertyKey} = ${newValue} (was ${oldValue})`);
|
||||
}
|
||||
|
||||
// 设置新值
|
||||
const result = Reflect.set(obj, prop, newValue);
|
||||
|
||||
if (result) {
|
||||
// 更新原始值记录
|
||||
originalValues.set(propertyKey, newValue);
|
||||
|
||||
// 记录变化到SyncVar管理器
|
||||
try {
|
||||
if (TypeGuards.isSyncVarValue(oldValue) && TypeGuards.isSyncVarValue(newValue)) {
|
||||
syncVarManager.recordChange(obj, propertyKey, oldValue, newValue);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarProxy] 记录SyncVar变化失败:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* 拦截属性删除
|
||||
*/
|
||||
deleteProperty(obj: T, prop: string | symbol): boolean {
|
||||
const propertyKey = prop as string;
|
||||
|
||||
if (typeof prop === 'string' && isSyncVar(obj, propertyKey)) {
|
||||
console.warn(`[SyncVarProxy] 尝试删除SyncVar属性 ${propertyKey},这可能会导致同步问题`);
|
||||
}
|
||||
|
||||
return Reflect.deleteProperty(obj, prop);
|
||||
},
|
||||
|
||||
/**
|
||||
* 拦截属性枚举
|
||||
*/
|
||||
ownKeys(obj: T): ArrayLike<string | symbol> {
|
||||
return Reflect.ownKeys(obj);
|
||||
},
|
||||
|
||||
/**
|
||||
* 拦截属性描述符获取
|
||||
*/
|
||||
getOwnPropertyDescriptor(obj: T, prop: string | symbol): PropertyDescriptor | undefined {
|
||||
return Reflect.getOwnPropertyDescriptor(obj, prop);
|
||||
},
|
||||
|
||||
/**
|
||||
* 拦截in操作符
|
||||
*/
|
||||
has(obj: T, prop: string | symbol): boolean {
|
||||
return Reflect.has(obj, prop);
|
||||
}
|
||||
});
|
||||
|
||||
// 标记为已代理
|
||||
(proxy as T & { _syncVarProxied: boolean; _syncVarOptions: SyncVarProxyOptions })._syncVarProxied = true;
|
||||
(proxy as T & { _syncVarProxied: boolean; _syncVarOptions: SyncVarProxyOptions })._syncVarOptions = options;
|
||||
|
||||
if (debugLog) {
|
||||
console.log(`[SyncVarProxy] ${target.constructor.name} 代理创建完成`);
|
||||
}
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查对象是否已经被SyncVar代理
|
||||
*
|
||||
* @param obj - 要检查的对象
|
||||
* @returns 是否已被代理
|
||||
*/
|
||||
export function isSyncVarProxied(obj: unknown): obj is { _syncVarProxied: boolean } {
|
||||
return typeof obj === 'object' && obj !== null && '_syncVarProxied' in obj && (obj as any)._syncVarProxied === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代理对象的原始目标
|
||||
*
|
||||
* @param proxy - 代理对象
|
||||
* @returns 原始目标对象,如果不是代理则返回原对象
|
||||
*/
|
||||
export function getSyncVarProxyTarget<T>(proxy: T): T {
|
||||
// 注意:JavaScript的Proxy没有直接方法获取target
|
||||
// 这里返回proxy本身,因为我们的代理是透明的
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁SyncVar代理
|
||||
*
|
||||
* 清理代理相关的资源,但注意JavaScript的Proxy无法真正"销毁"
|
||||
* 这个函数主要是清理管理器中的相关数据
|
||||
*
|
||||
* @param proxy - 代理对象
|
||||
*/
|
||||
export function destroySyncVarProxy(proxy: INetworkSyncable & { _syncVarProxied?: boolean; _syncVarDestroyed?: boolean }): void {
|
||||
if (!isSyncVarProxied(proxy)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 清理SyncVar管理器中的数据
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
syncVarManager.cleanupComponent(proxy);
|
||||
|
||||
// 标记为已销毁(虽然代理仍然存在)
|
||||
proxy._syncVarProxied = false;
|
||||
proxy._syncVarDestroyed = true;
|
||||
|
||||
console.log(`[SyncVarProxy] ${proxy.constructor?.name || 'Unknown'} 代理已销毁`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 临时禁用SyncVar代理监听
|
||||
*
|
||||
* 在回调函数执行期间禁用SyncVar变化监听,避免循环触发
|
||||
*
|
||||
* @param proxy - 代理对象
|
||||
* @param callback - 要执行的回调函数
|
||||
* @returns 回调函数的返回值
|
||||
*/
|
||||
export function withSyncVarDisabled<TResult>(proxy: INetworkSyncable & { _syncVarDisabled?: boolean; _syncVarProxied?: boolean }, callback: () => TResult): TResult {
|
||||
if (!isSyncVarProxied(proxy)) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
const wasDisabled = proxy._syncVarDisabled;
|
||||
proxy._syncVarDisabled = true;
|
||||
|
||||
try {
|
||||
return callback();
|
||||
} finally {
|
||||
proxy._syncVarDisabled = wasDisabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量更新SyncVar字段
|
||||
*
|
||||
* 在批量更新期间暂时禁用同步,最后一次性触发变化检测
|
||||
*
|
||||
* @param proxy - 代理对象
|
||||
* @param updates - 要更新的字段和值的映射
|
||||
*/
|
||||
export function batchUpdateSyncVar(proxy: INetworkSyncable & { _syncVarProxied?: boolean; _syncVarDisabled?: boolean }, updates: Record<string, unknown>): void {
|
||||
if (!isSyncVarProxied(proxy)) {
|
||||
// 如果不是代理对象,直接批量更新
|
||||
Object.assign(proxy, updates);
|
||||
return;
|
||||
}
|
||||
|
||||
withSyncVarDisabled(proxy, () => {
|
||||
// 记录旧值
|
||||
const oldValues: Record<string, unknown> = {};
|
||||
for (const key of Object.keys(updates)) {
|
||||
if (isSyncVar(proxy, key)) {
|
||||
oldValues[key] = (proxy as unknown as Record<string, unknown>)[key];
|
||||
}
|
||||
}
|
||||
|
||||
// 批量更新
|
||||
Object.assign(proxy, updates);
|
||||
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
for (const [key, newValue] of Object.entries(updates)) {
|
||||
if (isSyncVar(proxy, key)) {
|
||||
const oldValue = oldValues[key];
|
||||
if (TypeGuards.isSyncVarValue(oldValue) && TypeGuards.isSyncVarValue(newValue)) {
|
||||
syncVarManager.recordChange(proxy, key, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
477
packages/network/src/SyncVar/SyncVarSyncScheduler.ts
Normal file
477
packages/network/src/SyncVar/SyncVarSyncScheduler.ts
Normal file
@@ -0,0 +1,477 @@
|
||||
import { SyncVarManager } from './SyncVarManager';
|
||||
import { NetworkIdentityRegistry, NetworkIdentity } from '../Core/NetworkIdentity';
|
||||
import { SyncVarUpdateMessage } from '../Messaging/MessageTypes';
|
||||
import { NetworkEnvironment } from '../Core/NetworkEnvironment';
|
||||
import { ComponentRegistry } from '@esengine/ecs-framework';
|
||||
import { NetworkComponent } from '../NetworkComponent';
|
||||
|
||||
/**
|
||||
* SyncVar同步调度配置
|
||||
*/
|
||||
export interface SyncVarSyncConfig {
|
||||
/** 同步频率(毫秒) */
|
||||
syncInterval: number;
|
||||
/** 最大批处理消息数量 */
|
||||
maxBatchSize: number;
|
||||
/** 最大每帧处理对象数量 */
|
||||
maxObjectsPerFrame: number;
|
||||
/** 是否启用优先级排序 */
|
||||
enablePrioritySort: boolean;
|
||||
/** 最小同步间隔(防止过于频繁) */
|
||||
minSyncInterval: number;
|
||||
/** 是否启用增量同步 */
|
||||
enableIncrementalSync: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步优先级计算器
|
||||
*/
|
||||
export interface ISyncPriorityCalculator {
|
||||
/**
|
||||
* 计算组件的同步优先级
|
||||
*
|
||||
* @param component - 网络组件
|
||||
* @param identity - 网络身份
|
||||
* @returns 优先级值,数字越大优先级越高
|
||||
*/
|
||||
calculatePriority(component: any, identity: NetworkIdentity): number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认优先级计算器
|
||||
*/
|
||||
export class DefaultSyncPriorityCalculator implements ISyncPriorityCalculator {
|
||||
public calculatePriority(component: any, identity: NetworkIdentity): number {
|
||||
let priority = 0;
|
||||
|
||||
// 权威对象优先级更高
|
||||
if (identity.hasAuthority) {
|
||||
priority += 10;
|
||||
}
|
||||
|
||||
// 距离上次同步时间越长,优先级越高
|
||||
const timeSinceLastSync = Date.now() - identity.lastSyncTime;
|
||||
priority += Math.min(timeSinceLastSync / 1000, 10); // 最多加10分
|
||||
|
||||
// 变化数量越多,优先级越高
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
const changes = syncVarManager.getPendingChanges(component);
|
||||
priority += changes.length;
|
||||
|
||||
return priority;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar同步调度器
|
||||
*
|
||||
* 负责定期扫描网络对象的SyncVar变化,创建和分发同步消息
|
||||
* 支持批处理、优先级排序和性能优化
|
||||
*/
|
||||
export class SyncVarSyncScheduler {
|
||||
private static _instance: SyncVarSyncScheduler | null = null;
|
||||
|
||||
private _config: SyncVarSyncConfig;
|
||||
private _priorityCalculator: ISyncPriorityCalculator;
|
||||
private _isRunning: boolean = false;
|
||||
private _syncTimer: NodeJS.Timeout | null = null;
|
||||
private _lastSyncTime: number = 0;
|
||||
private _syncCounter: number = 0;
|
||||
|
||||
// 统计信息
|
||||
private _stats = {
|
||||
totalSyncCycles: 0,
|
||||
totalObjectsScanned: 0,
|
||||
totalMessagesSent: 0,
|
||||
totalChangesProcessed: 0,
|
||||
averageCycleTime: 0,
|
||||
lastCycleTime: 0,
|
||||
errors: 0
|
||||
};
|
||||
|
||||
// 消息发送回调
|
||||
private _messageSendCallback: ((message: SyncVarUpdateMessage) => Promise<void>) | null = null;
|
||||
|
||||
/**
|
||||
* 获取调度器单例
|
||||
*/
|
||||
public static get Instance(): SyncVarSyncScheduler {
|
||||
if (!SyncVarSyncScheduler._instance) {
|
||||
SyncVarSyncScheduler._instance = new SyncVarSyncScheduler();
|
||||
}
|
||||
return SyncVarSyncScheduler._instance;
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
// 默认配置
|
||||
this._config = {
|
||||
syncInterval: 50, // 20fps
|
||||
maxBatchSize: 10,
|
||||
maxObjectsPerFrame: 50,
|
||||
enablePrioritySort: true,
|
||||
minSyncInterval: 16, // 最小16ms (60fps)
|
||||
enableIncrementalSync: true
|
||||
};
|
||||
|
||||
this._priorityCalculator = new DefaultSyncPriorityCalculator();
|
||||
}
|
||||
|
||||
/**
|
||||
* 配置调度器
|
||||
*
|
||||
* @param config - 调度器配置
|
||||
*/
|
||||
public configure(config: Partial<SyncVarSyncConfig>): void {
|
||||
this._config = { ...this._config, ...config };
|
||||
|
||||
// 如果正在运行,重启以应用新配置
|
||||
if (this._isRunning) {
|
||||
this.stop();
|
||||
this.start();
|
||||
}
|
||||
|
||||
console.log('[SyncVarSyncScheduler] 调度器配置已更新:', this._config);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置优先级计算器
|
||||
*
|
||||
* @param calculator - 优先级计算器
|
||||
*/
|
||||
public setPriorityCalculator(calculator: ISyncPriorityCalculator): void {
|
||||
this._priorityCalculator = calculator;
|
||||
console.log('[SyncVarSyncScheduler] 优先级计算器已更新');
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息发送回调
|
||||
*
|
||||
* @param callback - 消息发送回调函数
|
||||
*/
|
||||
public setMessageSendCallback(callback: (message: SyncVarUpdateMessage) => Promise<void>): void {
|
||||
this._messageSendCallback = callback;
|
||||
console.log('[SyncVarSyncScheduler] 消息发送回调已设置');
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动调度器
|
||||
*/
|
||||
public start(): void {
|
||||
if (this._isRunning) {
|
||||
console.warn('[SyncVarSyncScheduler] 调度器已经在运行');
|
||||
return;
|
||||
}
|
||||
|
||||
this._isRunning = true;
|
||||
this._lastSyncTime = Date.now();
|
||||
|
||||
// 设置定时器
|
||||
this._syncTimer = setInterval(() => {
|
||||
this.performSyncCycle();
|
||||
}, this._config.syncInterval);
|
||||
|
||||
console.log(`[SyncVarSyncScheduler] 调度器已启动,同步间隔: ${this._config.syncInterval}ms`);
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止调度器
|
||||
*/
|
||||
public stop(): void {
|
||||
if (!this._isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._isRunning = false;
|
||||
|
||||
if (this._syncTimer) {
|
||||
clearInterval(this._syncTimer);
|
||||
this._syncTimer = null;
|
||||
}
|
||||
|
||||
console.log('[SyncVarSyncScheduler] 调度器已停止');
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行一次同步周期
|
||||
*/
|
||||
public performSyncCycle(): void {
|
||||
if (!this._isRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
const cycleStartTime = Date.now();
|
||||
|
||||
try {
|
||||
// 检查最小同步间隔
|
||||
if (cycleStartTime - this._lastSyncTime < this._config.minSyncInterval) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._stats.totalSyncCycles++;
|
||||
this._lastSyncTime = cycleStartTime;
|
||||
|
||||
// 获取所有激活的网络对象
|
||||
const activeObjects = NetworkIdentityRegistry.Instance.getActiveObjects();
|
||||
this._stats.totalObjectsScanned += activeObjects.length;
|
||||
|
||||
// 收集需要同步的组件
|
||||
const syncCandidates = this.collectSyncCandidates(activeObjects);
|
||||
|
||||
// 优先级排序
|
||||
if (this._config.enablePrioritySort) {
|
||||
syncCandidates.sort((a, b) => b.priority - a.priority);
|
||||
}
|
||||
|
||||
// 限制每帧处理的对象数量
|
||||
const objectsToProcess = syncCandidates.slice(0, this._config.maxObjectsPerFrame);
|
||||
|
||||
// 创建和发送同步消息
|
||||
this.processSyncCandidates(objectsToProcess);
|
||||
|
||||
// 更新统计信息
|
||||
const cycleTime = Date.now() - cycleStartTime;
|
||||
this._stats.lastCycleTime = cycleTime;
|
||||
this._stats.averageCycleTime = (this._stats.averageCycleTime * (this._stats.totalSyncCycles - 1) + cycleTime) / this._stats.totalSyncCycles;
|
||||
|
||||
} catch (error) {
|
||||
this._stats.errors++;
|
||||
console.error('[SyncVarSyncScheduler] 同步周期执行失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集同步候选对象
|
||||
*/
|
||||
private collectSyncCandidates(activeObjects: NetworkIdentity[]): Array<{
|
||||
identity: NetworkIdentity;
|
||||
component: any;
|
||||
priority: number;
|
||||
changeCount: number;
|
||||
}> {
|
||||
const candidates: Array<{
|
||||
identity: NetworkIdentity;
|
||||
component: any;
|
||||
priority: number;
|
||||
changeCount: number;
|
||||
}> = [];
|
||||
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
|
||||
for (const identity of activeObjects) {
|
||||
try {
|
||||
// 获取对象的所有网络组件
|
||||
const components = this.getNetworkComponents(identity);
|
||||
|
||||
for (const component of components) {
|
||||
// 检查组件是否有SyncVar变化
|
||||
const pendingChanges = syncVarManager.getPendingChanges(component);
|
||||
if (pendingChanges.length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 权限检查:只有有权限的对象才能发起同步
|
||||
if (!this.canComponentSync(component, identity)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算优先级
|
||||
const priority = this._priorityCalculator.calculatePriority(component, identity);
|
||||
|
||||
candidates.push({
|
||||
identity,
|
||||
component,
|
||||
priority,
|
||||
changeCount: pendingChanges.length
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarSyncScheduler] 处理网络对象失败: ${identity.networkId}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络对象的所有网络组件
|
||||
*/
|
||||
private getNetworkComponents(identity: NetworkIdentity): NetworkComponent[] {
|
||||
const entity = identity.entity;
|
||||
if (!entity) {
|
||||
console.warn(`[SyncVarSyncScheduler] NetworkIdentity ${identity.networkId} 缺少Entity引用`);
|
||||
return [];
|
||||
}
|
||||
|
||||
const networkComponents: NetworkComponent[] = [];
|
||||
|
||||
try {
|
||||
// 获取所有已注册的组件类型
|
||||
const allRegisteredTypes = ComponentRegistry.getAllRegisteredTypes();
|
||||
|
||||
for (const [ComponentClass] of allRegisteredTypes) {
|
||||
// 检查是否为NetworkComponent子类
|
||||
if (ComponentClass.prototype instanceof NetworkComponent || ComponentClass === NetworkComponent) {
|
||||
const component = entity.getComponent(ComponentClass as any);
|
||||
if (component && component instanceof NetworkComponent) {
|
||||
networkComponents.push(component);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarSyncScheduler] 获取网络组件失败 (${identity.networkId}):`, error);
|
||||
}
|
||||
|
||||
return networkComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查组件是否可以进行同步
|
||||
*/
|
||||
private canComponentSync(component: any, identity: NetworkIdentity): boolean {
|
||||
// 服务端对象通常有同步权限
|
||||
if (NetworkEnvironment.isServer && identity.hasAuthority) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 客户端只能同步自己拥有的对象
|
||||
if (NetworkEnvironment.isClient) {
|
||||
// 这里需要获取当前客户端ID,暂时简化处理
|
||||
return identity.hasAuthority;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理同步候选对象
|
||||
*/
|
||||
private processSyncCandidates(candidates: Array<{
|
||||
identity: NetworkIdentity;
|
||||
component: any;
|
||||
priority: number;
|
||||
changeCount: number;
|
||||
}>): void {
|
||||
const syncVarManager = SyncVarManager.Instance;
|
||||
const messageBatch: SyncVarUpdateMessage[] = [];
|
||||
|
||||
for (const candidate of candidates) {
|
||||
try {
|
||||
// 创建SyncVar更新消息
|
||||
const message = syncVarManager.createSyncVarUpdateMessage(
|
||||
candidate.component,
|
||||
candidate.identity.networkId,
|
||||
'', // senderId,后续可以从环境获取
|
||||
candidate.identity.getNextSyncSequence(),
|
||||
false // isFullSync
|
||||
);
|
||||
|
||||
if (message) {
|
||||
messageBatch.push(message);
|
||||
this._stats.totalChangesProcessed += candidate.changeCount;
|
||||
|
||||
// 更新对象的同步时间
|
||||
candidate.identity.updateSyncTime();
|
||||
|
||||
// 清除已同步的变化
|
||||
syncVarManager.clearChanges(candidate.component);
|
||||
}
|
||||
|
||||
// 检查批处理大小限制
|
||||
if (messageBatch.length >= this._config.maxBatchSize) {
|
||||
this.sendMessageBatch(messageBatch);
|
||||
messageBatch.length = 0; // 清空数组
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`[SyncVarSyncScheduler] 处理同步候选对象失败: ${candidate.identity.networkId}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// 发送剩余的消息
|
||||
if (messageBatch.length > 0) {
|
||||
this.sendMessageBatch(messageBatch);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息批次
|
||||
*/
|
||||
private async sendMessageBatch(messages: SyncVarUpdateMessage[]): Promise<void> {
|
||||
if (!this._messageSendCallback) {
|
||||
console.warn('[SyncVarSyncScheduler] 没有设置消息发送回调,消息被丢弃');
|
||||
return;
|
||||
}
|
||||
|
||||
for (const message of messages) {
|
||||
try {
|
||||
await this._messageSendCallback(message);
|
||||
this._stats.totalMessagesSent++;
|
||||
} catch (error) {
|
||||
console.error('[SyncVarSyncScheduler] 发送SyncVar消息失败:', error);
|
||||
this._stats.errors++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发同步
|
||||
*
|
||||
* @param networkId - 指定网络对象ID,如果不提供则同步所有对象
|
||||
*/
|
||||
public manualSync(networkId?: string): void {
|
||||
if (networkId) {
|
||||
const identity = NetworkIdentityRegistry.Instance.find(networkId);
|
||||
if (identity) {
|
||||
this.performSyncCycle();
|
||||
}
|
||||
} else {
|
||||
this.performSyncCycle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取调度器统计信息
|
||||
*/
|
||||
public getStats(): typeof SyncVarSyncScheduler.prototype._stats & {
|
||||
isRunning: boolean;
|
||||
config: SyncVarSyncConfig;
|
||||
uptime: number;
|
||||
} {
|
||||
return {
|
||||
...this._stats,
|
||||
isRunning: this._isRunning,
|
||||
config: { ...this._config },
|
||||
uptime: this._isRunning ? Date.now() - (this._lastSyncTime - this._config.syncInterval) : 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置统计信息
|
||||
*/
|
||||
public resetStats(): void {
|
||||
this._stats = {
|
||||
totalSyncCycles: 0,
|
||||
totalObjectsScanned: 0,
|
||||
totalMessagesSent: 0,
|
||||
totalChangesProcessed: 0,
|
||||
averageCycleTime: 0,
|
||||
lastCycleTime: 0,
|
||||
errors: 0
|
||||
};
|
||||
console.log('[SyncVarSyncScheduler] 统计信息已重置');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前配置
|
||||
*/
|
||||
public getConfig(): SyncVarSyncConfig {
|
||||
return { ...this._config };
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查调度器是否正在运行
|
||||
*/
|
||||
public get isRunning(): boolean {
|
||||
return this._isRunning;
|
||||
}
|
||||
}
|
||||
41
packages/network/src/SyncVar/index.ts
Normal file
41
packages/network/src/SyncVar/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* SyncVar同步变量系统导出
|
||||
*
|
||||
* 提供自动变量同步功能,支持网络状态的实时同步
|
||||
*/
|
||||
|
||||
// 装饰器和元数据
|
||||
export * from './SyncVarDecorator';
|
||||
|
||||
// 管理器
|
||||
export { SyncVarManager } from './SyncVarManager';
|
||||
|
||||
// 代理系统
|
||||
export * from './SyncVarProxy';
|
||||
|
||||
// 工厂函数
|
||||
export * from './SyncVarFactory';
|
||||
|
||||
// 消息处理器
|
||||
export { SyncVarMessageHandler } from './SyncVarMessageHandler';
|
||||
|
||||
// 同步调度器
|
||||
export {
|
||||
SyncVarSyncScheduler,
|
||||
DefaultSyncPriorityCalculator
|
||||
} from './SyncVarSyncScheduler';
|
||||
export type {
|
||||
SyncVarSyncConfig,
|
||||
ISyncPriorityCalculator
|
||||
} from './SyncVarSyncScheduler';
|
||||
|
||||
// 性能优化器
|
||||
export {
|
||||
SyncVarOptimizer,
|
||||
SyncVarMessageMerger,
|
||||
SyncVarRateLimiter,
|
||||
SyncVarDistanceCuller
|
||||
} from './SyncVarOptimizer';
|
||||
export type {
|
||||
SyncVarOptimizationConfig
|
||||
} from './SyncVarOptimizer';
|
||||
Reference in New Issue
Block a user