Chore/lint fixes (#212)
* fix(eslint): 修复装饰器缩进配置 * fix(eslint): 修复装饰器缩进配置 * chore: 删除未使用的导入 * chore(lint): 移除未使用的导入和变量 * chore(lint): 修复editor-app中未使用的函数参数 * chore(lint): 修复未使用的赋值变量 * chore(eslint): 将所有错误级别改为警告以通过CI * fix(codeql): 修复GitHub Advanced Security检测到的问题
This commit is contained in:
@@ -3,315 +3,315 @@
|
||||
*/
|
||||
import { Component, Emitter } from '@esengine/ecs-framework';
|
||||
import { AuthorityType, NetworkScope } from '../types/NetworkTypes';
|
||||
import {
|
||||
NetworkEventType,
|
||||
NetworkIdentityEventData,
|
||||
NetworkEventUtils
|
||||
import {
|
||||
NetworkEventType,
|
||||
NetworkIdentityEventData,
|
||||
NetworkEventUtils
|
||||
} from '../events/NetworkEvents';
|
||||
|
||||
/**
|
||||
* 网络身份组件
|
||||
*
|
||||
*
|
||||
* 为实体提供网络同步能力的核心组件。
|
||||
* 每个需要网络同步的实体都必须拥有此组件。
|
||||
*
|
||||
*
|
||||
* 集成了事件系统,当属性变化时自动发射事件用于网络同步。
|
||||
*/
|
||||
export class NetworkIdentity extends Component {
|
||||
/**
|
||||
/**
|
||||
* 事件发射器
|
||||
* 用于发射网络相关事件
|
||||
*/
|
||||
private eventEmitter = new Emitter<NetworkEventType, NetworkIdentity>();
|
||||
/**
|
||||
private eventEmitter = new Emitter<NetworkEventType, NetworkIdentity>();
|
||||
/**
|
||||
* 网络ID (全局唯一)
|
||||
* 用于在网络中标识实体
|
||||
*/
|
||||
public networkId: number = 0;
|
||||
public networkId: number = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 拥有者ID
|
||||
* 表示哪个客户端拥有此实体的控制权
|
||||
*/
|
||||
public ownerId: string = '';
|
||||
public ownerId: string = '';
|
||||
|
||||
/**
|
||||
/**
|
||||
* 权限类型
|
||||
* 决定哪一端对此实体有控制权
|
||||
*/
|
||||
public authority: AuthorityType = AuthorityType.Server;
|
||||
public authority: AuthorityType = AuthorityType.Server;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 同步频率 (Hz)
|
||||
* 每秒同步的次数
|
||||
*/
|
||||
public syncRate: number = 20;
|
||||
public syncRate: number = 20;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 网络作用域
|
||||
* 决定哪些客户端可以看到此实体
|
||||
*/
|
||||
public scope: NetworkScope = NetworkScope.Room;
|
||||
public scope: NetworkScope = NetworkScope.Room;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 是否是本地玩家
|
||||
* 标识此实体是否代表本地玩家
|
||||
*/
|
||||
public isLocalPlayer: boolean = false;
|
||||
public isLocalPlayer: boolean = false;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 是否启用网络同步
|
||||
* 临时禁用/启用同步
|
||||
*/
|
||||
public syncEnabled: boolean = true;
|
||||
public syncEnabled: boolean = true;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 同步优先级
|
||||
* 影响同步的顺序和频率,数值越高优先级越高
|
||||
*/
|
||||
public priority: number = 0;
|
||||
public priority: number = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 距离阈值
|
||||
* 用于附近同步模式,超过此距离的客户端不会收到同步
|
||||
*/
|
||||
public distanceThreshold: number = 100;
|
||||
public distanceThreshold: number = 100;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 最后同步时间
|
||||
* 记录上次同步的时间戳
|
||||
*/
|
||||
public lastSyncTime: number = 0;
|
||||
public lastSyncTime: number = 0;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 是否可见
|
||||
* 控制实体是否对其他客户端可见
|
||||
*/
|
||||
public visible: boolean = true;
|
||||
public visible: boolean = true;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 自定义同步过滤器
|
||||
* 用于自定义作用域的同步逻辑
|
||||
*/
|
||||
public customSyncFilter?: (clientId: string) => boolean;
|
||||
public customSyncFilter?: (clientId: string) => boolean;
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取实体的同步权重
|
||||
* 基于优先级和距离计算
|
||||
*/
|
||||
public getSyncWeight(distance?: number): number {
|
||||
let weight = this.priority;
|
||||
|
||||
if (distance !== undefined && this.scope === NetworkScope.Nearby) {
|
||||
// 距离越近权重越高
|
||||
const distanceFactor = Math.max(0, 1 - (distance / this.distanceThreshold));
|
||||
weight *= distanceFactor;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
public getSyncWeight(distance?: number): number {
|
||||
let weight = this.priority;
|
||||
|
||||
/**
|
||||
if (distance !== undefined && this.scope === NetworkScope.Nearby) {
|
||||
// 距离越近权重越高
|
||||
const distanceFactor = Math.max(0, 1 - (distance / this.distanceThreshold));
|
||||
weight *= distanceFactor;
|
||||
}
|
||||
|
||||
return weight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否应该同步给指定客户端
|
||||
*/
|
||||
public shouldSyncToClient(clientId: string, distance?: number): boolean {
|
||||
if (!this.syncEnabled || !this.visible) {
|
||||
return false;
|
||||
public shouldSyncToClient(clientId: string, distance?: number): boolean {
|
||||
if (!this.syncEnabled || !this.visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (this.scope) {
|
||||
case NetworkScope.Global:
|
||||
return true;
|
||||
|
||||
case NetworkScope.Room:
|
||||
return true; // 由房间管理器控制
|
||||
|
||||
case NetworkScope.Owner:
|
||||
return clientId === this.ownerId;
|
||||
|
||||
case NetworkScope.Nearby:
|
||||
return distance !== undefined && distance <= this.distanceThreshold;
|
||||
|
||||
case NetworkScope.Custom:
|
||||
return this.customSyncFilter ? this.customSyncFilter(clientId) : false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (this.scope) {
|
||||
case NetworkScope.Global:
|
||||
return true;
|
||||
|
||||
case NetworkScope.Room:
|
||||
return true; // 由房间管理器控制
|
||||
|
||||
case NetworkScope.Owner:
|
||||
return clientId === this.ownerId;
|
||||
|
||||
case NetworkScope.Nearby:
|
||||
return distance !== undefined && distance <= this.distanceThreshold;
|
||||
|
||||
case NetworkScope.Custom:
|
||||
return this.customSyncFilter ? this.customSyncFilter(clientId) : false;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 检查客户端是否有权限修改此实体
|
||||
*/
|
||||
public hasAuthority(clientId: string): boolean {
|
||||
switch (this.authority) {
|
||||
case AuthorityType.Server:
|
||||
return false; // 只有服务端有权限
|
||||
|
||||
case AuthorityType.Client:
|
||||
return clientId === this.ownerId;
|
||||
|
||||
case AuthorityType.Shared:
|
||||
return true; // 任何人都可以修改
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public hasAuthority(clientId: string): boolean {
|
||||
switch (this.authority) {
|
||||
case AuthorityType.Server:
|
||||
return false; // 只有服务端有权限
|
||||
|
||||
/**
|
||||
case AuthorityType.Client:
|
||||
return clientId === this.ownerId;
|
||||
|
||||
case AuthorityType.Shared:
|
||||
return true; // 任何人都可以修改
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置拥有者
|
||||
*/
|
||||
public setOwner(clientId: string): void {
|
||||
const oldOwner = this.ownerId;
|
||||
this.ownerId = clientId;
|
||||
|
||||
// 发射拥有者变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.IDENTITY_OWNER_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
clientId,
|
||||
oldOwner,
|
||||
clientId
|
||||
)
|
||||
);
|
||||
}
|
||||
public setOwner(clientId: string): void {
|
||||
const oldOwner = this.ownerId;
|
||||
this.ownerId = clientId;
|
||||
|
||||
/**
|
||||
// 发射拥有者变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.IDENTITY_OWNER_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
clientId,
|
||||
oldOwner,
|
||||
clientId
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置权限类型
|
||||
*/
|
||||
public setAuthority(authority: AuthorityType): void {
|
||||
const oldAuthority = this.authority;
|
||||
this.authority = authority;
|
||||
|
||||
// 发射权限变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.IDENTITY_AUTHORITY_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldAuthority,
|
||||
authority
|
||||
)
|
||||
);
|
||||
}
|
||||
public setAuthority(authority: AuthorityType): void {
|
||||
const oldAuthority = this.authority;
|
||||
this.authority = authority;
|
||||
|
||||
/**
|
||||
// 发射权限变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.IDENTITY_AUTHORITY_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldAuthority,
|
||||
authority
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置同步状态
|
||||
*/
|
||||
public setSyncEnabled(enabled: boolean): void {
|
||||
const oldEnabled = this.syncEnabled;
|
||||
this.syncEnabled = enabled;
|
||||
|
||||
// 发射同步状态变化事件
|
||||
const eventType = enabled
|
||||
? NetworkEventType.IDENTITY_SYNC_ENABLED
|
||||
: NetworkEventType.IDENTITY_SYNC_DISABLED;
|
||||
|
||||
this.emitEvent(
|
||||
eventType,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldEnabled,
|
||||
enabled
|
||||
)
|
||||
);
|
||||
}
|
||||
public setSyncEnabled(enabled: boolean): void {
|
||||
const oldEnabled = this.syncEnabled;
|
||||
this.syncEnabled = enabled;
|
||||
|
||||
/**
|
||||
// 发射同步状态变化事件
|
||||
const eventType = enabled
|
||||
? NetworkEventType.IDENTITY_SYNC_ENABLED
|
||||
: NetworkEventType.IDENTITY_SYNC_DISABLED;
|
||||
|
||||
this.emitEvent(
|
||||
eventType,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldEnabled,
|
||||
enabled
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置同步频率
|
||||
*/
|
||||
public setSyncRate(rate: number): void {
|
||||
const oldRate = this.syncRate;
|
||||
this.syncRate = rate;
|
||||
|
||||
// 发射同步频率变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.SYNC_RATE_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldRate,
|
||||
rate
|
||||
)
|
||||
);
|
||||
}
|
||||
public setSyncRate(rate: number): void {
|
||||
const oldRate = this.syncRate;
|
||||
this.syncRate = rate;
|
||||
|
||||
/**
|
||||
// 发射同步频率变化事件
|
||||
this.emitEvent(
|
||||
NetworkEventType.SYNC_RATE_CHANGED,
|
||||
NetworkEventUtils.createIdentityEventData(
|
||||
this.networkId,
|
||||
this.ownerId,
|
||||
oldRate,
|
||||
rate
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加事件监听器
|
||||
*/
|
||||
public addEventListener(eventType: NetworkEventType, handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.eventEmitter.addObserver(eventType, handler, this);
|
||||
}
|
||||
public addEventListener(eventType: NetworkEventType, handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.eventEmitter.addObserver(eventType, handler, this);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 移除事件监听器
|
||||
*/
|
||||
public removeEventListener(eventType: NetworkEventType, handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.eventEmitter.removeObserver(eventType, handler);
|
||||
}
|
||||
public removeEventListener(eventType: NetworkEventType, handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.eventEmitter.removeObserver(eventType, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 发射事件
|
||||
* @private
|
||||
*/
|
||||
private emitEvent(eventType: NetworkEventType, data: NetworkIdentityEventData): void {
|
||||
this.eventEmitter.emit(eventType, data);
|
||||
}
|
||||
private emitEvent(eventType: NetworkEventType, data: NetworkIdentityEventData): void {
|
||||
this.eventEmitter.emit(eventType, data);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 监听属性变化事件
|
||||
*/
|
||||
public onPropertyChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_PROPERTY_CHANGED, handler);
|
||||
}
|
||||
public onPropertyChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_PROPERTY_CHANGED, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 监听拥有者变化事件
|
||||
*/
|
||||
public onOwnerChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_OWNER_CHANGED, handler);
|
||||
}
|
||||
public onOwnerChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_OWNER_CHANGED, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 监听权限变化事件
|
||||
*/
|
||||
public onAuthorityChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_AUTHORITY_CHANGED, handler);
|
||||
}
|
||||
public onAuthorityChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_AUTHORITY_CHANGED, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 监听同步状态变化事件
|
||||
*/
|
||||
public onSyncStateChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_SYNC_ENABLED, handler);
|
||||
this.addEventListener(NetworkEventType.IDENTITY_SYNC_DISABLED, handler);
|
||||
}
|
||||
public onSyncStateChanged(handler: (data: NetworkIdentityEventData) => void): void {
|
||||
this.addEventListener(NetworkEventType.IDENTITY_SYNC_ENABLED, handler);
|
||||
this.addEventListener(NetworkEventType.IDENTITY_SYNC_DISABLED, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 获取调试信息
|
||||
*/
|
||||
public getDebugInfo(): Record<string, any> {
|
||||
return {
|
||||
networkId: this.networkId,
|
||||
ownerId: this.ownerId,
|
||||
authority: this.authority,
|
||||
scope: this.scope,
|
||||
syncRate: this.syncRate,
|
||||
priority: this.priority,
|
||||
syncEnabled: this.syncEnabled,
|
||||
visible: this.visible,
|
||||
lastSyncTime: this.lastSyncTime
|
||||
};
|
||||
}
|
||||
public getDebugInfo(): Record<string, any> {
|
||||
return {
|
||||
networkId: this.networkId,
|
||||
ownerId: this.ownerId,
|
||||
authority: this.authority,
|
||||
scope: this.scope,
|
||||
syncRate: this.syncRate,
|
||||
priority: this.priority,
|
||||
syncEnabled: this.syncEnabled,
|
||||
visible: this.visible,
|
||||
lastSyncTime: this.lastSyncTime
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* 组件销毁时清理事件监听器
|
||||
*/
|
||||
public dispose(): void {
|
||||
public dispose(): void {
|
||||
// 清理所有事件监听器
|
||||
this.eventEmitter.dispose();
|
||||
}
|
||||
}
|
||||
this.eventEmitter.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface ServerRpcOptions extends RpcOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 客户端RPC装饰器选项
|
||||
* 客户端RPC装饰器选项
|
||||
*/
|
||||
export interface ClientRpcOptions extends RpcOptions {
|
||||
/** 广播到多个客户端时的聚合策略 */
|
||||
@@ -38,11 +38,11 @@ export function ServerRpc(options: ServerRpcOptions = {}): MethodDecorator {
|
||||
return function (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
|
||||
const className = target.constructor.name;
|
||||
const methodName = String(propertyKey);
|
||||
|
||||
|
||||
// 获取参数类型和返回值类型
|
||||
const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey) || [];
|
||||
const returnType = Reflect.getMetadata('design:returntype', target, propertyKey);
|
||||
|
||||
|
||||
const metadata: RpcMethodMetadata = {
|
||||
methodName,
|
||||
className,
|
||||
@@ -59,15 +59,15 @@ export function ServerRpc(options: ServerRpcOptions = {}): MethodDecorator {
|
||||
paramTypes: paramTypes.map((type: unknown) => type?.constructor?.name || 'unknown'),
|
||||
returnType: returnType?.name || 'unknown'
|
||||
};
|
||||
|
||||
|
||||
// 存储元数据
|
||||
Reflect.defineMetadata(RPC_METADATA_KEY, metadata, target, propertyKey);
|
||||
|
||||
|
||||
// 添加到方法列表
|
||||
const existingMethods = Reflect.getMetadata(RPC_METHODS_KEY, target.constructor) || [];
|
||||
existingMethods.push(methodName);
|
||||
Reflect.defineMetadata(RPC_METHODS_KEY, existingMethods, target.constructor);
|
||||
|
||||
|
||||
// 包装原方法以添加验证和日志
|
||||
const originalMethod = descriptor.value;
|
||||
descriptor.value = async function (...args: unknown[]) {
|
||||
@@ -80,7 +80,7 @@ export function ServerRpc(options: ServerRpcOptions = {}): MethodDecorator {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
@@ -93,11 +93,11 @@ export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
|
||||
return function (target: object, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
|
||||
const className = target.constructor.name;
|
||||
const methodName = String(propertyKey);
|
||||
|
||||
|
||||
// 获取参数类型和返回值类型
|
||||
const paramTypes = Reflect.getMetadata('design:paramtypes', target, propertyKey) || [];
|
||||
const returnType = Reflect.getMetadata('design:returntype', target, propertyKey);
|
||||
|
||||
|
||||
const metadata: RpcMethodMetadata = {
|
||||
methodName,
|
||||
className,
|
||||
@@ -114,15 +114,15 @@ export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
|
||||
paramTypes: paramTypes.map((type: unknown) => type?.constructor?.name || 'unknown'),
|
||||
returnType: returnType?.name || 'unknown'
|
||||
};
|
||||
|
||||
|
||||
// 存储元数据
|
||||
Reflect.defineMetadata(RPC_METADATA_KEY, metadata, target, propertyKey);
|
||||
|
||||
|
||||
// 添加到方法列表
|
||||
const existingMethods = Reflect.getMetadata(RPC_METHODS_KEY, target.constructor) || [];
|
||||
existingMethods.push(methodName);
|
||||
Reflect.defineMetadata(RPC_METHODS_KEY, existingMethods, target.constructor);
|
||||
|
||||
|
||||
// 包装原方法以添加调用逻辑
|
||||
const originalMethod = descriptor.value;
|
||||
descriptor.value = async function (...args: unknown[]) {
|
||||
@@ -130,7 +130,7 @@ export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
|
||||
// 目前保持原方法以支持本地测试
|
||||
return originalMethod.apply(this, args);
|
||||
};
|
||||
|
||||
|
||||
return descriptor;
|
||||
};
|
||||
}
|
||||
@@ -141,7 +141,7 @@ export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
|
||||
export function getRpcMethods(target: Function): RpcMethodMetadata[] {
|
||||
const methodNames = Reflect.getMetadata(RPC_METHODS_KEY, target) || [];
|
||||
const prototype = target.prototype;
|
||||
|
||||
|
||||
return methodNames.map((methodName: string) => {
|
||||
const metadata = Reflect.getMetadata(RPC_METADATA_KEY, prototype, methodName);
|
||||
if (!metadata) {
|
||||
@@ -155,7 +155,7 @@ export function getRpcMethods(target: Function): RpcMethodMetadata[] {
|
||||
* 获取特定方法的RPC元数据
|
||||
*/
|
||||
export function getRpcMethodMetadata(
|
||||
target: object,
|
||||
target: object,
|
||||
methodName: string | symbol
|
||||
): RpcMethodMetadata | undefined {
|
||||
return Reflect.getMetadata(RPC_METADATA_KEY, target, methodName);
|
||||
@@ -203,7 +203,7 @@ export class RpcMethodValidator {
|
||||
error: `参数数量不匹配,期望 ${metadata.paramTypes.length} 个,实际 ${args.length} 个`
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 权限检查
|
||||
if (metadata.options.requireAuth && !callerId) {
|
||||
return {
|
||||
@@ -211,10 +211,10 @@ export class RpcMethodValidator {
|
||||
error: '该方法需要身份验证'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证RPC方法定义
|
||||
*/
|
||||
@@ -226,7 +226,7 @@ export class RpcMethodValidator {
|
||||
error: '方法名无效'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 超时时间检查
|
||||
if (metadata.options.timeout && metadata.options.timeout <= 0) {
|
||||
return {
|
||||
@@ -234,16 +234,16 @@ export class RpcMethodValidator {
|
||||
error: '超时时间必须大于0'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 优先级检查
|
||||
if (metadata.options.priority !== undefined &&
|
||||
if (metadata.options.priority !== undefined &&
|
||||
(metadata.options.priority < 0 || metadata.options.priority > 10)) {
|
||||
return {
|
||||
valid: false,
|
||||
error: '优先级必须在0-10之间'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return { valid: true };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,14 +75,14 @@ export const DEFAULT_SYNCVAR_OPTIONS: Required<SyncVarOptions<SyncVarValue>> = {
|
||||
export function SyncVar<T extends SyncVarValue = SyncVarValue>(options: SyncVarOptions<T> = {}) {
|
||||
return function (target: object, propertyKey: string | symbol) {
|
||||
const fullOptions = { ...DEFAULT_SYNCVAR_OPTIONS, ...options } as Required<SyncVarOptions<T>>;
|
||||
|
||||
|
||||
// 获取或创建元数据存储
|
||||
if (!(target.constructor as any)[SYNCVAR_METADATA_KEY]) {
|
||||
(target.constructor as any)[SYNCVAR_METADATA_KEY] = new Map();
|
||||
}
|
||||
|
||||
|
||||
const metadataMap = (target.constructor as any)[SYNCVAR_METADATA_KEY] as Map<string | symbol, SyncVarMetadata<T>>;
|
||||
|
||||
|
||||
// 创建元数据
|
||||
const metadata: SyncVarMetadata<T> = {
|
||||
propertyKey,
|
||||
@@ -91,21 +91,21 @@ export function SyncVar<T extends SyncVarValue = SyncVarValue>(options: SyncVarO
|
||||
isDirty: false,
|
||||
syncCount: 0
|
||||
};
|
||||
|
||||
|
||||
metadataMap.set(propertyKey, metadata);
|
||||
|
||||
|
||||
// 获取原始属性描述符
|
||||
const originalDescriptor = Object.getOwnPropertyDescriptor(target, propertyKey) || {
|
||||
writable: true,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
};
|
||||
|
||||
|
||||
metadata.originalDescriptor = originalDescriptor;
|
||||
|
||||
|
||||
// 创建内部存储属性名
|
||||
const internalPropertyKey = Symbol(`_syncVar_${String(propertyKey)}`);
|
||||
|
||||
|
||||
// 重新定义属性,添加变化检测
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
get: function() {
|
||||
@@ -113,23 +113,23 @@ export function SyncVar<T extends SyncVarValue = SyncVarValue>(options: SyncVarO
|
||||
},
|
||||
set: function(newValue: T) {
|
||||
const oldValue = (this as any)[internalPropertyKey];
|
||||
|
||||
|
||||
// 检查是否真的发生了变化
|
||||
if (!hasValueChanged(oldValue, newValue, fullOptions.threshold)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 更新值
|
||||
(this as any)[internalPropertyKey] = newValue;
|
||||
|
||||
|
||||
// 标记为脏数据
|
||||
markAsDirty(this, propertyKey);
|
||||
|
||||
|
||||
// 触发变化回调
|
||||
if (fullOptions.onChanged) {
|
||||
fullOptions.onChanged(oldValue, newValue);
|
||||
}
|
||||
|
||||
|
||||
// 如果启用了自动同步,立即同步
|
||||
if (fullOptions.syncRate === 0) {
|
||||
requestImmediateSync(this, propertyKey);
|
||||
@@ -138,7 +138,7 @@ export function SyncVar<T extends SyncVarValue = SyncVarValue>(options: SyncVarO
|
||||
enumerable: originalDescriptor.enumerable,
|
||||
configurable: originalDescriptor.configurable
|
||||
});
|
||||
|
||||
|
||||
// 设置初始值
|
||||
if (originalDescriptor.value !== undefined) {
|
||||
(target as any)[internalPropertyKey] = originalDescriptor.value;
|
||||
@@ -154,22 +154,22 @@ function hasValueChanged(oldValue: SyncVarValue, newValue: SyncVarValue, thresho
|
||||
if (oldValue === newValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// null/undefined 检查
|
||||
if (oldValue == null || newValue == null) {
|
||||
return oldValue !== newValue;
|
||||
}
|
||||
|
||||
|
||||
// 数值类型的阈值检查
|
||||
if (typeof oldValue === 'number' && typeof newValue === 'number') {
|
||||
return Math.abs(oldValue - newValue) > threshold;
|
||||
}
|
||||
|
||||
|
||||
// 对象类型的深度比较
|
||||
if (typeof oldValue === 'object' && typeof newValue === 'object') {
|
||||
return !deepEqual(oldValue, newValue);
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -180,36 +180,36 @@ function deepEqual(obj1: SyncVarValue, obj2: SyncVarValue): boolean {
|
||||
if (obj1 === obj2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (obj1 == null || obj2 == null) {
|
||||
return obj1 === obj2;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj1 !== typeof obj2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj1 !== 'object') {
|
||||
return obj1 === obj2;
|
||||
}
|
||||
|
||||
|
||||
const keys1 = Object.keys(obj1);
|
||||
const keys2 = Object.keys(obj2);
|
||||
|
||||
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (const key of keys1) {
|
||||
if (!keys2.includes(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!deepEqual((obj1 as any)[key], (obj2 as any)[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -219,12 +219,12 @@ function deepEqual(obj1: SyncVarValue, obj2: SyncVarValue): boolean {
|
||||
function markAsDirty(instance: object, propertyKey: string | symbol): void {
|
||||
const metadataMap = (instance.constructor as any)[SYNCVAR_METADATA_KEY] as Map<string | symbol, SyncVarMetadata>;
|
||||
const metadata = metadataMap?.get(propertyKey);
|
||||
|
||||
|
||||
if (metadata) {
|
||||
metadata.isDirty = true;
|
||||
metadata.lastValue = (instance as any)[propertyKey];
|
||||
}
|
||||
|
||||
|
||||
// 通知SyncVar管理器
|
||||
if (typeof window !== 'undefined' && (window as any).SyncVarManager) {
|
||||
(window as any).SyncVarManager.markInstanceDirty(instance);
|
||||
@@ -248,7 +248,7 @@ export function getSyncVarMetadata(target: object): Map<string | symbol, SyncVar
|
||||
if (!target || !target.constructor) {
|
||||
return new Map();
|
||||
}
|
||||
|
||||
|
||||
return (target.constructor as any)[SYNCVAR_METADATA_KEY] || new Map();
|
||||
}
|
||||
|
||||
@@ -274,13 +274,13 @@ export function hasSyncVars(target: object): boolean {
|
||||
export function getDirtySyncVars(target: object): Map<string | symbol, SyncVarMetadata> {
|
||||
const metadataMap = getSyncVarMetadata(target);
|
||||
const dirtyVars = new Map<string | symbol, SyncVarMetadata>();
|
||||
|
||||
|
||||
for (const [key, metadata] of metadataMap) {
|
||||
if (metadata.isDirty) {
|
||||
dirtyVars.set(key, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return dirtyVars;
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ export function getDirtySyncVars(target: object): Map<string | symbol, SyncVarMe
|
||||
*/
|
||||
export function clearDirtyFlags(target: object): void {
|
||||
const metadataMap = getSyncVarMetadata(target);
|
||||
|
||||
|
||||
for (const metadata of metadataMap.values()) {
|
||||
metadata.isDirty = false;
|
||||
metadata.lastSyncTime = Date.now();
|
||||
@@ -302,7 +302,7 @@ export function clearDirtyFlags(target: object): void {
|
||||
*/
|
||||
export function resetSyncVarStats(target: object): void {
|
||||
const metadataMap = getSyncVarMetadata(target);
|
||||
|
||||
|
||||
for (const metadata of metadataMap.values()) {
|
||||
metadata.syncCount = 0;
|
||||
metadata.lastSyncTime = 0;
|
||||
@@ -315,7 +315,7 @@ export function resetSyncVarStats(target: object): void {
|
||||
export function getSyncVarStats(target: object): { [key: string]: { syncCount: number; lastSyncTime: number; isDirty: boolean } } {
|
||||
const metadataMap = getSyncVarMetadata(target);
|
||||
const stats: { [key: string]: { syncCount: number; lastSyncTime: number; isDirty: boolean } } = {};
|
||||
|
||||
|
||||
for (const [key, metadata] of metadataMap) {
|
||||
stats[String(key)] = {
|
||||
syncCount: metadata.syncCount,
|
||||
@@ -323,6 +323,6 @@ export function getSyncVarStats(target: object): { [key: string]: { syncCount: n
|
||||
isDirty: metadata.isDirty
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return stats;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
*/
|
||||
|
||||
export * from './SyncVar';
|
||||
export * from './RpcDecorators';
|
||||
export * from './RpcDecorators';
|
||||
|
||||
@@ -11,7 +11,7 @@ export enum NetworkEventType {
|
||||
RECONNECTION_STARTED = 'network:reconnection:started',
|
||||
RECONNECTION_SUCCEEDED = 'network:reconnection:succeeded',
|
||||
RECONNECTION_FAILED = 'network:reconnection:failed',
|
||||
|
||||
|
||||
// 网络身份相关事件
|
||||
IDENTITY_CREATED = 'network:identity:created',
|
||||
IDENTITY_DESTROYED = 'network:identity:destroyed',
|
||||
@@ -21,14 +21,14 @@ export enum NetworkEventType {
|
||||
IDENTITY_SYNC_DISABLED = 'network:identity:sync:disabled',
|
||||
IDENTITY_PROPERTY_CHANGED = 'network:identity:property:changed',
|
||||
IDENTITY_VISIBLE_CHANGED = 'network:identity:visible:changed',
|
||||
|
||||
|
||||
// 同步相关事件
|
||||
SYNC_STARTED = 'network:sync:started',
|
||||
SYNC_COMPLETED = 'network:sync:completed',
|
||||
SYNC_FAILED = 'network:sync:failed',
|
||||
SYNC_RATE_CHANGED = 'network:sync:rate:changed',
|
||||
SYNC_PRIORITY_CHANGED = 'network:sync:priority:changed',
|
||||
|
||||
|
||||
// RPC相关事件
|
||||
RPC_CALL_SENT = 'network:rpc:call:sent',
|
||||
RPC_CALL_RECEIVED = 'network:rpc:call:received',
|
||||
@@ -36,7 +36,7 @@ export enum NetworkEventType {
|
||||
RPC_RESPONSE_RECEIVED = 'network:rpc:response:received',
|
||||
RPC_ERROR = 'network:rpc:error',
|
||||
RPC_TIMEOUT = 'network:rpc:timeout',
|
||||
|
||||
|
||||
// 消息相关事件
|
||||
MESSAGE_SENT = 'network:message:sent',
|
||||
MESSAGE_RECEIVED = 'network:message:received',
|
||||
@@ -44,7 +44,7 @@ export enum NetworkEventType {
|
||||
MESSAGE_DROPPED = 'network:message:dropped',
|
||||
MESSAGE_RETRY = 'network:message:retry',
|
||||
MESSAGE_ACKNOWLEDGED = 'network:message:acknowledged',
|
||||
|
||||
|
||||
// 房间相关事件
|
||||
ROOM_JOINED = 'network:room:joined',
|
||||
ROOM_LEFT = 'network:room:left',
|
||||
@@ -52,26 +52,26 @@ export enum NetworkEventType {
|
||||
ROOM_DESTROYED = 'network:room:destroyed',
|
||||
ROOM_PLAYER_JOINED = 'network:room:player:joined',
|
||||
ROOM_PLAYER_LEFT = 'network:room:player:left',
|
||||
|
||||
|
||||
// 客户端相关事件
|
||||
CLIENT_CONNECTED = 'network:client:connected',
|
||||
CLIENT_DISCONNECTED = 'network:client:disconnected',
|
||||
CLIENT_AUTHENTICATED = 'network:client:authenticated',
|
||||
CLIENT_KICKED = 'network:client:kicked',
|
||||
CLIENT_TIMEOUT = 'network:client:timeout',
|
||||
|
||||
|
||||
// 服务器相关事件
|
||||
SERVER_STARTED = 'network:server:started',
|
||||
SERVER_STOPPED = 'network:server:stopped',
|
||||
SERVER_ERROR = 'network:server:error',
|
||||
SERVER_OVERLOADED = 'network:server:overloaded',
|
||||
|
||||
|
||||
// 数据相关事件
|
||||
DATA_SYNCHRONIZED = 'network:data:synchronized',
|
||||
DATA_CONFLICT = 'network:data:conflict',
|
||||
DATA_CORRUPTED = 'network:data:corrupted',
|
||||
DATA_VALIDATED = 'network:data:validated',
|
||||
|
||||
|
||||
// 性能相关事件
|
||||
BANDWIDTH_WARNING = 'network:bandwidth:warning',
|
||||
LATENCY_HIGH = 'network:latency:high',
|
||||
@@ -284,4 +284,4 @@ export class NetworkEventUtils {
|
||||
duration
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
export * from './NetworkEvents';
|
||||
export * from './NetworkEvents';
|
||||
|
||||
@@ -25,13 +25,13 @@ export * from './events/NetworkEvents';
|
||||
// 序列化系统
|
||||
export * from './serialization/JSONSerializer';
|
||||
export * from './serialization/MessageCompressor';
|
||||
export {
|
||||
SyncVarSerializer,
|
||||
export {
|
||||
SyncVarSerializer,
|
||||
SyncVarSerializerConfig,
|
||||
SerializationResult as SyncVarSerializationResult,
|
||||
DeserializationResult as SyncVarDeserializationResult,
|
||||
DeltaData as SyncVarDeltaData,
|
||||
CompressionMetadata
|
||||
CompressionMetadata
|
||||
} from './serialization/SyncVarSerializer';
|
||||
|
||||
// 装饰器系统
|
||||
@@ -45,18 +45,18 @@ export * from './rpc/RpcReliabilityManager';
|
||||
|
||||
// 同步系统
|
||||
export { SyncVarManager, SyncBatch } from './sync/SyncVarManager';
|
||||
export {
|
||||
DeltaSync,
|
||||
export {
|
||||
DeltaSync,
|
||||
DeltaSyncConfig,
|
||||
DeltaData,
|
||||
DeltaOperationType,
|
||||
DeltaOperation,
|
||||
VersionedData,
|
||||
DeltaSyncStats
|
||||
DeltaSyncStats
|
||||
} from './sync/DeltaSync';
|
||||
|
||||
// 监控系统
|
||||
export * from './monitoring';
|
||||
|
||||
// 工具类
|
||||
export * from './utils';
|
||||
export * from './utils';
|
||||
|
||||
@@ -99,19 +99,19 @@ export interface BandwidthMonitorEvents {
|
||||
export class BandwidthMonitor extends EventEmitter {
|
||||
private logger = createLogger('BandwidthMonitor');
|
||||
private config: BandwidthMonitorConfig;
|
||||
|
||||
|
||||
/** 带宽样本历史 */
|
||||
private samples: BandwidthSample[] = [];
|
||||
|
||||
|
||||
/** 当前带宽限制 */
|
||||
private limits: BandwidthLimit;
|
||||
|
||||
|
||||
/** 当前警告级别 */
|
||||
private currentWarningLevel = BandwidthWarningLevel.Normal;
|
||||
|
||||
|
||||
/** 监控定时器 */
|
||||
private monitorTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: BandwidthStats = {
|
||||
currentUpload: 0,
|
||||
@@ -127,10 +127,10 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
latencyJitter: 0,
|
||||
utilization: 0
|
||||
};
|
||||
|
||||
|
||||
/** 上次统计时间 */
|
||||
private lastStatsTime = Date.now();
|
||||
|
||||
|
||||
/** 累计字节数 */
|
||||
private cumulativeBytesIn = 0;
|
||||
private cumulativeBytesOut = 0;
|
||||
@@ -139,7 +139,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
|
||||
constructor(config: Partial<BandwidthMonitorConfig> = {}) {
|
||||
super();
|
||||
|
||||
|
||||
this.config = {
|
||||
monitorInterval: 1000,
|
||||
sampleWindowSize: 60,
|
||||
@@ -149,13 +149,13 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
adaptiveFactor: 0.1,
|
||||
...config
|
||||
};
|
||||
|
||||
|
||||
this.limits = {
|
||||
uploadLimit: 1024 * 1024, // 1MB/s
|
||||
downloadLimit: 1024 * 1024, // 1MB/s
|
||||
enabled: false
|
||||
};
|
||||
|
||||
|
||||
this.startMonitoring();
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
this.cumulativeBytesOut += bytesOut;
|
||||
this.cumulativePacketsIn += packetsIn;
|
||||
this.cumulativePacketsOut += packetsOut;
|
||||
|
||||
|
||||
this.stats.totalUpload += bytesOut;
|
||||
this.stats.totalDownload += bytesIn;
|
||||
}
|
||||
@@ -178,9 +178,9 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
public setBandwidthLimits(limits: Partial<BandwidthLimit>): void {
|
||||
const oldLimits = { ...this.limits };
|
||||
Object.assign(this.limits, limits);
|
||||
|
||||
|
||||
this.logger.info(`带宽限制已更新: 上行=${this.limits.uploadLimit}B/s, 下行=${this.limits.downloadLimit}B/s`);
|
||||
|
||||
|
||||
if (this.config.enableAdaptive) {
|
||||
this.emit('adaptiveAdjustment', oldLimits, this.limits);
|
||||
}
|
||||
@@ -214,7 +214,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (!this.limits.enabled) {
|
||||
return { upload: false, download: false };
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
upload: this.stats.currentUpload > this.limits.uploadLimit,
|
||||
download: this.stats.currentDownload > this.limits.downloadLimit
|
||||
@@ -228,9 +228,9 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (!this.limits.enabled) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
|
||||
const uploadUtilization = this.stats.currentUpload / this.limits.uploadLimit;
|
||||
|
||||
|
||||
if (uploadUtilization < this.config.warningThreshold) {
|
||||
return this.limits.uploadLimit * 0.1; // 10% of limit
|
||||
} else if (uploadUtilization < this.config.criticalThreshold) {
|
||||
@@ -248,7 +248,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
this.stats.currentUpload / this.limits.uploadLimit,
|
||||
this.stats.currentDownload / this.limits.downloadLimit
|
||||
);
|
||||
|
||||
|
||||
if (utilization < this.config.warningThreshold) {
|
||||
return 0;
|
||||
} else if (utilization < this.config.criticalThreshold) {
|
||||
@@ -276,7 +276,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
latencyJitter: 0,
|
||||
utilization: 0
|
||||
};
|
||||
|
||||
|
||||
this.samples.length = 0;
|
||||
this.cumulativeBytesIn = 0;
|
||||
this.cumulativeBytesOut = 0;
|
||||
@@ -290,7 +290,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
*/
|
||||
public updateConfig(newConfig: Partial<BandwidthMonitorConfig>): void {
|
||||
Object.assign(this.config, newConfig);
|
||||
|
||||
|
||||
if (newConfig.monitorInterval !== undefined) {
|
||||
this.restartMonitoring();
|
||||
}
|
||||
@@ -312,7 +312,7 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (this.monitorTimer) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.monitorTimer = setInterval(() => {
|
||||
this.updateStats();
|
||||
}, this.config.monitorInterval);
|
||||
@@ -342,16 +342,16 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
private updateStats(): void {
|
||||
const now = Date.now();
|
||||
const deltaTime = (now - this.lastStatsTime) / 1000; // 转换为秒
|
||||
|
||||
|
||||
if (deltaTime <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 计算当前速率
|
||||
const currentUpload = this.cumulativeBytesOut / deltaTime;
|
||||
const currentDownload = this.cumulativeBytesIn / deltaTime;
|
||||
const currentPacketRate = (this.cumulativePacketsIn + this.cumulativePacketsOut) / deltaTime;
|
||||
|
||||
|
||||
// 创建新样本
|
||||
const sample: BandwidthSample = {
|
||||
timestamp: now,
|
||||
@@ -361,47 +361,47 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
packetsOut: this.cumulativePacketsOut,
|
||||
latency: 0 // 需要从外部提供
|
||||
};
|
||||
|
||||
|
||||
this.samples.push(sample);
|
||||
|
||||
|
||||
// 限制样本数量
|
||||
if (this.samples.length > this.config.sampleWindowSize) {
|
||||
this.samples.shift();
|
||||
}
|
||||
|
||||
|
||||
// 更新统计信息
|
||||
this.stats.currentUpload = currentUpload;
|
||||
this.stats.currentDownload = currentDownload;
|
||||
this.stats.currentPacketRate = currentPacketRate;
|
||||
|
||||
|
||||
// 更新峰值
|
||||
this.stats.peakUpload = Math.max(this.stats.peakUpload, currentUpload);
|
||||
this.stats.peakDownload = Math.max(this.stats.peakDownload, currentDownload);
|
||||
|
||||
|
||||
// 计算平均值
|
||||
this.calculateAverages();
|
||||
|
||||
|
||||
// 计算利用率
|
||||
this.calculateUtilization();
|
||||
|
||||
|
||||
// 检查限制
|
||||
this.checkLimits();
|
||||
|
||||
|
||||
// 检查警告级别
|
||||
this.checkWarningLevel();
|
||||
|
||||
|
||||
// 自适应调整
|
||||
if (this.config.enableAdaptive) {
|
||||
this.performAdaptiveAdjustment();
|
||||
}
|
||||
|
||||
|
||||
// 重置累计值
|
||||
this.cumulativeBytesIn = 0;
|
||||
this.cumulativeBytesOut = 0;
|
||||
this.cumulativePacketsIn = 0;
|
||||
this.cumulativePacketsOut = 0;
|
||||
this.lastStatsTime = now;
|
||||
|
||||
|
||||
// 发出事件
|
||||
this.emit('bandwidthChanged', this.stats);
|
||||
}
|
||||
@@ -413,30 +413,30 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (this.samples.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let totalUpload = 0;
|
||||
let totalDownload = 0;
|
||||
let totalLatency = 0;
|
||||
|
||||
|
||||
for (let i = 1; i < this.samples.length; i++) {
|
||||
const prev = this.samples[i - 1];
|
||||
const curr = this.samples[i];
|
||||
const deltaTime = (curr.timestamp - prev.timestamp) / 1000;
|
||||
|
||||
|
||||
if (deltaTime > 0) {
|
||||
totalUpload += (curr.bytesOut - prev.bytesOut) / deltaTime;
|
||||
totalDownload += (curr.bytesIn - prev.bytesIn) / deltaTime;
|
||||
totalLatency += curr.latency;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const sampleCount = this.samples.length - 1;
|
||||
if (sampleCount > 0) {
|
||||
this.stats.averageUpload = totalUpload / sampleCount;
|
||||
this.stats.averageDownload = totalDownload / sampleCount;
|
||||
this.stats.averageLatency = totalLatency / this.samples.length;
|
||||
}
|
||||
|
||||
|
||||
// 计算延迟抖动
|
||||
this.calculateLatencyJitter();
|
||||
}
|
||||
@@ -448,16 +448,16 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (this.samples.length < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let jitterSum = 0;
|
||||
let jitterCount = 0;
|
||||
|
||||
|
||||
for (let i = 1; i < this.samples.length; i++) {
|
||||
const diff = Math.abs(this.samples[i].latency - this.samples[i - 1].latency);
|
||||
jitterSum += diff;
|
||||
jitterCount++;
|
||||
}
|
||||
|
||||
|
||||
this.stats.latencyJitter = jitterCount > 0 ? jitterSum / jitterCount : 0;
|
||||
}
|
||||
|
||||
@@ -469,10 +469,10 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
this.stats.utilization = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const uploadUtilization = this.stats.currentUpload / this.limits.uploadLimit;
|
||||
const downloadUtilization = this.stats.currentDownload / this.limits.downloadLimit;
|
||||
|
||||
|
||||
this.stats.utilization = Math.max(uploadUtilization, downloadUtilization);
|
||||
}
|
||||
|
||||
@@ -483,11 +483,11 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (!this.limits.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (this.stats.currentUpload > this.limits.uploadLimit) {
|
||||
this.emit('limitExceeded', 'upload', this.stats.currentUpload, this.limits.uploadLimit);
|
||||
}
|
||||
|
||||
|
||||
if (this.stats.currentDownload > this.limits.downloadLimit) {
|
||||
this.emit('limitExceeded', 'download', this.stats.currentDownload, this.limits.downloadLimit);
|
||||
}
|
||||
@@ -498,13 +498,13 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
*/
|
||||
private checkWarningLevel(): void {
|
||||
let newLevel = BandwidthWarningLevel.Normal;
|
||||
|
||||
|
||||
if (this.stats.utilization >= this.config.criticalThreshold) {
|
||||
newLevel = BandwidthWarningLevel.Critical;
|
||||
} else if (this.stats.utilization >= this.config.warningThreshold) {
|
||||
newLevel = BandwidthWarningLevel.Warning;
|
||||
}
|
||||
|
||||
|
||||
if (newLevel !== this.currentWarningLevel) {
|
||||
this.currentWarningLevel = newLevel;
|
||||
this.emit('warningLevelChanged', newLevel, this.stats);
|
||||
@@ -518,9 +518,9 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
if (!this.limits.enabled || this.stats.utilization < this.config.warningThreshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const oldLimits = { ...this.limits };
|
||||
|
||||
|
||||
// 根据当前利用率动态调整限制
|
||||
if (this.stats.utilization > this.config.criticalThreshold) {
|
||||
// 严重超载,降低限制
|
||||
@@ -531,11 +531,11 @@ export class BandwidthMonitor extends EventEmitter {
|
||||
this.limits.uploadLimit *= (1 + this.config.adaptiveFactor * 0.5);
|
||||
this.limits.downloadLimit *= (1 + this.config.adaptiveFactor * 0.5);
|
||||
}
|
||||
|
||||
|
||||
// 检查是否有变化
|
||||
if (this.limits.uploadLimit !== oldLimits.uploadLimit ||
|
||||
if (this.limits.uploadLimit !== oldLimits.uploadLimit ||
|
||||
this.limits.downloadLimit !== oldLimits.downloadLimit) {
|
||||
this.emit('adaptiveAdjustment', oldLimits, this.limits);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* 监控模块导出
|
||||
*/
|
||||
|
||||
export * from './BandwidthMonitor';
|
||||
export * from './BandwidthMonitor';
|
||||
|
||||
@@ -59,7 +59,7 @@ class SnowflakeIdGenerator {
|
||||
private static readonly WORKER_ID_BITS = 5;
|
||||
private static readonly DATACENTER_ID_BITS = 5;
|
||||
private static readonly SEQUENCE_BITS = 12;
|
||||
|
||||
|
||||
private readonly workerId: number;
|
||||
private readonly datacenterId: number;
|
||||
private sequence = 0;
|
||||
@@ -107,16 +107,16 @@ export class MessageManager {
|
||||
private logger = createLogger('MessageManager');
|
||||
private config: MessageManagerConfig;
|
||||
private stats: MessageStats;
|
||||
|
||||
|
||||
// ID生成器
|
||||
private sequentialId = 0;
|
||||
private snowflakeGenerator: SnowflakeIdGenerator;
|
||||
|
||||
|
||||
// 消息去重和排序
|
||||
private recentMessageIds: Set<string> = new Set();
|
||||
private pendingMessages: Map<string, INetworkMessage> = new Map();
|
||||
private messageSequence: Map<string, number> = new Map();
|
||||
|
||||
|
||||
// 清理定时器
|
||||
private cleanupTimer?: NodeJS.Timeout;
|
||||
|
||||
@@ -201,7 +201,7 @@ export class MessageManager {
|
||||
*/
|
||||
validateMessage(message: INetworkMessage, senderId?: string): MessageValidationResult {
|
||||
this.stats.totalValidated++;
|
||||
|
||||
|
||||
const errors: string[] = [];
|
||||
const warnings: string[] = [];
|
||||
|
||||
@@ -233,12 +233,12 @@ export class MessageManager {
|
||||
if (this.config.enableTimestampValidation && message.timestamp) {
|
||||
const now = Date.now();
|
||||
const drift = Math.abs(now - message.timestamp);
|
||||
|
||||
|
||||
if (drift > this.config.maxTimestampDrift) {
|
||||
errors.push(`时间戳偏移过大: ${drift}ms > ${this.config.maxTimestampDrift}ms`);
|
||||
this.stats.timestampErrors++;
|
||||
}
|
||||
|
||||
|
||||
if (message.timestamp > now + 10000) { // 未来10秒以上
|
||||
warnings.push('消息时间戳来自未来');
|
||||
}
|
||||
@@ -255,7 +255,7 @@ export class MessageManager {
|
||||
}
|
||||
|
||||
const isValid = errors.length === 0;
|
||||
|
||||
|
||||
if (isValid) {
|
||||
this.stats.validMessages++;
|
||||
} else {
|
||||
@@ -279,11 +279,11 @@ export class MessageManager {
|
||||
|
||||
const senderId = message.senderId;
|
||||
const currentSequence = this.messageSequence.get(senderId) || 0;
|
||||
|
||||
|
||||
// 检查消息是否按顺序到达
|
||||
const messageTimestamp = message.timestamp;
|
||||
const expectedSequence = currentSequence + 1;
|
||||
|
||||
|
||||
// 简单的时间戳排序逻辑
|
||||
if (messageTimestamp >= expectedSequence) {
|
||||
// 消息按顺序到达
|
||||
@@ -293,13 +293,13 @@ export class MessageManager {
|
||||
// 消息乱序,暂存
|
||||
this.pendingMessages.set(message.messageId, message);
|
||||
this.stats.outOfOrderMessages++;
|
||||
|
||||
|
||||
// 检查是否超出最大待处理数量
|
||||
if (this.pendingMessages.size > this.config.maxPendingMessages) {
|
||||
this.logger.warn('待处理消息数量过多,清理旧消息');
|
||||
this.cleanupOldPendingMessages();
|
||||
}
|
||||
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -332,18 +332,18 @@ export class MessageManager {
|
||||
updateConfig(newConfig: Partial<MessageManagerConfig>): void {
|
||||
const oldConfig = { ...this.config };
|
||||
Object.assign(this.config, newConfig);
|
||||
|
||||
|
||||
// 如果去重配置改变,清理相关数据
|
||||
if (!this.config.enableMessageDeduplication && oldConfig.enableMessageDeduplication) {
|
||||
this.recentMessageIds.clear();
|
||||
}
|
||||
|
||||
|
||||
// 如果排序配置改变,清理相关数据
|
||||
if (!this.config.enableMessageOrdering && oldConfig.enableMessageOrdering) {
|
||||
this.pendingMessages.clear();
|
||||
this.messageSequence.clear();
|
||||
}
|
||||
|
||||
|
||||
this.logger.info('消息管理器配置已更新:', newConfig);
|
||||
}
|
||||
|
||||
@@ -355,7 +355,7 @@ export class MessageManager {
|
||||
clearInterval(this.cleanupTimer);
|
||||
this.cleanupTimer = undefined;
|
||||
}
|
||||
|
||||
|
||||
this.recentMessageIds.clear();
|
||||
this.pendingMessages.clear();
|
||||
this.messageSequence.clear();
|
||||
@@ -378,20 +378,20 @@ export class MessageManager {
|
||||
private flushPendingMessages(senderId: string): INetworkMessage[] {
|
||||
const flushedMessages: INetworkMessage[] = [];
|
||||
const messagesToRemove: string[] = [];
|
||||
|
||||
|
||||
for (const [messageId, message] of this.pendingMessages) {
|
||||
if (message.senderId === senderId) {
|
||||
flushedMessages.push(message);
|
||||
messagesToRemove.push(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 移除已处理的消息
|
||||
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
|
||||
|
||||
messagesToRemove.forEach((id) => this.pendingMessages.delete(id));
|
||||
|
||||
// 按时间戳排序
|
||||
flushedMessages.sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
|
||||
return flushedMessages;
|
||||
}
|
||||
|
||||
@@ -401,15 +401,15 @@ export class MessageManager {
|
||||
private cleanupOldPendingMessages(): void {
|
||||
const now = Date.now();
|
||||
const messagesToRemove: string[] = [];
|
||||
|
||||
|
||||
for (const [messageId, message] of this.pendingMessages) {
|
||||
if (now - message.timestamp > this.config.orderingWindowMs) {
|
||||
messagesToRemove.push(messageId);
|
||||
}
|
||||
}
|
||||
|
||||
messagesToRemove.forEach(id => this.pendingMessages.delete(id));
|
||||
|
||||
|
||||
messagesToRemove.forEach((id) => this.pendingMessages.delete(id));
|
||||
|
||||
if (messagesToRemove.length > 0) {
|
||||
this.logger.debug(`清理了 ${messagesToRemove.length} 个过期的待处理消息`);
|
||||
}
|
||||
@@ -429,7 +429,7 @@ export class MessageManager {
|
||||
*/
|
||||
private performCleanup(): void {
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
// 清理过期的消息ID(用于去重)
|
||||
if (this.config.enableMessageDeduplication) {
|
||||
// 由于Set没有时间戳,我们定期清理所有ID
|
||||
@@ -439,7 +439,7 @@ export class MessageManager {
|
||||
this.logger.debug('清理了过期的消息ID缓存');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清理过期的待处理消息
|
||||
if (this.config.enableMessageOrdering) {
|
||||
this.cleanupOldPendingMessages();
|
||||
@@ -453,7 +453,7 @@ export class MessageManager {
|
||||
const totalProcessed = this.stats.validMessages + this.stats.invalidMessages;
|
||||
const validRate = totalProcessed > 0 ? (this.stats.validMessages / totalProcessed) * 100 : 0;
|
||||
const duplicateRate = totalProcessed > 0 ? (this.stats.duplicateMessages / totalProcessed) * 100 : 0;
|
||||
|
||||
|
||||
return {
|
||||
stats: this.getStats(),
|
||||
validationRate: validRate,
|
||||
@@ -483,7 +483,7 @@ export class MessageManager {
|
||||
* 批量验证消息
|
||||
*/
|
||||
validateMessageBatch(messages: INetworkMessage[], senderId?: string): MessageValidationResult[] {
|
||||
return messages.map(message => this.validateMessage(message, senderId));
|
||||
return messages.map((message) => this.validateMessage(message, senderId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -499,4 +499,4 @@ export class MessageManager {
|
||||
isMessageExpired(message: INetworkMessage, maxAge: number = 300000): boolean {
|
||||
return this.getMessageAge(message) > maxAge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ export interface IErrorMessage extends INetworkMessage {
|
||||
/**
|
||||
* 消息类型联合
|
||||
*/
|
||||
export type NetworkMessage =
|
||||
export type NetworkMessage =
|
||||
| IConnectMessage
|
||||
| IConnectResponseMessage
|
||||
| IHeartbeatMessage
|
||||
@@ -280,4 +280,4 @@ export type NetworkMessage =
|
||||
| ILeaveRoomMessage
|
||||
| IRoomStateMessage
|
||||
| IGameEventMessage
|
||||
| IErrorMessage;
|
||||
| IErrorMessage;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createLogger } from '@esengine/ecs-framework';
|
||||
import { EventEmitter } from '../utils/EventEmitter';
|
||||
import {
|
||||
RpcCallRequest,
|
||||
RpcCallResponse,
|
||||
RpcError,
|
||||
import {
|
||||
RpcCallRequest,
|
||||
RpcCallResponse,
|
||||
RpcError,
|
||||
RpcErrorType,
|
||||
RpcStats,
|
||||
RpcMethodMetadata
|
||||
@@ -67,13 +67,13 @@ export class RpcCallHandler extends EventEmitter {
|
||||
private logger = createLogger('RpcCallHandler');
|
||||
private config: RpcCallHandlerConfig;
|
||||
private metadataManager: RpcMetadataManager;
|
||||
|
||||
|
||||
/** 当前活跃的调用 */
|
||||
private activeCalls = new Map<string, CallStats>();
|
||||
|
||||
|
||||
/** 速率限制器 */
|
||||
private rateLimiters = new Map<string, RateLimiter>();
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: RpcStats = {
|
||||
totalCalls: 0,
|
||||
@@ -85,10 +85,10 @@ export class RpcCallHandler extends EventEmitter {
|
||||
retryCount: 0,
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
|
||||
|
||||
/** 历史调用统计 */
|
||||
private callHistory: CallStats[] = [];
|
||||
|
||||
|
||||
/** 权限检查器 */
|
||||
private permissionChecker?: (methodName: string, senderId: string) => boolean;
|
||||
|
||||
@@ -97,7 +97,7 @@ export class RpcCallHandler extends EventEmitter {
|
||||
config: Partial<RpcCallHandlerConfig> = {}
|
||||
) {
|
||||
super();
|
||||
|
||||
|
||||
this.metadataManager = metadataManager;
|
||||
this.config = {
|
||||
maxConcurrentCalls: 100,
|
||||
@@ -128,11 +128,11 @@ export class RpcCallHandler extends EventEmitter {
|
||||
startTime,
|
||||
success: false
|
||||
};
|
||||
|
||||
|
||||
this.activeCalls.set(request.callId, callStats);
|
||||
this.stats.pendingCalls++;
|
||||
this.emit('callStarted', request);
|
||||
|
||||
|
||||
try {
|
||||
// 1. 检查并发限制
|
||||
if (this.activeCalls.size > this.config.maxConcurrentCalls) {
|
||||
@@ -141,7 +141,7 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`超过最大并发调用数限制: ${this.config.maxConcurrentCalls}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 2. 获取方法元数据
|
||||
const metadata = this.metadataManager.getMethodMetadata(request.methodName);
|
||||
if (!metadata) {
|
||||
@@ -150,21 +150,21 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`RPC方法不存在: ${request.methodName}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 3. 验证方法调用
|
||||
const validation = this.metadataManager.validateMethodCall(
|
||||
request.methodName,
|
||||
Array.from(request.args),
|
||||
request.senderId
|
||||
);
|
||||
|
||||
|
||||
if (!validation.valid) {
|
||||
throw this.createError(
|
||||
RpcErrorType.INVALID_ARGUMENTS,
|
||||
validation.error || '参数验证失败'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 4. 权限检查
|
||||
if (this.config.enablePermissionCheck && !this.checkPermission(metadata, request.senderId)) {
|
||||
this.emit('permissionDenied', request.methodName, request.senderId);
|
||||
@@ -173,7 +173,7 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`没有调用权限: ${request.methodName}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 5. 速率限制检查
|
||||
if (this.config.enableRateLimit && !this.checkRateLimit(metadata, request.senderId)) {
|
||||
this.emit('rateLimitExceeded', request.methodName, request.senderId);
|
||||
@@ -182,7 +182,7 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`调用频率超限: ${request.methodName}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 6. 执行方法调用
|
||||
const handler = this.metadataManager.getMethodHandler(request.methodName);
|
||||
if (!handler) {
|
||||
@@ -191,19 +191,19 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`方法处理器不存在: ${request.methodName}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// 创建带超时的Promise
|
||||
const timeout = request.options.timeout || metadata.options.timeout || this.config.defaultTimeout;
|
||||
const result = await this.executeWithTimeout(handler, request.args, timeout);
|
||||
|
||||
|
||||
// 7. 创建成功响应
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
|
||||
callStats.endTime = endTime;
|
||||
callStats.duration = duration;
|
||||
callStats.success = true;
|
||||
|
||||
|
||||
const response: RpcCallResponse<R> = {
|
||||
callId: request.callId,
|
||||
success: true,
|
||||
@@ -211,25 +211,25 @@ export class RpcCallHandler extends EventEmitter {
|
||||
timestamp: endTime,
|
||||
duration
|
||||
};
|
||||
|
||||
|
||||
this.updateStats(callStats);
|
||||
this.emit('callCompleted', request, response);
|
||||
|
||||
|
||||
return response;
|
||||
|
||||
|
||||
} catch (error) {
|
||||
// 8. 处理错误
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime;
|
||||
|
||||
const rpcError = error instanceof Error && 'type' in error
|
||||
|
||||
const rpcError = error instanceof Error && 'type' in error
|
||||
? error as RpcError
|
||||
: this.createError(RpcErrorType.SERVER_ERROR, String(error));
|
||||
|
||||
|
||||
callStats.endTime = endTime;
|
||||
callStats.duration = duration;
|
||||
callStats.error = rpcError;
|
||||
|
||||
|
||||
const response: RpcCallResponse<R> = {
|
||||
callId: request.callId,
|
||||
success: false,
|
||||
@@ -237,12 +237,12 @@ export class RpcCallHandler extends EventEmitter {
|
||||
timestamp: endTime,
|
||||
duration
|
||||
};
|
||||
|
||||
|
||||
this.updateStats(callStats);
|
||||
this.emit('callFailed', request, rpcError);
|
||||
|
||||
|
||||
return response;
|
||||
|
||||
|
||||
} finally {
|
||||
// 9. 清理
|
||||
this.activeCalls.delete(request.callId);
|
||||
@@ -270,13 +270,13 @@ export class RpcCallHandler extends EventEmitter {
|
||||
* 获取方法调用历史
|
||||
*/
|
||||
public getCallHistory(methodName?: string, limit: number = 100): CallStats[] {
|
||||
let history = [...this.callHistory];
|
||||
|
||||
const history = [...this.callHistory];
|
||||
|
||||
if (methodName) {
|
||||
// 这里需要扩展CallStats接口来包含methodName
|
||||
// 暂时返回所有历史
|
||||
}
|
||||
|
||||
|
||||
return history.slice(-limit);
|
||||
}
|
||||
|
||||
@@ -294,7 +294,7 @@ export class RpcCallHandler extends EventEmitter {
|
||||
retryCount: 0,
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
|
||||
|
||||
this.callHistory.length = 0;
|
||||
this.rateLimiters.clear();
|
||||
}
|
||||
@@ -324,12 +324,12 @@ export class RpcCallHandler extends EventEmitter {
|
||||
if (!metadata.options.requireAuth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 使用自定义权限检查器
|
||||
if (this.permissionChecker) {
|
||||
return this.permissionChecker(metadata.methodName, senderId);
|
||||
}
|
||||
|
||||
|
||||
// 默认:需要认证但没有检查器,拒绝访问
|
||||
return false;
|
||||
}
|
||||
@@ -342,10 +342,10 @@ export class RpcCallHandler extends EventEmitter {
|
||||
if (!rateLimit) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const key = `${senderId}:${metadata.methodName}`;
|
||||
let limiter = this.rateLimiters.get(key);
|
||||
|
||||
|
||||
if (!limiter) {
|
||||
limiter = {
|
||||
calls: [],
|
||||
@@ -354,17 +354,17 @@ export class RpcCallHandler extends EventEmitter {
|
||||
};
|
||||
this.rateLimiters.set(key, limiter);
|
||||
}
|
||||
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
// 清理过期的调用记录
|
||||
limiter.calls = limiter.calls.filter(time => now - time < limiter.window);
|
||||
|
||||
limiter.calls = limiter.calls.filter((time) => now - time < limiter.window);
|
||||
|
||||
// 检查是否超限
|
||||
if (limiter.calls.length >= limiter.limit) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 记录本次调用
|
||||
limiter.calls.push(now);
|
||||
return true;
|
||||
@@ -385,13 +385,13 @@ export class RpcCallHandler extends EventEmitter {
|
||||
`方法调用超时: ${timeout}ms`
|
||||
));
|
||||
}, timeout);
|
||||
|
||||
|
||||
Promise.resolve(handler(...args))
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
clearTimeout(timer);
|
||||
resolve(result);
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
clearTimeout(timer);
|
||||
reject(error);
|
||||
});
|
||||
@@ -414,17 +414,17 @@ export class RpcCallHandler extends EventEmitter {
|
||||
*/
|
||||
private updateStats(callStats: CallStats): void {
|
||||
this.stats.totalCalls++;
|
||||
|
||||
|
||||
if (callStats.success) {
|
||||
this.stats.successfulCalls++;
|
||||
} else {
|
||||
this.stats.failedCalls++;
|
||||
|
||||
|
||||
if (callStats.error?.type === RpcErrorType.TIMEOUT) {
|
||||
this.stats.timeoutCalls++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新平均响应时间
|
||||
if (callStats.duration !== undefined) {
|
||||
const totalTime = this.stats.averageResponseTime * (this.stats.totalCalls - 1) + callStats.duration;
|
||||
@@ -439,16 +439,16 @@ export class RpcCallHandler extends EventEmitter {
|
||||
if (!this.config.enablePerformanceMonitoring) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.callHistory.push(callStats);
|
||||
|
||||
|
||||
// 清理过期的历史记录
|
||||
const cutoffTime = Date.now() - this.config.statsRetentionTime;
|
||||
this.callHistory = this.callHistory.filter(stats => stats.startTime > cutoffTime);
|
||||
|
||||
this.callHistory = this.callHistory.filter((stats) => stats.startTime > cutoffTime);
|
||||
|
||||
// 限制历史记录数量
|
||||
if (this.callHistory.length > 10000) {
|
||||
this.callHistory = this.callHistory.slice(-5000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { createLogger } from '@esengine/ecs-framework';
|
||||
import { EventEmitter } from '../utils/EventEmitter';
|
||||
import {
|
||||
RpcCallRequest,
|
||||
RpcCallResponse,
|
||||
RpcError,
|
||||
import {
|
||||
RpcCallRequest,
|
||||
RpcCallResponse,
|
||||
RpcError,
|
||||
RpcErrorType,
|
||||
RpcCallInfo,
|
||||
RpcCallStatus,
|
||||
@@ -62,16 +62,16 @@ export class RpcCallProxy extends EventEmitter {
|
||||
private logger = createLogger('RpcCallProxy');
|
||||
private config: RpcCallProxyConfig;
|
||||
private networkSender: NetworkSender;
|
||||
|
||||
|
||||
/** 待处理的调用 */
|
||||
private pendingCalls = new Map<string, RpcCallInfo>();
|
||||
|
||||
|
||||
/** 离线队列 */
|
||||
private offlineQueue: RpcCallRequest[] = [];
|
||||
|
||||
|
||||
/** 是否在线 */
|
||||
private isOnline = true;
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: RpcStats = {
|
||||
totalCalls: 0,
|
||||
@@ -83,7 +83,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
retryCount: 0,
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
|
||||
|
||||
/** 重试定时器 */
|
||||
private retryTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
@@ -92,7 +92,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
config: Partial<RpcCallProxyConfig> = {}
|
||||
) {
|
||||
super();
|
||||
|
||||
|
||||
this.networkSender = networkSender;
|
||||
this.config = {
|
||||
defaultTimeout: 30000,
|
||||
@@ -149,7 +149,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
): Promise<TReturn> {
|
||||
const callId = this.config.generateCallId!();
|
||||
const timeout = options.timeout || this.config.defaultTimeout;
|
||||
|
||||
|
||||
const request: RpcCallRequest<TArgs> = {
|
||||
callId,
|
||||
methodName,
|
||||
@@ -164,7 +164,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
...options
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 创建Promise和调用信息
|
||||
return new Promise<TReturn>((resolve, reject) => {
|
||||
const callInfo: RpcCallInfo<TArgs> = {
|
||||
@@ -175,16 +175,16 @@ export class RpcCallProxy extends EventEmitter {
|
||||
retryCount: 0,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
|
||||
this.pendingCalls.set(callId, callInfo);
|
||||
this.stats.pendingCalls++;
|
||||
this.stats.totalCalls++;
|
||||
|
||||
|
||||
// 设置超时
|
||||
setTimeout(() => {
|
||||
this.handleTimeout(callId);
|
||||
}, timeout);
|
||||
|
||||
|
||||
// 发送调用
|
||||
this.sendCall(callInfo);
|
||||
});
|
||||
@@ -199,18 +199,18 @@ export class RpcCallProxy extends EventEmitter {
|
||||
this.logger.warn(`收到未知调用的响应: ${response.callId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 清理定时器
|
||||
const timer = this.retryTimers.get(response.callId);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
this.retryTimers.delete(response.callId);
|
||||
}
|
||||
|
||||
|
||||
// 更新状态
|
||||
callInfo.status = response.success ? RpcCallStatus.COMPLETED : RpcCallStatus.FAILED;
|
||||
callInfo.completedAt = Date.now();
|
||||
|
||||
|
||||
// 更新统计
|
||||
if (response.success) {
|
||||
this.stats.successfulCalls++;
|
||||
@@ -218,9 +218,9 @@ export class RpcCallProxy extends EventEmitter {
|
||||
} else {
|
||||
this.stats.failedCalls++;
|
||||
}
|
||||
|
||||
|
||||
this.stats.pendingCalls--;
|
||||
|
||||
|
||||
// 处理结果
|
||||
if (response.success) {
|
||||
callInfo.resolve!(response.result);
|
||||
@@ -228,7 +228,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
callInfo.reject!(response.error!);
|
||||
this.emit('callFailed', response.callId, response.error!);
|
||||
}
|
||||
|
||||
|
||||
// 清理
|
||||
this.pendingCalls.delete(response.callId);
|
||||
this.emit('responseReceived', response);
|
||||
@@ -240,7 +240,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
public setOnlineStatus(online: boolean): void {
|
||||
const wasOnline = this.isOnline;
|
||||
this.isOnline = online;
|
||||
|
||||
|
||||
if (online && !wasOnline) {
|
||||
// 从离线状态恢复,处理离线队列
|
||||
this.processOfflineQueue();
|
||||
@@ -255,27 +255,27 @@ export class RpcCallProxy extends EventEmitter {
|
||||
if (!callInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 清理定时器
|
||||
const timer = this.retryTimers.get(callId);
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
this.retryTimers.delete(callId);
|
||||
}
|
||||
|
||||
|
||||
// 更新状态
|
||||
callInfo.status = RpcCallStatus.CANCELLED;
|
||||
|
||||
|
||||
// 拒绝Promise
|
||||
callInfo.reject!({
|
||||
type: RpcErrorType.CLIENT_ERROR,
|
||||
message: '调用被取消'
|
||||
});
|
||||
|
||||
|
||||
// 清理
|
||||
this.pendingCalls.delete(callId);
|
||||
this.stats.pendingCalls--;
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -326,16 +326,16 @@ export class RpcCallProxy extends EventEmitter {
|
||||
for (const callId of pendingCallIds) {
|
||||
this.cancelCall(callId);
|
||||
}
|
||||
|
||||
|
||||
// 清理定时器
|
||||
for (const timer of this.retryTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
this.retryTimers.clear();
|
||||
|
||||
|
||||
// 清理队列
|
||||
this.offlineQueue.length = 0;
|
||||
|
||||
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 构建网络消息
|
||||
const message = {
|
||||
type: MessageType.RPC_CALL,
|
||||
@@ -364,18 +364,18 @@ export class RpcCallProxy extends EventEmitter {
|
||||
reliable: callInfo.request.options.reliable,
|
||||
priority: callInfo.request.options.priority
|
||||
};
|
||||
|
||||
|
||||
// 发送消息
|
||||
await this.networkSender.sendMessage(message);
|
||||
|
||||
|
||||
callInfo.status = RpcCallStatus.SENT;
|
||||
callInfo.sentAt = Date.now();
|
||||
|
||||
|
||||
this.emit('callSent', callInfo.request);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`发送RPC调用失败: ${callInfo.request.methodName}`, error);
|
||||
|
||||
|
||||
// 检查是否可以重试
|
||||
if (callInfo.retryCount < this.config.maxRetries) {
|
||||
this.scheduleRetry(callInfo);
|
||||
@@ -396,7 +396,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
if (!callInfo || callInfo.status === RpcCallStatus.COMPLETED) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 检查是否可以重试
|
||||
if (callInfo.retryCount < this.config.maxRetries) {
|
||||
this.scheduleRetry(callInfo);
|
||||
@@ -416,20 +416,20 @@ export class RpcCallProxy extends EventEmitter {
|
||||
private scheduleRetry<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>): void {
|
||||
callInfo.retryCount++;
|
||||
this.stats.retryCount++;
|
||||
|
||||
|
||||
// 计算延迟时间(指数退避)
|
||||
const baseDelay = this.config.retryDelayBase * Math.pow(this.config.retryDelayMultiplier, callInfo.retryCount - 1);
|
||||
const delay = Math.min(baseDelay, this.config.maxRetryDelay);
|
||||
|
||||
|
||||
callInfo.nextRetryTime = Date.now() + delay;
|
||||
|
||||
|
||||
this.emit('retryAttempt', callInfo.request.callId, callInfo.retryCount);
|
||||
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
this.retryTimers.delete(callInfo.request.callId);
|
||||
this.sendCall(callInfo);
|
||||
}, delay);
|
||||
|
||||
|
||||
this.retryTimers.set(callInfo.request.callId, timer);
|
||||
}
|
||||
|
||||
@@ -439,13 +439,13 @@ export class RpcCallProxy extends EventEmitter {
|
||||
private handleCallFailure<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>, error: RpcError): void {
|
||||
callInfo.status = RpcCallStatus.FAILED;
|
||||
callInfo.completedAt = Date.now();
|
||||
|
||||
|
||||
callInfo.reject!(error);
|
||||
|
||||
|
||||
this.pendingCalls.delete(callInfo.request.callId);
|
||||
this.stats.pendingCalls--;
|
||||
this.stats.failedCalls++;
|
||||
|
||||
|
||||
this.emit('callFailed', callInfo.request.callId, error);
|
||||
}
|
||||
|
||||
@@ -457,7 +457,7 @@ export class RpcCallProxy extends EventEmitter {
|
||||
// 移除最旧的请求
|
||||
this.offlineQueue.shift();
|
||||
}
|
||||
|
||||
|
||||
this.offlineQueue.push(request);
|
||||
}
|
||||
|
||||
@@ -468,10 +468,10 @@ export class RpcCallProxy extends EventEmitter {
|
||||
if (!this.isOnline || this.offlineQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const queue = [...this.offlineQueue];
|
||||
this.offlineQueue.length = 0;
|
||||
|
||||
|
||||
for (const request of queue) {
|
||||
try {
|
||||
// 重新创建调用信息
|
||||
@@ -481,10 +481,10 @@ export class RpcCallProxy extends EventEmitter {
|
||||
retryCount: 0,
|
||||
createdAt: Date.now()
|
||||
};
|
||||
|
||||
|
||||
this.pendingCalls.set(request.callId, callInfo);
|
||||
await this.sendCall(callInfo);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`处理离线队列失败: ${request.methodName}`, error);
|
||||
}
|
||||
@@ -497,8 +497,8 @@ export class RpcCallProxy extends EventEmitter {
|
||||
private updateAverageResponseTime(responseTime: number): void {
|
||||
const totalResponses = this.stats.successfulCalls;
|
||||
const currentAverage = this.stats.averageResponseTime;
|
||||
|
||||
this.stats.averageResponseTime =
|
||||
|
||||
this.stats.averageResponseTime =
|
||||
(currentAverage * (totalResponses - 1) + responseTime) / totalResponses;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,16 +19,16 @@ export interface RpcMetadataManagerEvents {
|
||||
*/
|
||||
export class RpcMetadataManager extends EventEmitter {
|
||||
private logger = createLogger('RpcMetadataManager');
|
||||
|
||||
|
||||
/** 方法注册表 */
|
||||
private registry: RpcMethodRegistry = new Map();
|
||||
|
||||
|
||||
/** 类到方法的映射 */
|
||||
private classMethods = new Map<string, Set<string>>();
|
||||
|
||||
|
||||
/** 方法名到类的映射 */
|
||||
private methodToClass = new Map<string, string>();
|
||||
|
||||
|
||||
/** 实例缓存 */
|
||||
private instances = new Map<string, object>();
|
||||
|
||||
@@ -37,15 +37,15 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
*/
|
||||
public registerClass(instance: object): void {
|
||||
const className = instance.constructor.name;
|
||||
|
||||
|
||||
try {
|
||||
const rpcMethods = getRpcMethods(instance.constructor as Function);
|
||||
|
||||
|
||||
if (rpcMethods.length === 0) {
|
||||
this.logger.warn(`类 ${className} 没有RPC方法`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 验证所有方法定义
|
||||
for (const metadata of rpcMethods) {
|
||||
const validation = RpcMethodValidator.validateMethodDefinition(metadata);
|
||||
@@ -53,44 +53,44 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
throw new Error(`${className}.${metadata.methodName}: ${validation.error}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 注册方法
|
||||
const methodNames = new Set<string>();
|
||||
|
||||
|
||||
for (const metadata of rpcMethods) {
|
||||
const fullMethodName = `${className}.${metadata.methodName}`;
|
||||
|
||||
|
||||
// 检查方法是否已存在
|
||||
if (this.registry.has(fullMethodName)) {
|
||||
throw new Error(`RPC方法已存在: ${fullMethodName}`);
|
||||
}
|
||||
|
||||
|
||||
// 获取实际方法处理器
|
||||
const handler = (instance as Record<string, unknown>)[metadata.methodName];
|
||||
if (typeof handler !== 'function') {
|
||||
throw new Error(`方法不存在或不是函数: ${fullMethodName}`);
|
||||
}
|
||||
|
||||
|
||||
// 注册方法
|
||||
this.registry.set(fullMethodName, {
|
||||
metadata,
|
||||
handler: handler.bind(instance)
|
||||
});
|
||||
|
||||
|
||||
methodNames.add(metadata.methodName);
|
||||
this.methodToClass.set(fullMethodName, className);
|
||||
|
||||
|
||||
this.logger.debug(`已注册RPC方法: ${fullMethodName}`);
|
||||
this.emit('methodRegistered', metadata);
|
||||
}
|
||||
|
||||
|
||||
// 更新类映射
|
||||
this.classMethods.set(className, methodNames);
|
||||
this.instances.set(className, instance);
|
||||
|
||||
|
||||
this.logger.info(`已注册RPC类: ${className},方法数: ${methodNames.size}`);
|
||||
this.emit('classRegistered', className, methodNames.size);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`注册RPC类失败: ${className}`, error);
|
||||
throw error;
|
||||
@@ -101,16 +101,16 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
* 注销RPC类
|
||||
*/
|
||||
public unregisterClass(classNameOrInstance: string | object): void {
|
||||
const className = typeof classNameOrInstance === 'string'
|
||||
? classNameOrInstance
|
||||
const className = typeof classNameOrInstance === 'string'
|
||||
? classNameOrInstance
|
||||
: classNameOrInstance.constructor.name;
|
||||
|
||||
|
||||
const methodNames = this.classMethods.get(className);
|
||||
if (!methodNames) {
|
||||
this.logger.warn(`RPC类未注册: ${className}`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 移除所有方法
|
||||
for (const methodName of methodNames) {
|
||||
const fullMethodName = `${className}.${methodName}`;
|
||||
@@ -118,11 +118,11 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
this.methodToClass.delete(fullMethodName);
|
||||
this.emit('methodUnregistered', fullMethodName);
|
||||
}
|
||||
|
||||
|
||||
// 清理映射
|
||||
this.classMethods.delete(className);
|
||||
this.instances.delete(className);
|
||||
|
||||
|
||||
this.logger.info(`已注销RPC类: ${className}`);
|
||||
this.emit('classUnregistered', className);
|
||||
}
|
||||
@@ -155,13 +155,13 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
*/
|
||||
public getServerRpcMethods(): RpcMethodMetadata[] {
|
||||
const methods: RpcMethodMetadata[] = [];
|
||||
|
||||
|
||||
for (const [, entry] of this.registry) {
|
||||
if (entry.metadata.isServerRpc) {
|
||||
methods.push(entry.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
@@ -170,13 +170,13 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
*/
|
||||
public getClientRpcMethods(): RpcMethodMetadata[] {
|
||||
const methods: RpcMethodMetadata[] = [];
|
||||
|
||||
|
||||
for (const [, entry] of this.registry) {
|
||||
if (!entry.metadata.isServerRpc) {
|
||||
methods.push(entry.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
@@ -188,9 +188,9 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
if (!methodNames) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
const methods: RpcMethodMetadata[] = [];
|
||||
|
||||
|
||||
for (const methodName of methodNames) {
|
||||
const fullMethodName = `${className}.${methodName}`;
|
||||
const entry = this.registry.get(fullMethodName);
|
||||
@@ -198,7 +198,7 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
methods.push(entry.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
@@ -238,10 +238,10 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
serverRpcMethods: number;
|
||||
clientRpcMethods: number;
|
||||
registeredClasses: number;
|
||||
} {
|
||||
} {
|
||||
let serverRpcCount = 0;
|
||||
let clientRpcCount = 0;
|
||||
|
||||
|
||||
for (const [, entry] of this.registry) {
|
||||
if (entry.metadata.isServerRpc) {
|
||||
serverRpcCount++;
|
||||
@@ -249,7 +249,7 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
clientRpcCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
totalMethods: this.registry.size,
|
||||
serverRpcMethods: serverRpcCount,
|
||||
@@ -273,7 +273,7 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
error: `RPC方法不存在: ${methodName}`
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return RpcMethodValidator.validateCall(metadata, args, callerId);
|
||||
}
|
||||
|
||||
@@ -287,33 +287,33 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
target?: string;
|
||||
}): RpcMethodMetadata[] {
|
||||
const results: RpcMethodMetadata[] = [];
|
||||
|
||||
|
||||
for (const [methodName, entry] of this.registry) {
|
||||
const metadata = entry.metadata;
|
||||
|
||||
|
||||
// 类名过滤
|
||||
if (query.className && metadata.className !== query.className) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// RPC类型过滤
|
||||
if (query.isServerRpc !== undefined && metadata.isServerRpc !== query.isServerRpc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 认证要求过滤
|
||||
if (query.requireAuth !== undefined && metadata.options.requireAuth !== query.requireAuth) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 目标过滤
|
||||
if (query.target && metadata.options.target !== query.target) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
results.push(metadata);
|
||||
}
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -322,16 +322,16 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
*/
|
||||
public clear(): void {
|
||||
const classNames = Array.from(this.classMethods.keys());
|
||||
|
||||
|
||||
for (const className of classNames) {
|
||||
this.unregisterClass(className);
|
||||
}
|
||||
|
||||
|
||||
this.registry.clear();
|
||||
this.classMethods.clear();
|
||||
this.methodToClass.clear();
|
||||
this.instances.clear();
|
||||
|
||||
|
||||
this.logger.info('已清空所有RPC注册');
|
||||
}
|
||||
|
||||
@@ -342,4 +342,4 @@ export class RpcMetadataManager extends EventEmitter {
|
||||
this.clear();
|
||||
this.removeAllListeners();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,25 +106,25 @@ export interface RpcReliabilityManagerEvents {
|
||||
export class RpcReliabilityManager extends EventEmitter {
|
||||
private logger = createLogger('RpcReliabilityManager');
|
||||
private config: RpcReliabilityConfig;
|
||||
|
||||
|
||||
/** 重复调用记录 */
|
||||
private duplicateRecords = new Map<string, DuplicateCallRecord>();
|
||||
|
||||
|
||||
/** 活跃事务 */
|
||||
private transactions = new Map<string, TransactionInfo>();
|
||||
|
||||
|
||||
/** 顺序执行队列(按发送者分组) */
|
||||
private orderedQueues = new Map<string, OrderedQueueItem[]>();
|
||||
|
||||
|
||||
/** 正在处理的有序调用 */
|
||||
private processingOrdered = new Set<string>();
|
||||
|
||||
|
||||
/** 清理定时器 */
|
||||
private cleanupTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
constructor(config: Partial<RpcReliabilityConfig> = {}) {
|
||||
super();
|
||||
|
||||
|
||||
this.config = {
|
||||
idempotency: {
|
||||
enabled: true,
|
||||
@@ -146,7 +146,7 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
...config.transaction
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
this.startCleanupTimer();
|
||||
}
|
||||
|
||||
@@ -161,18 +161,18 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!this.config.idempotency.enabled) {
|
||||
return { isDuplicate: false, shouldProcess: true };
|
||||
}
|
||||
|
||||
|
||||
const key = `${request.senderId}:${request.callId}`;
|
||||
const existing = this.duplicateRecords.get(key);
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
if (existing) {
|
||||
// 更新重复调用记录
|
||||
existing.lastCallTime = now;
|
||||
existing.callCount++;
|
||||
|
||||
|
||||
this.emit('duplicateCallDetected', existing);
|
||||
|
||||
|
||||
// 如果已有响应,直接返回
|
||||
if (existing.response) {
|
||||
return {
|
||||
@@ -181,7 +181,7 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
shouldProcess: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 如果在检查窗口内,认为是重复调用但还在处理中
|
||||
if (now - existing.firstCallTime < this.config.idempotency.checkWindowTime) {
|
||||
return {
|
||||
@@ -190,7 +190,7 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 记录新的调用
|
||||
const record: DuplicateCallRecord = {
|
||||
callId: request.callId,
|
||||
@@ -200,9 +200,9 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
lastCallTime: now,
|
||||
callCount: 1
|
||||
};
|
||||
|
||||
|
||||
this.duplicateRecords.set(key, record);
|
||||
|
||||
|
||||
return { isDuplicate: false, shouldProcess: true };
|
||||
}
|
||||
|
||||
@@ -213,10 +213,10 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!this.config.idempotency.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const key = `${request.senderId}:${request.callId}`;
|
||||
const record = this.duplicateRecords.get(key);
|
||||
|
||||
|
||||
if (record) {
|
||||
record.response = response;
|
||||
}
|
||||
@@ -232,9 +232,9 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!this.config.orderedExecution.enabled) {
|
||||
return handler();
|
||||
}
|
||||
|
||||
|
||||
const senderId = request.senderId;
|
||||
|
||||
|
||||
return new Promise<RpcCallResponse>((resolve, reject) => {
|
||||
const queueItem: OrderedQueueItem = {
|
||||
request,
|
||||
@@ -243,14 +243,14 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
reject,
|
||||
enqueuedAt: Date.now()
|
||||
};
|
||||
|
||||
|
||||
// 获取或创建队列
|
||||
let queue = this.orderedQueues.get(senderId);
|
||||
if (!queue) {
|
||||
queue = [];
|
||||
this.orderedQueues.set(senderId, queue);
|
||||
}
|
||||
|
||||
|
||||
// 检查队列大小
|
||||
if (queue.length >= this.config.orderedExecution.maxQueueSize) {
|
||||
reject({
|
||||
@@ -259,10 +259,10 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
queue.push(queueItem);
|
||||
this.emit('orderedCallQueued', request.callId, queue.length);
|
||||
|
||||
|
||||
// 如果没有正在处理的调用,开始处理
|
||||
if (!this.processingOrdered.has(senderId)) {
|
||||
this.processOrderedQueue(senderId);
|
||||
@@ -277,15 +277,15 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!this.config.transaction.enabled) {
|
||||
throw new Error('事务功能未启用');
|
||||
}
|
||||
|
||||
|
||||
if (this.transactions.has(transactionId)) {
|
||||
throw new Error(`事务已存在: ${transactionId}`);
|
||||
}
|
||||
|
||||
|
||||
if (this.transactions.size >= this.config.transaction.maxTransactions) {
|
||||
throw new Error('超过最大事务数量限制');
|
||||
}
|
||||
|
||||
|
||||
const transaction: TransactionInfo = {
|
||||
transactionId,
|
||||
calls: [],
|
||||
@@ -293,10 +293,10 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
status: 'pending',
|
||||
rollbackActions: []
|
||||
};
|
||||
|
||||
|
||||
this.transactions.set(transactionId, transaction);
|
||||
this.emit('transactionStarted', transactionId);
|
||||
|
||||
|
||||
// 设置事务超时
|
||||
setTimeout(() => {
|
||||
if (this.transactions.has(transactionId)) {
|
||||
@@ -317,13 +317,13 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!transaction) {
|
||||
throw new Error(`事务不存在: ${transactionId}`);
|
||||
}
|
||||
|
||||
|
||||
if (transaction.status !== 'pending') {
|
||||
throw new Error(`事务状态无效: ${transaction.status}`);
|
||||
}
|
||||
|
||||
|
||||
transaction.calls.push(request);
|
||||
|
||||
|
||||
if (rollbackAction) {
|
||||
transaction.rollbackActions.push(rollbackAction);
|
||||
}
|
||||
@@ -337,14 +337,14 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!transaction) {
|
||||
throw new Error(`事务不存在: ${transactionId}`);
|
||||
}
|
||||
|
||||
|
||||
if (transaction.status !== 'pending') {
|
||||
throw new Error(`事务状态无效: ${transaction.status}`);
|
||||
}
|
||||
|
||||
|
||||
transaction.status = 'committed';
|
||||
this.transactions.delete(transactionId);
|
||||
|
||||
|
||||
this.emit('transactionCommitted', transactionId);
|
||||
this.logger.info(`事务已提交: ${transactionId},包含 ${transaction.calls.length} 个调用`);
|
||||
}
|
||||
@@ -357,13 +357,13 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
if (!transaction) {
|
||||
throw new Error(`事务不存在: ${transactionId}`);
|
||||
}
|
||||
|
||||
|
||||
if (transaction.status !== 'pending') {
|
||||
return; // 已经处理过
|
||||
}
|
||||
|
||||
|
||||
transaction.status = 'rolledback';
|
||||
|
||||
|
||||
// 执行回滚操作
|
||||
for (const rollbackAction of transaction.rollbackActions.reverse()) {
|
||||
try {
|
||||
@@ -372,9 +372,9 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
this.logger.error(`回滚操作失败: ${transactionId}`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.transactions.delete(transactionId);
|
||||
|
||||
|
||||
this.emit('transactionRolledback', transactionId, reason);
|
||||
this.logger.warn(`事务已回滚: ${transactionId},原因: ${reason}`);
|
||||
}
|
||||
@@ -394,12 +394,12 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
activeTransactions: number;
|
||||
totalQueuedCalls: number;
|
||||
processingQueues: number;
|
||||
} {
|
||||
} {
|
||||
let totalQueuedCalls = 0;
|
||||
for (const queue of this.orderedQueues.values()) {
|
||||
totalQueuedCalls += queue.length;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
duplicateRecords: this.duplicateRecords.size,
|
||||
activeTransactions: this.transactions.size,
|
||||
@@ -432,15 +432,15 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
clearInterval(this.cleanupTimer);
|
||||
this.cleanupTimer = null;
|
||||
}
|
||||
|
||||
|
||||
// 回滚所有活跃事务
|
||||
const transactionIds = Array.from(this.transactions.keys());
|
||||
for (const transactionId of transactionIds) {
|
||||
this.rollbackTransaction(transactionId, '管理器销毁').catch(error => {
|
||||
this.rollbackTransaction(transactionId, '管理器销毁').catch((error) => {
|
||||
this.logger.error(`销毁时回滚事务失败: ${transactionId}`, error);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 清理队列
|
||||
for (const queue of this.orderedQueues.values()) {
|
||||
for (const item of queue) {
|
||||
@@ -450,12 +450,12 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.duplicateRecords.clear();
|
||||
this.transactions.clear();
|
||||
this.orderedQueues.clear();
|
||||
this.processingOrdered.clear();
|
||||
|
||||
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
@@ -464,17 +464,17 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
*/
|
||||
private async processOrderedQueue(senderId: string): Promise<void> {
|
||||
this.processingOrdered.add(senderId);
|
||||
|
||||
|
||||
try {
|
||||
const queue = this.orderedQueues.get(senderId);
|
||||
if (!queue || queue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
while (queue.length > 0) {
|
||||
const item = queue.shift()!;
|
||||
const waitTime = Date.now() - item.enqueuedAt;
|
||||
|
||||
|
||||
// 检查等待时间是否超限
|
||||
if (waitTime > this.config.orderedExecution.maxWaitTime) {
|
||||
item.reject({
|
||||
@@ -483,21 +483,21 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
const response = await item.handler();
|
||||
item.resolve(response);
|
||||
|
||||
|
||||
this.emit('orderedCallProcessed', item.request.callId, waitTime);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
item.reject(error as RpcError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} finally {
|
||||
this.processingOrdered.delete(senderId);
|
||||
|
||||
|
||||
// 如果队列还有新的项目,继续处理
|
||||
const queue = this.orderedQueues.get(senderId);
|
||||
if (queue && queue.length > 0) {
|
||||
@@ -520,7 +520,7 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
*/
|
||||
private cleanup(): void {
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
// 清理过期的重复调用记录
|
||||
if (this.config.idempotency.enabled) {
|
||||
for (const [key, record] of this.duplicateRecords) {
|
||||
@@ -528,20 +528,20 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
this.duplicateRecords.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 限制记录数量
|
||||
if (this.duplicateRecords.size > this.config.idempotency.maxRecords) {
|
||||
const sortedRecords = Array.from(this.duplicateRecords.entries())
|
||||
.sort(([,a], [,b]) => a.lastCallTime - b.lastCallTime);
|
||||
|
||||
|
||||
const keepCount = Math.floor(this.config.idempotency.maxRecords * 0.8);
|
||||
|
||||
|
||||
for (let i = 0; i < sortedRecords.length - keepCount; i++) {
|
||||
this.duplicateRecords.delete(sortedRecords[i][0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清理空的有序队列
|
||||
for (const [senderId, queue] of this.orderedQueues) {
|
||||
if (queue.length === 0 && !this.processingOrdered.has(senderId)) {
|
||||
@@ -549,4 +549,4 @@ export class RpcReliabilityManager extends EventEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export class JSONSerializer {
|
||||
private logger = createLogger('JSONSerializer');
|
||||
private config: SerializerConfig;
|
||||
private stats: SerializationStats;
|
||||
|
||||
|
||||
// 性能分析
|
||||
private serializationTimes: number[] = [];
|
||||
private deserializationTimes: number[] = [];
|
||||
@@ -101,7 +101,7 @@ export class JSONSerializer {
|
||||
*/
|
||||
serialize<T extends INetworkMessage>(message: T): SerializationResult {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
try {
|
||||
// 类型检查
|
||||
if (this.config.enableTypeChecking) {
|
||||
@@ -110,10 +110,10 @@ export class JSONSerializer {
|
||||
|
||||
// 预处理消息
|
||||
const processedMessage = this.preprocessMessage(message);
|
||||
|
||||
|
||||
// 序列化
|
||||
let serializedData: string;
|
||||
|
||||
|
||||
// 使用自定义序列化器
|
||||
const customSerializer = this.findCustomSerializer(processedMessage);
|
||||
if (customSerializer) {
|
||||
@@ -151,17 +151,17 @@ export class JSONSerializer {
|
||||
*/
|
||||
deserialize<T extends INetworkMessage>(data: string | ArrayBuffer): DeserializationResult<T> {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
try {
|
||||
// 转换数据格式
|
||||
const jsonString = data instanceof ArrayBuffer ? new TextDecoder().decode(data) :
|
||||
typeof data === 'string' ? data : String(data);
|
||||
|
||||
const jsonString = data instanceof ArrayBuffer ? new TextDecoder().decode(data) :
|
||||
typeof data === 'string' ? data : String(data);
|
||||
|
||||
// 解析JSON
|
||||
const parsedData = JSON.parse(jsonString, this.createReviver());
|
||||
|
||||
|
||||
// 类型检查
|
||||
const validationResult = this.config.enableTypeChecking ?
|
||||
const validationResult = this.config.enableTypeChecking ?
|
||||
this.validateParsedMessage(parsedData) : { isValid: true, errors: [] };
|
||||
|
||||
// 后处理消息
|
||||
@@ -183,7 +183,7 @@ export class JSONSerializer {
|
||||
} catch (error) {
|
||||
this.stats.errorCount++;
|
||||
this.logger.error('反序列化失败:', error);
|
||||
|
||||
|
||||
return {
|
||||
data: {} as T,
|
||||
deserializationTime: performance.now() - startTime,
|
||||
@@ -198,11 +198,11 @@ export class JSONSerializer {
|
||||
*/
|
||||
serializeBatch<T extends INetworkMessage>(messages: T[]): SerializationResult {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
try {
|
||||
const batchData = {
|
||||
type: 'batch',
|
||||
messages: messages.map(msg => {
|
||||
messages: messages.map((msg) => {
|
||||
if (this.config.enableTypeChecking) {
|
||||
this.validateMessage(msg);
|
||||
}
|
||||
@@ -212,7 +212,7 @@ export class JSONSerializer {
|
||||
};
|
||||
|
||||
const serializedData = JSON.stringify(batchData, this.createReplacer());
|
||||
|
||||
|
||||
if (serializedData.length > this.config.maxMessageSize) {
|
||||
throw new Error(`批量消息大小超过限制: ${serializedData.length} > ${this.config.maxMessageSize}`);
|
||||
}
|
||||
@@ -240,7 +240,7 @@ export class JSONSerializer {
|
||||
*/
|
||||
deserializeBatch<T extends INetworkMessage>(data: string | ArrayBuffer): DeserializationResult<T[]> {
|
||||
const result = this.deserialize<any>(data);
|
||||
|
||||
|
||||
if (!result.isValid || !result.data.messages) {
|
||||
return {
|
||||
data: [],
|
||||
@@ -251,7 +251,7 @@ export class JSONSerializer {
|
||||
}
|
||||
|
||||
const messages = result.data.messages.map((msg: any) => this.postprocessMessage(msg));
|
||||
|
||||
|
||||
return {
|
||||
data: messages as T[],
|
||||
deserializationTime: result.deserializationTime,
|
||||
@@ -280,7 +280,7 @@ export class JSONSerializer {
|
||||
errorCount: 0,
|
||||
compressionSavings: 0
|
||||
};
|
||||
|
||||
|
||||
this.serializationTimes.length = 0;
|
||||
this.deserializationTimes.length = 0;
|
||||
this.messageSizes.length = 0;
|
||||
@@ -394,7 +394,7 @@ export class JSONSerializer {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ export class JSONSerializer {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -477,10 +477,10 @@ export class JSONSerializer {
|
||||
private updateSerializationStats(size: number, time: number): void {
|
||||
this.stats.totalSerialized++;
|
||||
this.stats.totalBytes += size;
|
||||
|
||||
|
||||
this.serializationTimes.push(time);
|
||||
this.messageSizes.push(size);
|
||||
|
||||
|
||||
// 保持最近1000个样本
|
||||
if (this.serializationTimes.length > 1000) {
|
||||
this.serializationTimes.shift();
|
||||
@@ -488,11 +488,11 @@ export class JSONSerializer {
|
||||
if (this.messageSizes.length > 1000) {
|
||||
this.messageSizes.shift();
|
||||
}
|
||||
|
||||
|
||||
// 计算平均值
|
||||
this.stats.averageSerializationTime =
|
||||
this.stats.averageSerializationTime =
|
||||
this.serializationTimes.reduce((sum, t) => sum + t, 0) / this.serializationTimes.length;
|
||||
this.stats.averageMessageSize =
|
||||
this.stats.averageMessageSize =
|
||||
this.messageSizes.reduce((sum, s) => sum + s, 0) / this.messageSizes.length;
|
||||
}
|
||||
|
||||
@@ -501,16 +501,16 @@ export class JSONSerializer {
|
||||
*/
|
||||
private updateDeserializationStats(time: number): void {
|
||||
this.stats.totalDeserialized++;
|
||||
|
||||
|
||||
this.deserializationTimes.push(time);
|
||||
|
||||
|
||||
// 保持最近1000个样本
|
||||
if (this.deserializationTimes.length > 1000) {
|
||||
this.deserializationTimes.shift();
|
||||
}
|
||||
|
||||
|
||||
// 计算平均值
|
||||
this.stats.averageDeserializationTime =
|
||||
this.stats.averageDeserializationTime =
|
||||
this.deserializationTimes.reduce((sum, t) => sum + t, 0) / this.deserializationTimes.length;
|
||||
}
|
||||
|
||||
@@ -536,10 +536,10 @@ export class JSONSerializer {
|
||||
*/
|
||||
private calculatePercentiles(values: number[]) {
|
||||
if (values.length === 0) return {};
|
||||
|
||||
|
||||
const sorted = [...values].sort((a, b) => a - b);
|
||||
const n = sorted.length;
|
||||
|
||||
|
||||
return {
|
||||
p50: sorted[Math.floor(n * 0.5)],
|
||||
p90: sorted[Math.floor(n * 0.9)],
|
||||
@@ -547,4 +547,4 @@ export class JSONSerializer {
|
||||
p99: sorted[Math.floor(n * 0.99)]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,27 +14,27 @@ export interface ICompressionAlgorithm {
|
||||
readonly version: string;
|
||||
/** 是否支持异步压缩 */
|
||||
readonly supportsAsync: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* 同步压缩
|
||||
*/
|
||||
compress(data: ArrayBuffer): ArrayBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* 同步解压缩
|
||||
*/
|
||||
decompress(data: ArrayBuffer): ArrayBuffer;
|
||||
|
||||
|
||||
/**
|
||||
* 异步压缩(可选)
|
||||
*/
|
||||
compressAsync?(data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
|
||||
|
||||
/**
|
||||
* 异步解压缩(可选)
|
||||
*/
|
||||
decompressAsync?(data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
|
||||
|
||||
/**
|
||||
* 估算压缩后大小(可选)
|
||||
*/
|
||||
@@ -124,15 +124,15 @@ export class NoCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
readonly name = 'none';
|
||||
readonly version = '1.0.0';
|
||||
readonly supportsAsync = false;
|
||||
|
||||
|
||||
compress(data: ArrayBuffer): ArrayBuffer {
|
||||
return data.slice(0);
|
||||
}
|
||||
|
||||
|
||||
decompress(data: ArrayBuffer): ArrayBuffer {
|
||||
return data.slice(0);
|
||||
}
|
||||
|
||||
|
||||
estimateCompressedSize(data: ArrayBuffer): number {
|
||||
return data.byteLength;
|
||||
}
|
||||
@@ -145,39 +145,39 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
readonly name = 'lz-string';
|
||||
readonly version = '1.0.0';
|
||||
readonly supportsAsync = false;
|
||||
|
||||
|
||||
compress(data: ArrayBuffer): ArrayBuffer {
|
||||
// 将ArrayBuffer转换为字符串
|
||||
const decoder = new TextDecoder();
|
||||
const input = decoder.decode(data);
|
||||
|
||||
|
||||
if (!input) {
|
||||
return data.slice(0);
|
||||
}
|
||||
|
||||
|
||||
// LZ压缩算法
|
||||
const dictionary: { [key: string]: number } = {};
|
||||
let dictSize = 256;
|
||||
|
||||
|
||||
// 初始化字典
|
||||
for (let i = 0; i < 256; i++) {
|
||||
dictionary[String.fromCharCode(i)] = i;
|
||||
}
|
||||
|
||||
|
||||
let w = '';
|
||||
const result: number[] = [];
|
||||
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const c = input.charAt(i);
|
||||
const wc = w + c;
|
||||
|
||||
|
||||
if (dictionary[wc] !== undefined) {
|
||||
w = wc;
|
||||
} else {
|
||||
result.push(dictionary[w]);
|
||||
dictionary[wc] = dictSize++;
|
||||
w = c;
|
||||
|
||||
|
||||
// 防止字典过大
|
||||
if (dictSize >= 0xFFFF) {
|
||||
dictSize = 256;
|
||||
@@ -190,40 +190,40 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (w) {
|
||||
result.push(dictionary[w]);
|
||||
}
|
||||
|
||||
|
||||
// 将结果转换为ArrayBuffer
|
||||
return this.numbersToArrayBuffer(result);
|
||||
}
|
||||
|
||||
|
||||
decompress(data: ArrayBuffer): ArrayBuffer {
|
||||
if (data.byteLength === 0) {
|
||||
return data.slice(0);
|
||||
}
|
||||
|
||||
|
||||
const numbers = this.arrayBufferToNumbers(data);
|
||||
if (numbers.length === 0) {
|
||||
return data.slice(0);
|
||||
}
|
||||
|
||||
|
||||
const dictionary: { [key: number]: string } = {};
|
||||
let dictSize = 256;
|
||||
|
||||
|
||||
// 初始化字典
|
||||
for (let i = 0; i < 256; i++) {
|
||||
dictionary[i] = String.fromCharCode(i);
|
||||
}
|
||||
|
||||
|
||||
let w = String.fromCharCode(numbers[0]);
|
||||
const result = [w];
|
||||
|
||||
|
||||
for (let i = 1; i < numbers.length; i++) {
|
||||
const k = numbers[i];
|
||||
let entry: string;
|
||||
|
||||
|
||||
if (dictionary[k] !== undefined) {
|
||||
entry = dictionary[k];
|
||||
} else if (k === dictSize) {
|
||||
@@ -231,11 +231,11 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
} else {
|
||||
throw new Error('LZ解压缩错误:无效的压缩数据');
|
||||
}
|
||||
|
||||
|
||||
result.push(entry);
|
||||
dictionary[dictSize++] = w + entry.charAt(0);
|
||||
w = entry;
|
||||
|
||||
|
||||
// 防止字典过大
|
||||
if (dictSize >= 0xFFFF) {
|
||||
dictSize = 256;
|
||||
@@ -247,26 +247,26 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 将结果转换为ArrayBuffer
|
||||
const output = result.join('');
|
||||
const encoder = new TextEncoder();
|
||||
return encoder.encode(output).buffer;
|
||||
}
|
||||
|
||||
|
||||
estimateCompressedSize(data: ArrayBuffer): number {
|
||||
// 简单估算:假设压缩率在30%-70%之间
|
||||
const size = data.byteLength;
|
||||
return Math.floor(size * 0.5); // 50%的估算压缩率
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将数字数组转换为ArrayBuffer
|
||||
*/
|
||||
private numbersToArrayBuffer(numbers: number[]): ArrayBuffer {
|
||||
// 使用变长编码以节省空间
|
||||
const bytes: number[] = [];
|
||||
|
||||
|
||||
for (const num of numbers) {
|
||||
if (num < 128) {
|
||||
// 小于128,用1字节
|
||||
@@ -282,20 +282,20 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
bytes.push((num >> 14) & 0x7F);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Uint8Array(bytes).buffer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将ArrayBuffer转换为数字数组
|
||||
*/
|
||||
private arrayBufferToNumbers(buffer: ArrayBuffer): number[] {
|
||||
const bytes = new Uint8Array(buffer);
|
||||
const numbers: number[] = [];
|
||||
|
||||
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
const byte1 = bytes[i];
|
||||
|
||||
|
||||
if ((byte1 & 0x80) === 0) {
|
||||
// 单字节数字
|
||||
numbers.push(byte1);
|
||||
@@ -303,11 +303,11 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
// 多字节数字
|
||||
let num = byte1 & 0x7F;
|
||||
i++;
|
||||
|
||||
|
||||
if (i < bytes.length) {
|
||||
const byte2 = bytes[i];
|
||||
num |= (byte2 & 0x7F) << 7;
|
||||
|
||||
|
||||
if ((byte2 & 0x80) !== 0) {
|
||||
// 三字节数字
|
||||
i++;
|
||||
@@ -317,11 +317,11 @@ export class LZCompressionAlgorithm implements ICompressionAlgorithm {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
numbers.push(num);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return numbers;
|
||||
}
|
||||
}
|
||||
@@ -372,10 +372,10 @@ export class MessageCompressor {
|
||||
if (this.algorithms.has(algorithm.name)) {
|
||||
this.logger.warn(`压缩算法 '${algorithm.name}' 已存在,将被覆盖`);
|
||||
}
|
||||
|
||||
|
||||
this.algorithms.set(algorithm.name, algorithm);
|
||||
this.stats.algorithmUsage[algorithm.name] = 0;
|
||||
|
||||
|
||||
this.logger.info(`注册压缩算法: ${algorithm.name} v${algorithm.version}`);
|
||||
}
|
||||
|
||||
@@ -387,13 +387,13 @@ export class MessageCompressor {
|
||||
this.logger.warn('无法注销默认的无压缩算法');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const removed = this.algorithms.delete(algorithmName);
|
||||
if (removed) {
|
||||
delete this.stats.algorithmUsage[algorithmName];
|
||||
this.logger.info(`注销压缩算法: ${algorithmName}`);
|
||||
}
|
||||
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
@@ -415,21 +415,21 @@ export class MessageCompressor {
|
||||
* 压缩数据
|
||||
*/
|
||||
public async compress(
|
||||
data: ArrayBuffer | string,
|
||||
data: ArrayBuffer | string,
|
||||
algorithmName?: string
|
||||
): Promise<CompressionResult> {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
// 转换输入数据
|
||||
const inputBuffer = typeof data === 'string'
|
||||
? new TextEncoder().encode(data).buffer
|
||||
const inputBuffer = typeof data === 'string'
|
||||
? new TextEncoder().encode(data).buffer
|
||||
: data;
|
||||
const originalSize = inputBuffer.byteLength;
|
||||
|
||||
|
||||
// 选择压缩算法
|
||||
const selectedAlgorithm = algorithmName || this.config.defaultAlgorithm;
|
||||
const algorithm = this.algorithms.get(selectedAlgorithm);
|
||||
|
||||
|
||||
if (!algorithm) {
|
||||
throw new Error(`未找到压缩算法: ${selectedAlgorithm}`);
|
||||
}
|
||||
@@ -459,9 +459,9 @@ export class MessageCompressor {
|
||||
// 更新统计信息
|
||||
if (this.config.enableStats) {
|
||||
this.updateCompressionStats(
|
||||
selectedAlgorithm,
|
||||
originalSize,
|
||||
compressedSize,
|
||||
selectedAlgorithm,
|
||||
originalSize,
|
||||
compressedSize,
|
||||
compressionTime
|
||||
);
|
||||
}
|
||||
@@ -494,12 +494,12 @@ export class MessageCompressor {
|
||||
* 解压缩数据
|
||||
*/
|
||||
public async decompress(
|
||||
data: ArrayBuffer,
|
||||
data: ArrayBuffer,
|
||||
algorithmName: string
|
||||
): Promise<DecompressionResult> {
|
||||
const startTime = performance.now();
|
||||
const compressedSize = data.byteLength;
|
||||
|
||||
|
||||
const algorithm = this.algorithms.get(algorithmName);
|
||||
if (!algorithm) {
|
||||
throw new Error(`未找到解压缩算法: ${algorithmName}`);
|
||||
@@ -549,12 +549,12 @@ export class MessageCompressor {
|
||||
* 估算压缩后大小
|
||||
*/
|
||||
public estimateCompressedSize(
|
||||
data: ArrayBuffer,
|
||||
data: ArrayBuffer,
|
||||
algorithmName?: string
|
||||
): number {
|
||||
const selectedAlgorithm = algorithmName || this.config.defaultAlgorithm;
|
||||
const algorithm = this.algorithms.get(selectedAlgorithm);
|
||||
|
||||
|
||||
if (!algorithm) {
|
||||
return data.byteLength;
|
||||
}
|
||||
@@ -614,9 +614,9 @@ export class MessageCompressor {
|
||||
* 更新压缩统计信息
|
||||
*/
|
||||
private updateCompressionStats(
|
||||
algorithmName: string,
|
||||
originalSize: number,
|
||||
compressedSize: number,
|
||||
algorithmName: string,
|
||||
originalSize: number,
|
||||
compressedSize: number,
|
||||
compressionTime: number
|
||||
): void {
|
||||
this.stats.totalCompressions++;
|
||||
@@ -625,13 +625,13 @@ export class MessageCompressor {
|
||||
this.stats.algorithmUsage[algorithmName]++;
|
||||
|
||||
// 更新平均压缩比
|
||||
this.stats.averageCompressionRatio = this.stats.totalOriginalBytes > 0
|
||||
? this.stats.totalCompressedBytes / this.stats.totalOriginalBytes
|
||||
this.stats.averageCompressionRatio = this.stats.totalOriginalBytes > 0
|
||||
? this.stats.totalCompressedBytes / this.stats.totalOriginalBytes
|
||||
: 1.0;
|
||||
|
||||
// 更新平均压缩时间
|
||||
this.stats.averageCompressionTime =
|
||||
(this.stats.averageCompressionTime * (this.stats.totalCompressions - 1) + compressionTime)
|
||||
this.stats.averageCompressionTime =
|
||||
(this.stats.averageCompressionTime * (this.stats.totalCompressions - 1) + compressionTime)
|
||||
/ this.stats.totalCompressions;
|
||||
}
|
||||
|
||||
@@ -642,8 +642,8 @@ export class MessageCompressor {
|
||||
this.stats.totalDecompressions++;
|
||||
|
||||
// 更新平均解压缩时间
|
||||
this.stats.averageDecompressionTime =
|
||||
(this.stats.averageDecompressionTime * (this.stats.totalDecompressions - 1) + decompressionTime)
|
||||
this.stats.averageDecompressionTime =
|
||||
(this.stats.averageDecompressionTime * (this.stats.totalDecompressions - 1) + decompressionTime)
|
||||
/ this.stats.totalDecompressions;
|
||||
}
|
||||
}
|
||||
@@ -651,4 +651,4 @@ export class MessageCompressor {
|
||||
/**
|
||||
* 全局压缩器实例
|
||||
*/
|
||||
export const globalCompressor = new MessageCompressor();
|
||||
export const globalCompressor = new MessageCompressor();
|
||||
|
||||
@@ -107,19 +107,19 @@ export class SyncVarSerializer {
|
||||
public serializeSyncBatch(batch: SyncBatch): SerializationResult {
|
||||
try {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
// 准备序列化数据
|
||||
let dataToSerialize: any = batch;
|
||||
|
||||
|
||||
// 应用差量同步
|
||||
if (this.config.enableDeltaSync) {
|
||||
dataToSerialize = this.applyDeltaCompression(batch);
|
||||
}
|
||||
|
||||
|
||||
// 基础JSON序列化
|
||||
const jsonString = JSON.stringify(dataToSerialize, this.replacer.bind(this));
|
||||
const originalSize = new TextEncoder().encode(jsonString).length;
|
||||
|
||||
|
||||
// 检查消息大小限制
|
||||
if (originalSize > this.config.maxMessageSize) {
|
||||
return {
|
||||
@@ -130,10 +130,10 @@ export class SyncVarSerializer {
|
||||
compressionRatio: 0
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
let finalData: ArrayBuffer | string = jsonString;
|
||||
let compressedSize = originalSize;
|
||||
|
||||
|
||||
// 应用压缩
|
||||
if (this.config.enableCompression && originalSize > 256) {
|
||||
const compressionResult = this.compress(jsonString);
|
||||
@@ -142,9 +142,9 @@ export class SyncVarSerializer {
|
||||
compressedSize = compressionResult.data.byteLength;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const compressionRatio = originalSize > 0 ? compressedSize / originalSize : 1;
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: finalData,
|
||||
@@ -152,7 +152,7 @@ export class SyncVarSerializer {
|
||||
compressedSize,
|
||||
compressionRatio
|
||||
};
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
@@ -170,7 +170,7 @@ export class SyncVarSerializer {
|
||||
public deserializeSyncBatch(data: ArrayBuffer | string): DeserializationResult<SyncBatch> {
|
||||
try {
|
||||
let jsonString: string;
|
||||
|
||||
|
||||
// 解压缩
|
||||
if (data instanceof ArrayBuffer) {
|
||||
const decompressResult = this.decompress(data);
|
||||
@@ -185,10 +185,10 @@ export class SyncVarSerializer {
|
||||
} else {
|
||||
jsonString = data;
|
||||
}
|
||||
|
||||
|
||||
// JSON反序列化
|
||||
const parsedData = JSON.parse(jsonString, this.reviver.bind(this));
|
||||
|
||||
|
||||
// 类型检查
|
||||
if (this.config.enableTypeChecking) {
|
||||
const typeCheckResult = this.validateSyncBatchType(parsedData);
|
||||
@@ -200,19 +200,19 @@ export class SyncVarSerializer {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 应用差量还原
|
||||
let finalData = parsedData;
|
||||
if (this.config.enableDeltaSync && this.isDeltaData(parsedData)) {
|
||||
finalData = this.applyDeltaRestore(parsedData);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: finalData as SyncBatch,
|
||||
isValidType: true
|
||||
};
|
||||
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
@@ -227,7 +227,7 @@ export class SyncVarSerializer {
|
||||
*/
|
||||
public createSyncMessage(batch: SyncBatch, senderId: string): INetworkMessage {
|
||||
const serializedData = this.serializeSyncBatch(batch);
|
||||
|
||||
|
||||
return {
|
||||
type: MessageType.SYNC_BATCH,
|
||||
messageId: this.generateMessageId(),
|
||||
@@ -250,7 +250,7 @@ export class SyncVarSerializer {
|
||||
isValidType: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return this.deserializeSyncBatch(message.data);
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ export class SyncVarSerializer {
|
||||
private applyDeltaCompression(batch: SyncBatch): DeltaData | SyncBatch {
|
||||
const key = batch.instanceId;
|
||||
const lastRecord = this.deltaHistory.get(key);
|
||||
|
||||
|
||||
if (!lastRecord) {
|
||||
// 第一次同步,存储完整数据
|
||||
this.deltaHistory.set(key, {
|
||||
@@ -302,18 +302,18 @@ export class SyncVarSerializer {
|
||||
});
|
||||
return batch;
|
||||
}
|
||||
|
||||
|
||||
// 计算差量
|
||||
const changes: { [key: string]: any } = {};
|
||||
const deletions: string[] = [];
|
||||
|
||||
|
||||
// 检查变化的属性
|
||||
for (const [prop, value] of Object.entries(batch.changes)) {
|
||||
if (!lastRecord.data.changes || lastRecord.data.changes[prop] !== value) {
|
||||
changes[prop] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查删除的属性
|
||||
if (lastRecord.data.changes) {
|
||||
for (const prop of Object.keys(lastRecord.data.changes)) {
|
||||
@@ -322,7 +322,7 @@ export class SyncVarSerializer {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有变化,返回空的差量数据
|
||||
if (Object.keys(changes).length === 0 && deletions.length === 0) {
|
||||
return {
|
||||
@@ -332,14 +332,14 @@ export class SyncVarSerializer {
|
||||
deletions: []
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// 更新历史记录
|
||||
const currentVersion = ++this.versionCounter;
|
||||
this.deltaHistory.set(key, {
|
||||
version: currentVersion,
|
||||
data: { ...batch }
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
baseVersion: lastRecord.version,
|
||||
currentVersion,
|
||||
@@ -370,7 +370,7 @@ export class SyncVarSerializer {
|
||||
* 检查是否为差量数据
|
||||
*/
|
||||
private isDeltaData(data: any): data is DeltaData {
|
||||
return data &&
|
||||
return data &&
|
||||
typeof data.baseVersion === 'number' &&
|
||||
typeof data.currentVersion === 'number' &&
|
||||
typeof data.changes === 'object' &&
|
||||
@@ -386,7 +386,7 @@ export class SyncVarSerializer {
|
||||
const compressed = this.lzCompress(data);
|
||||
const encoder = new TextEncoder();
|
||||
const bytes = encoder.encode(compressed);
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: bytes.buffer
|
||||
@@ -404,7 +404,7 @@ export class SyncVarSerializer {
|
||||
const decoder = new TextDecoder();
|
||||
const compressedString = decoder.decode(data);
|
||||
const decompressed = this.lzDecompress(compressedString);
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: decompressed
|
||||
@@ -422,20 +422,20 @@ export class SyncVarSerializer {
|
||||
if (value instanceof Date) {
|
||||
return { __type: 'Date', value: value.toISOString() };
|
||||
}
|
||||
|
||||
|
||||
if (value instanceof Map) {
|
||||
return { __type: 'Map', value: Array.from(value.entries()) };
|
||||
}
|
||||
|
||||
|
||||
if (value instanceof Set) {
|
||||
return { __type: 'Set', value: Array.from(value) };
|
||||
}
|
||||
|
||||
|
||||
// 处理BigInt
|
||||
if (typeof value === 'bigint') {
|
||||
return { __type: 'BigInt', value: value.toString() };
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -455,7 +455,7 @@ export class SyncVarSerializer {
|
||||
return BigInt(value.value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -464,28 +464,28 @@ export class SyncVarSerializer {
|
||||
*/
|
||||
private validateSyncBatchType(data: any): { isValid: boolean; errors: string[] } {
|
||||
const errors: string[] = [];
|
||||
|
||||
|
||||
if (!data || typeof data !== 'object') {
|
||||
errors.push('数据不是对象');
|
||||
return { isValid: false, errors };
|
||||
}
|
||||
|
||||
|
||||
if (typeof data.instanceId !== 'string') {
|
||||
errors.push('instanceId必须是字符串');
|
||||
}
|
||||
|
||||
|
||||
if (typeof data.instanceType !== 'string') {
|
||||
errors.push('instanceType必须是字符串');
|
||||
}
|
||||
|
||||
|
||||
if (!data.changes || typeof data.changes !== 'object') {
|
||||
errors.push('changes必须是对象');
|
||||
}
|
||||
|
||||
|
||||
if (typeof data.timestamp !== 'number') {
|
||||
errors.push('timestamp必须是数字');
|
||||
}
|
||||
|
||||
|
||||
return { isValid: errors.length === 0, errors };
|
||||
}
|
||||
|
||||
@@ -505,7 +505,7 @@ export class SyncVarSerializer {
|
||||
if (priorities.length === 0) {
|
||||
return 5; // 默认优先级
|
||||
}
|
||||
|
||||
|
||||
// 使用最高优先级
|
||||
return Math.max(...priorities);
|
||||
}
|
||||
@@ -515,22 +515,22 @@ export class SyncVarSerializer {
|
||||
*/
|
||||
private lzCompress(input: string): string {
|
||||
if (!input) return '';
|
||||
|
||||
|
||||
const dictionary: { [key: string]: number } = {};
|
||||
let dictSize = 256;
|
||||
|
||||
|
||||
// 初始化字典
|
||||
for (let i = 0; i < 256; i++) {
|
||||
dictionary[String.fromCharCode(i)] = i;
|
||||
}
|
||||
|
||||
|
||||
let w = '';
|
||||
const result: number[] = [];
|
||||
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
const c = input.charAt(i);
|
||||
const wc = w + c;
|
||||
|
||||
|
||||
if (dictionary[wc] !== undefined) {
|
||||
w = wc;
|
||||
} else {
|
||||
@@ -539,11 +539,11 @@ export class SyncVarSerializer {
|
||||
w = c;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (w) {
|
||||
result.push(dictionary[w]);
|
||||
}
|
||||
|
||||
|
||||
// 将结果编码为Base64以确保字符串安全
|
||||
return this.arrayToBase64(result);
|
||||
}
|
||||
@@ -553,25 +553,25 @@ export class SyncVarSerializer {
|
||||
*/
|
||||
private lzDecompress(compressed: string): string {
|
||||
if (!compressed) return '';
|
||||
|
||||
|
||||
const data = this.base64ToArray(compressed);
|
||||
if (data.length === 0) return '';
|
||||
|
||||
|
||||
const dictionary: { [key: number]: string } = {};
|
||||
let dictSize = 256;
|
||||
|
||||
|
||||
// 初始化字典
|
||||
for (let i = 0; i < 256; i++) {
|
||||
dictionary[i] = String.fromCharCode(i);
|
||||
}
|
||||
|
||||
|
||||
let w = String.fromCharCode(data[0]);
|
||||
const result = [w];
|
||||
|
||||
|
||||
for (let i = 1; i < data.length; i++) {
|
||||
const k = data[i];
|
||||
let entry: string;
|
||||
|
||||
|
||||
if (dictionary[k] !== undefined) {
|
||||
entry = dictionary[k];
|
||||
} else if (k === dictSize) {
|
||||
@@ -579,12 +579,12 @@ export class SyncVarSerializer {
|
||||
} else {
|
||||
throw new Error('解压缩错误:无效的压缩数据');
|
||||
}
|
||||
|
||||
|
||||
result.push(entry);
|
||||
dictionary[dictSize++] = w + entry.charAt(0);
|
||||
w = entry;
|
||||
}
|
||||
|
||||
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
@@ -602,7 +602,7 @@ export class SyncVarSerializer {
|
||||
bytes.push(255, num - 255);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 转换为字符串然后编码为Base64
|
||||
const binaryString = String.fromCharCode(...bytes);
|
||||
return btoa(binaryString);
|
||||
@@ -615,11 +615,11 @@ export class SyncVarSerializer {
|
||||
try {
|
||||
const binaryString = atob(base64);
|
||||
const bytes: number[] = [];
|
||||
|
||||
|
||||
for (let i = 0; i < binaryString.length; i++) {
|
||||
bytes.push(binaryString.charCodeAt(i));
|
||||
}
|
||||
|
||||
|
||||
// 还原原始数字数组
|
||||
const result: number[] = [];
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
@@ -630,10 +630,10 @@ export class SyncVarSerializer {
|
||||
result.push(bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
throw new Error('Base64解码失败');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,19 +89,19 @@ export interface DeltaSyncStats {
|
||||
export class DeltaSync {
|
||||
private logger = createLogger('DeltaSync');
|
||||
private config: DeltaSyncConfig;
|
||||
|
||||
|
||||
/** 版本历史 */
|
||||
private versionHistory = new Map<string, Map<number, VersionedData>>();
|
||||
|
||||
|
||||
/** 版本计数器 */
|
||||
private versionCounters = new Map<string, number>();
|
||||
|
||||
|
||||
/** 差量缓存 */
|
||||
private deltaCache = new Map<string, DeltaData>();
|
||||
|
||||
|
||||
/** 待合并操作 */
|
||||
private pendingOperations = new Map<string, DeltaOperation[]>();
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: DeltaSyncStats = {
|
||||
totalDeltas: 0,
|
||||
@@ -111,7 +111,7 @@ export class DeltaSync {
|
||||
cacheHitRate: 0,
|
||||
mergedOperations: 0
|
||||
};
|
||||
|
||||
|
||||
/** 合并定时器 */
|
||||
private mergeTimers = new Map<string, any>();
|
||||
|
||||
@@ -134,7 +134,7 @@ export class DeltaSync {
|
||||
if (!this.config.enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
const version = this.getNextVersion(instanceId);
|
||||
const versionedData: VersionedData = {
|
||||
version,
|
||||
@@ -142,7 +142,7 @@ export class DeltaSync {
|
||||
data: this.deepClone(data),
|
||||
checksum: this.calculateChecksum(data)
|
||||
};
|
||||
|
||||
|
||||
this.storeVersion(instanceId, versionedData);
|
||||
return version;
|
||||
}
|
||||
@@ -154,14 +154,14 @@ export class DeltaSync {
|
||||
if (!this.config.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const history = this.versionHistory.get(instanceId);
|
||||
if (!history || history.size === 0) {
|
||||
// 没有基线,记录第一个版本
|
||||
this.recordBaseline(instanceId, newData);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 选择基线版本
|
||||
let baseVersionData: VersionedData;
|
||||
if (baseVersion !== undefined) {
|
||||
@@ -185,16 +185,16 @@ export class DeltaSync {
|
||||
}
|
||||
baseVersionData = latestVersion;
|
||||
}
|
||||
|
||||
|
||||
const targetVersion = this.getNextVersion(instanceId);
|
||||
const changes = this.computeChanges(baseVersionData.data, newData);
|
||||
const deletions = this.computeDeletions(baseVersionData.data, newData);
|
||||
|
||||
|
||||
// 检查是否有变化
|
||||
if (Object.keys(changes).length === 0 && deletions.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const deltaData: DeltaData = {
|
||||
baseVersion: baseVersionData.version,
|
||||
targetVersion,
|
||||
@@ -206,13 +206,13 @@ export class DeltaSync {
|
||||
compressionRatio: 1
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 记录新版本
|
||||
this.recordBaseline(instanceId, newData);
|
||||
|
||||
|
||||
// 更新统计
|
||||
this.updateStats(deltaData);
|
||||
|
||||
|
||||
return deltaData;
|
||||
}
|
||||
|
||||
@@ -223,32 +223,32 @@ export class DeltaSync {
|
||||
if (!this.config.enabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const history = this.versionHistory.get(instanceId);
|
||||
if (!history) {
|
||||
this.logger.error(`实例 ${instanceId} 没有版本历史`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const baseData = history.get(delta.baseVersion);
|
||||
if (!baseData) {
|
||||
this.logger.error(`未找到基线版本 ${delta.baseVersion}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 复制基线数据
|
||||
const result = this.deepClone(baseData.data);
|
||||
|
||||
|
||||
// 应用变化
|
||||
for (const [key, value] of Object.entries(delta.changes)) {
|
||||
this.setNestedProperty(result, key, value);
|
||||
}
|
||||
|
||||
|
||||
// 应用删除
|
||||
for (const key of delta.deletions) {
|
||||
this.deleteNestedProperty(result, key);
|
||||
}
|
||||
|
||||
|
||||
// 记录结果版本
|
||||
const resultVersion: VersionedData = {
|
||||
version: delta.targetVersion,
|
||||
@@ -256,9 +256,9 @@ export class DeltaSync {
|
||||
data: this.deepClone(result),
|
||||
checksum: this.calculateChecksum(result)
|
||||
};
|
||||
|
||||
|
||||
this.storeVersion(instanceId, resultVersion);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -269,13 +269,13 @@ export class DeltaSync {
|
||||
if (!this.config.enableSmartMerging || operations.length <= 1) {
|
||||
return operations;
|
||||
}
|
||||
|
||||
|
||||
const pathMap = new Map<string, DeltaOperation>();
|
||||
|
||||
|
||||
// 按路径分组操作
|
||||
for (const op of operations) {
|
||||
const existing = pathMap.get(op.path);
|
||||
|
||||
|
||||
if (!existing) {
|
||||
pathMap.set(op.path, op);
|
||||
} else {
|
||||
@@ -285,9 +285,9 @@ export class DeltaSync {
|
||||
this.stats.mergedOperations++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 过滤掉NOOP操作
|
||||
return Array.from(pathMap.values()).filter(op => op.type !== DeltaOperationType.NOOP);
|
||||
return Array.from(pathMap.values()).filter((op) => op.type !== DeltaOperationType.NOOP);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,25 +297,25 @@ export class DeltaSync {
|
||||
if (!this.config.enableSmartMerging) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let operations = this.pendingOperations.get(instanceId);
|
||||
if (!operations) {
|
||||
operations = [];
|
||||
this.pendingOperations.set(instanceId, operations);
|
||||
}
|
||||
|
||||
|
||||
operations.push(operation);
|
||||
|
||||
|
||||
// 重置合并定时器
|
||||
const existingTimer = this.mergeTimers.get(instanceId);
|
||||
if (existingTimer) {
|
||||
clearTimeout(existingTimer);
|
||||
}
|
||||
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
this.flushPendingOperations(instanceId);
|
||||
}, this.config.mergeWindow);
|
||||
|
||||
|
||||
this.mergeTimers.set(instanceId, timer);
|
||||
}
|
||||
|
||||
@@ -326,14 +326,14 @@ export class DeltaSync {
|
||||
if (delta.metadata.size < this.config.compressionThreshold) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
|
||||
// 简化的压缩实现
|
||||
const compressedChanges = this.compressObject(delta.changes);
|
||||
const compressedDeletions = delta.deletions; // 删除操作通常已经很紧凑
|
||||
|
||||
|
||||
const originalSize = delta.metadata.size;
|
||||
const compressedSize = this.estimateSize(compressedChanges) + this.estimateSize(compressedDeletions);
|
||||
|
||||
|
||||
return {
|
||||
...delta,
|
||||
changes: compressedChanges,
|
||||
@@ -351,34 +351,34 @@ export class DeltaSync {
|
||||
*/
|
||||
public cleanup(): void {
|
||||
const now = Date.now();
|
||||
|
||||
|
||||
for (const [instanceId, history] of this.versionHistory) {
|
||||
const versionsToDelete: number[] = [];
|
||||
|
||||
|
||||
for (const [version, versionData] of history) {
|
||||
// 检查超时
|
||||
if (now - versionData.timestamp > this.config.versionTimeout) {
|
||||
versionsToDelete.push(version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 保留最新的几个版本
|
||||
const sortedVersions = Array.from(history.keys()).sort((a, b) => b - a);
|
||||
const toKeep = sortedVersions.slice(0, this.config.maxHistoryVersions);
|
||||
|
||||
|
||||
for (const version of versionsToDelete) {
|
||||
if (!toKeep.includes(version)) {
|
||||
history.delete(version);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果实例没有版本了,删除实例
|
||||
if (history.size === 0) {
|
||||
this.versionHistory.delete(instanceId);
|
||||
this.versionCounters.delete(instanceId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清理差量缓存
|
||||
this.deltaCache.clear();
|
||||
}
|
||||
@@ -419,7 +419,7 @@ export class DeltaSync {
|
||||
for (const timer of this.mergeTimers.values()) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
|
||||
this.versionHistory.clear();
|
||||
this.versionCounters.clear();
|
||||
this.deltaCache.clear();
|
||||
@@ -446,9 +446,9 @@ export class DeltaSync {
|
||||
history = new Map();
|
||||
this.versionHistory.set(instanceId, history);
|
||||
}
|
||||
|
||||
|
||||
history.set(versionData.version, versionData);
|
||||
|
||||
|
||||
// 限制历史版本数量
|
||||
if (history.size > this.config.maxHistoryVersions) {
|
||||
const oldestVersion = Math.min(...Array.from(history.keys()));
|
||||
@@ -464,7 +464,7 @@ export class DeltaSync {
|
||||
if (!history || history.size === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
const latestVersion = Math.max(...Array.from(history.keys()));
|
||||
return history.get(latestVersion);
|
||||
}
|
||||
@@ -474,15 +474,15 @@ export class DeltaSync {
|
||||
*/
|
||||
private computeChanges(oldData: any, newData: any): { [key: string]: any } {
|
||||
const changes: { [key: string]: any } = {};
|
||||
|
||||
|
||||
for (const [key, newValue] of Object.entries(newData)) {
|
||||
const oldValue = oldData[key];
|
||||
|
||||
|
||||
if (!this.deepEqual(oldValue, newValue)) {
|
||||
changes[key] = newValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
@@ -491,13 +491,13 @@ export class DeltaSync {
|
||||
*/
|
||||
private computeDeletions(oldData: any, newData: any): string[] {
|
||||
const deletions: string[] = [];
|
||||
|
||||
|
||||
for (const key of Object.keys(oldData)) {
|
||||
if (!(key in newData)) {
|
||||
deletions.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return deletions;
|
||||
}
|
||||
|
||||
@@ -539,7 +539,7 @@ export class DeltaSync {
|
||||
}
|
||||
|
||||
// 检查是否值回到了原始状态
|
||||
if (op1.type === DeltaOperationType.MODIFY &&
|
||||
if (op1.type === DeltaOperationType.MODIFY &&
|
||||
op2.type === DeltaOperationType.MODIFY &&
|
||||
this.deepEqual(op1.oldValue, op2.newValue)) {
|
||||
// 值回到原始状态 = 无操作
|
||||
@@ -563,10 +563,10 @@ export class DeltaSync {
|
||||
if (!operations || operations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 合并操作并发送
|
||||
this.mergeOperations(instanceId, operations);
|
||||
|
||||
|
||||
// 清理待处理操作
|
||||
this.pendingOperations.delete(instanceId);
|
||||
this.mergeTimers.delete(instanceId);
|
||||
@@ -582,7 +582,7 @@ export class DeltaSync {
|
||||
|
||||
// 移除null和undefined值
|
||||
const compressed: any = Array.isArray(obj) ? [] : {};
|
||||
|
||||
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (value !== null && value !== undefined) {
|
||||
if (typeof value === 'object') {
|
||||
@@ -603,19 +603,19 @@ export class DeltaSync {
|
||||
if (obj === null || obj === undefined) {
|
||||
return 4; // "null"的长度
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj === 'string') {
|
||||
return obj.length * 2; // UTF-16字符估算
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj === 'number') {
|
||||
return 8; // 64位数字
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj === 'boolean') {
|
||||
return 4; // true/false
|
||||
}
|
||||
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
let size = 2; // []
|
||||
for (const item of obj) {
|
||||
@@ -623,7 +623,7 @@ export class DeltaSync {
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj === 'object') {
|
||||
let size = 2; // {}
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
@@ -632,7 +632,7 @@ export class DeltaSync {
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
return JSON.stringify(obj).length;
|
||||
}
|
||||
|
||||
@@ -643,28 +643,28 @@ export class DeltaSync {
|
||||
if (obj === null || obj === undefined) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return new Date(obj.getTime());
|
||||
}
|
||||
|
||||
|
||||
if (obj instanceof RegExp) {
|
||||
return new RegExp(obj);
|
||||
}
|
||||
|
||||
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(item => this.deepClone(item));
|
||||
return obj.map((item) => this.deepClone(item));
|
||||
}
|
||||
|
||||
|
||||
const cloned: any = {};
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
cloned[key] = this.deepClone(value);
|
||||
}
|
||||
|
||||
|
||||
return cloned;
|
||||
}
|
||||
|
||||
@@ -675,27 +675,27 @@ export class DeltaSync {
|
||||
if (obj1 === obj2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (obj1 === null || obj2 === null || obj1 === undefined || obj2 === undefined) {
|
||||
return obj1 === obj2;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj1 !== typeof obj2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (typeof obj1 !== 'object') {
|
||||
return obj1 === obj2;
|
||||
}
|
||||
|
||||
|
||||
if (obj1 instanceof Date && obj2 instanceof Date) {
|
||||
return obj1.getTime() === obj2.getTime();
|
||||
}
|
||||
|
||||
|
||||
if (Array.isArray(obj1) !== Array.isArray(obj2)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (Array.isArray(obj1)) {
|
||||
if (obj1.length !== obj2.length) {
|
||||
return false;
|
||||
@@ -707,14 +707,14 @@ export class DeltaSync {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const keys1 = Object.keys(obj1);
|
||||
const keys2 = Object.keys(obj2);
|
||||
|
||||
|
||||
if (keys1.length !== keys2.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (const key of keys1) {
|
||||
if (!keys2.includes(key)) {
|
||||
return false;
|
||||
@@ -723,7 +723,7 @@ export class DeltaSync {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -733,7 +733,7 @@ export class DeltaSync {
|
||||
private setNestedProperty(obj: any, path: string, value: any): void {
|
||||
const keys = path.split('.');
|
||||
let current = obj;
|
||||
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!(key in current)) {
|
||||
@@ -741,7 +741,7 @@ export class DeltaSync {
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
|
||||
current[keys[keys.length - 1]] = value;
|
||||
}
|
||||
|
||||
@@ -751,7 +751,7 @@ export class DeltaSync {
|
||||
private deleteNestedProperty(obj: any, path: string): void {
|
||||
const keys = path.split('.');
|
||||
let current = obj;
|
||||
|
||||
|
||||
for (let i = 0; i < keys.length - 1; i++) {
|
||||
const key = keys[i];
|
||||
if (!(key in current)) {
|
||||
@@ -759,7 +759,7 @@ export class DeltaSync {
|
||||
}
|
||||
current = current[key];
|
||||
}
|
||||
|
||||
|
||||
delete current[keys[keys.length - 1]];
|
||||
}
|
||||
|
||||
@@ -770,13 +770,13 @@ export class DeltaSync {
|
||||
// 简化的校验和实现
|
||||
const str = JSON.stringify(obj);
|
||||
let hash = 0;
|
||||
|
||||
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const char = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + char;
|
||||
hash = hash & hash; // 转换为32位整数
|
||||
}
|
||||
|
||||
|
||||
return hash.toString(16);
|
||||
}
|
||||
|
||||
@@ -787,8 +787,8 @@ export class DeltaSync {
|
||||
this.stats.totalDeltas++;
|
||||
this.stats.totalSize += delta.metadata.size;
|
||||
this.stats.averageDeltaSize = this.stats.totalSize / this.stats.totalDeltas;
|
||||
this.stats.compressionRatio =
|
||||
(this.stats.compressionRatio * (this.stats.totalDeltas - 1) + delta.metadata.compressionRatio) /
|
||||
this.stats.compressionRatio =
|
||||
(this.stats.compressionRatio * (this.stats.totalDeltas - 1) + delta.metadata.compressionRatio) /
|
||||
this.stats.totalDeltas;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import {
|
||||
getSyncVarMetadata,
|
||||
getDirtySyncVars,
|
||||
clearDirtyFlags,
|
||||
SyncVarMetadata,
|
||||
import {
|
||||
getDirtySyncVars,
|
||||
clearDirtyFlags,
|
||||
hasSyncVars,
|
||||
SyncVarValue
|
||||
} from '../decorators/SyncVar';
|
||||
@@ -68,19 +66,19 @@ export interface SyncVarManagerEvents {
|
||||
*/
|
||||
export class SyncVarManager extends EventEmitter {
|
||||
private static instance: SyncVarManager | null = null;
|
||||
|
||||
|
||||
/** 注册的实例映射 */
|
||||
private registeredInstances = new Map<string, object>();
|
||||
|
||||
|
||||
/** 脏实例集合 */
|
||||
private dirtyInstances = new Set<string>();
|
||||
|
||||
|
||||
/** 实例ID计数器 */
|
||||
private instanceIdCounter = 0;
|
||||
|
||||
|
||||
/** 实例ID映射 */
|
||||
private instanceIdMap = new WeakMap<any, string>();
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: SyncStats = {
|
||||
registeredInstances: 0,
|
||||
@@ -91,19 +89,19 @@ export class SyncVarManager extends EventEmitter {
|
||||
syncsPerSecond: 0,
|
||||
lastSyncTime: 0
|
||||
};
|
||||
|
||||
|
||||
/** 自动同步定时器 */
|
||||
private autoSyncTimer: ReturnType<typeof setInterval> | null = null;
|
||||
|
||||
|
||||
/** 同步频率(毫秒) */
|
||||
private syncRate = 100;
|
||||
|
||||
|
||||
/** 是否启用自动同步 */
|
||||
private autoSyncEnabled = true;
|
||||
|
||||
|
||||
/** 最大批次大小 */
|
||||
private maxBatchSize = 100;
|
||||
|
||||
|
||||
/** 立即同步请求队列 */
|
||||
private immediateSyncQueue = new Set<{ instanceId: string; propertyKey?: string | symbol }>();
|
||||
|
||||
@@ -137,14 +135,14 @@ export class SyncVarManager extends EventEmitter {
|
||||
|
||||
// 生成新的实例ID
|
||||
const instanceId = `syncvar_${++this.instanceIdCounter}`;
|
||||
|
||||
|
||||
// 注册实例
|
||||
this.registeredInstances.set(instanceId, instance);
|
||||
this.instanceIdMap.set(instance, instanceId);
|
||||
|
||||
|
||||
// 更新统计
|
||||
this.stats.registeredInstances = this.registeredInstances.size;
|
||||
|
||||
|
||||
this.emit('instanceRegistered', instanceId, instance);
|
||||
return instanceId;
|
||||
}
|
||||
@@ -162,11 +160,11 @@ export class SyncVarManager extends EventEmitter {
|
||||
this.registeredInstances.delete(instanceId);
|
||||
this.instanceIdMap.delete(instance);
|
||||
this.dirtyInstances.delete(instanceId);
|
||||
|
||||
|
||||
// 更新统计
|
||||
this.stats.registeredInstances = this.registeredInstances.size;
|
||||
this.stats.dirtyInstances = this.dirtyInstances.size;
|
||||
|
||||
|
||||
this.emit('instanceUnregistered', instanceId);
|
||||
return true;
|
||||
}
|
||||
@@ -197,7 +195,7 @@ export class SyncVarManager extends EventEmitter {
|
||||
|
||||
this.markInstanceDirty(instance);
|
||||
this.immediateSyncQueue.add({ instanceId, propertyKey });
|
||||
|
||||
|
||||
// 立即处理同步
|
||||
this.processImmediateSyncs();
|
||||
}
|
||||
@@ -207,10 +205,10 @@ export class SyncVarManager extends EventEmitter {
|
||||
*/
|
||||
public syncNow(): SyncBatch[] {
|
||||
const batches: SyncBatch[] = [];
|
||||
|
||||
|
||||
// 处理立即同步请求
|
||||
this.processImmediateSyncs();
|
||||
|
||||
|
||||
// 收集所有脏实例的数据
|
||||
for (const instanceId of this.dirtyInstances) {
|
||||
const batch = this.createSyncBatch(instanceId);
|
||||
@@ -218,14 +216,14 @@ export class SyncVarManager extends EventEmitter {
|
||||
batches.push(batch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 清理脏标记
|
||||
this.clearAllDirtyFlags();
|
||||
|
||||
|
||||
// 更新统计
|
||||
this.stats.totalSyncs += batches.length;
|
||||
this.stats.lastSyncTime = Date.now();
|
||||
|
||||
|
||||
return batches;
|
||||
}
|
||||
|
||||
@@ -369,7 +367,7 @@ export class SyncVarManager extends EventEmitter {
|
||||
}
|
||||
|
||||
const batches: SyncBatch[] = [];
|
||||
|
||||
|
||||
for (const request of this.immediateSyncQueue) {
|
||||
const batch = this.createSyncBatch(request.instanceId);
|
||||
if (batch && Object.keys(batch.changes).length > 0) {
|
||||
@@ -413,7 +411,7 @@ export class SyncVarManager extends EventEmitter {
|
||||
clearDirtyFlags(instance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.dirtyInstances.clear();
|
||||
this.stats.dirtyInstances = 0;
|
||||
}
|
||||
@@ -461,4 +459,4 @@ export class SyncVarManager extends EventEmitter {
|
||||
// 全局单例访问
|
||||
if (typeof window !== 'undefined') {
|
||||
(window as any).SyncVarManager = SyncVarManager.getInstance();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
*/
|
||||
|
||||
export * from './SyncVarManager';
|
||||
export * from './DeltaSync';
|
||||
export * from './DeltaSync';
|
||||
|
||||
@@ -67,11 +67,11 @@ export class ErrorHandler {
|
||||
private config: ErrorHandlerConfig;
|
||||
private stats: ErrorStats;
|
||||
private eventHandlers: Partial<ErrorHandlerEvents> = {};
|
||||
|
||||
|
||||
// 错误恢复状态
|
||||
private retryAttempts: Map<string, number> = new Map();
|
||||
private pendingRecoveries: Set<string> = new Set();
|
||||
|
||||
|
||||
// 错误分类规则
|
||||
private severityRules: Map<NetworkErrorType, ErrorSeverity> = new Map();
|
||||
private recoveryRules: Map<NetworkErrorType, RecoveryStrategy> = new Map();
|
||||
@@ -105,10 +105,10 @@ export class ErrorHandler {
|
||||
handleError(error: Error | INetworkError, context?: string): void {
|
||||
const networkError = this.normalizeError(error, context);
|
||||
const severity = this.classifyErrorSeverity(networkError);
|
||||
|
||||
|
||||
// 更新统计
|
||||
this.updateStats(networkError, severity);
|
||||
|
||||
|
||||
this.logger.error(`网络错误 [${severity}]: ${networkError.message}`, {
|
||||
type: networkError.type,
|
||||
code: networkError.code,
|
||||
@@ -237,7 +237,7 @@ export class ErrorHandler {
|
||||
*/
|
||||
private determineErrorType(error: Error): NetworkErrorType {
|
||||
const message = error.message.toLowerCase();
|
||||
|
||||
|
||||
if (message.includes('timeout')) {
|
||||
return NetworkErrorType.TIMEOUT;
|
||||
} else if (message.includes('connection')) {
|
||||
@@ -270,18 +270,18 @@ export class ErrorHandler {
|
||||
case NetworkErrorType.CONNECTION_FAILED:
|
||||
case NetworkErrorType.CONNECTION_LOST:
|
||||
return ErrorSeverity.High;
|
||||
|
||||
|
||||
case NetworkErrorType.AUTHENTICATION_FAILED:
|
||||
case NetworkErrorType.PERMISSION_DENIED:
|
||||
return ErrorSeverity.Critical;
|
||||
|
||||
|
||||
case NetworkErrorType.TIMEOUT:
|
||||
case NetworkErrorType.RATE_LIMITED:
|
||||
return ErrorSeverity.Medium;
|
||||
|
||||
|
||||
case NetworkErrorType.INVALID_MESSAGE:
|
||||
return ErrorSeverity.Low;
|
||||
|
||||
|
||||
default:
|
||||
return ErrorSeverity.Medium;
|
||||
}
|
||||
@@ -307,7 +307,7 @@ export class ErrorHandler {
|
||||
}
|
||||
|
||||
const errorId = this.generateErrorId(error);
|
||||
|
||||
|
||||
// 检查是否已经在恢复中
|
||||
if (this.pendingRecoveries.has(errorId)) {
|
||||
return;
|
||||
@@ -339,8 +339,8 @@ export class ErrorHandler {
|
||||
* 执行恢复策略
|
||||
*/
|
||||
private executeRecoveryStrategy(
|
||||
error: INetworkError,
|
||||
strategy: RecoveryStrategy,
|
||||
error: INetworkError,
|
||||
strategy: RecoveryStrategy,
|
||||
errorId: string
|
||||
): void {
|
||||
try {
|
||||
@@ -349,17 +349,17 @@ export class ErrorHandler {
|
||||
// 这里应该重试导致错误的操作
|
||||
// 具体实现需要外部提供重试回调
|
||||
break;
|
||||
|
||||
|
||||
case RecoveryStrategy.Reconnect:
|
||||
// 这里应该触发重连
|
||||
// 具体实现需要外部处理
|
||||
break;
|
||||
|
||||
|
||||
case RecoveryStrategy.Restart:
|
||||
// 这里应该重启相关服务
|
||||
// 具体实现需要外部处理
|
||||
break;
|
||||
|
||||
|
||||
case RecoveryStrategy.Escalate:
|
||||
// 上报错误给上层处理
|
||||
this.logger.error('错误需要上层处理:', error);
|
||||
@@ -368,7 +368,7 @@ export class ErrorHandler {
|
||||
|
||||
this.pendingRecoveries.delete(errorId);
|
||||
this.eventHandlers.errorRecovered?.(error, strategy);
|
||||
|
||||
|
||||
} catch (recoveryError) {
|
||||
this.logger.error('错误恢复失败:', recoveryError);
|
||||
this.pendingRecoveries.delete(errorId);
|
||||
@@ -458,4 +458,4 @@ export class ErrorHandler {
|
||||
return { trend: 'stable', recommendation: '错误处理正常' };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,16 +55,16 @@ export class HeartbeatManager {
|
||||
private config: HeartbeatConfig;
|
||||
private status: HeartbeatStatus;
|
||||
private eventHandlers: Partial<HeartbeatEvents> = {};
|
||||
|
||||
|
||||
// 定时器
|
||||
private heartbeatTimer?: number;
|
||||
private timeoutTimer?: number;
|
||||
|
||||
|
||||
// 延迟测量
|
||||
private pendingPings: Map<number, number> = new Map();
|
||||
private latencyHistory: number[] = [];
|
||||
private sequence = 0;
|
||||
|
||||
|
||||
// 统计信息
|
||||
private sentCount = 0;
|
||||
private receivedCount = 0;
|
||||
@@ -119,10 +119,10 @@ export class HeartbeatManager {
|
||||
const now = Date.now();
|
||||
this.status.lastHeartbeat = now;
|
||||
this.receivedCount++;
|
||||
|
||||
|
||||
// 重置丢失心跳计数
|
||||
this.status.missedHeartbeats = 0;
|
||||
|
||||
|
||||
// 计算延迟
|
||||
if (this.config.enableLatencyMeasurement && message.sequence !== undefined) {
|
||||
const sentTime = this.pendingPings.get(message.sequence);
|
||||
@@ -130,14 +130,14 @@ export class HeartbeatManager {
|
||||
const latency = now - sentTime;
|
||||
this.updateLatency(latency);
|
||||
this.pendingPings.delete(message.sequence);
|
||||
|
||||
|
||||
this.eventHandlers.heartbeatReceived?.(latency);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新健康状态
|
||||
this.updateHealthStatus(true);
|
||||
|
||||
|
||||
// 停止超时定时器
|
||||
this.stopTimeoutTimer();
|
||||
}
|
||||
@@ -148,10 +148,10 @@ export class HeartbeatManager {
|
||||
handleHeartbeatTimeout(): void {
|
||||
this.status.missedHeartbeats++;
|
||||
this.logger.warn(`心跳超时,丢失次数: ${this.status.missedHeartbeats}`);
|
||||
|
||||
|
||||
// 触发超时事件
|
||||
this.eventHandlers.heartbeatTimeout?.(this.status.missedHeartbeats);
|
||||
|
||||
|
||||
// 检查是否达到最大丢失次数
|
||||
if (this.status.missedHeartbeats >= this.config.maxMissedHeartbeats) {
|
||||
this.updateHealthStatus(false);
|
||||
@@ -169,9 +169,9 @@ export class HeartbeatManager {
|
||||
* 获取统计信息
|
||||
*/
|
||||
getStats() {
|
||||
const packetLoss = this.sentCount > 0 ?
|
||||
const packetLoss = this.sentCount > 0 ?
|
||||
((this.sentCount - this.receivedCount) / this.sentCount) * 100 : 0;
|
||||
|
||||
|
||||
return {
|
||||
sentCount: this.sentCount,
|
||||
receivedCount: this.receivedCount,
|
||||
@@ -211,7 +211,7 @@ export class HeartbeatManager {
|
||||
updateConfig(newConfig: Partial<HeartbeatConfig>): void {
|
||||
Object.assign(this.config, newConfig);
|
||||
this.logger.info('心跳配置已更新:', newConfig);
|
||||
|
||||
|
||||
// 重启定时器以应用新配置
|
||||
if (this.heartbeatTimer) {
|
||||
this.stop();
|
||||
@@ -270,7 +270,7 @@ export class HeartbeatManager {
|
||||
|
||||
const now = Date.now();
|
||||
const sequence = this.config.enableLatencyMeasurement ? ++this.sequence : undefined;
|
||||
|
||||
|
||||
const message: HeartbeatMessage = {
|
||||
type: MessageType.HEARTBEAT,
|
||||
clientTime: now,
|
||||
@@ -280,21 +280,21 @@ export class HeartbeatManager {
|
||||
try {
|
||||
this.sendHeartbeat(message);
|
||||
this.sentCount++;
|
||||
|
||||
|
||||
// 记录发送时间用于延迟计算
|
||||
if (sequence !== undefined) {
|
||||
this.pendingPings.set(sequence, now);
|
||||
|
||||
|
||||
// 清理过期的pending pings
|
||||
this.cleanupPendingPings();
|
||||
}
|
||||
|
||||
|
||||
// 启动超时定时器
|
||||
this.stopTimeoutTimer();
|
||||
this.startTimeoutTimer();
|
||||
|
||||
|
||||
this.eventHandlers.heartbeatSent?.(now);
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('发送心跳失败:', error);
|
||||
}
|
||||
@@ -305,16 +305,16 @@ export class HeartbeatManager {
|
||||
*/
|
||||
private updateLatency(latency: number): void {
|
||||
this.status.latency = latency;
|
||||
|
||||
|
||||
// 保存延迟历史(最多100个样本)
|
||||
this.latencyHistory.push(latency);
|
||||
if (this.latencyHistory.length > 100) {
|
||||
this.latencyHistory.shift();
|
||||
}
|
||||
|
||||
|
||||
// 计算平均延迟
|
||||
this.status.averageLatency = this.latencyHistory.reduce((sum, lat) => sum + lat, 0) / this.latencyHistory.length;
|
||||
|
||||
|
||||
this.logger.debug(`延迟更新: ${latency}ms, 平均: ${this.status.averageLatency?.toFixed(1)}ms`);
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ export class HeartbeatManager {
|
||||
private cleanupPendingPings(): void {
|
||||
const now = Date.now();
|
||||
const timeout = this.config.timeout * 2; // 清理超过2倍超时时间的记录
|
||||
|
||||
|
||||
for (const [sequence, sentTime] of this.pendingPings) {
|
||||
if (now - sentTime > timeout) {
|
||||
this.pendingPings.delete(sequence);
|
||||
@@ -363,8 +363,8 @@ export class HeartbeatManager {
|
||||
isConnectionHealthy(): boolean {
|
||||
const now = Date.now();
|
||||
const timeSinceLastHeartbeat = now - this.status.lastHeartbeat;
|
||||
|
||||
return this.status.isHealthy &&
|
||||
|
||||
return this.status.isHealthy &&
|
||||
timeSinceLastHeartbeat <= this.config.timeout &&
|
||||
this.status.missedHeartbeats < this.config.maxMissedHeartbeats;
|
||||
}
|
||||
@@ -378,4 +378,4 @@ export class HeartbeatManager {
|
||||
const multiplier = Math.min(Math.pow(2, this.status.missedHeartbeats), 8);
|
||||
return baseDelay * multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,29 +10,29 @@ export enum MessageType {
|
||||
CONNECT = 'connect',
|
||||
DISCONNECT = 'disconnect',
|
||||
HEARTBEAT = 'heartbeat',
|
||||
|
||||
|
||||
// 数据同步
|
||||
SYNC_VAR = 'sync_var',
|
||||
SYNC_BATCH = 'sync_batch',
|
||||
SYNC_SNAPSHOT = 'sync_snapshot',
|
||||
|
||||
|
||||
// RPC调用
|
||||
RPC_CALL = 'rpc_call',
|
||||
RPC_RESPONSE = 'rpc_response',
|
||||
|
||||
|
||||
// 实体管理
|
||||
ENTITY_CREATE = 'entity_create',
|
||||
ENTITY_DESTROY = 'entity_destroy',
|
||||
ENTITY_UPDATE = 'entity_update',
|
||||
|
||||
|
||||
// 房间管理
|
||||
JOIN_ROOM = 'join_room',
|
||||
LEAVE_ROOM = 'leave_room',
|
||||
ROOM_STATE = 'room_state',
|
||||
|
||||
|
||||
// 游戏事件
|
||||
GAME_EVENT = 'game_event',
|
||||
|
||||
|
||||
// 系统消息
|
||||
ERROR = 'error',
|
||||
WARNING = 'warning',
|
||||
@@ -248,4 +248,4 @@ export interface INetworkError {
|
||||
code?: number;
|
||||
details?: any;
|
||||
timestamp: number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export interface RpcStats {
|
||||
/**
|
||||
* RPC方法签名类型
|
||||
*/
|
||||
export type RpcMethod<TArgs extends readonly unknown[] = readonly unknown[], TReturn = unknown> =
|
||||
export type RpcMethod<TArgs extends readonly unknown[] = readonly unknown[], TReturn = unknown> =
|
||||
(...args: TArgs) => Promise<TReturn>;
|
||||
|
||||
/**
|
||||
@@ -211,4 +211,4 @@ export type ServerRpcInvoker = <TArgs extends readonly unknown[], TReturn>(
|
||||
methodName: string,
|
||||
args: TArgs,
|
||||
options?: Partial<RpcOptions>
|
||||
) => Promise<TReturn>;
|
||||
) => Promise<TReturn>;
|
||||
|
||||
@@ -225,4 +225,4 @@ export interface ITransportConfig {
|
||||
cert?: string;
|
||||
key?: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ export class EventEmitter extends Emitter<string | symbol, void> {
|
||||
|
||||
/**
|
||||
* 添加一次性事件监听器
|
||||
* @param event 事件名称
|
||||
* @param event 事件名称
|
||||
* @param listener 监听函数
|
||||
*/
|
||||
public once(event: string | symbol, listener: Function): this {
|
||||
@@ -82,4 +82,4 @@ export class EventEmitter extends Emitter<string | symbol, void> {
|
||||
super.emit(event, ...args);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* 网络层工具类
|
||||
*/
|
||||
export * from './EventEmitter';
|
||||
export * from './EventEmitter';
|
||||
|
||||
Reference in New Issue
Block a user