Feature/render pipeline (#232)

* refactor(engine): 重构2D渲染管线坐标系统

* feat(engine): 完善2D渲染管线和编辑器视口功能

* feat(editor): 实现Viewport变换工具系统

* feat(editor): 优化Inspector渲染性能并修复Gizmo变换工具显示

* feat(editor): 实现Run on Device移动预览功能

* feat(editor): 添加组件属性控制和依赖关系系统

* feat(editor): 实现动画预览功能和优化SpriteAnimator编辑器

* feat(editor): 修复SpriteAnimator动画预览功能并迁移CI到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(editor): 修复SpriteAnimator动画预览并迁移到pnpm

* feat(ci): 迁移项目到pnpm并修复CI构建问题

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 迁移CI工作流到pnpm并添加WASM构建支持

* chore: 移除 network 相关包

* chore: 移除 network 相关包
This commit is contained in:
YHH
2025-11-23 14:49:37 +08:00
committed by GitHub
parent b15cbab313
commit a3f7cc38b1
247 changed files with 33561 additions and 52047 deletions

View File

@@ -1,124 +0,0 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
console.log('🚀 使用 Rollup 构建 @esengine/network-shared 包...');
async function main() {
try {
// 清理旧的dist目录
if (fs.existsSync('./dist')) {
console.log('🧹 清理旧的构建文件...');
execSync('rimraf ./dist', { stdio: 'inherit' });
}
// 执行Rollup构建
console.log('📦 执行 Rollup 构建...');
execSync('npx rollup -c rollup.config.cjs', { stdio: 'inherit' });
// 生成package.json
console.log('📋 生成 package.json...');
generatePackageJson();
// 复制其他文件
console.log('📁 复制必要文件...');
copyFiles();
// 输出构建结果
showBuildResults();
console.log('✅ @esengine/network-shared 构建完成!');
console.log('\n🚀 发布命令:');
console.log('cd dist && npm publish');
} catch (error) {
console.error('❌ 构建失败:', error.message);
process.exit(1);
}
}
function generatePackageJson() {
const sourcePackage = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
const distPackage = {
name: sourcePackage.name,
version: sourcePackage.version,
description: sourcePackage.description,
main: 'index.cjs',
module: 'index.mjs',
unpkg: 'index.umd.js',
types: 'index.d.ts',
exports: {
'.': {
import: './index.mjs',
require: './index.cjs',
types: './index.d.ts'
}
},
files: [
'index.mjs',
'index.mjs.map',
'index.cjs',
'index.cjs.map',
'index.umd.js',
'index.umd.js.map',
'index.d.ts',
'README.md',
'LICENSE'
],
keywords: [
'ecs',
'network',
'multiplayer',
'game',
'shared',
'typescript',
'cocos-creator',
'laya'
],
author: sourcePackage.author,
license: sourcePackage.license,
repository: sourcePackage.repository,
peerDependencies: sourcePackage.peerDependencies,
dependencies: sourcePackage.dependencies,
publishConfig: sourcePackage.publishConfig,
engines: {
node: '>=16.0.0'
},
sideEffects: false
};
fs.writeFileSync('./dist/package.json', JSON.stringify(distPackage, null, 2));
}
function copyFiles() {
const filesToCopy = [
{ src: './README.md', dest: './dist/README.md' },
{ src: './LICENSE', dest: './dist/LICENSE' }
];
filesToCopy.forEach(({ src, dest }) => {
if (fs.existsSync(src)) {
fs.copyFileSync(src, dest);
console.log(` ✓ 复制: ${path.basename(dest)}`);
} else {
console.log(` ⚠️ 文件不存在: ${src}`);
}
});
}
function showBuildResults() {
const distDir = './dist';
const files = ['index.mjs', 'index.cjs', 'index.umd.js', 'index.d.ts'];
console.log('\n📊 构建结果:');
files.forEach(file => {
const filePath = path.join(distDir, file);
if (fs.existsSync(filePath)) {
const size = fs.statSync(filePath).size;
console.log(` ${file}: ${(size / 1024).toFixed(1)}KB`);
}
});
}
main().catch(console.error);

View File

@@ -1,51 +0,0 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/*.test.ts', '**/*.spec.ts'],
testPathIgnorePatterns: ['/node_modules/'],
collectCoverage: false,
collectCoverageFrom: [
'src/**/*.ts',
'!src/index.ts',
'!src/**/index.ts',
'!**/*.d.ts',
'!src/**/*.test.ts',
'!src/**/*.spec.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
coverageThreshold: {
global: {
branches: 10,
functions: 20,
lines: 25,
statements: 25
}
},
verbose: true,
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.json',
useESM: false,
}],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'^@esengine/ecs-framework$': '<rootDir>/../core/src/index.ts',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
setupFilesAfterEnv: ['<rootDir>/tests/setup.ts'],
testTimeout: 10000,
clearMocks: true,
restoreMocks: true,
modulePathIgnorePatterns: [
'<rootDir>/bin/',
'<rootDir>/dist/',
'<rootDir>/node_modules/'
],
transformIgnorePatterns: [
'node_modules/(?!(.*\\.mjs$|@esengine))'
]
};

View File

@@ -1,72 +0,0 @@
{
"name": "@esengine/network-shared",
"version": "1.0.2",
"description": "ECS Framework网络层 - 共享组件和协议",
"main": "bin/index.js",
"types": "bin/index.d.ts",
"exports": {
".": {
"types": "./bin/index.d.ts",
"import": "./bin/index.js",
"development": {
"types": "./src/index.ts",
"import": "./src/index.ts"
}
}
},
"files": [
"bin/**/*",
"README.md",
"LICENSE"
],
"keywords": [
"ecs",
"network",
"multiplayer",
"game",
"typescript",
"shared"
],
"scripts": {
"clean": "rimraf bin dist tsconfig.tsbuildinfo",
"build:ts": "tsc --build",
"prebuild": "npm run clean",
"build": "npm run build:ts",
"build:watch": "tsc --watch",
"rebuild": "npm run clean && npm run build",
"build:npm": "npm run build && node build-rollup.cjs",
"publish:npm": "npm run build:npm && cd dist && npm publish",
"publish:patch": "npm version patch && npm run build:npm && cd dist && npm publish",
"publish:minor": "npm version minor && npm run build:npm && cd dist && npm publish",
"publish:major": "npm version major && npm run build:npm && cd dist && npm publish",
"preversion": "npm run rebuild",
"test": "jest --config jest.config.cjs",
"test:watch": "jest --watch --config jest.config.cjs",
"test:coverage": "jest --coverage --config jest.config.cjs",
"pretest:ci": "npm run build",
"test:ci": "jest --ci --coverage --config jest.config.cjs"
},
"author": "yhh",
"license": "MIT",
"dependencies": {
"@esengine/ecs-framework": "file:../core",
"reflect-metadata": "^0.2.2"
},
"devDependencies": {
"@types/jest": "^29.5.14",
"@types/node": "^20.19.0",
"jest": "^29.7.0",
"rimraf": "^5.0.0",
"ts-jest": "^29.4.0",
"typescript": "^5.8.3"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org/"
},
"repository": {
"type": "git",
"url": "https://github.com/esengine/ecs-framework.git",
"directory": "packages/network-shared"
}
}

View File

@@ -1,128 +0,0 @@
const resolve = require('@rollup/plugin-node-resolve');
const commonjs = require('@rollup/plugin-commonjs');
const terser = require('@rollup/plugin-terser');
const dts = require('rollup-plugin-dts').default;
const { readFileSync } = require('fs');
const pkg = JSON.parse(readFileSync('./package.json', 'utf8'));
const banner = `/**
* @esengine/network-shared v${pkg.version}
* ECS网络层共享组件和协议
*
* @author ${pkg.author}
* @license ${pkg.license}
*/`;
// 外部依赖不打包进bundle
const external = ['@esengine/ecs-framework', 'reflect-metadata'];
const commonPlugins = [
resolve({
browser: true,
preferBuiltins: false
}),
commonjs({
include: /node_modules/
})
];
module.exports = [
// ES模块构建
{
input: 'bin/index.js',
output: {
file: 'dist/index.mjs',
format: 'es',
banner,
sourcemap: true,
exports: 'named'
},
plugins: [
...commonPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false,
unknownGlobalSideEffects: false
}
},
// CommonJS构建
{
input: 'bin/index.js',
output: {
file: 'dist/index.cjs',
format: 'cjs',
banner,
sourcemap: true,
exports: 'named'
},
plugins: [
...commonPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
treeshake: {
moduleSideEffects: false
}
},
// UMD构建 - 包含所有依赖,用于浏览器直接使用
{
input: 'bin/index.js',
output: {
file: 'dist/index.umd.js',
format: 'umd',
name: 'ECSNetworkShared',
banner,
sourcemap: true,
exports: 'named',
globals: {
'@esengine/ecs-framework': 'ECS',
'reflect-metadata': 'Reflect'
}
},
plugins: [
...commonPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
treeshake: {
moduleSideEffects: false
}
},
// 类型定义构建
{
input: 'bin/index.d.ts',
output: {
file: 'dist/index.d.ts',
format: 'es',
banner: `/**
* @esengine/network-shared v${pkg.version}
* TypeScript definitions
*/`
},
plugins: [
dts({
respectExternal: true
})
],
external
}
];

View File

@@ -1,317 +0,0 @@
/**
* 网络身份组件
*/
import { Component, Emitter } from '@esengine/ecs-framework';
import { AuthorityType, NetworkScope } from '../types/NetworkTypes';
import {
NetworkEventType,
NetworkIdentityEventData,
NetworkEventUtils
} from '../events/NetworkEvents';
/**
* 网络身份组件
*
* 为实体提供网络同步能力的核心组件。
* 每个需要网络同步的实体都必须拥有此组件。
*
* 集成了事件系统,当属性变化时自动发射事件用于网络同步。
*/
export class NetworkIdentity extends Component {
/**
* 事件发射器
* 用于发射网络相关事件
*/
private eventEmitter = new Emitter<NetworkEventType, NetworkIdentity>();
/**
* 网络ID (全局唯一)
* 用于在网络中标识实体
*/
public networkId: number = 0;
/**
* 拥有者ID
* 表示哪个客户端拥有此实体的控制权
*/
public ownerId: string = '';
/**
* 权限类型
* 决定哪一端对此实体有控制权
*/
public authority: AuthorityType = AuthorityType.Server;
/**
* 同步频率 (Hz)
* 每秒同步的次数
*/
public syncRate: number = 20;
/**
* 网络作用域
* 决定哪些客户端可以看到此实体
*/
public scope: NetworkScope = NetworkScope.Room;
/**
* 是否是本地玩家
* 标识此实体是否代表本地玩家
*/
public isLocalPlayer: boolean = false;
/**
* 是否启用网络同步
* 临时禁用/启用同步
*/
public syncEnabled: boolean = true;
/**
* 同步优先级
* 影响同步的顺序和频率,数值越高优先级越高
*/
public priority: number = 0;
/**
* 距离阈值
* 用于附近同步模式,超过此距离的客户端不会收到同步
*/
public distanceThreshold: number = 100;
/**
* 最后同步时间
* 记录上次同步的时间戳
*/
public lastSyncTime: number = 0;
/**
* 是否可见
* 控制实体是否对其他客户端可见
*/
public visible: boolean = true;
/**
* 自定义同步过滤器
* 用于自定义作用域的同步逻辑
*/
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 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;
}
}
/**
* 检查客户端是否有权限修改此实体
*/
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 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 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 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);
}
/**
* 监听属性变化事件
*/
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 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 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 {
// 清理所有事件监听器
this.eventEmitter.dispose();
}
}

View File

@@ -1,249 +0,0 @@
import 'reflect-metadata';
import { RpcTarget } from '../types/NetworkTypes';
import { RpcOptions, RpcMethodMetadata } from '../types/RpcTypes';
/**
* RPC元数据键
*/
const RPC_METADATA_KEY = Symbol('rpc:metadata');
const RPC_METHODS_KEY = Symbol('rpc:methods');
/**
* 服务端RPC装饰器选项
*/
export interface ServerRpcOptions extends RpcOptions {
/** 允许的调用者类型 */
allowedCallers?: 'all' | 'authenticated' | 'admin';
/** 最大并发调用数 */
maxConcurrent?: number;
}
/**
* 客户端RPC装饰器选项
*/
export interface ClientRpcOptions extends RpcOptions {
/** 广播到多个客户端时的聚合策略 */
aggregationStrategy?: 'first' | 'all' | 'majority';
/** 缓存策略 */
cacheStrategy?: 'none' | 'memory' | 'persistent';
/** 缓存有效期(毫秒) */
cacheTTL?: number;
}
/**
* 服务端RPC装饰器
* 标记方法可以被客户端调用
*/
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,
isServerRpc: true,
options: {
reliable: true,
priority: 5,
target: RpcTarget.Server,
timeout: 30000,
requireAuth: false,
rateLimit: 60,
...options
},
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[]) {
// 这里可以添加调用前的验证逻辑
try {
const result = await originalMethod.apply(this, args);
return result;
} catch (error) {
// 这里可以添加错误处理逻辑
throw error;
}
};
return descriptor;
};
}
/**
* 客户端RPC装饰器
* 标记方法可以调用到客户端
*/
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,
isServerRpc: false,
options: {
reliable: false,
priority: 3,
target: RpcTarget.All,
timeout: 10000,
requireAuth: false,
rateLimit: 30,
...options
},
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[]) {
// 这里将被RPC调用器替换
// 目前保持原方法以支持本地测试
return originalMethod.apply(this, args);
};
return descriptor;
};
}
/**
* 获取类的所有RPC方法元数据
*/
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) {
throw new Error(`RPC元数据未找到: ${target.name}.${methodName}`);
}
return metadata;
});
}
/**
* 获取特定方法的RPC元数据
*/
export function getRpcMethodMetadata(
target: object,
methodName: string | symbol
): RpcMethodMetadata | undefined {
return Reflect.getMetadata(RPC_METADATA_KEY, target, methodName);
}
/**
* 检查方法是否为RPC方法
*/
export function isRpcMethod(target: object, methodName: string | symbol): boolean {
return Reflect.hasMetadata(RPC_METADATA_KEY, target, methodName);
}
/**
* 检查方法是否为服务端RPC
*/
export function isServerRpcMethod(target: object, methodName: string | symbol): boolean {
const metadata = getRpcMethodMetadata(target, methodName);
return metadata?.isServerRpc === true;
}
/**
* 检查方法是否为客户端RPC
*/
export function isClientRpcMethod(target: object, methodName: string | symbol): boolean {
const metadata = getRpcMethodMetadata(target, methodName);
return metadata?.isServerRpc === false;
}
/**
* RPC方法验证器
*/
export class RpcMethodValidator {
/**
* 验证RPC方法调用参数
*/
static validateCall(
metadata: RpcMethodMetadata,
args: unknown[],
callerId?: string
): { valid: boolean; error?: string } {
// 参数数量检查
if (args.length !== metadata.paramTypes.length) {
return {
valid: false,
error: `参数数量不匹配,期望 ${metadata.paramTypes.length} 个,实际 ${args.length}`
};
}
// 权限检查
if (metadata.options.requireAuth && !callerId) {
return {
valid: false,
error: '该方法需要身份验证'
};
}
return { valid: true };
}
/**
* 验证RPC方法定义
*/
static validateMethodDefinition(metadata: RpcMethodMetadata): { valid: boolean; error?: string } {
// 方法名检查
if (!metadata.methodName || typeof metadata.methodName !== 'string') {
return {
valid: false,
error: '方法名无效'
};
}
// 超时时间检查
if (metadata.options.timeout && metadata.options.timeout <= 0) {
return {
valid: false,
error: '超时时间必须大于0'
};
}
// 优先级检查
if (metadata.options.priority !== undefined &&
(metadata.options.priority < 0 || metadata.options.priority > 10)) {
return {
valid: false,
error: '优先级必须在0-10之间'
};
}
return { valid: true };
}
}

View File

@@ -1,328 +0,0 @@
import { SyncMode, AuthorityType, NetworkScope } from '../types/NetworkTypes';
/**
* SyncVar支持的值类型
*/
export type SyncVarValue = string | number | boolean | object | null | undefined;
/**
* SyncVar配置选项
*/
export interface SyncVarOptions<T = SyncVarValue> {
/** 同步模式 */
mode?: SyncMode;
/** 同步频率(毫秒) */
syncRate?: number;
/** 权限类型 */
authority?: AuthorityType;
/** 网络作用域 */
scope?: NetworkScope;
/** 变化阈值,用于数值类型 */
threshold?: number;
/** 是否启用压缩 */
compression?: boolean;
/** 优先级(0-10数值越高优先级越高) */
priority?: number;
/** 是否启用插值 */
interpolation?: boolean;
/** 自定义序列化函数 */
customSerializer?: (value: T) => unknown;
/** 自定义反序列化函数 */
customDeserializer?: (value: unknown) => T;
/** 变化回调函数 */
onChanged?: (oldValue: T, newValue: T) => void;
}
/**
* SyncVar元数据
*/
export interface SyncVarMetadata<T = SyncVarValue> {
propertyKey: string | symbol;
options: Required<SyncVarOptions<T>>;
originalDescriptor?: PropertyDescriptor;
lastValue?: T;
lastSyncTime: number;
isDirty: boolean;
syncCount: number;
}
/**
* 存储SyncVar元数据的Symbol键
*/
export const SYNCVAR_METADATA_KEY = Symbol('SyncVarMetadata');
/**
* 默认SyncVar配置
*/
export const DEFAULT_SYNCVAR_OPTIONS: Required<SyncVarOptions<SyncVarValue>> = {
mode: SyncMode.All,
syncRate: 100,
authority: AuthorityType.Server,
scope: NetworkScope.Global,
threshold: 0.001,
compression: true,
priority: 5,
interpolation: false,
customSerializer: (value: SyncVarValue) => value,
customDeserializer: (value: unknown) => value as SyncVarValue,
onChanged: () => {}
};
/**
* SyncVar装饰器
* 用于标记需要网络同步的属性
*/
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,
options: fullOptions,
lastSyncTime: 0,
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() {
return (this as any)[internalPropertyKey];
},
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);
}
},
enumerable: originalDescriptor.enumerable,
configurable: originalDescriptor.configurable
});
// 设置初始值
if (originalDescriptor.value !== undefined) {
(target as any)[internalPropertyKey] = originalDescriptor.value;
}
};
}
/**
* 检查值是否发生了变化
*/
function hasValueChanged(oldValue: SyncVarValue, newValue: SyncVarValue, threshold: number): boolean {
// 严格相等检查
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;
}
/**
* 深度比较两个对象是否相等
*/
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;
}
/**
* 标记属性为脏数据
*/
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);
}
}
/**
* 请求立即同步
*/
function requestImmediateSync(instance: object, propertyKey: string | symbol): void {
// 通知SyncVar管理器立即同步
if (typeof window !== 'undefined' && (window as any).SyncVarManager) {
(window as any).SyncVarManager.requestImmediateSync(instance, propertyKey);
}
}
/**
* 获取对象的所有SyncVar元数据
*/
export function getSyncVarMetadata(target: object): Map<string | symbol, SyncVarMetadata> {
if (!target || !target.constructor) {
return new Map();
}
return (target.constructor as any)[SYNCVAR_METADATA_KEY] || new Map();
}
/**
* 获取特定属性的SyncVar元数据
*/
export function getSyncVarPropertyMetadata(target: object, propertyKey: string | symbol): SyncVarMetadata | undefined {
const metadataMap = getSyncVarMetadata(target);
return metadataMap.get(propertyKey);
}
/**
* 检查对象是否有SyncVar属性
*/
export function hasSyncVars(target: object): boolean {
const metadataMap = getSyncVarMetadata(target);
return metadataMap.size > 0;
}
/**
* 获取对象的所有脏SyncVar属性
*/
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;
}
/**
* 清理所有脏标记
*/
export function clearDirtyFlags(target: object): void {
const metadataMap = getSyncVarMetadata(target);
for (const metadata of metadataMap.values()) {
metadata.isDirty = false;
metadata.lastSyncTime = Date.now();
metadata.syncCount++;
}
}
/**
* 重置SyncVar统计信息
*/
export function resetSyncVarStats(target: object): void {
const metadataMap = getSyncVarMetadata(target);
for (const metadata of metadataMap.values()) {
metadata.syncCount = 0;
metadata.lastSyncTime = 0;
}
}
/**
* 获取SyncVar统计信息
*/
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,
lastSyncTime: metadata.lastSyncTime,
isDirty: metadata.isDirty
};
}
return stats;
}

View File

@@ -1,6 +0,0 @@
/**
* 网络装饰器导出
*/
export * from './SyncVar';
export * from './RpcDecorators';

View File

@@ -1,287 +0,0 @@
/**
* 网络事件类型枚举
* 定义网络层中的所有事件类型
*/
export enum NetworkEventType {
// 连接相关事件
CONNECTION_ESTABLISHED = 'network:connection:established',
CONNECTION_LOST = 'network:connection:lost',
CONNECTION_ERROR = 'network:connection:error',
CONNECTION_TIMEOUT = 'network:connection:timeout',
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',
IDENTITY_OWNER_CHANGED = 'network:identity:owner:changed',
IDENTITY_AUTHORITY_CHANGED = 'network:identity:authority:changed',
IDENTITY_SYNC_ENABLED = 'network:identity:sync:enabled',
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',
RPC_RESPONSE_SENT = 'network:rpc:response:sent',
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',
MESSAGE_QUEUED = 'network:message:queued',
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',
ROOM_CREATED = 'network:room:created',
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',
PACKET_LOSS_DETECTED = 'network:packet:loss:detected',
PERFORMANCE_DEGRADED = 'network:performance:degraded'
}
/**
* 网络事件优先级
*/
export enum NetworkEventPriority {
LOW = 10,
NORMAL = 20,
HIGH = 30,
CRITICAL = 40,
EMERGENCY = 50
}
/**
* 网络事件数据基础接口
*/
export interface NetworkEventData {
timestamp: number;
networkId?: number;
clientId?: string;
roomId?: string;
[key: string]: any;
}
/**
* 网络身份事件数据
*/
export interface NetworkIdentityEventData extends NetworkEventData {
networkId: number;
ownerId: string;
oldValue?: any;
newValue?: any;
}
/**
* RPC事件数据
*/
export interface RpcEventData extends NetworkEventData {
rpcId: string;
methodName: string;
parameters?: any[];
result?: any;
error?: string;
}
/**
* 消息事件数据
*/
export interface MessageEventData extends NetworkEventData {
messageId: string;
messageType: string;
payload: any;
reliable: boolean;
size: number;
}
/**
* 连接事件数据
*/
export interface ConnectionEventData extends NetworkEventData {
clientId: string;
address?: string;
reason?: string;
reconnectAttempt?: number;
}
/**
* 房间事件数据
*/
export interface RoomEventData extends NetworkEventData {
roomId: string;
playerId?: string;
playerCount?: number;
maxPlayers?: number;
}
/**
* 性能事件数据
*/
export interface PerformanceEventData extends NetworkEventData {
metric: string;
value: number;
threshold?: number;
duration?: number;
}
/**
* 网络事件工具类
*/
export class NetworkEventUtils {
/**
* 创建网络身份事件数据
*/
static createIdentityEventData(
networkId: number,
ownerId: string,
oldValue?: any,
newValue?: any
): NetworkIdentityEventData {
return {
timestamp: Date.now(),
networkId,
ownerId,
oldValue,
newValue
};
}
/**
* 创建RPC事件数据
*/
static createRpcEventData(
rpcId: string,
methodName: string,
clientId?: string,
parameters?: any[],
result?: any,
error?: string
): RpcEventData {
return {
timestamp: Date.now(),
clientId,
rpcId,
methodName,
parameters,
result,
error
};
}
/**
* 创建消息事件数据
*/
static createMessageEventData(
messageId: string,
messageType: string,
payload: any,
reliable: boolean = true,
clientId?: string
): MessageEventData {
const size = JSON.stringify(payload).length;
return {
timestamp: Date.now(),
clientId,
messageId,
messageType,
payload,
reliable,
size
};
}
/**
* 创建连接事件数据
*/
static createConnectionEventData(
clientId: string,
address?: string,
reason?: string,
reconnectAttempt?: number
): ConnectionEventData {
return {
timestamp: Date.now(),
clientId,
address,
reason,
reconnectAttempt
};
}
/**
* 创建房间事件数据
*/
static createRoomEventData(
roomId: string,
playerId?: string,
playerCount?: number,
maxPlayers?: number
): RoomEventData {
return {
timestamp: Date.now(),
roomId,
playerId,
playerCount,
maxPlayers
};
}
/**
* 创建性能事件数据
*/
static createPerformanceEventData(
metric: string,
value: number,
threshold?: number,
duration?: number,
clientId?: string
): PerformanceEventData {
return {
timestamp: Date.now(),
clientId,
metric,
value,
threshold,
duration
};
}
}

View File

@@ -1 +0,0 @@
export * from './NetworkEvents';

View File

@@ -1,62 +0,0 @@
/**
* @esengine/network-shared
* ECS Framework网络层 - 共享组件和协议
*/
// 类型定义
export * from './types/NetworkTypes';
export * from './types/TransportTypes';
export * from './types/RpcTypes';
// 协议消息
export * from './protocols/MessageTypes';
export * from './protocols/MessageManager';
// 核心组件
export * from './components/NetworkIdentity';
// 传输层
export * from './transport/HeartbeatManager';
export * from './transport/ErrorHandler';
// 事件系统
export * from './events/NetworkEvents';
// 序列化系统
export * from './serialization/JSONSerializer';
export * from './serialization/MessageCompressor';
export {
SyncVarSerializer,
SyncVarSerializerConfig,
SerializationResult as SyncVarSerializationResult,
DeserializationResult as SyncVarDeserializationResult,
DeltaData as SyncVarDeltaData,
CompressionMetadata
} from './serialization/SyncVarSerializer';
// 装饰器系统
export * from './decorators';
// RPC系统
export * from './rpc/RpcMetadataManager';
export * from './rpc/RpcCallHandler';
export * from './rpc/RpcCallProxy';
export * from './rpc/RpcReliabilityManager';
// 同步系统
export { SyncVarManager, SyncBatch } from './sync/SyncVarManager';
export {
DeltaSync,
DeltaSyncConfig,
DeltaData,
DeltaOperationType,
DeltaOperation,
VersionedData,
DeltaSyncStats
} from './sync/DeltaSync';
// 监控系统
export * from './monitoring';
// 工具类
export * from './utils';

View File

@@ -1,541 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
/**
* 带宽监控配置
*/
export interface BandwidthMonitorConfig {
/** 监控间隔(毫秒) */
monitorInterval: number;
/** 采样窗口大小 */
sampleWindowSize: number;
/** 预警阈值(0-1) */
warningThreshold: number;
/** 严重阈值(0-1) */
criticalThreshold: number;
/** 是否启用自适应调整 */
enableAdaptive: boolean;
/** 自适应调整因子 */
adaptiveFactor: number;
}
/**
* 带宽样本
*/
export interface BandwidthSample {
timestamp: number;
bytesIn: number;
bytesOut: number;
packetsIn: number;
packetsOut: number;
latency: number;
}
/**
* 带宽统计
*/
export interface BandwidthStats {
/** 当前上行带宽(bytes/s) */
currentUpload: number;
/** 当前下行带宽(bytes/s) */
currentDownload: number;
/** 平均上行带宽(bytes/s) */
averageUpload: number;
/** 平均下行带宽(bytes/s) */
averageDownload: number;
/** 峰值上行带宽(bytes/s) */
peakUpload: number;
/** 峰值下行带宽(bytes/s) */
peakDownload: number;
/** 总上传字节数 */
totalUpload: number;
/** 总下载字节数 */
totalDownload: number;
/** 当前包速率(packets/s) */
currentPacketRate: number;
/** 平均延迟(ms) */
averageLatency: number;
/** 延迟抖动(ms) */
latencyJitter: number;
/** 利用率(0-1) */
utilization: number;
}
/**
* 带宽限制
*/
export interface BandwidthLimit {
/** 上行限制(bytes/s) */
uploadLimit: number;
/** 下行限制(bytes/s) */
downloadLimit: number;
/** 是否启用限制 */
enabled: boolean;
}
/**
* 带宽警告级别
*/
export enum BandwidthWarningLevel {
Normal = 'normal',
Warning = 'warning',
Critical = 'critical'
}
/**
* 带宽事件
*/
export interface BandwidthMonitorEvents {
bandwidthChanged: (stats: BandwidthStats) => void;
limitExceeded: (direction: 'upload' | 'download', current: number, limit: number) => void;
warningLevelChanged: (level: BandwidthWarningLevel, stats: BandwidthStats) => void;
adaptiveAdjustment: (oldLimits: BandwidthLimit, newLimits: BandwidthLimit) => void;
}
/**
* 带宽监控器
* 负责监控网络带宽使用情况并提供自适应调整
*/
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,
currentDownload: 0,
averageUpload: 0,
averageDownload: 0,
peakUpload: 0,
peakDownload: 0,
totalUpload: 0,
totalDownload: 0,
currentPacketRate: 0,
averageLatency: 0,
latencyJitter: 0,
utilization: 0
};
/** 上次统计时间 */
private lastStatsTime = Date.now();
/** 累计字节数 */
private cumulativeBytesIn = 0;
private cumulativeBytesOut = 0;
private cumulativePacketsIn = 0;
private cumulativePacketsOut = 0;
constructor(config: Partial<BandwidthMonitorConfig> = {}) {
super();
this.config = {
monitorInterval: 1000,
sampleWindowSize: 60,
warningThreshold: 0.8,
criticalThreshold: 0.95,
enableAdaptive: true,
adaptiveFactor: 0.1,
...config
};
this.limits = {
uploadLimit: 1024 * 1024, // 1MB/s
downloadLimit: 1024 * 1024, // 1MB/s
enabled: false
};
this.startMonitoring();
}
/**
* 记录网络活动
*/
public recordActivity(bytesIn: number, bytesOut: number, packetsIn: number = 0, packetsOut: number = 0, latency: number = 0): void {
this.cumulativeBytesIn += bytesIn;
this.cumulativeBytesOut += bytesOut;
this.cumulativePacketsIn += packetsIn;
this.cumulativePacketsOut += packetsOut;
this.stats.totalUpload += bytesOut;
this.stats.totalDownload += bytesIn;
}
/**
* 设置带宽限制
*/
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);
}
}
/**
* 获取当前统计信息
*/
public getStats(): BandwidthStats {
return { ...this.stats };
}
/**
* 获取当前限制
*/
public getLimits(): BandwidthLimit {
return { ...this.limits };
}
/**
* 获取当前警告级别
*/
public getWarningLevel(): BandwidthWarningLevel {
return this.currentWarningLevel;
}
/**
* 检查是否超过限制
*/
public isOverLimit(): { upload: boolean; download: boolean } {
if (!this.limits.enabled) {
return { upload: false, download: false };
}
return {
upload: this.stats.currentUpload > this.limits.uploadLimit,
download: this.stats.currentDownload > this.limits.downloadLimit
};
}
/**
* 获取建议的数据发送大小
*/
public getRecommendedSendSize(): number {
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) {
return this.limits.uploadLimit * 0.05; // 5% of limit
} else {
return this.limits.uploadLimit * 0.01; // 1% of limit
}
}
/**
* 获取发送延迟建议
*/
public getRecommendedDelay(): number {
const utilization = Math.max(
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) {
return 100; // 100ms delay
} else {
return 500; // 500ms delay
}
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
currentUpload: 0,
currentDownload: 0,
averageUpload: 0,
averageDownload: 0,
peakUpload: 0,
peakDownload: 0,
totalUpload: 0,
totalDownload: 0,
currentPacketRate: 0,
averageLatency: 0,
latencyJitter: 0,
utilization: 0
};
this.samples.length = 0;
this.cumulativeBytesIn = 0;
this.cumulativeBytesOut = 0;
this.cumulativePacketsIn = 0;
this.cumulativePacketsOut = 0;
this.lastStatsTime = Date.now();
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<BandwidthMonitorConfig>): void {
Object.assign(this.config, newConfig);
if (newConfig.monitorInterval !== undefined) {
this.restartMonitoring();
}
}
/**
* 销毁监控器
*/
public destroy(): void {
this.stopMonitoring();
this.samples.length = 0;
this.removeAllListeners();
}
/**
* 开始监控
*/
private startMonitoring(): void {
if (this.monitorTimer) {
return;
}
this.monitorTimer = setInterval(() => {
this.updateStats();
}, this.config.monitorInterval);
}
/**
* 停止监控
*/
private stopMonitoring(): void {
if (this.monitorTimer) {
clearInterval(this.monitorTimer);
this.monitorTimer = null;
}
}
/**
* 重启监控
*/
private restartMonitoring(): void {
this.stopMonitoring();
this.startMonitoring();
}
/**
* 更新统计信息
*/
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,
bytesIn: this.cumulativeBytesIn,
bytesOut: this.cumulativeBytesOut,
packetsIn: this.cumulativePacketsIn,
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);
}
/**
* 计算平均值
*/
private calculateAverages(): void {
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();
}
/**
* 计算延迟抖动
*/
private calculateLatencyJitter(): void {
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;
}
/**
* 计算利用率
*/
private calculateUtilization(): void {
if (!this.limits.enabled) {
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);
}
/**
* 检查限制
*/
private checkLimits(): void {
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);
}
}
/**
* 检查警告级别
*/
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);
}
}
/**
* 执行自适应调整
*/
private performAdaptiveAdjustment(): void {
if (!this.limits.enabled || this.stats.utilization < this.config.warningThreshold) {
return;
}
const oldLimits = { ...this.limits };
// 根据当前利用率动态调整限制
if (this.stats.utilization > this.config.criticalThreshold) {
// 严重超载,降低限制
this.limits.uploadLimit *= (1 - this.config.adaptiveFactor);
this.limits.downloadLimit *= (1 - this.config.adaptiveFactor);
} else if (this.stats.utilization < this.config.warningThreshold * 0.5) {
// 利用率较低,可以提高限制
this.limits.uploadLimit *= (1 + this.config.adaptiveFactor * 0.5);
this.limits.downloadLimit *= (1 + this.config.adaptiveFactor * 0.5);
}
// 检查是否有变化
if (this.limits.uploadLimit !== oldLimits.uploadLimit ||
this.limits.downloadLimit !== oldLimits.downloadLimit) {
this.emit('adaptiveAdjustment', oldLimits, this.limits);
}
}
}

View File

@@ -1,5 +0,0 @@
/**
* 监控模块导出
*/
export * from './BandwidthMonitor';

View File

@@ -1,502 +0,0 @@
/**
* 消息管理器
* 负责消息ID生成、时间戳管理和消息验证
*/
import { createLogger } from '@esengine/ecs-framework';
import { INetworkMessage, MessageType } from '../types/NetworkTypes';
/**
* 消息ID生成器类型
*/
export enum MessageIdGeneratorType {
UUID = 'uuid',
SNOWFLAKE = 'snowflake',
SEQUENTIAL = 'sequential',
TIMESTAMP = 'timestamp'
}
/**
* 消息管理器配置
*/
export interface MessageManagerConfig {
idGenerator: MessageIdGeneratorType;
enableTimestampValidation: boolean;
maxTimestampDrift: number; // 最大时间戳偏移(毫秒)
enableMessageDeduplication: boolean;
deduplicationWindowMs: number; // 去重窗口时间
enableMessageOrdering: boolean;
orderingWindowMs: number; // 排序窗口时间
maxPendingMessages: number; // 最大待处理消息数
}
/**
* 消息验证结果
*/
export interface MessageValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}
/**
* 消息统计信息
*/
export interface MessageStats {
totalGenerated: number;
totalValidated: number;
validMessages: number;
invalidMessages: number;
duplicateMessages: number;
outOfOrderMessages: number;
timestampErrors: number;
}
/**
* Snowflake ID生成器
*/
class SnowflakeIdGenerator {
private static readonly EPOCH = 1640995200000; // 2022-01-01 00:00:00 UTC
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;
private lastTimestamp = -1;
constructor(workerId: number = 1, datacenterId: number = 1) {
this.workerId = workerId & ((1 << SnowflakeIdGenerator.WORKER_ID_BITS) - 1);
this.datacenterId = datacenterId & ((1 << SnowflakeIdGenerator.DATACENTER_ID_BITS) - 1);
}
generate(): string {
let timestamp = Date.now();
if (timestamp < this.lastTimestamp) {
throw new Error('时钟回拨无法生成ID');
}
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1) & ((1 << SnowflakeIdGenerator.SEQUENCE_BITS) - 1);
if (this.sequence === 0) {
// 等待下一毫秒
while (timestamp <= this.lastTimestamp) {
timestamp = Date.now();
}
}
} else {
this.sequence = 0;
}
this.lastTimestamp = timestamp;
const id = ((timestamp - SnowflakeIdGenerator.EPOCH) << 22) |
(this.datacenterId << 17) |
(this.workerId << 12) |
this.sequence;
return id.toString();
}
}
/**
* 消息管理器
*/
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;
/**
* 构造函数
*/
constructor(config: Partial<MessageManagerConfig> = {}) {
this.config = {
idGenerator: MessageIdGeneratorType.UUID,
enableTimestampValidation: true,
maxTimestampDrift: 60000, // 1分钟
enableMessageDeduplication: true,
deduplicationWindowMs: 300000, // 5分钟
enableMessageOrdering: false,
orderingWindowMs: 10000, // 10秒
maxPendingMessages: 1000,
...config
};
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
this.snowflakeGenerator = new SnowflakeIdGenerator();
this.startCleanupTimer();
}
/**
* 生成消息ID
*/
generateMessageId(): string {
this.stats.totalGenerated++;
switch (this.config.idGenerator) {
case MessageIdGeneratorType.UUID:
return this.generateUUID();
case MessageIdGeneratorType.SNOWFLAKE:
return this.snowflakeGenerator.generate();
case MessageIdGeneratorType.SEQUENTIAL:
return (++this.sequentialId).toString();
case MessageIdGeneratorType.TIMESTAMP:
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
default:
return this.generateUUID();
}
}
/**
* 创建网络消息
*/
createMessage<T extends INetworkMessage>(
type: MessageType,
data: any,
senderId: string,
options: {
reliable?: boolean;
priority?: number;
timestamp?: number;
} = {}
): T {
const message: INetworkMessage = {
type,
messageId: this.generateMessageId(),
timestamp: options.timestamp || Date.now(),
senderId,
data,
reliable: options.reliable,
priority: options.priority
};
return message as T;
}
/**
* 验证消息
*/
validateMessage(message: INetworkMessage, senderId?: string): MessageValidationResult {
this.stats.totalValidated++;
const errors: string[] = [];
const warnings: string[] = [];
// 基础字段验证
if (!message.messageId) {
errors.push('消息ID不能为空');
}
if (!message.type) {
errors.push('消息类型不能为空');
} else if (!Object.values(MessageType).includes(message.type)) {
errors.push(`无效的消息类型: ${message.type}`);
}
if (!message.timestamp) {
errors.push('时间戳不能为空');
}
if (!message.senderId) {
errors.push('发送者ID不能为空');
}
// 发送者验证
if (senderId && message.senderId !== senderId) {
errors.push('消息发送者ID不匹配');
}
// 时间戳验证
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('消息时间戳来自未来');
}
}
// 消息去重验证
if (this.config.enableMessageDeduplication) {
if (this.recentMessageIds.has(message.messageId)) {
errors.push('重复的消息ID');
this.stats.duplicateMessages++;
} else {
this.recentMessageIds.add(message.messageId);
}
}
const isValid = errors.length === 0;
if (isValid) {
this.stats.validMessages++;
} else {
this.stats.invalidMessages++;
}
return {
isValid,
errors,
warnings
};
}
/**
* 处理消息排序
*/
processMessageOrdering(message: INetworkMessage): INetworkMessage[] {
if (!this.config.enableMessageOrdering) {
return [message];
}
const senderId = message.senderId;
const currentSequence = this.messageSequence.get(senderId) || 0;
// 检查消息是否按顺序到达
const messageTimestamp = message.timestamp;
const expectedSequence = currentSequence + 1;
// 简单的时间戳排序逻辑
if (messageTimestamp >= expectedSequence) {
// 消息按顺序到达
this.messageSequence.set(senderId, messageTimestamp);
return this.flushPendingMessages(senderId).concat([message]);
} else {
// 消息乱序,暂存
this.pendingMessages.set(message.messageId, message);
this.stats.outOfOrderMessages++;
// 检查是否超出最大待处理数量
if (this.pendingMessages.size > this.config.maxPendingMessages) {
this.logger.warn('待处理消息数量过多,清理旧消息');
this.cleanupOldPendingMessages();
}
return [];
}
}
/**
* 获取统计信息
*/
getStats(): MessageStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalGenerated: 0,
totalValidated: 0,
validMessages: 0,
invalidMessages: 0,
duplicateMessages: 0,
outOfOrderMessages: 0,
timestampErrors: 0
};
}
/**
* 更新配置
*/
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);
}
/**
* 销毁管理器
*/
destroy(): void {
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = undefined;
}
this.recentMessageIds.clear();
this.pendingMessages.clear();
this.messageSequence.clear();
}
/**
* 生成UUID
*/
private generateUUID(): string {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* 刷新待处理消息
*/
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));
// 按时间戳排序
flushedMessages.sort((a, b) => a.timestamp - b.timestamp);
return flushedMessages;
}
/**
* 清理过期的待处理消息
*/
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));
if (messagesToRemove.length > 0) {
this.logger.debug(`清理了 ${messagesToRemove.length} 个过期的待处理消息`);
}
}
/**
* 启动清理定时器
*/
private startCleanupTimer(): void {
this.cleanupTimer = setInterval(() => {
this.performCleanup();
}, 60000); // 每分钟清理一次
}
/**
* 执行清理操作
*/
private performCleanup(): void {
const now = Date.now();
// 清理过期的消息ID用于去重
if (this.config.enableMessageDeduplication) {
// 由于Set没有时间戳我们定期清理所有ID
// 这是一个简化实现,实际项目中可以使用更复杂的数据结构
if (this.recentMessageIds.size > 10000) {
this.recentMessageIds.clear();
this.logger.debug('清理了过期的消息ID缓存');
}
}
// 清理过期的待处理消息
if (this.config.enableMessageOrdering) {
this.cleanupOldPendingMessages();
}
}
/**
* 获取消息处理报告
*/
getProcessingReport() {
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,
duplicateRate: duplicateRate,
pendingMessagesCount: this.pendingMessages.size,
cachedMessageIdsCount: this.recentMessageIds.size,
recommendation: this.generateRecommendation(validRate, duplicateRate)
};
}
/**
* 生成优化建议
*/
private generateRecommendation(validRate: number, duplicateRate: number): string {
if (validRate < 90) {
return '消息验证失败率较高,建议检查消息格式和发送逻辑';
} else if (duplicateRate > 5) {
return '重复消息较多,建议检查客户端重发逻辑或调整去重窗口';
} else if (this.pendingMessages.size > this.config.maxPendingMessages * 0.8) {
return '待处理消息过多,建议优化网络或调整排序窗口';
} else {
return '消息处理正常';
}
}
/**
* 批量验证消息
*/
validateMessageBatch(messages: INetworkMessage[], senderId?: string): MessageValidationResult[] {
return messages.map((message) => this.validateMessage(message, senderId));
}
/**
* 获取消息年龄(毫秒)
*/
getMessageAge(message: INetworkMessage): number {
return Date.now() - message.timestamp;
}
/**
* 检查消息是否过期
*/
isMessageExpired(message: INetworkMessage, maxAge: number = 300000): boolean {
return this.getMessageAge(message) > maxAge;
}
}

View File

@@ -1,283 +0,0 @@
/**
* 网络消息协议定义
*/
import { MessageType, INetworkMessage, AuthorityType, SyncMode, RpcTarget } from '../types/NetworkTypes';
/**
* 连接请求消息
*/
export interface IConnectMessage extends INetworkMessage {
type: MessageType.CONNECT;
data: {
/** 客户端版本 */
clientVersion: string;
/** 协议版本 */
protocolVersion: string;
/** 认证令牌 */
authToken?: string;
/** 客户端信息 */
clientInfo: {
name: string;
platform: string;
version: string;
};
};
}
/**
* 连接响应消息
*/
export interface IConnectResponseMessage extends INetworkMessage {
type: MessageType.CONNECT;
data: {
/** 是否成功 */
success: boolean;
/** 分配的客户端ID */
clientId?: string;
/** 错误信息 */
error?: string;
/** 服务器信息 */
serverInfo?: {
name: string;
version: string;
maxPlayers: number;
currentPlayers: number;
};
};
}
/**
* 心跳消息
*/
export interface IHeartbeatMessage extends INetworkMessage {
type: MessageType.HEARTBEAT;
data: {
/** 客户端时间戳 */
clientTime: number;
/** 服务器时间戳(响应时包含) */
serverTime?: number;
};
}
/**
* 同步变量消息
*/
export interface ISyncVarMessage extends INetworkMessage {
type: MessageType.SYNC_VAR;
data: {
/** 网络实体ID */
networkId: number;
/** 组件类型名称 */
componentType: string;
/** 变化的属性 */
changes: Record<string, any>;
/** 同步模式 */
syncMode: SyncMode;
/** 时间戳 */
timestamp: number;
};
}
/**
* 批量同步消息
*/
export interface ISyncBatchMessage extends INetworkMessage {
type: MessageType.SYNC_BATCH;
data: {
/** 同步数据列表 */
syncData: Array<{
networkId: number;
componentType: string;
changes: Record<string, any>;
syncMode: SyncMode;
}>;
/** 批次时间戳 */
batchTimestamp: number;
};
}
/**
* RPC调用消息
*/
export interface IRpcCallMessage extends INetworkMessage {
type: MessageType.RPC_CALL;
data: {
/** 网络实体ID */
networkId: number;
/** 组件类型名称 */
componentType: string;
/** 方法名 */
methodName: string;
/** 参数列表 */
args: any[];
/** 调用ID用于响应匹配 */
callId?: string;
/** RPC目标 */
target: RpcTarget;
/** 是否需要响应 */
expectResponse?: boolean;
/** 超时时间 */
timeout?: number;
};
}
/**
* RPC响应消息
*/
export interface IRpcResponseMessage extends INetworkMessage {
type: MessageType.RPC_RESPONSE;
data: {
/** 调用ID */
callId: string;
/** 是否成功 */
success: boolean;
/** 返回值 */
result?: any;
/** 错误信息 */
error?: string;
};
}
/**
* 实体创建消息
*/
export interface IEntityCreateMessage extends INetworkMessage {
type: MessageType.ENTITY_CREATE;
data: {
/** 网络实体ID */
networkId: number;
/** 实体名称 */
entityName: string;
/** 拥有者ID */
ownerId: string;
/** 权限类型 */
authority: AuthorityType;
/** 初始组件数据 */
components: Array<{
type: string;
data: any;
}>;
/** 位置信息 */
position?: { x: number; y: number; z?: number };
};
}
/**
* 实体销毁消息
*/
export interface IEntityDestroyMessage extends INetworkMessage {
type: MessageType.ENTITY_DESTROY;
data: {
/** 网络实体ID */
networkId: number;
/** 销毁原因 */
reason?: string;
};
}
/**
* 加入房间消息
*/
export interface IJoinRoomMessage extends INetworkMessage {
type: MessageType.JOIN_ROOM;
data: {
/** 房间ID */
roomId: string;
/** 密码(如果需要) */
password?: string;
/** 玩家信息 */
playerInfo?: {
name: string;
avatar?: string;
customData?: Record<string, any>;
};
};
}
/**
* 离开房间消息
*/
export interface ILeaveRoomMessage extends INetworkMessage {
type: MessageType.LEAVE_ROOM;
data: {
/** 房间ID */
roomId: string;
/** 离开原因 */
reason?: string;
};
}
/**
* 房间状态消息
*/
export interface IRoomStateMessage extends INetworkMessage {
type: MessageType.ROOM_STATE;
data: {
/** 房间ID */
roomId: string;
/** 房间状态 */
state: string;
/** 玩家列表 */
players: Array<{
id: string;
name: string;
isHost: boolean;
customData?: Record<string, any>;
}>;
/** 房间设置 */
settings?: Record<string, any>;
};
}
/**
* 游戏事件消息
*/
export interface IGameEventMessage extends INetworkMessage {
type: MessageType.GAME_EVENT;
data: {
/** 事件类型 */
eventType: string;
/** 事件数据 */
eventData: any;
/** 目标客户端 */
target?: RpcTarget;
/** 事件优先级 */
priority?: number;
};
}
/**
* 错误消息
*/
export interface IErrorMessage extends INetworkMessage {
type: MessageType.ERROR;
data: {
/** 错误代码 */
code: string;
/** 错误消息 */
message: string;
/** 错误详情 */
details?: any;
/** 相关的消息ID */
relatedMessageId?: string;
};
}
/**
* 消息类型联合
*/
export type NetworkMessage =
| IConnectMessage
| IConnectResponseMessage
| IHeartbeatMessage
| ISyncVarMessage
| ISyncBatchMessage
| IRpcCallMessage
| IRpcResponseMessage
| IEntityCreateMessage
| IEntityDestroyMessage
| IJoinRoomMessage
| ILeaveRoomMessage
| IRoomStateMessage
| IGameEventMessage
| IErrorMessage;

View File

@@ -1,454 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
import {
RpcCallRequest,
RpcCallResponse,
RpcError,
RpcErrorType,
RpcStats,
RpcMethodMetadata
} from '../types/RpcTypes';
import { RpcMetadataManager } from './RpcMetadataManager';
/**
* RPC调用统计信息
*/
interface CallStats {
startTime: number;
endTime?: number;
duration?: number;
success: boolean;
error?: RpcError;
}
/**
* 速率限制器
*/
interface RateLimiter {
calls: number[];
limit: number;
window: number; // 时间窗口(毫秒)
}
/**
* RPC调用处理器事件
*/
export interface RpcCallHandlerEvents {
callStarted: (request: RpcCallRequest) => void;
callCompleted: (request: RpcCallRequest, response: RpcCallResponse) => void;
callFailed: (request: RpcCallRequest, error: RpcError) => void;
rateLimitExceeded: (methodName: string, senderId: string) => void;
permissionDenied: (methodName: string, senderId: string) => void;
}
/**
* RPC调用处理器配置
*/
export interface RpcCallHandlerConfig {
/** 最大并发调用数 */
maxConcurrentCalls: number;
/** 默认超时时间(毫秒) */
defaultTimeout: number;
/** 是否启用速率限制 */
enableRateLimit: boolean;
/** 是否启用权限检查 */
enablePermissionCheck: boolean;
/** 是否启用性能监控 */
enablePerformanceMonitoring: boolean;
/** 统计数据保留时间(毫秒) */
statsRetentionTime: number;
}
/**
* RPC调用处理器
* 负责处理来自客户端的RPC调用请求
*/
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,
successfulCalls: 0,
failedCalls: 0,
averageResponseTime: 0,
pendingCalls: 0,
timeoutCalls: 0,
retryCount: 0,
lastUpdated: Date.now()
};
/** 历史调用统计 */
private callHistory: CallStats[] = [];
/** 权限检查器 */
private permissionChecker?: (methodName: string, senderId: string) => boolean;
constructor(
metadataManager: RpcMetadataManager,
config: Partial<RpcCallHandlerConfig> = {}
) {
super();
this.metadataManager = metadataManager;
this.config = {
maxConcurrentCalls: 100,
defaultTimeout: 30000,
enableRateLimit: true,
enablePermissionCheck: true,
enablePerformanceMonitoring: true,
statsRetentionTime: 300000, // 5分钟
...config
};
}
/**
* 设置权限检查器
*/
public setPermissionChecker(checker: (methodName: string, senderId: string) => boolean): void {
this.permissionChecker = checker;
}
/**
* 处理RPC调用请求
*/
public async handleCall<T extends readonly unknown[], R>(
request: RpcCallRequest<T>
): Promise<RpcCallResponse<R>> {
const startTime = Date.now();
const callStats: CallStats = {
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) {
throw this.createError(
RpcErrorType.RATE_LIMITED,
`超过最大并发调用数限制: ${this.config.maxConcurrentCalls}`
);
}
// 2. 获取方法元数据
const metadata = this.metadataManager.getMethodMetadata(request.methodName);
if (!metadata) {
throw this.createError(
RpcErrorType.METHOD_NOT_FOUND,
`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);
throw this.createError(
RpcErrorType.PERMISSION_DENIED,
`没有调用权限: ${request.methodName}`
);
}
// 5. 速率限制检查
if (this.config.enableRateLimit && !this.checkRateLimit(metadata, request.senderId)) {
this.emit('rateLimitExceeded', request.methodName, request.senderId);
throw this.createError(
RpcErrorType.RATE_LIMITED,
`调用频率超限: ${request.methodName}`
);
}
// 6. 执行方法调用
const handler = this.metadataManager.getMethodHandler(request.methodName);
if (!handler) {
throw this.createError(
RpcErrorType.SERVER_ERROR,
`方法处理器不存在: ${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,
result: result as R,
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
? 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,
error: rpcError,
timestamp: endTime,
duration
};
this.updateStats(callStats);
this.emit('callFailed', request, rpcError);
return response;
} finally {
// 9. 清理
this.activeCalls.delete(request.callId);
this.stats.pendingCalls--;
this.addToHistory(callStats);
}
}
/**
* 获取统计信息
*/
public getStats(): RpcStats {
this.stats.lastUpdated = Date.now();
return { ...this.stats };
}
/**
* 获取当前活跃调用数
*/
public getActiveCalls(): number {
return this.activeCalls.size;
}
/**
* 获取方法调用历史
*/
public getCallHistory(methodName?: string, limit: number = 100): CallStats[] {
const history = [...this.callHistory];
if (methodName) {
// 这里需要扩展CallStats接口来包含methodName
// 暂时返回所有历史
}
return history.slice(-limit);
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
totalCalls: 0,
successfulCalls: 0,
failedCalls: 0,
averageResponseTime: 0,
pendingCalls: this.activeCalls.size,
timeoutCalls: 0,
retryCount: 0,
lastUpdated: Date.now()
};
this.callHistory.length = 0;
this.rateLimiters.clear();
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<RpcCallHandlerConfig>): void {
Object.assign(this.config, newConfig);
}
/**
* 销毁处理器
*/
public destroy(): void {
this.activeCalls.clear();
this.rateLimiters.clear();
this.callHistory.length = 0;
this.removeAllListeners();
}
/**
* 检查权限
*/
private checkPermission(metadata: RpcMethodMetadata, senderId: string): boolean {
// 如果方法不需要认证,直接通过
if (!metadata.options.requireAuth) {
return true;
}
// 使用自定义权限检查器
if (this.permissionChecker) {
return this.permissionChecker(metadata.methodName, senderId);
}
// 默认:需要认证但没有检查器,拒绝访问
return false;
}
/**
* 检查速率限制
*/
private checkRateLimit(metadata: RpcMethodMetadata, senderId: string): boolean {
const rateLimit = metadata.options.rateLimit;
if (!rateLimit) {
return true;
}
const key = `${senderId}:${metadata.methodName}`;
let limiter = this.rateLimiters.get(key);
if (!limiter) {
limiter = {
calls: [],
limit: rateLimit,
window: 60000 // 1分钟窗口
};
this.rateLimiters.set(key, limiter);
}
const now = Date.now();
// 清理过期的调用记录
limiter.calls = limiter.calls.filter((time) => now - time < limiter.window);
// 检查是否超限
if (limiter.calls.length >= limiter.limit) {
return false;
}
// 记录本次调用
limiter.calls.push(now);
return true;
}
/**
* 执行带超时的方法调用
*/
private async executeWithTimeout(
handler: Function,
args: readonly unknown[],
timeout: number
): Promise<unknown> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(this.createError(
RpcErrorType.TIMEOUT,
`方法调用超时: ${timeout}ms`
));
}, timeout);
Promise.resolve(handler(...args))
.then((result) => {
clearTimeout(timer);
resolve(result);
})
.catch((error) => {
clearTimeout(timer);
reject(error);
});
});
}
/**
* 创建RPC错误
*/
private createError(type: RpcErrorType, message: string, code?: number): RpcError {
return {
type,
message,
code
};
}
/**
* 更新统计信息
*/
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;
this.stats.averageResponseTime = totalTime / this.stats.totalCalls;
}
}
/**
* 添加到历史记录
*/
private addToHistory(callStats: CallStats): void {
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);
// 限制历史记录数量
if (this.callHistory.length > 10000) {
this.callHistory = this.callHistory.slice(-5000);
}
}
}

View File

@@ -1,504 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
import {
RpcCallRequest,
RpcCallResponse,
RpcError,
RpcErrorType,
RpcCallInfo,
RpcCallStatus,
RpcStats,
RpcOptions,
ClientRpcInvoker,
ServerRpcInvoker
} from '../types/RpcTypes';
import { MessageType, RpcTarget } from '../types/NetworkTypes';
/**
* 网络发送器接口
*/
export interface NetworkSender {
sendMessage(message: object): Promise<void>;
}
/**
* RPC调用代理事件
*/
export interface RpcCallProxyEvents {
callSent: (request: RpcCallRequest) => void;
responseReceived: (response: RpcCallResponse) => void;
callTimeout: (callId: string) => void;
callFailed: (callId: string, error: RpcError) => void;
retryAttempt: (callId: string, attempt: number) => void;
}
/**
* RPC调用代理配置
*/
export interface RpcCallProxyConfig {
/** 默认超时时间(毫秒) */
defaultTimeout: number;
/** 最大重试次数 */
maxRetries: number;
/** 重试延迟基数(毫秒) */
retryDelayBase: number;
/** 重试延迟倍数 */
retryDelayMultiplier: number;
/** 最大重试延迟(毫秒) */
maxRetryDelay: number;
/** 是否启用离线队列 */
enableOfflineQueue: boolean;
/** 离线队列最大大小 */
maxOfflineQueueSize: number;
/** 调用ID生成器 */
generateCallId?: () => string;
}
/**
* RPC调用代理
* 负责发送RPC调用并处理响应
*/
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,
successfulCalls: 0,
failedCalls: 0,
averageResponseTime: 0,
pendingCalls: 0,
timeoutCalls: 0,
retryCount: 0,
lastUpdated: Date.now()
};
/** 重试定时器 */
private retryTimers = new Map<string, ReturnType<typeof setTimeout>>();
constructor(
networkSender: NetworkSender,
config: Partial<RpcCallProxyConfig> = {}
) {
super();
this.networkSender = networkSender;
this.config = {
defaultTimeout: 30000,
maxRetries: 3,
retryDelayBase: 1000,
retryDelayMultiplier: 2,
maxRetryDelay: 10000,
enableOfflineQueue: true,
maxOfflineQueueSize: 100,
generateCallId: () => `rpc_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
...config
};
}
/**
* 客户端RPC调用器
*/
public get clientRpc(): ClientRpcInvoker {
return <TArgs extends readonly unknown[], TReturn>(
methodName: string,
args: TArgs,
options?: Partial<RpcOptions>
): Promise<TReturn> => {
return this.call(methodName, args, options);
};
}
/**
* 服务端RPC调用器用于服务端调用客户端
*/
public get serverRpc(): ServerRpcInvoker {
return <TArgs extends readonly unknown[], TReturn>(
clientId: string,
methodName: string,
args: TArgs,
options?: Partial<RpcOptions>
): Promise<TReturn> => {
const callOptions = {
...options,
target: RpcTarget.Client
};
return this.call(methodName, args, callOptions, clientId);
};
}
/**
* 发起RPC调用
*/
public async call<TArgs extends readonly unknown[], TReturn>(
methodName: string,
args: TArgs,
options: Partial<RpcOptions> = {},
targetId?: string
): Promise<TReturn> {
const callId = this.config.generateCallId!();
const timeout = options.timeout || this.config.defaultTimeout;
const request: RpcCallRequest<TArgs> = {
callId,
methodName,
args,
senderId: 'client', // 这应该从认证系统获取
targetId,
timestamp: Date.now(),
options: {
reliable: true,
priority: 5,
timeout,
...options
}
};
// 创建Promise和调用信息
return new Promise<TReturn>((resolve, reject) => {
const callInfo: RpcCallInfo<TArgs> = {
request,
status: RpcCallStatus.PENDING,
resolve: resolve as (value: unknown) => void,
reject: (reason: RpcError) => reject(reason),
retryCount: 0,
createdAt: Date.now()
};
this.pendingCalls.set(callId, callInfo);
this.stats.pendingCalls++;
this.stats.totalCalls++;
// 设置超时
setTimeout(() => {
this.handleTimeout(callId);
}, timeout);
// 发送调用
this.sendCall(callInfo);
});
}
/**
* 处理RPC响应
*/
public handleResponse(response: RpcCallResponse): void {
const callInfo = this.pendingCalls.get(response.callId);
if (!callInfo) {
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++;
this.updateAverageResponseTime(response.duration);
} else {
this.stats.failedCalls++;
}
this.stats.pendingCalls--;
// 处理结果
if (response.success) {
callInfo.resolve!(response.result);
} else {
callInfo.reject!(response.error!);
this.emit('callFailed', response.callId, response.error!);
}
// 清理
this.pendingCalls.delete(response.callId);
this.emit('responseReceived', response);
}
/**
* 设置网络状态
*/
public setOnlineStatus(online: boolean): void {
const wasOnline = this.isOnline;
this.isOnline = online;
if (online && !wasOnline) {
// 从离线状态恢复,处理离线队列
this.processOfflineQueue();
}
}
/**
* 取消RPC调用
*/
public cancelCall(callId: string): boolean {
const callInfo = this.pendingCalls.get(callId);
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;
}
/**
* 获取统计信息
*/
public getStats(): RpcStats {
this.stats.lastUpdated = Date.now();
return { ...this.stats };
}
/**
* 获取待处理的调用
*/
public getPendingCalls(): RpcCallInfo[] {
return Array.from(this.pendingCalls.values());
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
totalCalls: 0,
successfulCalls: 0,
failedCalls: 0,
averageResponseTime: 0,
pendingCalls: this.pendingCalls.size,
timeoutCalls: 0,
retryCount: 0,
lastUpdated: Date.now()
};
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<RpcCallProxyConfig>): void {
Object.assign(this.config, newConfig);
}
/**
* 销毁代理
*/
public destroy(): void {
// 取消所有待处理的调用
const pendingCallIds = Array.from(this.pendingCalls.keys());
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();
}
/**
* 发送调用
*/
private async sendCall<T extends readonly unknown[]>(callInfo: RpcCallInfo<T>): Promise<void> {
try {
// 检查网络状态
if (!this.isOnline) {
if (this.config.enableOfflineQueue) {
this.addToOfflineQueue(callInfo.request);
} else {
throw new Error('网络不可用');
}
return;
}
// 构建网络消息
const message = {
type: MessageType.RPC_CALL,
messageId: callInfo.request.callId,
timestamp: Date.now(),
senderId: callInfo.request.senderId,
data: callInfo.request,
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);
} else {
this.handleCallFailure(callInfo, {
type: RpcErrorType.NETWORK_ERROR,
message: String(error)
});
}
}
}
/**
* 处理超时
*/
private handleTimeout(callId: string): void {
const callInfo = this.pendingCalls.get(callId);
if (!callInfo || callInfo.status === RpcCallStatus.COMPLETED) {
return;
}
// 检查是否可以重试
if (callInfo.retryCount < this.config.maxRetries) {
this.scheduleRetry(callInfo);
} else {
this.stats.timeoutCalls++;
this.handleCallFailure(callInfo, {
type: RpcErrorType.TIMEOUT,
message: `调用超时: ${callInfo.request.options.timeout}ms`
});
this.emit('callTimeout', callId);
}
}
/**
* 安排重试
*/
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);
}
/**
* 处理调用失败
*/
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);
}
/**
* 添加到离线队列
*/
private addToOfflineQueue<T extends readonly unknown[]>(request: RpcCallRequest<T>): void {
if (this.offlineQueue.length >= this.config.maxOfflineQueueSize) {
// 移除最旧的请求
this.offlineQueue.shift();
}
this.offlineQueue.push(request);
}
/**
* 处理离线队列
*/
private async processOfflineQueue(): Promise<void> {
if (!this.isOnline || this.offlineQueue.length === 0) {
return;
}
const queue = [...this.offlineQueue];
this.offlineQueue.length = 0;
for (const request of queue) {
try {
// 重新创建调用信息
const callInfo: RpcCallInfo = {
request,
status: RpcCallStatus.PENDING,
retryCount: 0,
createdAt: Date.now()
};
this.pendingCalls.set(request.callId, callInfo);
await this.sendCall(callInfo);
} catch (error) {
this.logger.error(`处理离线队列失败: ${request.methodName}`, error);
}
}
}
/**
* 更新平均响应时间
*/
private updateAverageResponseTime(responseTime: number): void {
const totalResponses = this.stats.successfulCalls;
const currentAverage = this.stats.averageResponseTime;
this.stats.averageResponseTime =
(currentAverage * (totalResponses - 1) + responseTime) / totalResponses;
}
}

View File

@@ -1,345 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
import { RpcMethodMetadata, RpcMethodRegistry } from '../types/RpcTypes';
import { getRpcMethods, RpcMethodValidator } from '../decorators/RpcDecorators';
/**
* RPC元数据管理器事件
*/
export interface RpcMetadataManagerEvents {
methodRegistered: (metadata: RpcMethodMetadata) => void;
methodUnregistered: (methodName: string) => void;
classRegistered: (className: string, methodCount: number) => void;
classUnregistered: (className: string) => void;
}
/**
* RPC元数据管理器
* 负责管理所有RPC方法的元数据和注册信息
*/
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>();
/**
* 注册RPC类
*/
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);
if (!validation.valid) {
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;
}
}
/**
* 注销RPC类
*/
public unregisterClass(classNameOrInstance: string | object): void {
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}`;
this.registry.delete(fullMethodName);
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);
}
/**
* 获取RPC方法元数据
*/
public getMethodMetadata(methodName: string): RpcMethodMetadata | undefined {
const entry = this.registry.get(methodName);
return entry?.metadata;
}
/**
* 获取RPC方法处理器
*/
public getMethodHandler(methodName: string): Function | undefined {
const entry = this.registry.get(methodName);
return entry?.handler;
}
/**
* 检查方法是否存在
*/
public hasMethod(methodName: string): boolean {
return this.registry.has(methodName);
}
/**
* 获取所有服务端RPC方法
*/
public getServerRpcMethods(): RpcMethodMetadata[] {
const methods: RpcMethodMetadata[] = [];
for (const [, entry] of this.registry) {
if (entry.metadata.isServerRpc) {
methods.push(entry.metadata);
}
}
return methods;
}
/**
* 获取所有客户端RPC方法
*/
public getClientRpcMethods(): RpcMethodMetadata[] {
const methods: RpcMethodMetadata[] = [];
for (const [, entry] of this.registry) {
if (!entry.metadata.isServerRpc) {
methods.push(entry.metadata);
}
}
return methods;
}
/**
* 获取类的所有RPC方法
*/
public getClassMethods(className: string): RpcMethodMetadata[] {
const methodNames = this.classMethods.get(className);
if (!methodNames) {
return [];
}
const methods: RpcMethodMetadata[] = [];
for (const methodName of methodNames) {
const fullMethodName = `${className}.${methodName}`;
const entry = this.registry.get(fullMethodName);
if (entry) {
methods.push(entry.metadata);
}
}
return methods;
}
/**
* 获取已注册的类名列表
*/
public getRegisteredClasses(): string[] {
return Array.from(this.classMethods.keys());
}
/**
* 获取所有已注册的方法名
*/
public getAllMethodNames(): string[] {
return Array.from(this.registry.keys());
}
/**
* 根据方法名获取所属类
*/
public getMethodClass(methodName: string): string | undefined {
return this.methodToClass.get(methodName);
}
/**
* 获取类实例
*/
public getClassInstance(className: string): object | undefined {
return this.instances.get(className);
}
/**
* 获取注册统计信息
*/
public getStats(): {
totalMethods: number;
serverRpcMethods: number;
clientRpcMethods: number;
registeredClasses: number;
} {
let serverRpcCount = 0;
let clientRpcCount = 0;
for (const [, entry] of this.registry) {
if (entry.metadata.isServerRpc) {
serverRpcCount++;
} else {
clientRpcCount++;
}
}
return {
totalMethods: this.registry.size,
serverRpcMethods: serverRpcCount,
clientRpcMethods: clientRpcCount,
registeredClasses: this.classMethods.size
};
}
/**
* 验证方法调用
*/
public validateMethodCall(
methodName: string,
args: unknown[],
callerId?: string
): { valid: boolean; error?: string } {
const metadata = this.getMethodMetadata(methodName);
if (!metadata) {
return {
valid: false,
error: `RPC方法不存在: ${methodName}`
};
}
return RpcMethodValidator.validateCall(metadata, args, callerId);
}
/**
* 搜索方法
*/
public searchMethods(query: {
className?: string;
isServerRpc?: boolean;
requireAuth?: boolean;
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;
}
/**
* 清空所有注册
*/
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注册');
}
/**
* 销毁管理器
*/
public destroy(): void {
this.clear();
this.removeAllListeners();
}
}

View File

@@ -1,552 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
import { EventEmitter } from '../utils/EventEmitter';
import { RpcCallRequest, RpcCallResponse, RpcError, RpcErrorType } from '../types/RpcTypes';
/**
* 重复调用记录
*/
interface DuplicateCallRecord {
callId: string;
methodName: string;
senderId: string;
firstCallTime: number;
lastCallTime: number;
callCount: number;
response?: RpcCallResponse;
}
/**
* 幂等性配置
*/
interface IdempotencyConfig {
/** 是否启用幂等性检查 */
enabled: boolean;
/** 记录保留时间(毫秒) */
recordRetentionTime: number;
/** 最大记录数量 */
maxRecords: number;
/** 检查窗口时间(毫秒) */
checkWindowTime: number;
}
/**
* 顺序执行配置
*/
interface OrderedExecutionConfig {
/** 是否启用顺序执行 */
enabled: boolean;
/** 最大等待时间(毫秒) */
maxWaitTime: number;
/** 队列最大大小 */
maxQueueSize: number;
}
/**
* 事务配置
*/
interface TransactionConfig {
/** 是否启用事务支持 */
enabled: boolean;
/** 事务超时时间(毫秒) */
transactionTimeout: number;
/** 最大事务数量 */
maxTransactions: number;
}
/**
* RPC可靠性管理器配置
*/
export interface RpcReliabilityConfig {
/** 幂等性配置 */
idempotency: IdempotencyConfig;
/** 顺序执行配置 */
orderedExecution: OrderedExecutionConfig;
/** 事务配置 */
transaction: TransactionConfig;
}
/**
* 事务信息
*/
interface TransactionInfo {
transactionId: string;
calls: RpcCallRequest[];
startTime: number;
status: 'pending' | 'committed' | 'rolledback';
rollbackActions: Array<() => Promise<void>>;
}
/**
* 顺序执行队列项
*/
interface OrderedQueueItem {
request: RpcCallRequest;
handler: () => Promise<RpcCallResponse>;
resolve: (response: RpcCallResponse) => void;
reject: (error: RpcError) => void;
enqueuedAt: number;
}
/**
* RPC可靠性管理器事件
*/
export interface RpcReliabilityManagerEvents {
duplicateCallDetected: (record: DuplicateCallRecord) => void;
transactionStarted: (transactionId: string) => void;
transactionCommitted: (transactionId: string) => void;
transactionRolledback: (transactionId: string, reason: string) => void;
orderedCallQueued: (callId: string, queueSize: number) => void;
orderedCallProcessed: (callId: string, waitTime: number) => void;
}
/**
* RPC可靠性管理器
* 提供重复检测、幂等性、顺序执行和事务支持
*/
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,
recordRetentionTime: 300000, // 5分钟
maxRecords: 10000,
checkWindowTime: 60000, // 1分钟
...config.idempotency
},
orderedExecution: {
enabled: false,
maxWaitTime: 30000,
maxQueueSize: 1000,
...config.orderedExecution
},
transaction: {
enabled: false,
transactionTimeout: 60000,
maxTransactions: 100,
...config.transaction
}
};
this.startCleanupTimer();
}
/**
* 检查并处理重复调用
*/
public checkDuplicateCall(request: RpcCallRequest): {
isDuplicate: boolean;
response?: RpcCallResponse;
shouldProcess: boolean;
} {
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 {
isDuplicate: true,
response: existing.response,
shouldProcess: false
};
}
// 如果在检查窗口内,认为是重复调用但还在处理中
if (now - existing.firstCallTime < this.config.idempotency.checkWindowTime) {
return {
isDuplicate: true,
shouldProcess: false
};
}
}
// 记录新的调用
const record: DuplicateCallRecord = {
callId: request.callId,
methodName: request.methodName,
senderId: request.senderId,
firstCallTime: now,
lastCallTime: now,
callCount: 1
};
this.duplicateRecords.set(key, record);
return { isDuplicate: false, shouldProcess: true };
}
/**
* 记录调用响应(用于幂等性)
*/
public recordCallResponse(request: RpcCallRequest, response: RpcCallResponse): void {
if (!this.config.idempotency.enabled) {
return;
}
const key = `${request.senderId}:${request.callId}`;
const record = this.duplicateRecords.get(key);
if (record) {
record.response = response;
}
}
/**
* 处理有序调用
*/
public async handleOrderedCall(
request: RpcCallRequest,
handler: () => Promise<RpcCallResponse>
): Promise<RpcCallResponse> {
if (!this.config.orderedExecution.enabled) {
return handler();
}
const senderId = request.senderId;
return new Promise<RpcCallResponse>((resolve, reject) => {
const queueItem: OrderedQueueItem = {
request,
handler,
resolve,
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({
type: RpcErrorType.RATE_LIMITED,
message: '有序执行队列已满'
});
return;
}
queue.push(queueItem);
this.emit('orderedCallQueued', request.callId, queue.length);
// 如果没有正在处理的调用,开始处理
if (!this.processingOrdered.has(senderId)) {
this.processOrderedQueue(senderId);
}
});
}
/**
* 开始事务
*/
public startTransaction(transactionId: string): void {
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: [],
startTime: Date.now(),
status: 'pending',
rollbackActions: []
};
this.transactions.set(transactionId, transaction);
this.emit('transactionStarted', transactionId);
// 设置事务超时
setTimeout(() => {
if (this.transactions.has(transactionId)) {
this.rollbackTransaction(transactionId, '事务超时');
}
}, this.config.transaction.transactionTimeout);
}
/**
* 添加事务调用
*/
public addTransactionCall(
transactionId: string,
request: RpcCallRequest,
rollbackAction?: () => Promise<void>
): void {
const transaction = this.transactions.get(transactionId);
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);
}
}
/**
* 提交事务
*/
public async commitTransaction(transactionId: string): Promise<void> {
const transaction = this.transactions.get(transactionId);
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} 个调用`);
}
/**
* 回滚事务
*/
public async rollbackTransaction(transactionId: string, reason: string): Promise<void> {
const transaction = this.transactions.get(transactionId);
if (!transaction) {
throw new Error(`事务不存在: ${transactionId}`);
}
if (transaction.status !== 'pending') {
return; // 已经处理过
}
transaction.status = 'rolledback';
// 执行回滚操作
for (const rollbackAction of transaction.rollbackActions.reverse()) {
try {
await rollbackAction();
} catch (error) {
this.logger.error(`回滚操作失败: ${transactionId}`, error);
}
}
this.transactions.delete(transactionId);
this.emit('transactionRolledback', transactionId, reason);
this.logger.warn(`事务已回滚: ${transactionId},原因: ${reason}`);
}
/**
* 获取事务信息
*/
public getTransaction(transactionId: string): TransactionInfo | undefined {
return this.transactions.get(transactionId);
}
/**
* 获取统计信息
*/
public getStats(): {
duplicateRecords: number;
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,
totalQueuedCalls,
processingQueues: this.processingOrdered.size
};
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<RpcReliabilityConfig>): void {
if (newConfig.idempotency) {
Object.assign(this.config.idempotency, newConfig.idempotency);
}
if (newConfig.orderedExecution) {
Object.assign(this.config.orderedExecution, newConfig.orderedExecution);
}
if (newConfig.transaction) {
Object.assign(this.config.transaction, newConfig.transaction);
}
}
/**
* 销毁管理器
*/
public destroy(): void {
// 停止清理定时器
if (this.cleanupTimer) {
clearInterval(this.cleanupTimer);
this.cleanupTimer = null;
}
// 回滚所有活跃事务
const transactionIds = Array.from(this.transactions.keys());
for (const transactionId of transactionIds) {
this.rollbackTransaction(transactionId, '管理器销毁').catch((error) => {
this.logger.error(`销毁时回滚事务失败: ${transactionId}`, error);
});
}
// 清理队列
for (const queue of this.orderedQueues.values()) {
for (const item of queue) {
item.reject({
type: RpcErrorType.CLIENT_ERROR,
message: '服务关闭'
});
}
}
this.duplicateRecords.clear();
this.transactions.clear();
this.orderedQueues.clear();
this.processingOrdered.clear();
this.removeAllListeners();
}
/**
* 处理有序队列
*/
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({
type: RpcErrorType.TIMEOUT,
message: `有序执行等待超时: ${waitTime}ms`
});
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) {
setImmediate(() => this.processOrderedQueue(senderId));
}
}
}
/**
* 开始清理定时器
*/
private startCleanupTimer(): void {
this.cleanupTimer = setInterval(() => {
this.cleanup();
}, 60000); // 每分钟清理一次
}
/**
* 清理过期数据
*/
private cleanup(): void {
const now = Date.now();
// 清理过期的重复调用记录
if (this.config.idempotency.enabled) {
for (const [key, record] of this.duplicateRecords) {
if (now - record.lastCallTime > this.config.idempotency.recordRetentionTime) {
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)) {
this.orderedQueues.delete(senderId);
}
}
}
}

View File

@@ -1,550 +0,0 @@
/**
* JSON序列化器
* 提供高性能的消息序列化和反序列化功能,包括类型安全检查
*/
import { createLogger } from '@esengine/ecs-framework';
import { INetworkMessage, MessageType } from '../types/NetworkTypes';
/**
* 序列化器配置
*/
export interface SerializerConfig {
enableTypeChecking: boolean;
enableCompression: boolean;
maxMessageSize: number;
enableProfiling: boolean;
customSerializers?: Map<string, ICustomSerializer>;
}
/**
* 自定义序列化器接口
*/
export interface ICustomSerializer {
serialize(data: any): any;
deserialize(data: any): any;
canHandle(data: any): boolean;
}
/**
* 序列化结果
*/
export interface SerializationResult {
data: string | Buffer;
size: number;
compressionRatio?: number;
serializationTime: number;
}
/**
* 反序列化结果
*/
export interface DeserializationResult<T = any> {
data: T;
deserializationTime: number;
isValid: boolean;
errors?: string[];
}
/**
* 序列化统计信息
*/
export interface SerializationStats {
totalSerialized: number;
totalDeserialized: number;
totalBytes: number;
averageSerializationTime: number;
averageDeserializationTime: number;
averageMessageSize: number;
errorCount: number;
compressionSavings: number;
}
/**
* JSON序列化器
*/
export class JSONSerializer {
private logger = createLogger('JSONSerializer');
private config: SerializerConfig;
private stats: SerializationStats;
// 性能分析
private serializationTimes: number[] = [];
private deserializationTimes: number[] = [];
private messageSizes: number[] = [];
/**
* 构造函数
*/
constructor(config: Partial<SerializerConfig> = {}) {
this.config = {
enableTypeChecking: true,
enableCompression: false,
maxMessageSize: 1024 * 1024, // 1MB
enableProfiling: false,
...config
};
this.stats = {
totalSerialized: 0,
totalDeserialized: 0,
totalBytes: 0,
averageSerializationTime: 0,
averageDeserializationTime: 0,
averageMessageSize: 0,
errorCount: 0,
compressionSavings: 0
};
}
/**
* 序列化消息
*/
serialize<T extends INetworkMessage>(message: T): SerializationResult {
const startTime = performance.now();
try {
// 类型检查
if (this.config.enableTypeChecking) {
this.validateMessage(message);
}
// 预处理消息
const processedMessage = this.preprocessMessage(message);
// 序列化
let serializedData: string;
// 使用自定义序列化器
const customSerializer = this.findCustomSerializer(processedMessage);
if (customSerializer) {
serializedData = JSON.stringify(customSerializer.serialize(processedMessage));
} else {
serializedData = JSON.stringify(processedMessage, this.createReplacer());
}
// 检查大小限制
if (serializedData.length > this.config.maxMessageSize) {
throw new Error(`消息大小超过限制: ${serializedData.length} > ${this.config.maxMessageSize}`);
}
const endTime = performance.now();
const serializationTime = endTime - startTime;
// 更新统计
this.updateSerializationStats(serializedData.length, serializationTime);
return {
data: serializedData,
size: serializedData.length,
serializationTime
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('序列化失败:', error);
throw error;
}
}
/**
* 反序列化消息
*/
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);
// 解析JSON
const parsedData = JSON.parse(jsonString, this.createReviver());
// 类型检查
const validationResult = this.config.enableTypeChecking ?
this.validateParsedMessage(parsedData) : { isValid: true, errors: [] };
// 后处理消息
const processedMessage = this.postprocessMessage(parsedData);
const endTime = performance.now();
const deserializationTime = endTime - startTime;
// 更新统计
this.updateDeserializationStats(deserializationTime);
return {
data: processedMessage as T,
deserializationTime,
isValid: validationResult.isValid,
errors: validationResult.errors
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('反序列化失败:', error);
return {
data: {} as T,
deserializationTime: performance.now() - startTime,
isValid: false,
errors: [error instanceof Error ? error.message : '未知错误']
};
}
}
/**
* 批量序列化
*/
serializeBatch<T extends INetworkMessage>(messages: T[]): SerializationResult {
const startTime = performance.now();
try {
const batchData = {
type: 'batch',
messages: messages.map((msg) => {
if (this.config.enableTypeChecking) {
this.validateMessage(msg);
}
return this.preprocessMessage(msg);
}),
timestamp: Date.now()
};
const serializedData = JSON.stringify(batchData, this.createReplacer());
if (serializedData.length > this.config.maxMessageSize) {
throw new Error(`批量消息大小超过限制: ${serializedData.length} > ${this.config.maxMessageSize}`);
}
const endTime = performance.now();
const serializationTime = endTime - startTime;
this.updateSerializationStats(serializedData.length, serializationTime);
return {
data: serializedData,
size: serializedData.length,
serializationTime
};
} catch (error) {
this.stats.errorCount++;
this.logger.error('批量序列化失败:', error);
throw error;
}
}
/**
* 批量反序列化
*/
deserializeBatch<T extends INetworkMessage>(data: string | ArrayBuffer): DeserializationResult<T[]> {
const result = this.deserialize<any>(data);
if (!result.isValid || !result.data.messages) {
return {
data: [],
deserializationTime: result.deserializationTime,
isValid: false,
errors: ['无效的批量消息格式']
};
}
const messages = result.data.messages.map((msg: any) => this.postprocessMessage(msg));
return {
data: messages as T[],
deserializationTime: result.deserializationTime,
isValid: true
};
}
/**
* 获取统计信息
*/
getStats(): SerializationStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalSerialized: 0,
totalDeserialized: 0,
totalBytes: 0,
averageSerializationTime: 0,
averageDeserializationTime: 0,
averageMessageSize: 0,
errorCount: 0,
compressionSavings: 0
};
this.serializationTimes.length = 0;
this.deserializationTimes.length = 0;
this.messageSizes.length = 0;
}
/**
* 添加自定义序列化器
*/
addCustomSerializer(name: string, serializer: ICustomSerializer): void {
if (!this.config.customSerializers) {
this.config.customSerializers = new Map();
}
this.config.customSerializers.set(name, serializer);
}
/**
* 移除自定义序列化器
*/
removeCustomSerializer(name: string): boolean {
return this.config.customSerializers?.delete(name) || false;
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<SerializerConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('序列化器配置已更新:', newConfig);
}
/**
* 验证消息格式
*/
private validateMessage(message: INetworkMessage): void {
if (!message.type || !message.messageId || !message.timestamp) {
throw new Error('消息格式无效:缺少必需字段');
}
if (!Object.values(MessageType).includes(message.type)) {
throw new Error(`无效的消息类型: ${message.type}`);
}
if (typeof message.timestamp !== 'number' || message.timestamp <= 0) {
throw new Error('无效的时间戳');
}
}
/**
* 验证解析后的消息
*/
private validateParsedMessage(data: any): { isValid: boolean; errors: string[] } {
const errors: string[] = [];
if (!data || typeof data !== 'object') {
errors.push('消息必须是对象');
} else {
if (!data.type) errors.push('缺少消息类型');
if (!data.messageId) errors.push('缺少消息ID');
if (!data.timestamp) errors.push('缺少时间戳');
if (!data.senderId) errors.push('缺少发送者ID');
}
return {
isValid: errors.length === 0,
errors
};
}
/**
* 预处理消息(序列化前)
*/
private preprocessMessage(message: INetworkMessage): any {
// 克隆消息以避免修改原始对象
const processed = { ...message };
// 处理特殊数据类型
if (processed.data) {
processed.data = this.serializeSpecialTypes(processed.data);
}
return processed;
}
/**
* 后处理消息(反序列化后)
*/
private postprocessMessage(data: any): any {
if (data.data) {
data.data = this.deserializeSpecialTypes(data.data);
}
return data;
}
/**
* 序列化特殊类型
*/
private serializeSpecialTypes(data: any): any {
if (data instanceof Date) {
return { __type: 'Date', value: data.toISOString() };
} else if (data instanceof Map) {
return { __type: 'Map', value: Array.from(data.entries()) };
} else if (data instanceof Set) {
return { __type: 'Set', value: Array.from(data) };
} else if (ArrayBuffer.isView(data)) {
return { __type: 'TypedArray', value: Array.from(data as any), constructor: data.constructor.name };
} else if (data && typeof data === 'object') {
const result: any = {};
for (const [key, value] of Object.entries(data)) {
result[key] = this.serializeSpecialTypes(value);
}
return result;
}
return data;
}
/**
* 反序列化特殊类型
*/
private deserializeSpecialTypes(data: any): any {
if (data && typeof data === 'object' && data.__type) {
switch (data.__type) {
case 'Date':
return new Date(data.value);
case 'Map':
return new Map(data.value);
case 'Set':
return new Set(data.value);
case 'TypedArray':
const constructor = (globalThis as any)[data.constructor];
return constructor ? new constructor(data.value) : data.value;
}
} else if (data && typeof data === 'object') {
const result: any = {};
for (const [key, value] of Object.entries(data)) {
result[key] = this.deserializeSpecialTypes(value);
}
return result;
}
return data;
}
/**
* 创建JSON.stringify替换函数
*/
private createReplacer() {
return (key: string, value: any) => {
// 处理循环引用
if (value && typeof value === 'object') {
if (value.__serializing) {
return '[Circular Reference]';
}
value.__serializing = true;
}
return value;
};
}
/**
* 创建JSON.parse恢复函数
*/
private createReviver() {
return (key: string, value: any) => {
// 清理序列化标记
if (value && typeof value === 'object') {
delete value.__serializing;
}
return value;
};
}
/**
* 查找自定义序列化器
*/
private findCustomSerializer(data: any): ICustomSerializer | undefined {
if (!this.config.customSerializers) {
return undefined;
}
for (const serializer of this.config.customSerializers.values()) {
if (serializer.canHandle(data)) {
return serializer;
}
}
return undefined;
}
/**
* 更新序列化统计
*/
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();
}
if (this.messageSizes.length > 1000) {
this.messageSizes.shift();
}
// 计算平均值
this.stats.averageSerializationTime =
this.serializationTimes.reduce((sum, t) => sum + t, 0) / this.serializationTimes.length;
this.stats.averageMessageSize =
this.messageSizes.reduce((sum, s) => sum + s, 0) / this.messageSizes.length;
}
/**
* 更新反序列化统计
*/
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.deserializationTimes.reduce((sum, t) => sum + t, 0) / this.deserializationTimes.length;
}
/**
* 获取性能分析报告
*/
getPerformanceReport() {
return {
stats: this.getStats(),
serializationTimes: [...this.serializationTimes],
deserializationTimes: [...this.deserializationTimes],
messageSizes: [...this.messageSizes],
percentiles: {
serialization: this.calculatePercentiles(this.serializationTimes),
deserialization: this.calculatePercentiles(this.deserializationTimes),
messageSize: this.calculatePercentiles(this.messageSizes)
}
};
}
/**
* 计算百分位数
*/
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)],
p95: sorted[Math.floor(n * 0.95)],
p99: sorted[Math.floor(n * 0.99)]
};
}
}

View File

@@ -1,654 +0,0 @@
/**
* 消息压缩器框架
* 提供可扩展的压缩算法接口,用户可以注册自定义压缩算法
*/
import { createLogger } from '@esengine/ecs-framework';
/**
* 压缩算法接口
*/
export interface ICompressionAlgorithm {
/** 算法名称 */
readonly name: string;
/** 算法版本 */
readonly version: string;
/** 是否支持异步压缩 */
readonly supportsAsync: boolean;
/**
* 同步压缩
*/
compress(data: ArrayBuffer): ArrayBuffer;
/**
* 同步解压缩
*/
decompress(data: ArrayBuffer): ArrayBuffer;
/**
* 异步压缩(可选)
*/
compressAsync?(data: ArrayBuffer): Promise<ArrayBuffer>;
/**
* 异步解压缩(可选)
*/
decompressAsync?(data: ArrayBuffer): Promise<ArrayBuffer>;
/**
* 估算压缩后大小(可选)
*/
estimateCompressedSize?(data: ArrayBuffer): number;
}
/**
* 压缩配置
*/
export interface CompressionConfig {
/** 默认压缩算法名称 */
defaultAlgorithm: string;
/** 最小压缩阈值(字节) */
threshold: number;
/** 是否启用异步压缩 */
enableAsync: boolean;
/** 压缩级别提示 (0-9) */
level: number;
/** 分块大小 */
chunkSize: number;
/** 是否启用压缩统计 */
enableStats: boolean;
}
/**
* 压缩结果
*/
export interface CompressionResult {
/** 压缩后的数据 */
data: ArrayBuffer;
/** 原始大小 */
originalSize: number;
/** 压缩后大小 */
compressedSize: number;
/** 压缩比 */
compressionRatio: number;
/** 压缩耗时 */
compressionTime: number;
/** 使用的算法 */
algorithm: string;
/** 是否实际进行了压缩 */
wasCompressed: boolean;
}
/**
* 解压缩结果
*/
export interface DecompressionResult {
/** 解压缩后的数据 */
data: ArrayBuffer;
/** 原始压缩大小 */
compressedSize: number;
/** 解压缩后大小 */
decompressedSize: number;
/** 解压缩耗时 */
decompressionTime: number;
/** 使用的算法 */
algorithm: string;
}
/**
* 压缩统计信息
*/
export interface CompressionStats {
/** 总压缩次数 */
totalCompressions: number;
/** 总解压缩次数 */
totalDecompressions: number;
/** 总原始字节数 */
totalOriginalBytes: number;
/** 总压缩字节数 */
totalCompressedBytes: number;
/** 平均压缩比 */
averageCompressionRatio: number;
/** 平均压缩时间 */
averageCompressionTime: number;
/** 平均解压缩时间 */
averageDecompressionTime: number;
/** 算法使用统计 */
algorithmUsage: Record<string, number>;
}
/**
* 无压缩算法实现(默认)
*/
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;
}
}
/**
* LZ字符串压缩算法实现
*/
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;
// 重置字典
for (const key in dictionary) {
if (dictionary[key] >= 256) {
delete dictionary[key];
}
}
}
}
}
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) {
entry = w + w.charAt(0);
} else {
throw new Error('LZ解压缩错误无效的压缩数据');
}
result.push(entry);
dictionary[dictSize++] = w + entry.charAt(0);
w = entry;
// 防止字典过大
if (dictSize >= 0xFFFF) {
dictSize = 256;
// 重置字典
for (const key in dictionary) {
if (parseInt(key) >= 256) {
delete dictionary[key];
}
}
}
}
// 将结果转换为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字节
bytes.push(num);
} else if (num < 16384) {
// 小于16384用2字节最高位为1表示有下一字节
bytes.push(0x80 | (num & 0x7F));
bytes.push((num >> 7) & 0x7F);
} else {
// 大于等于16384用3字节
bytes.push(0x80 | (num & 0x7F));
bytes.push(0x80 | ((num >> 7) & 0x7F));
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);
} else {
// 多字节数字
let num = byte1 & 0x7F;
i++;
if (i < bytes.length) {
const byte2 = bytes[i];
num |= (byte2 & 0x7F) << 7;
if ((byte2 & 0x80) !== 0) {
// 三字节数字
i++;
if (i < bytes.length) {
const byte3 = bytes[i];
num |= (byte3 & 0x7F) << 14;
}
}
}
numbers.push(num);
}
}
return numbers;
}
}
/**
* 消息压缩器
*/
export class MessageCompressor {
private logger = createLogger('MessageCompressor');
private config: CompressionConfig;
private algorithms = new Map<string, ICompressionAlgorithm>();
private stats: CompressionStats;
/**
* 构造函数
*/
constructor(config: Partial<CompressionConfig> = {}) {
this.config = {
defaultAlgorithm: 'none',
threshold: 1024, // 1KB以上才压缩
enableAsync: true,
level: 6,
chunkSize: 64 * 1024, // 64KB分块
enableStats: true,
...config
};
this.stats = {
totalCompressions: 0,
totalDecompressions: 0,
totalOriginalBytes: 0,
totalCompressedBytes: 0,
averageCompressionRatio: 1.0,
averageCompressionTime: 0,
averageDecompressionTime: 0,
algorithmUsage: {}
};
// 注册默认算法
this.registerAlgorithm(new NoCompressionAlgorithm());
this.registerAlgorithm(new LZCompressionAlgorithm());
}
/**
* 注册压缩算法
*/
public registerAlgorithm(algorithm: ICompressionAlgorithm): void {
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}`);
}
/**
* 注销压缩算法
*/
public unregisterAlgorithm(algorithmName: string): boolean {
if (algorithmName === 'none') {
this.logger.warn('无法注销默认的无压缩算法');
return false;
}
const removed = this.algorithms.delete(algorithmName);
if (removed) {
delete this.stats.algorithmUsage[algorithmName];
this.logger.info(`注销压缩算法: ${algorithmName}`);
}
return removed;
}
/**
* 获取已注册的算法列表
*/
public getRegisteredAlgorithms(): string[] {
return Array.from(this.algorithms.keys());
}
/**
* 检查算法是否已注册
*/
public hasAlgorithm(algorithmName: string): boolean {
return this.algorithms.has(algorithmName);
}
/**
* 压缩数据
*/
public async compress(
data: ArrayBuffer | string,
algorithmName?: string
): Promise<CompressionResult> {
const startTime = performance.now();
// 转换输入数据
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}`);
}
try {
let compressedData: ArrayBuffer;
let wasCompressed = false;
// 检查是否需要压缩
if (originalSize < this.config.threshold || selectedAlgorithm === 'none') {
compressedData = inputBuffer.slice(0);
} else {
// 选择同步或异步压缩
if (this.config.enableAsync && algorithm.supportsAsync && algorithm.compressAsync) {
compressedData = await algorithm.compressAsync(inputBuffer);
} else {
compressedData = algorithm.compress(inputBuffer);
}
wasCompressed = true;
}
const endTime = performance.now();
const compressionTime = endTime - startTime;
const compressedSize = compressedData.byteLength;
const compressionRatio = originalSize > 0 ? compressedSize / originalSize : 1;
// 更新统计信息
if (this.config.enableStats) {
this.updateCompressionStats(
selectedAlgorithm,
originalSize,
compressedSize,
compressionTime
);
}
const result: CompressionResult = {
data: compressedData,
originalSize,
compressedSize,
compressionRatio,
compressionTime,
algorithm: selectedAlgorithm,
wasCompressed
};
this.logger.debug(
`压缩完成: ${originalSize}B -> ${compressedSize}B ` +
`(${(compressionRatio * 100).toFixed(1)}%) ` +
`用时 ${compressionTime.toFixed(2)}ms, 算法: ${selectedAlgorithm}`
);
return result;
} catch (error) {
this.logger.error(`压缩失败 (${selectedAlgorithm}):`, error);
throw error;
}
}
/**
* 解压缩数据
*/
public async decompress(
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}`);
}
try {
let decompressedData: ArrayBuffer;
// 选择同步或异步解压缩
if (this.config.enableAsync && algorithm.supportsAsync && algorithm.decompressAsync) {
decompressedData = await algorithm.decompressAsync(data);
} else {
decompressedData = algorithm.decompress(data);
}
const endTime = performance.now();
const decompressionTime = endTime - startTime;
const decompressedSize = decompressedData.byteLength;
// 更新统计信息
if (this.config.enableStats) {
this.updateDecompressionStats(algorithmName, decompressionTime);
}
const result: DecompressionResult = {
data: decompressedData,
compressedSize,
decompressedSize,
decompressionTime,
algorithm: algorithmName
};
this.logger.debug(
`解压缩完成: ${compressedSize}B -> ${decompressedSize}B ` +
`用时 ${decompressionTime.toFixed(2)}ms, 算法: ${algorithmName}`
);
return result;
} catch (error) {
this.logger.error(`解压缩失败 (${algorithmName}):`, error);
throw error;
}
}
/**
* 估算压缩后大小
*/
public estimateCompressedSize(
data: ArrayBuffer,
algorithmName?: string
): number {
const selectedAlgorithm = algorithmName || this.config.defaultAlgorithm;
const algorithm = this.algorithms.get(selectedAlgorithm);
if (!algorithm) {
return data.byteLength;
}
if (algorithm.estimateCompressedSize) {
return algorithm.estimateCompressedSize(data);
}
// 如果没有估算函数,返回原始大小
return data.byteLength;
}
/**
* 获取压缩统计信息
*/
public getStats(): CompressionStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
totalCompressions: 0,
totalDecompressions: 0,
totalOriginalBytes: 0,
totalCompressedBytes: 0,
averageCompressionRatio: 1.0,
averageCompressionTime: 0,
averageDecompressionTime: 0,
algorithmUsage: {}
};
// 重新初始化算法使用统计
for (const algorithmName of this.algorithms.keys()) {
this.stats.algorithmUsage[algorithmName] = 0;
}
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<CompressionConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('压缩器配置已更新');
}
/**
* 获取配置
*/
public getConfig(): CompressionConfig {
return { ...this.config };
}
/**
* 更新压缩统计信息
*/
private updateCompressionStats(
algorithmName: string,
originalSize: number,
compressedSize: number,
compressionTime: number
): void {
this.stats.totalCompressions++;
this.stats.totalOriginalBytes += originalSize;
this.stats.totalCompressedBytes += compressedSize;
this.stats.algorithmUsage[algorithmName]++;
// 更新平均压缩比
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.totalCompressions;
}
/**
* 更新解压缩统计信息
*/
private updateDecompressionStats(algorithmName: string, decompressionTime: number): void {
this.stats.totalDecompressions++;
// 更新平均解压缩时间
this.stats.averageDecompressionTime =
(this.stats.averageDecompressionTime * (this.stats.totalDecompressions - 1) + decompressionTime)
/ this.stats.totalDecompressions;
}
}
/**
* 全局压缩器实例
*/
export const globalCompressor = new MessageCompressor();

View File

@@ -1,639 +0,0 @@
import { SyncBatch } from '../sync/SyncVarManager';
import { MessageType, INetworkMessage } from '../types/NetworkTypes';
/**
* 序列化配置
*/
export interface SyncVarSerializerConfig {
/** 是否启用压缩 */
enableCompression: boolean;
/** 是否启用差量同步 */
enableDeltaSync: boolean;
/** 是否启用类型检查 */
enableTypeChecking: boolean;
/** 最大消息大小(字节) */
maxMessageSize: number;
/** 是否启用批量优化 */
enableBatching: boolean;
/** 批量超时时间(毫秒) */
batchTimeout: number;
}
/**
* 序列化结果
*/
export interface SerializationResult {
/** 是否成功 */
success: boolean;
/** 序列化的数据 */
data?: ArrayBuffer | string;
/** 错误信息 */
error?: string;
/** 原始大小 */
originalSize: number;
/** 压缩后大小 */
compressedSize: number;
/** 压缩比 */
compressionRatio: number;
}
/**
* 反序列化结果
*/
export interface DeserializationResult<T = any> {
/** 是否成功 */
success: boolean;
/** 反序列化的数据 */
data?: T;
/** 错误信息 */
errors?: string[];
/** 是否通过类型检查 */
isValidType: boolean;
}
/**
* 差量数据
*/
export interface DeltaData {
/** 基础版本 */
baseVersion: number;
/** 当前版本 */
currentVersion: number;
/** 变化的字段 */
changes: { [key: string]: any };
/** 删除的字段 */
deletions: string[];
}
/**
* 压缩元数据
*/
export interface CompressionMetadata {
/** 压缩算法 */
algorithm: string;
/** 原始大小 */
originalSize: number;
/** 压缩大小 */
compressedSize: number;
/** 压缩时间戳 */
timestamp: number;
}
/**
* SyncVar专用序列化器
* 针对SyncVar数据进行优化的序列化系统
*/
export class SyncVarSerializer {
private config: SyncVarSerializerConfig;
private deltaHistory = new Map<string, { version: number; data: any }>();
private versionCounter = 0;
private compressionCache = new Map<string, ArrayBuffer>();
constructor(config: Partial<SyncVarSerializerConfig> = {}) {
this.config = {
enableCompression: true,
enableDeltaSync: true,
enableTypeChecking: true,
maxMessageSize: 64 * 1024, // 64KB
enableBatching: true,
batchTimeout: 16, // 16ms (60fps)
...config
};
}
/**
* 序列化SyncVar批次数据
*/
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 {
success: false,
error: `消息大小超出限制: ${originalSize} > ${this.config.maxMessageSize}`,
originalSize,
compressedSize: 0,
compressionRatio: 0
};
}
let finalData: ArrayBuffer | string = jsonString;
let compressedSize = originalSize;
// 应用压缩
if (this.config.enableCompression && originalSize > 256) {
const compressionResult = this.compress(jsonString);
if (compressionResult.success && compressionResult.data) {
finalData = compressionResult.data;
compressedSize = compressionResult.data.byteLength;
}
}
const compressionRatio = originalSize > 0 ? compressedSize / originalSize : 1;
return {
success: true,
data: finalData,
originalSize,
compressedSize,
compressionRatio
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : '序列化失败',
originalSize: 0,
compressedSize: 0,
compressionRatio: 1
};
}
}
/**
* 反序列化SyncVar批次数据
*/
public deserializeSyncBatch(data: ArrayBuffer | string): DeserializationResult<SyncBatch> {
try {
let jsonString: string;
// 解压缩
if (data instanceof ArrayBuffer) {
const decompressResult = this.decompress(data);
if (!decompressResult.success || !decompressResult.data) {
return {
success: false,
errors: ['解压缩失败'],
isValidType: false
};
}
jsonString = decompressResult.data;
} else {
jsonString = data;
}
// JSON反序列化
const parsedData = JSON.parse(jsonString, this.reviver.bind(this));
// 类型检查
if (this.config.enableTypeChecking) {
const typeCheckResult = this.validateSyncBatchType(parsedData);
if (!typeCheckResult.isValid) {
return {
success: false,
errors: typeCheckResult.errors,
isValidType: false
};
}
}
// 应用差量还原
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,
errors: [error instanceof Error ? error.message : '反序列化失败'],
isValidType: false
};
}
}
/**
* 创建网络消息
*/
public createSyncMessage(batch: SyncBatch, senderId: string): INetworkMessage {
const serializedData = this.serializeSyncBatch(batch);
return {
type: MessageType.SYNC_BATCH,
messageId: this.generateMessageId(),
timestamp: Date.now(),
senderId,
data: serializedData.data,
reliable: true,
priority: this.calculateMessagePriority(batch)
};
}
/**
* 解析网络消息
*/
public parseSyncMessage(message: INetworkMessage): DeserializationResult<SyncBatch> {
if (message.type !== MessageType.SYNC_BATCH) {
return {
success: false,
errors: ['消息类型不匹配'],
isValidType: false
};
}
return this.deserializeSyncBatch(message.data);
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<SyncVarSerializerConfig>): void {
Object.assign(this.config, newConfig);
}
/**
* 获取配置
*/
public getConfig(): SyncVarSerializerConfig {
return { ...this.config };
}
/**
* 清理缓存
*/
public clearCache(): void {
this.deltaHistory.clear();
this.compressionCache.clear();
this.versionCounter = 0;
}
/**
* 获取缓存统计
*/
public getCacheStats(): { deltaHistorySize: number; compressionCacheSize: number } {
return {
deltaHistorySize: this.deltaHistory.size,
compressionCacheSize: this.compressionCache.size
};
}
/**
* 应用差量压缩
*/
private applyDeltaCompression(batch: SyncBatch): DeltaData | SyncBatch {
const key = batch.instanceId;
const lastRecord = this.deltaHistory.get(key);
if (!lastRecord) {
// 第一次同步,存储完整数据
this.deltaHistory.set(key, {
version: ++this.versionCounter,
data: { ...batch }
});
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)) {
if (!(prop in batch.changes)) {
deletions.push(prop);
}
}
}
// 如果没有变化,返回空的差量数据
if (Object.keys(changes).length === 0 && deletions.length === 0) {
return {
baseVersion: lastRecord.version,
currentVersion: lastRecord.version,
changes: {},
deletions: []
};
}
// 更新历史记录
const currentVersion = ++this.versionCounter;
this.deltaHistory.set(key, {
version: currentVersion,
data: { ...batch }
});
return {
baseVersion: lastRecord.version,
currentVersion,
changes,
deletions
};
}
/**
* 应用差量还原
*/
private applyDeltaRestore(deltaData: DeltaData): SyncBatch {
// 这里应该根据baseVersion找到对应的基础数据
// 简化实现返回一个基本的SyncBatch
return {
instanceId: 'unknown',
instanceType: 'unknown',
changes: deltaData.changes,
timestamp: Date.now(),
syncModes: {},
authorities: {},
scopes: {},
priorities: {}
};
}
/**
* 检查是否为差量数据
*/
private isDeltaData(data: any): data is DeltaData {
return data &&
typeof data.baseVersion === 'number' &&
typeof data.currentVersion === 'number' &&
typeof data.changes === 'object' &&
Array.isArray(data.deletions);
}
/**
* 压缩数据
*/
private compress(data: string): { success: boolean; data?: ArrayBuffer } {
try {
// 使用LZ字符串压缩算法
const compressed = this.lzCompress(data);
const encoder = new TextEncoder();
const bytes = encoder.encode(compressed);
return {
success: true,
data: bytes.buffer
};
} catch (error) {
return { success: false };
}
}
/**
* 解压缩数据
*/
private decompress(data: ArrayBuffer): { success: boolean; data?: string } {
try {
const decoder = new TextDecoder();
const compressedString = decoder.decode(data);
const decompressed = this.lzDecompress(compressedString);
return {
success: true,
data: decompressed
};
} catch (error) {
return { success: false };
}
}
/**
* JSON序列化替换函数
*/
private replacer(key: string, value: any): any {
// 处理特殊类型的序列化
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;
}
/**
* JSON反序列化恢复函数
*/
private reviver(key: string, value: any): any {
if (value && typeof value === 'object' && value.__type) {
switch (value.__type) {
case 'Date':
return new Date(value.value);
case 'Map':
return new Map(value.value);
case 'Set':
return new Set(value.value);
case 'BigInt':
return BigInt(value.value);
}
}
return value;
}
/**
* 验证SyncBatch类型
*/
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 };
}
/**
* 生成消息ID
*/
private generateMessageId(): string {
return `sync_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
/**
* 计算消息优先级
*/
private calculateMessagePriority(batch: SyncBatch): number {
// 根据批次中属性的优先级计算整体优先级
const priorities = Object.values(batch.priorities);
if (priorities.length === 0) {
return 5; // 默认优先级
}
// 使用最高优先级
return Math.max(...priorities);
}
/**
* LZ字符串压缩算法
*/
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 {
result.push(dictionary[w]);
dictionary[wc] = dictSize++;
w = c;
}
}
if (w) {
result.push(dictionary[w]);
}
// 将结果编码为Base64以确保字符串安全
return this.arrayToBase64(result);
}
/**
* LZ字符串解压缩算法
*/
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) {
entry = w + w.charAt(0);
} else {
throw new Error('解压缩错误:无效的压缩数据');
}
result.push(entry);
dictionary[dictSize++] = w + entry.charAt(0);
w = entry;
}
return result.join('');
}
/**
* 数组转Base64
*/
private arrayToBase64(array: number[]): string {
// 将数字数组转换为字节数组
const bytes: number[] = [];
for (const num of array) {
if (num < 256) {
bytes.push(num);
} else {
// 大于255的数字用两个字节表示
bytes.push(255, num - 255);
}
}
// 转换为字符串然后编码为Base64
const binaryString = String.fromCharCode(...bytes);
return btoa(binaryString);
}
/**
* Base64转数组
*/
private base64ToArray(base64: string): number[] {
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++) {
if (bytes[i] === 255 && i + 1 < bytes.length) {
result.push(255 + bytes[i + 1]);
i++; // 跳过下一个字节
} else {
result.push(bytes[i]);
}
}
return result;
} catch (error) {
throw new Error('Base64解码失败');
}
}
}

View File

@@ -1,794 +0,0 @@
import { createLogger } from '@esengine/ecs-framework';
/**
* 差量同步配置
*/
export interface DeltaSyncConfig {
/** 是否启用差量同步 */
enabled: boolean;
/** 最大历史版本数 */
maxHistoryVersions: number;
/** 版本超时时间(毫秒) */
versionTimeout: number;
/** 差量压缩阈值 */
compressionThreshold: number;
/** 是否启用智能合并 */
enableSmartMerging: boolean;
/** 合并时间窗口(毫秒) */
mergeWindow: number;
}
/**
* 版本化数据
*/
export interface VersionedData {
version: number;
timestamp: number;
data: any;
checksum?: string;
}
/**
* 差量数据
*/
export interface DeltaData {
baseVersion: number;
targetVersion: number;
changes: { [key: string]: any };
deletions: string[];
metadata: {
timestamp: number;
size: number;
compressionRatio: number;
};
}
/**
* 差量操作类型
*/
export enum DeltaOperationType {
/** 添加属性 */
ADD = 'add',
/** 修改属性 */
MODIFY = 'modify',
/** 删除属性 */
DELETE = 'delete',
/** 批量操作 */
BATCH = 'batch',
/** 无操作(合并后消除的操作) */
NOOP = 'noop'
}
/**
* 差量操作
*/
export interface DeltaOperation {
type: DeltaOperationType;
path: string;
oldValue?: any;
newValue?: any;
timestamp: number;
}
/**
* 差量同步统计
*/
export interface DeltaSyncStats {
totalDeltas: number;
totalSize: number;
compressionRatio: number;
averageDeltaSize: number;
cacheHitRate: number;
mergedOperations: number;
}
/**
* 差量同步器
* 负责计算和应用数据差量,减少网络传输量
*/
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,
totalSize: 0,
compressionRatio: 1,
averageDeltaSize: 0,
cacheHitRate: 0,
mergedOperations: 0
};
/** 合并定时器 */
private mergeTimers = new Map<string, any>();
constructor(config: Partial<DeltaSyncConfig> = {}) {
this.config = {
enabled: true,
maxHistoryVersions: 10,
versionTimeout: 30000,
compressionThreshold: 100,
enableSmartMerging: true,
mergeWindow: 50,
...config
};
}
/**
* 记录基线版本
*/
public recordBaseline(instanceId: string, data: any): number {
if (!this.config.enabled) {
return 0;
}
const version = this.getNextVersion(instanceId);
const versionedData: VersionedData = {
version,
timestamp: Date.now(),
data: this.deepClone(data),
checksum: this.calculateChecksum(data)
};
this.storeVersion(instanceId, versionedData);
return version;
}
/**
* 计算差量
*/
public calculateDelta(instanceId: string, newData: any, baseVersion?: number): DeltaData | null {
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) {
const foundVersion = history.get(baseVersion);
if (!foundVersion) {
this.logger.warn(`未找到版本 ${baseVersion},使用最新版本`);
const latestVersion = this.getLatestVersion(instanceId);
if (!latestVersion) {
this.logger.error(`实例 ${instanceId} 没有任何版本历史`);
return null;
}
baseVersionData = latestVersion;
} else {
baseVersionData = foundVersion;
}
} else {
const latestVersion = this.getLatestVersion(instanceId);
if (!latestVersion) {
this.logger.error(`实例 ${instanceId} 没有任何版本历史`);
return null;
}
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,
changes,
deletions,
metadata: {
timestamp: Date.now(),
size: this.estimateSize(changes) + this.estimateSize(deletions),
compressionRatio: 1
}
};
// 记录新版本
this.recordBaseline(instanceId, newData);
// 更新统计
this.updateStats(deltaData);
return deltaData;
}
/**
* 应用差量
*/
public applyDelta(instanceId: string, delta: DeltaData): any {
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,
timestamp: delta.metadata.timestamp,
data: this.deepClone(result),
checksum: this.calculateChecksum(result)
};
this.storeVersion(instanceId, resultVersion);
return result;
}
/**
* 智能合并操作
*/
public mergeOperations(instanceId: string, operations: DeltaOperation[]): DeltaOperation[] {
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 {
// 合并同路径的操作
const mergedOp = this.mergeTwoOperations(existing, op);
pathMap.set(op.path, mergedOp);
this.stats.mergedOperations++;
}
}
// 过滤掉NOOP操作
return Array.from(pathMap.values()).filter((op) => op.type !== DeltaOperationType.NOOP);
}
/**
* 延迟合并操作
*/
public scheduleOperation(instanceId: string, operation: DeltaOperation): void {
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);
}
/**
* 压缩差量数据
*/
public compressDelta(delta: DeltaData): DeltaData {
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,
deletions: compressedDeletions,
metadata: {
...delta.metadata,
size: compressedSize,
compressionRatio: compressedSize / originalSize
}
};
}
/**
* 清理过期版本
*/
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();
}
/**
* 获取统计信息
*/
public getStats(): DeltaSyncStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
totalDeltas: 0,
totalSize: 0,
compressionRatio: 1,
averageDeltaSize: 0,
cacheHitRate: 0,
mergedOperations: 0
};
}
/**
* 更新配置
*/
public updateConfig(newConfig: Partial<DeltaSyncConfig>): void {
Object.assign(this.config, newConfig);
}
/**
* 销毁同步器
*/
public destroy(): void {
// 清理定时器
for (const timer of this.mergeTimers.values()) {
clearTimeout(timer);
}
this.versionHistory.clear();
this.versionCounters.clear();
this.deltaCache.clear();
this.pendingOperations.clear();
this.mergeTimers.clear();
}
/**
* 获取下一个版本号
*/
private getNextVersion(instanceId: string): number {
const current = this.versionCounters.get(instanceId) || 0;
const next = current + 1;
this.versionCounters.set(instanceId, next);
return next;
}
/**
* 存储版本数据
*/
private storeVersion(instanceId: string, versionData: VersionedData): void {
let history = this.versionHistory.get(instanceId);
if (!history) {
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()));
history.delete(oldestVersion);
}
}
/**
* 获取最新版本
*/
private getLatestVersion(instanceId: string): VersionedData | undefined {
const history = this.versionHistory.get(instanceId);
if (!history || history.size === 0) {
return undefined;
}
const latestVersion = Math.max(...Array.from(history.keys()));
return history.get(latestVersion);
}
/**
* 计算变化
*/
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;
}
/**
* 计算删除
*/
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;
}
/**
* 合并两个操作
*/
private mergeTwoOperations(op1: DeltaOperation, op2: DeltaOperation): DeltaOperation {
// 智能合并逻辑
const mergedOp: DeltaOperation = {
type: op2.type,
path: op2.path,
oldValue: op1.oldValue, // 保留最初的旧值
newValue: op2.newValue,
timestamp: op2.timestamp
};
// 处理特殊合并情况
if (op1.type === DeltaOperationType.ADD && op2.type === DeltaOperationType.DELETE) {
// 添加后删除 = 无操作
return {
type: DeltaOperationType.NOOP,
path: op2.path,
oldValue: undefined,
newValue: undefined,
timestamp: op2.timestamp
};
}
if (op1.type === DeltaOperationType.DELETE && op2.type === DeltaOperationType.ADD) {
// 删除后添加 = 修改
mergedOp.type = DeltaOperationType.MODIFY;
mergedOp.oldValue = op1.oldValue;
}
if (op1.type === DeltaOperationType.MODIFY && op2.type === DeltaOperationType.DELETE) {
// 修改后删除 = 删除原始值
mergedOp.type = DeltaOperationType.DELETE;
mergedOp.newValue = undefined;
}
// 检查是否值回到了原始状态
if (op1.type === DeltaOperationType.MODIFY &&
op2.type === DeltaOperationType.MODIFY &&
this.deepEqual(op1.oldValue, op2.newValue)) {
// 值回到原始状态 = 无操作
return {
type: DeltaOperationType.NOOP,
path: op2.path,
oldValue: undefined,
newValue: undefined,
timestamp: op2.timestamp
};
}
return mergedOp;
}
/**
* 刷新待处理操作
*/
private flushPendingOperations(instanceId: string): void {
const operations = this.pendingOperations.get(instanceId);
if (!operations || operations.length === 0) {
return;
}
// 合并操作并发送
this.mergeOperations(instanceId, operations);
// 清理待处理操作
this.pendingOperations.delete(instanceId);
this.mergeTimers.delete(instanceId);
}
/**
* 压缩对象
*/
private compressObject(obj: any): any {
if (!obj || typeof obj !== 'object') {
return obj;
}
// 移除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') {
compressed[key] = this.compressObject(value);
} else {
compressed[key] = value;
}
}
}
return compressed;
}
/**
* 估算大小
*/
private estimateSize(obj: any): number {
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) {
size += this.estimateSize(item) + 1; // +1 for comma
}
return size;
}
if (typeof obj === 'object') {
let size = 2; // {}
for (const [key, value] of Object.entries(obj)) {
size += key.length * 2 + 3; // key + ":"
size += this.estimateSize(value) + 1; // value + comma
}
return size;
}
return JSON.stringify(obj).length;
}
/**
* 深度克隆
*/
private deepClone(obj: any): any {
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));
}
const cloned: any = {};
for (const [key, value] of Object.entries(obj)) {
cloned[key] = this.deepClone(value);
}
return cloned;
}
/**
* 深度比较
*/
private deepEqual(obj1: any, obj2: any): boolean {
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;
}
for (let i = 0; i < obj1.length; i++) {
if (!this.deepEqual(obj1[i], obj2[i])) {
return false;
}
}
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;
}
if (!this.deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
/**
* 设置嵌套属性
*/
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)) {
current[key] = {};
}
current = current[key];
}
current[keys[keys.length - 1]] = value;
}
/**
* 删除嵌套属性
*/
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)) {
return; // 路径不存在
}
current = current[key];
}
delete current[keys[keys.length - 1]];
}
/**
* 计算校验和
*/
private calculateChecksum(obj: any): string {
// 简化的校验和实现
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);
}
/**
* 更新统计信息
*/
private updateStats(delta: DeltaData): void {
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.totalDeltas;
}
}

View File

@@ -1,462 +0,0 @@
import {
getDirtySyncVars,
clearDirtyFlags,
hasSyncVars,
SyncVarValue
} from '../decorators/SyncVar';
import { SyncMode, AuthorityType, NetworkScope } from '../types/NetworkTypes';
import { EventEmitter } from '../utils/EventEmitter';
/**
* 同步批次数据
*/
export interface SyncBatch {
/** 实例ID */
instanceId: string;
/** 实例类型 */
instanceType: string;
/** 变化的属性数据 */
changes: { [propertyKey: string]: SyncVarValue };
/** 时间戳 */
timestamp: number;
/** 同步模式映射 */
syncModes: { [propertyKey: string]: SyncMode };
/** 权限映射 */
authorities: { [propertyKey: string]: AuthorityType };
/** 作用域映射 */
scopes: { [propertyKey: string]: NetworkScope };
/** 优先级映射 */
priorities: { [propertyKey: string]: number };
}
/**
* 同步统计信息
*/
export interface SyncStats {
/** 注册的实例数量 */
registeredInstances: number;
/** 脏实例数量 */
dirtyInstances: number;
/** 总同步次数 */
totalSyncs: number;
/** 总传输字节数 */
totalBytes: number;
/** 平均同步延迟 */
averageLatency: number;
/** 每秒同步次数 */
syncsPerSecond: number;
/** 最后同步时间 */
lastSyncTime: number;
}
/**
* SyncVar管理器事件
*/
export interface SyncVarManagerEvents {
instanceRegistered: (instanceId: string, instance: object) => void;
instanceUnregistered: (instanceId: string) => void;
syncBatchReady: (batch: SyncBatch) => void;
syncCompleted: (instanceId: string, propertyCount: number) => void;
syncError: (error: Error, instanceId?: string) => void;
}
/**
* SyncVar管理器
* 负责管理所有带有SyncVar的实例追踪变化并生成同步批次
*/
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,
dirtyInstances: 0,
totalSyncs: 0,
totalBytes: 0,
averageLatency: 0,
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 }>();
private constructor() {
super();
this.startAutoSync();
}
/**
* 获取单例实例
*/
public static getInstance(): SyncVarManager {
if (!SyncVarManager.instance) {
SyncVarManager.instance = new SyncVarManager();
}
return SyncVarManager.instance;
}
/**
* 注册实例
*/
public registerInstance(instance: object): string {
if (!hasSyncVars(instance)) {
throw new Error('实例没有SyncVar属性无法注册');
}
// 检查是否已经注册
if (this.instanceIdMap.has(instance)) {
return this.instanceIdMap.get(instance)!;
}
// 生成新的实例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;
}
/**
* 注销实例
*/
public unregisterInstance(instance: object): boolean {
const instanceId = this.instanceIdMap.get(instance);
if (!instanceId) {
return false;
}
// 删除注册信息
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;
}
/**
* 标记实例为脏数据
*/
public markInstanceDirty(instance: object): void {
const instanceId = this.instanceIdMap.get(instance);
if (!instanceId) {
// 自动注册实例
this.registerInstance(instance);
return this.markInstanceDirty(instance);
}
this.dirtyInstances.add(instanceId);
this.stats.dirtyInstances = this.dirtyInstances.size;
}
/**
* 请求立即同步
*/
public requestImmediateSync(instance: object, propertyKey?: string | symbol): void {
const instanceId = this.instanceIdMap.get(instance);
if (!instanceId) {
return;
}
this.markInstanceDirty(instance);
this.immediateSyncQueue.add({ instanceId, propertyKey });
// 立即处理同步
this.processImmediateSyncs();
}
/**
* 手动触发同步
*/
public syncNow(): SyncBatch[] {
const batches: SyncBatch[] = [];
// 处理立即同步请求
this.processImmediateSyncs();
// 收集所有脏实例的数据
for (const instanceId of this.dirtyInstances) {
const batch = this.createSyncBatch(instanceId);
if (batch && Object.keys(batch.changes).length > 0) {
batches.push(batch);
}
}
// 清理脏标记
this.clearAllDirtyFlags();
// 更新统计
this.stats.totalSyncs += batches.length;
this.stats.lastSyncTime = Date.now();
return batches;
}
/**
* 设置同步频率
*/
public setSyncRate(rate: number): void {
this.syncRate = Math.max(1, rate);
if (this.autoSyncEnabled) {
this.restartAutoSync();
}
}
/**
* 启用/禁用自动同步
*/
public setAutoSyncEnabled(enabled: boolean): void {
this.autoSyncEnabled = enabled;
if (enabled) {
this.startAutoSync();
} else {
this.stopAutoSync();
}
}
/**
* 设置最大批次大小
*/
public setMaxBatchSize(size: number): void {
this.maxBatchSize = Math.max(1, size);
}
/**
* 获取统计信息
*/
public getStats(): SyncStats {
return { ...this.stats };
}
/**
* 获取实例ID
*/
public getInstanceId(instance: object): string | undefined {
return this.instanceIdMap.get(instance);
}
/**
* 获取实例
*/
public getInstance(instanceId: string): object | undefined {
return this.registeredInstances.get(instanceId);
}
/**
* 获取所有注册的实例ID
*/
public getAllInstanceIds(): string[] {
return Array.from(this.registeredInstances.keys());
}
/**
* 检查实例是否为脏数据
*/
public isInstanceDirty(instanceId: string): boolean {
return this.dirtyInstances.has(instanceId);
}
/**
* 重置统计信息
*/
public resetStats(): void {
this.stats = {
...this.stats,
totalSyncs: 0,
totalBytes: 0,
averageLatency: 0,
syncsPerSecond: 0
};
}
/**
* 销毁管理器
*/
public destroy(): void {
this.stopAutoSync();
this.registeredInstances.clear();
this.dirtyInstances.clear();
this.instanceIdMap = new WeakMap();
this.immediateSyncQueue.clear();
this.removeAllListeners();
SyncVarManager.instance = null;
}
/**
* 创建同步批次
*/
private createSyncBatch(instanceId: string): SyncBatch | null {
const instance = this.registeredInstances.get(instanceId);
if (!instance) {
return null;
}
const dirtyVars = getDirtySyncVars(instance);
if (dirtyVars.size === 0) {
return null;
}
const changes: { [propertyKey: string]: SyncVarValue } = {};
const syncModes: { [propertyKey: string]: SyncMode } = {};
const authorities: { [propertyKey: string]: AuthorityType } = {};
const scopes: { [propertyKey: string]: NetworkScope } = {};
const priorities: { [propertyKey: string]: number } = {};
for (const [propertyKey, metadata] of dirtyVars) {
const key = String(propertyKey);
changes[key] = (instance as any)[propertyKey];
syncModes[key] = metadata.options.mode;
authorities[key] = metadata.options.authority;
scopes[key] = metadata.options.scope;
priorities[key] = metadata.options.priority;
}
return {
instanceId,
instanceType: instance.constructor.name,
changes,
timestamp: Date.now(),
syncModes,
authorities,
scopes,
priorities
};
}
/**
* 处理立即同步请求
*/
private processImmediateSyncs(): void {
if (this.immediateSyncQueue.size === 0) {
return;
}
const batches: SyncBatch[] = [];
for (const request of this.immediateSyncQueue) {
const batch = this.createSyncBatch(request.instanceId);
if (batch && Object.keys(batch.changes).length > 0) {
// 如果指定了特定属性,只同步该属性
if (request.propertyKey) {
const key = String(request.propertyKey);
if (batch.changes[key] !== undefined) {
const filteredBatch: SyncBatch = {
...batch,
changes: { [key]: batch.changes[key] },
syncModes: { [key]: batch.syncModes[key] },
authorities: { [key]: batch.authorities[key] },
scopes: { [key]: batch.scopes[key] },
priorities: { [key]: batch.priorities[key] }
};
batches.push(filteredBatch);
}
} else {
batches.push(batch);
}
}
}
// 清空立即同步队列
this.immediateSyncQueue.clear();
// 发送批次
for (const batch of batches) {
this.emit('syncBatchReady', batch);
this.emit('syncCompleted', batch.instanceId, Object.keys(batch.changes).length);
}
}
/**
* 清理所有脏标记
*/
private clearAllDirtyFlags(): void {
for (const instanceId of this.dirtyInstances) {
const instance = this.registeredInstances.get(instanceId);
if (instance) {
clearDirtyFlags(instance);
}
}
this.dirtyInstances.clear();
this.stats.dirtyInstances = 0;
}
/**
* 启动自动同步
*/
private startAutoSync(): void {
if (this.autoSyncTimer || !this.autoSyncEnabled) {
return;
}
this.autoSyncTimer = setInterval(() => {
try {
const batches = this.syncNow();
for (const batch of batches) {
this.emit('syncBatchReady', batch);
this.emit('syncCompleted', batch.instanceId, Object.keys(batch.changes).length);
}
} catch (error) {
this.emit('syncError', error as Error);
}
}, this.syncRate);
}
/**
* 停止自动同步
*/
private stopAutoSync(): void {
if (this.autoSyncTimer) {
clearInterval(this.autoSyncTimer);
this.autoSyncTimer = null;
}
}
/**
* 重启自动同步
*/
private restartAutoSync(): void {
this.stopAutoSync();
this.startAutoSync();
}
}
// 全局单例访问
if (typeof window !== 'undefined') {
(window as any).SyncVarManager = SyncVarManager.getInstance();
}

View File

@@ -1,6 +0,0 @@
/**
* 同步系统导出
*/
export * from './SyncVarManager';
export * from './DeltaSync';

View File

@@ -1,461 +0,0 @@
/**
* 网络错误处理器
* 提供统一的错误处理、分类和恢复策略
*/
import { createLogger } from '@esengine/ecs-framework';
import { NetworkErrorType, INetworkError } from '../types/NetworkTypes';
/**
* 错误严重级别
*/
export enum ErrorSeverity {
Low = 'low', // 低级错误,可以忽略
Medium = 'medium', // 中级错误,需要记录但不影响功能
High = 'high', // 高级错误,影响功能但可以恢复
Critical = 'critical' // 严重错误,需要立即处理
}
/**
* 错误恢复策略
*/
export enum RecoveryStrategy {
Ignore = 'ignore', // 忽略错误
Retry = 'retry', // 重试操作
Reconnect = 'reconnect', // 重新连接
Restart = 'restart', // 重启服务
Escalate = 'escalate' // 上报错误
}
/**
* 错误处理配置
*/
export interface ErrorHandlerConfig {
maxRetryAttempts: number;
retryDelay: number;
enableAutoRecovery: boolean;
enableErrorReporting: boolean;
errorReportingEndpoint?: string;
}
/**
* 错误统计信息
*/
export interface ErrorStats {
totalErrors: number;
errorsByType: Record<NetworkErrorType, number>;
errorsBySeverity: Record<ErrorSeverity, number>;
recoveredErrors: number;
unrecoveredErrors: number;
lastError?: INetworkError;
}
/**
* 错误处理事件
*/
export interface ErrorHandlerEvents {
errorOccurred: (error: INetworkError, severity: ErrorSeverity) => void;
errorRecovered: (error: INetworkError, strategy: RecoveryStrategy) => void;
errorUnrecoverable: (error: INetworkError) => void;
criticalError: (error: INetworkError) => void;
}
/**
* 网络错误处理器
*/
export class ErrorHandler {
private logger = createLogger('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();
/**
* 构造函数
*/
constructor(config: Partial<ErrorHandlerConfig> = {}) {
this.config = {
maxRetryAttempts: 3,
retryDelay: 1000,
enableAutoRecovery: true,
enableErrorReporting: false,
...config
};
this.stats = {
totalErrors: 0,
errorsByType: {} as Record<NetworkErrorType, number>,
errorsBySeverity: {} as Record<ErrorSeverity, number>,
recoveredErrors: 0,
unrecoveredErrors: 0
};
this.initializeDefaultRules();
}
/**
* 处理错误
*/
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,
details: networkError.details,
context
});
// 触发错误事件
this.eventHandlers.errorOccurred?.(networkError, severity);
// 处理严重错误
if (severity === ErrorSeverity.Critical) {
this.eventHandlers.criticalError?.(networkError);
}
// 尝试自动恢复
if (this.config.enableAutoRecovery) {
this.attemptRecovery(networkError, severity);
}
// 错误报告
if (this.config.enableErrorReporting) {
this.reportError(networkError, severity);
}
}
/**
* 设置错误分类规则
*/
setErrorSeverityRule(errorType: NetworkErrorType, severity: ErrorSeverity): void {
this.severityRules.set(errorType, severity);
}
/**
* 设置错误恢复策略
*/
setRecoveryStrategy(errorType: NetworkErrorType, strategy: RecoveryStrategy): void {
this.recoveryRules.set(errorType, strategy);
}
/**
* 获取错误统计
*/
getStats(): ErrorStats {
return { ...this.stats };
}
/**
* 重置统计信息
*/
resetStats(): void {
this.stats = {
totalErrors: 0,
errorsByType: {} as Record<NetworkErrorType, number>,
errorsBySeverity: {} as Record<ErrorSeverity, number>,
recoveredErrors: 0,
unrecoveredErrors: 0
};
this.retryAttempts.clear();
this.pendingRecoveries.clear();
}
/**
* 设置事件处理器
*/
on<K extends keyof ErrorHandlerEvents>(event: K, handler: ErrorHandlerEvents[K]): void {
this.eventHandlers[event] = handler;
}
/**
* 移除事件处理器
*/
off<K extends keyof ErrorHandlerEvents>(event: K): void {
delete this.eventHandlers[event];
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<ErrorHandlerConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('错误处理器配置已更新:', newConfig);
}
/**
* 手动标记错误已恢复
*/
markErrorRecovered(errorId: string): void {
this.retryAttempts.delete(errorId);
this.pendingRecoveries.delete(errorId);
this.stats.recoveredErrors++;
}
/**
* 检查错误是否可恢复
*/
isRecoverable(errorType: NetworkErrorType): boolean {
const strategy = this.recoveryRules.get(errorType);
return strategy !== undefined && strategy !== RecoveryStrategy.Ignore;
}
/**
* 标准化错误对象
*/
private normalizeError(error: Error | INetworkError, context?: string): INetworkError {
if ('type' in error && 'message' in error && 'timestamp' in error) {
return error as INetworkError;
}
// 将普通Error转换为INetworkError
return {
type: this.determineErrorType(error),
message: error.message || '未知错误',
code: (error as any).code,
details: {
context,
stack: error.stack,
name: error.name
},
timestamp: Date.now()
};
}
/**
* 确定错误类型
*/
private determineErrorType(error: Error): NetworkErrorType {
const message = error.message.toLowerCase();
if (message.includes('timeout')) {
return NetworkErrorType.TIMEOUT;
} else if (message.includes('connection')) {
return NetworkErrorType.CONNECTION_LOST;
} else if (message.includes('auth')) {
return NetworkErrorType.AUTHENTICATION_FAILED;
} else if (message.includes('permission')) {
return NetworkErrorType.PERMISSION_DENIED;
} else if (message.includes('rate') || message.includes('limit')) {
return NetworkErrorType.RATE_LIMITED;
} else if (message.includes('invalid') || message.includes('format')) {
return NetworkErrorType.INVALID_MESSAGE;
} else {
return NetworkErrorType.UNKNOWN;
}
}
/**
* 分类错误严重程度
*/
private classifyErrorSeverity(error: INetworkError): ErrorSeverity {
// 使用自定义规则
const customSeverity = this.severityRules.get(error.type);
if (customSeverity) {
return customSeverity;
}
// 默认分类规则
switch (error.type) {
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;
}
}
/**
* 更新统计信息
*/
private updateStats(error: INetworkError, severity: ErrorSeverity): void {
this.stats.totalErrors++;
this.stats.errorsByType[error.type] = (this.stats.errorsByType[error.type] || 0) + 1;
this.stats.errorsBySeverity[severity] = (this.stats.errorsBySeverity[severity] || 0) + 1;
this.stats.lastError = error;
}
/**
* 尝试错误恢复
*/
private attemptRecovery(error: INetworkError, severity: ErrorSeverity): void {
const strategy = this.recoveryRules.get(error.type);
if (!strategy || strategy === RecoveryStrategy.Ignore) {
return;
}
const errorId = this.generateErrorId(error);
// 检查是否已经在恢复中
if (this.pendingRecoveries.has(errorId)) {
return;
}
// 检查重试次数
const retryCount = this.retryAttempts.get(errorId) || 0;
if (retryCount >= this.config.maxRetryAttempts) {
this.stats.unrecoveredErrors++;
this.eventHandlers.errorUnrecoverable?.(error);
return;
}
this.pendingRecoveries.add(errorId);
this.retryAttempts.set(errorId, retryCount + 1);
this.logger.info(`尝试错误恢复: ${strategy} (第 ${retryCount + 1} 次)`, {
errorType: error.type,
strategy
});
// 延迟执行恢复策略
setTimeout(() => {
this.executeRecoveryStrategy(error, strategy, errorId);
}, this.config.retryDelay * (retryCount + 1));
}
/**
* 执行恢复策略
*/
private executeRecoveryStrategy(
error: INetworkError,
strategy: RecoveryStrategy,
errorId: string
): void {
try {
switch (strategy) {
case RecoveryStrategy.Retry:
// 这里应该重试导致错误的操作
// 具体实现需要外部提供重试回调
break;
case RecoveryStrategy.Reconnect:
// 这里应该触发重连
// 具体实现需要外部处理
break;
case RecoveryStrategy.Restart:
// 这里应该重启相关服务
// 具体实现需要外部处理
break;
case RecoveryStrategy.Escalate:
// 上报错误给上层处理
this.logger.error('错误需要上层处理:', error);
break;
}
this.pendingRecoveries.delete(errorId);
this.eventHandlers.errorRecovered?.(error, strategy);
} catch (recoveryError) {
this.logger.error('错误恢复失败:', recoveryError);
this.pendingRecoveries.delete(errorId);
}
}
/**
* 报告错误
*/
private async reportError(error: INetworkError, severity: ErrorSeverity): Promise<void> {
if (!this.config.errorReportingEndpoint) {
return;
}
try {
const report = {
error,
severity,
timestamp: Date.now(),
userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'Node.js',
url: typeof window !== 'undefined' ? window.location.href : 'server'
};
// 发送错误报告
await fetch(this.config.errorReportingEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
});
} catch (reportError) {
this.logger.error('发送错误报告失败:', reportError);
}
}
/**
* 生成错误ID
*/
private generateErrorId(error: INetworkError): string {
return `${error.type}-${error.code || 'no-code'}-${error.timestamp}`;
}
/**
* 初始化默认规则
*/
private initializeDefaultRules(): void {
// 默认严重程度规则
this.severityRules.set(NetworkErrorType.CONNECTION_FAILED, ErrorSeverity.High);
this.severityRules.set(NetworkErrorType.CONNECTION_LOST, ErrorSeverity.High);
this.severityRules.set(NetworkErrorType.AUTHENTICATION_FAILED, ErrorSeverity.Critical);
this.severityRules.set(NetworkErrorType.PERMISSION_DENIED, ErrorSeverity.Critical);
this.severityRules.set(NetworkErrorType.TIMEOUT, ErrorSeverity.Medium);
this.severityRules.set(NetworkErrorType.RATE_LIMITED, ErrorSeverity.Medium);
this.severityRules.set(NetworkErrorType.INVALID_MESSAGE, ErrorSeverity.Low);
// 默认恢复策略
this.recoveryRules.set(NetworkErrorType.CONNECTION_FAILED, RecoveryStrategy.Reconnect);
this.recoveryRules.set(NetworkErrorType.CONNECTION_LOST, RecoveryStrategy.Reconnect);
this.recoveryRules.set(NetworkErrorType.TIMEOUT, RecoveryStrategy.Retry);
this.recoveryRules.set(NetworkErrorType.RATE_LIMITED, RecoveryStrategy.Retry);
this.recoveryRules.set(NetworkErrorType.INVALID_MESSAGE, RecoveryStrategy.Ignore);
this.recoveryRules.set(NetworkErrorType.AUTHENTICATION_FAILED, RecoveryStrategy.Escalate);
this.recoveryRules.set(NetworkErrorType.PERMISSION_DENIED, RecoveryStrategy.Escalate);
}
/**
* 获取错误趋势分析
*/
getErrorTrends() {
const totalErrors = this.stats.totalErrors;
if (totalErrors === 0) {
return { trend: 'stable', recommendation: '系统运行正常' };
}
const criticalRate = (this.stats.errorsBySeverity[ErrorSeverity.Critical] || 0) / totalErrors;
const recoveryRate = this.stats.recoveredErrors / totalErrors;
if (criticalRate > 0.1) {
return { trend: 'critical', recommendation: '存在严重错误,需要立即处理' };
} else if (recoveryRate < 0.5) {
return { trend: 'degrading', recommendation: '错误恢复率偏低,建议检查恢复策略' };
} else if (totalErrors > 100) {
return { trend: 'high_volume', recommendation: '错误量较大,建议分析根本原因' };
} else {
return { trend: 'stable', recommendation: '错误处理正常' };
}
}
}

View File

@@ -1,381 +0,0 @@
/**
* 心跳管理器
* 负责管理网络连接的心跳检测,包括延迟测算和连接健康检测
*/
import { createLogger } from '@esengine/ecs-framework';
import { MessageType } from '../types/NetworkTypes';
/**
* 心跳配置
*/
export interface HeartbeatConfig {
interval: number; // 心跳间隔(毫秒)
timeout: number; // 心跳超时(毫秒)
maxMissedHeartbeats: number; // 最大丢失心跳数
enableLatencyMeasurement: boolean; // 是否启用延迟测量
}
/**
* 心跳状态
*/
export interface HeartbeatStatus {
isHealthy: boolean;
lastHeartbeat: number;
latency?: number;
missedHeartbeats: number;
averageLatency?: number;
packetLoss?: number;
}
/**
* 心跳事件接口
*/
export interface HeartbeatEvents {
heartbeatSent: (timestamp: number) => void;
heartbeatReceived: (latency: number) => void;
heartbeatTimeout: (missedCount: number) => void;
healthStatusChanged: (isHealthy: boolean) => void;
}
/**
* 心跳消息接口
*/
export interface HeartbeatMessage {
type: MessageType.HEARTBEAT;
clientTime: number;
serverTime?: number;
sequence?: number;
}
/**
* 心跳管理器
*/
export class HeartbeatManager {
private logger = createLogger('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;
/**
* 发送心跳回调
*/
private sendHeartbeat?: (message: HeartbeatMessage) => void;
/**
* 构造函数
*/
constructor(config: Partial<HeartbeatConfig> = {}) {
this.config = {
interval: 30000, // 30秒
timeout: 60000, // 60秒
maxMissedHeartbeats: 3, // 最大丢失3次
enableLatencyMeasurement: true,
...config
};
this.status = {
isHealthy: true,
lastHeartbeat: Date.now(),
missedHeartbeats: 0
};
}
/**
* 启动心跳
*/
start(sendCallback: (message: HeartbeatMessage) => void): void {
this.sendHeartbeat = sendCallback;
this.startHeartbeatTimer();
this.logger.info('心跳管理器已启动');
}
/**
* 停止心跳
*/
stop(): void {
this.stopHeartbeatTimer();
this.stopTimeoutTimer();
this.pendingPings.clear();
this.logger.info('心跳管理器已停止');
}
/**
* 处理接收到的心跳响应
*/
handleHeartbeatResponse(message: HeartbeatMessage): void {
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);
if (sentTime) {
const latency = now - sentTime;
this.updateLatency(latency);
this.pendingPings.delete(message.sequence);
this.eventHandlers.heartbeatReceived?.(latency);
}
}
// 更新健康状态
this.updateHealthStatus(true);
// 停止超时定时器
this.stopTimeoutTimer();
}
/**
* 处理心跳超时
*/
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);
}
}
/**
* 获取心跳状态
*/
getStatus(): HeartbeatStatus {
return { ...this.status };
}
/**
* 获取统计信息
*/
getStats() {
const packetLoss = this.sentCount > 0 ?
((this.sentCount - this.receivedCount) / this.sentCount) * 100 : 0;
return {
sentCount: this.sentCount,
receivedCount: this.receivedCount,
packetLoss,
averageLatency: this.status.averageLatency,
currentLatency: this.status.latency,
isHealthy: this.status.isHealthy,
missedHeartbeats: this.status.missedHeartbeats,
latencyHistory: [...this.latencyHistory]
};
}
/**
* 设置事件处理器
*/
on<K extends keyof HeartbeatEvents>(event: K, handler: HeartbeatEvents[K]): void {
this.eventHandlers[event] = handler;
}
/**
* 移除事件处理器
*/
off<K extends keyof HeartbeatEvents>(event: K): void {
delete this.eventHandlers[event];
}
/**
* 手动发送心跳
*/
sendHeartbeatNow(): void {
this.doSendHeartbeat();
}
/**
* 更新配置
*/
updateConfig(newConfig: Partial<HeartbeatConfig>): void {
Object.assign(this.config, newConfig);
this.logger.info('心跳配置已更新:', newConfig);
// 重启定时器以应用新配置
if (this.heartbeatTimer) {
this.stop();
if (this.sendHeartbeat) {
this.start(this.sendHeartbeat);
}
}
}
/**
* 启动心跳定时器
*/
private startHeartbeatTimer(): void {
this.heartbeatTimer = window.setInterval(() => {
this.doSendHeartbeat();
}, this.config.interval);
}
/**
* 停止心跳定时器
*/
private stopHeartbeatTimer(): void {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = undefined;
}
}
/**
* 启动超时定时器
*/
private startTimeoutTimer(): void {
this.timeoutTimer = window.setTimeout(() => {
this.handleHeartbeatTimeout();
}, this.config.timeout);
}
/**
* 停止超时定时器
*/
private stopTimeoutTimer(): void {
if (this.timeoutTimer) {
clearTimeout(this.timeoutTimer);
this.timeoutTimer = undefined;
}
}
/**
* 执行发送心跳
*/
private doSendHeartbeat(): void {
if (!this.sendHeartbeat) {
this.logger.error('心跳发送回调未设置');
return;
}
const now = Date.now();
const sequence = this.config.enableLatencyMeasurement ? ++this.sequence : undefined;
const message: HeartbeatMessage = {
type: MessageType.HEARTBEAT,
clientTime: now,
sequence
};
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);
}
}
/**
* 更新延迟信息
*/
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`);
}
/**
* 更新健康状态
*/
private updateHealthStatus(isHealthy: boolean): void {
if (this.status.isHealthy !== isHealthy) {
this.status.isHealthy = isHealthy;
this.logger.info(`连接健康状态变更: ${isHealthy ? '健康' : '不健康'}`);
this.eventHandlers.healthStatusChanged?.(isHealthy);
}
}
/**
* 清理过期的pending pings
*/
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);
}
}
}
/**
* 重置统计信息
*/
resetStats(): void {
this.sentCount = 0;
this.receivedCount = 0;
this.latencyHistory.length = 0;
this.status.averageLatency = undefined;
this.status.latency = undefined;
this.status.missedHeartbeats = 0;
this.pendingPings.clear();
this.logger.info('心跳统计信息已重置');
}
/**
* 检查连接是否健康
*/
isConnectionHealthy(): boolean {
const now = Date.now();
const timeSinceLastHeartbeat = now - this.status.lastHeartbeat;
return this.status.isHealthy &&
timeSinceLastHeartbeat <= this.config.timeout &&
this.status.missedHeartbeats < this.config.maxMissedHeartbeats;
}
/**
* 获取建议的重连延迟
*/
getReconnectDelay(): number {
// 基于丢失心跳次数计算重连延迟
const baseDelay = this.config.interval;
const multiplier = Math.min(Math.pow(2, this.status.missedHeartbeats), 8);
return baseDelay * multiplier;
}
}

View File

@@ -1,251 +0,0 @@
/**
* 网络层核心类型定义
*/
/**
* 网络消息类型枚举
*/
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',
INFO = 'info'
}
/**
* 网络消息基础接口
*/
export interface INetworkMessage {
/** 消息类型 */
type: MessageType;
/** 消息唯一ID */
messageId: string;
/** 时间戳 */
timestamp: number;
/** 发送者ID */
senderId: string;
/** 消息数据 */
data: any;
/** 是否可靠传输 */
reliable?: boolean;
/** 消息优先级 */
priority?: number;
}
/**
* 同步权限类型
*/
export enum AuthorityType {
/** 服务端权限 */
Server = 'server',
/** 客户端权限 */
Client = 'client',
/** 共享权限 */
Shared = 'shared'
}
/**
* 网络作用域
*/
export enum NetworkScope {
/** 全局可见 */
Global = 'global',
/** 房间内可见 */
Room = 'room',
/** 仅拥有者可见 */
Owner = 'owner',
/** 附近玩家可见 */
Nearby = 'nearby',
/** 自定义作用域 */
Custom = 'custom'
}
/**
* 同步模式
*/
export enum SyncMode {
/** 同步给所有客户端 */
All = 'all',
/** 只同步给拥有者 */
Owner = 'owner',
/** 同步给除拥有者外的客户端 */
Others = 'others',
/** 同步给附近的客户端 */
Nearby = 'nearby',
/** 自定义同步逻辑 */
Custom = 'custom'
}
/**
* RPC目标
*/
export enum RpcTarget {
/** 服务端 */
Server = 'server',
/** 客户端 */
Client = 'client',
/** 所有客户端 */
All = 'all',
/** 除发送者外的客户端 */
Others = 'others',
/** 拥有者客户端 */
Owner = 'owner',
/** 附近的客户端 */
Nearby = 'nearby'
}
/**
* 客户端信息
*/
export interface IClientInfo {
/** 客户端ID */
id: string;
/** 客户端名称 */
name: string;
/** 加入时间 */
joinTime: number;
/** 是否已认证 */
authenticated: boolean;
/** 延迟(毫秒) */
latency?: number;
/** 自定义数据 */
userData?: Record<string, any>;
}
/**
* 房间信息
*/
export interface IRoomInfo {
/** 房间ID */
id: string;
/** 房间名称 */
name: string;
/** 当前玩家数量 */
playerCount: number;
/** 最大玩家数量 */
maxPlayers: number;
/** 房间状态 */
state: RoomState;
/** 自定义数据 */
metadata?: Record<string, any>;
}
/**
* 房间状态
*/
export enum RoomState {
/** 等待中 */
Waiting = 'waiting',
/** 游戏中 */
Playing = 'playing',
/** 已暂停 */
Paused = 'paused',
/** 已结束 */
Finished = 'finished'
}
/**
* 网络统计信息
*/
export interface INetworkStats {
/** 总发送字节数 */
bytesSent: number;
/** 总接收字节数 */
bytesReceived: number;
/** 发送消息数 */
messagesSent: number;
/** 接收消息数 */
messagesReceived: number;
/** 平均延迟 */
averageLatency: number;
/** 丢包率 */
packetLoss: number;
/** 连接时长 */
connectionTime: number;
}
/**
* 向量2D
*/
export interface IVector2 {
x: number;
y: number;
}
/**
* 向量3D
*/
export interface IVector3 extends IVector2 {
z: number;
}
/**
* 四元数
*/
export interface IQuaternion {
x: number;
y: number;
z: number;
w: number;
}
/**
* 变换信息
*/
export interface ITransform {
position: IVector3;
rotation: IQuaternion;
scale: IVector3;
}
/**
* 网络错误类型
*/
export enum NetworkErrorType {
CONNECTION_FAILED = 'connection_failed',
CONNECTION_LOST = 'connection_lost',
AUTHENTICATION_FAILED = 'authentication_failed',
PERMISSION_DENIED = 'permission_denied',
RATE_LIMITED = 'rate_limited',
INVALID_MESSAGE = 'invalid_message',
TIMEOUT = 'timeout',
UNKNOWN = 'unknown'
}
/**
* 网络错误信息
*/
export interface INetworkError {
type: NetworkErrorType;
message: string;
code?: number;
details?: any;
timestamp: number;
}

View File

@@ -1,214 +0,0 @@
import { RpcTarget } from './NetworkTypes';
/**
* RPC调用配置
*/
export interface RpcOptions {
/** 是否可靠传输 */
reliable?: boolean;
/** 调用优先级 0-1010为最高 */
priority?: number;
/** 调用目标 */
target?: RpcTarget;
/** 超时时间(毫秒) */
timeout?: number;
/** 是否需要身份验证 */
requireAuth?: boolean;
/** 调用频率限制(每秒) */
rateLimit?: number;
}
/**
* RPC方法元数据
*/
export interface RpcMethodMetadata {
/** 方法名 */
methodName: string;
/** 所属类名 */
className: string;
/** 是否为服务端RPC */
isServerRpc: boolean;
/** RPC配置 */
options: RpcOptions;
/** 参数类型 */
paramTypes: string[];
/** 返回值类型 */
returnType: string;
}
/**
* RPC调用请求
*/
export interface RpcCallRequest<T extends readonly unknown[] = readonly unknown[]> {
/** 调用ID */
callId: string;
/** 方法名 */
methodName: string;
/** 调用参数 */
args: T;
/** 发送者ID */
senderId: string;
/** 目标ID(可选,用于特定客户端调用) */
targetId?: string;
/** 调用时间戳 */
timestamp: number;
/** 调用配置 */
options: RpcOptions;
}
/**
* RPC调用响应
*/
export interface RpcCallResponse<T = unknown> {
/** 调用ID */
callId: string;
/** 是否成功 */
success: boolean;
/** 返回值 */
result?: T;
/** 错误信息 */
error?: RpcError;
/** 响应时间戳 */
timestamp: number;
/** 处理时长(毫秒) */
duration: number;
}
/**
* RPC错误类型
*/
export enum RpcErrorType {
/** 方法不存在 */
METHOD_NOT_FOUND = 'method_not_found',
/** 参数无效 */
INVALID_ARGUMENTS = 'invalid_arguments',
/** 权限不足 */
PERMISSION_DENIED = 'permission_denied',
/** 调用超时 */
TIMEOUT = 'timeout',
/** 速率限制 */
RATE_LIMITED = 'rate_limited',
/** 网络错误 */
NETWORK_ERROR = 'network_error',
/** 服务端错误 */
SERVER_ERROR = 'server_error',
/** 客户端错误 */
CLIENT_ERROR = 'client_error',
/** 未知错误 */
UNKNOWN = 'unknown'
}
/**
* RPC错误信息
*/
export interface RpcError {
/** 错误类型 */
type: RpcErrorType;
/** 错误消息 */
message: string;
/** 错误代码 */
code?: number;
/** 详细信息 */
details?: Record<string, unknown>;
/** 堆栈信息 */
stack?: string;
}
/**
* RPC调用状态
*/
export enum RpcCallStatus {
/** 待发送 */
PENDING = 'pending',
/** 已发送 */
SENT = 'sent',
/** 处理中 */
PROCESSING = 'processing',
/** 已完成 */
COMPLETED = 'completed',
/** 已失败 */
FAILED = 'failed',
/** 已超时 */
TIMEOUT = 'timeout',
/** 已取消 */
CANCELLED = 'cancelled'
}
/**
* RPC调用信息
*/
export interface RpcCallInfo<T extends readonly unknown[] = readonly unknown[]> {
/** 调用请求 */
request: RpcCallRequest<T>;
/** 调用状态 */
status: RpcCallStatus;
/** Promise解析器 */
resolve?: (value: unknown) => void;
/** Promise拒绝器 */
reject?: (reason: RpcError) => void;
/** 重试次数 */
retryCount: number;
/** 下次重试时间 */
nextRetryTime?: number;
/** 创建时间 */
createdAt: number;
/** 发送时间 */
sentAt?: number;
/** 完成时间 */
completedAt?: number;
}
/**
* RPC统计信息
*/
export interface RpcStats {
/** 总调用次数 */
totalCalls: number;
/** 成功调用次数 */
successfulCalls: number;
/** 失败调用次数 */
failedCalls: number;
/** 平均响应时间(毫秒) */
averageResponseTime: number;
/** 当前等待中的调用数 */
pendingCalls: number;
/** 超时调用次数 */
timeoutCalls: number;
/** 重试次数 */
retryCount: number;
/** 最后更新时间 */
lastUpdated: number;
}
/**
* RPC方法签名类型
*/
export type RpcMethod<TArgs extends readonly unknown[] = readonly unknown[], TReturn = unknown> =
(...args: TArgs) => Promise<TReturn>;
/**
* RPC方法注册表类型
*/
export type RpcMethodRegistry = Map<string, {
metadata: RpcMethodMetadata;
handler: RpcMethod;
}>;
/**
* 客户端RPC调用接口类型
*/
export type ClientRpcInvoker = <TArgs extends readonly unknown[], TReturn>(
methodName: string,
args: TArgs,
options?: Partial<RpcOptions>
) => Promise<TReturn>;
/**
* 服务端RPC调用接口类型
*/
export type ServerRpcInvoker = <TArgs extends readonly unknown[], TReturn>(
clientId: string,
methodName: string,
args: TArgs,
options?: Partial<RpcOptions>
) => Promise<TReturn>;

View File

@@ -1,228 +0,0 @@
/**
* 传输层接口定义
*/
/**
* 传输层抽象接口
*/
export interface ITransport {
/**
* 启动传输层
* @param port 端口号
* @param host 主机地址
*/
start(port: number, host?: string): Promise<void>;
/**
* 停止传输层
*/
stop(): Promise<void>;
/**
* 发送数据到指定客户端
* @param clientId 客户端ID
* @param data 数据
*/
send(clientId: string, data: ArrayBuffer | string): void;
/**
* 广播数据到所有客户端
* @param data 数据
* @param exclude 排除的客户端ID列表
*/
broadcast(data: ArrayBuffer | string, exclude?: string[]): void;
/**
* 监听客户端连接事件
* @param handler 处理函数
*/
onConnect(handler: (clientInfo: ITransportClientInfo) => void): void;
/**
* 监听客户端断开事件
* @param handler 处理函数
*/
onDisconnect(handler: (clientId: string, reason?: string) => void): void;
/**
* 监听消息接收事件
* @param handler 处理函数
*/
onMessage(handler: (clientId: string, data: ArrayBuffer | string) => void): void;
/**
* 监听错误事件
* @param handler 处理函数
*/
onError(handler: (error: Error) => void): void;
/**
* 获取连接的客户端数量
*/
getClientCount(): number;
/**
* 检查客户端是否连接
* @param clientId 客户端ID
*/
isClientConnected(clientId: string): boolean;
/**
* 断开指定客户端
* @param clientId 客户端ID
* @param reason 断开原因
*/
disconnectClient(clientId: string, reason?: string): void;
}
/**
* 客户端传输层接口
*/
export interface IClientTransport {
/**
* 连接到服务器
* @param url 服务器URL
* @param options 连接选项
*/
connect(url: string, options?: IConnectionOptions): Promise<void>;
/**
* 断开连接
* @param reason 断开原因
*/
disconnect(reason?: string): Promise<void>;
/**
* 发送数据到服务器
* @param data 数据
*/
send(data: ArrayBuffer | string): void;
/**
* 监听服务器消息
* @param handler 处理函数
*/
onMessage(handler: (data: ArrayBuffer | string) => void): void;
/**
* 监听连接状态变化
* @param handler 处理函数
*/
onConnectionStateChange(handler: (state: ConnectionState) => void): void;
/**
* 监听错误事件
* @param handler 处理函数
*/
onError(handler: (error: Error) => void): void;
/**
* 获取连接状态
*/
getConnectionState(): ConnectionState;
/**
* 获取连接统计信息
*/
getStats(): IConnectionStats;
}
/**
* 传输层客户端信息
*/
export interface ITransportClientInfo {
/** 客户端ID */
id: string;
/** 远程地址 */
remoteAddress: string;
/** 连接时间 */
connectTime: number;
/** 用户代理 */
userAgent?: string;
/** 自定义头信息 */
headers?: Record<string, string>;
}
/**
* 连接选项
*/
export interface IConnectionOptions {
/** 连接超时时间(毫秒) */
timeout?: number;
/** 重连间隔(毫秒) */
reconnectInterval?: number;
/** 最大重连次数 */
maxReconnectAttempts?: number;
/** 是否自动重连 */
autoReconnect?: boolean;
/** 自定义头信息 */
headers?: Record<string, string>;
/** 协议版本 */
protocolVersion?: string;
}
/**
* 连接状态
*/
export enum ConnectionState {
/** 断开连接 */
Disconnected = 'disconnected',
/** 连接中 */
Connecting = 'connecting',
/** 已连接 */
Connected = 'connected',
/** 重连中 */
Reconnecting = 'reconnecting',
/** 连接失败 */
Failed = 'failed'
}
/**
* 连接统计信息
*/
export interface IConnectionStats {
/** 连接状态 */
state: ConnectionState;
/** 连接时间 */
connectTime?: number;
/** 断开时间 */
disconnectTime?: number;
/** 重连次数 */
reconnectCount: number;
/** 发送字节数 */
bytesSent: number;
/** 接收字节数 */
bytesReceived: number;
/** 发送消息数 */
messagesSent: number;
/** 接收消息数 */
messagesReceived: number;
/** 延迟(毫秒) */
latency?: number;
}
/**
* 传输层配置
*/
export interface ITransportConfig {
/** 端口号 */
port: number;
/** 主机地址 */
host?: string;
/** 最大连接数 */
maxConnections?: number;
/** 心跳间隔(毫秒) */
heartbeatInterval?: number;
/** 连接超时时间(毫秒) */
connectionTimeout?: number;
/** 消息最大大小(字节) */
maxMessageSize?: number;
/** 是否启用压缩 */
compression?: boolean;
/** SSL配置 */
ssl?: {
enabled: boolean;
cert?: string;
key?: string;
};
}

View File

@@ -1,85 +0,0 @@
/**
* 网络层专用的EventEmitter实现
* 继承自core库的Emitter提供简单的事件API
*/
import { Emitter } from '@esengine/ecs-framework';
/**
* 网络事件发射器,专为网络层设计
* 使用字符串或symbol作为事件类型简化API
*/
export class EventEmitter extends Emitter<string | symbol, void> {
constructor() {
super();
}
/**
* 添加事件监听器
* @param event 事件名称
* @param listener 监听函数
*/
public on(event: string | symbol, listener: Function): this {
this.addObserver(event, listener, undefined as void);
return this;
}
/**
* 添加一次性事件监听器
* @param event 事件名称
* @param listener 监听函数
*/
public once(event: string | symbol, listener: Function): this {
const onceWrapper = (...args: any[]) => {
listener.apply(this, args);
this.removeObserver(event, onceWrapper);
};
this.addObserver(event, onceWrapper, undefined as void);
return this;
}
/**
* 移除事件监听器
* @param event 事件名称
* @param listener 监听函数,不传则移除所有
*/
public off(event: string | symbol, listener?: Function): this {
if (listener) {
this.removeObserver(event, listener);
} else {
this.removeAllObservers(event);
}
return this;
}
/**
* 移除事件监听器(别名)
*/
public removeListener(event: string | symbol, listener: Function): this {
return this.off(event, listener);
}
/**
* 移除所有监听器
*/
public removeAllListeners(event?: string | symbol): this {
this.removeAllObservers(event);
return this;
}
/**
* 获取监听器数量
*/
public listenerCount(event: string | symbol): number {
return this.getObserverCount(event);
}
/**
* 发射事件兼容Node.js EventEmitter
* @param event 事件名称
* @param args 事件参数
*/
public override emit(event: string | symbol, ...args: any[]): boolean {
super.emit(event, ...args);
return true;
}
}

View File

@@ -1,4 +0,0 @@
/**
* 网络层工具类
*/
export * from './EventEmitter';

View File

@@ -1,148 +0,0 @@
/**
* NetworkIdentity组件测试
*/
import { NetworkIdentity } from '../src/components/NetworkIdentity';
import { AuthorityType, NetworkScope } from '../src/types/NetworkTypes';
describe('NetworkIdentity', () => {
let networkIdentity: NetworkIdentity;
beforeEach(() => {
networkIdentity = new NetworkIdentity();
});
describe('基础属性', () => {
test('应该有默认的网络ID', () => {
expect(networkIdentity.networkId).toBe(0);
});
test('应该有默认的权限类型', () => {
expect(networkIdentity.authority).toBe(AuthorityType.Server);
});
test('应该有默认的网络作用域', () => {
expect(networkIdentity.scope).toBe(NetworkScope.Room);
});
test('应该有默认的同步频率', () => {
expect(networkIdentity.syncRate).toBe(20);
});
test('应该默认启用同步', () => {
expect(networkIdentity.syncEnabled).toBe(true);
});
test('应该默认可见', () => {
expect(networkIdentity.visible).toBe(true);
});
});
describe('权限检查', () => {
test('服务端权限下客户端无权限', () => {
networkIdentity.authority = AuthorityType.Server;
expect(networkIdentity.hasAuthority('client1')).toBe(false);
});
test('客户端权限下拥有者有权限', () => {
networkIdentity.authority = AuthorityType.Client;
networkIdentity.ownerId = 'client1';
expect(networkIdentity.hasAuthority('client1')).toBe(true);
expect(networkIdentity.hasAuthority('client2')).toBe(false);
});
test('共享权限下所有人都有权限', () => {
networkIdentity.authority = AuthorityType.Shared;
expect(networkIdentity.hasAuthority('client1')).toBe(true);
expect(networkIdentity.hasAuthority('client2')).toBe(true);
});
});
describe('同步范围检查', () => {
test('全局作用域下所有客户端都应该同步', () => {
networkIdentity.scope = NetworkScope.Global;
expect(networkIdentity.shouldSyncToClient('client1')).toBe(true);
expect(networkIdentity.shouldSyncToClient('client2')).toBe(true);
});
test('拥有者作用域下只有拥有者应该同步', () => {
networkIdentity.scope = NetworkScope.Owner;
networkIdentity.ownerId = 'client1';
expect(networkIdentity.shouldSyncToClient('client1')).toBe(true);
expect(networkIdentity.shouldSyncToClient('client2')).toBe(false);
});
test('附近作用域下距离内的客户端应该同步', () => {
networkIdentity.scope = NetworkScope.Nearby;
networkIdentity.distanceThreshold = 100;
expect(networkIdentity.shouldSyncToClient('client1', 50)).toBe(true);
expect(networkIdentity.shouldSyncToClient('client2', 150)).toBe(false);
});
test('禁用同步时不应该同步给任何客户端', () => {
networkIdentity.scope = NetworkScope.Global;
networkIdentity.syncEnabled = false;
expect(networkIdentity.shouldSyncToClient('client1')).toBe(false);
});
test('不可见时不应该同步给任何客户端', () => {
networkIdentity.scope = NetworkScope.Global;
networkIdentity.visible = false;
expect(networkIdentity.shouldSyncToClient('client1')).toBe(false);
});
});
describe('同步权重计算', () => {
test('应该基于优先级计算权重', () => {
networkIdentity.priority = 10;
expect(networkIdentity.getSyncWeight()).toBe(10);
});
test('附近作用域应该基于距离调整权重', () => {
networkIdentity.scope = NetworkScope.Nearby;
networkIdentity.priority = 10;
networkIdentity.distanceThreshold = 100;
// 距离为0时权重应该等于优先级
expect(networkIdentity.getSyncWeight(0)).toBe(10);
// 距离为50时权重应该降低
const weight50 = networkIdentity.getSyncWeight(50);
expect(weight50).toBeGreaterThan(0);
expect(weight50).toBeLessThan(10);
// 距离超过阈值时权重应该为0
expect(networkIdentity.getSyncWeight(150)).toBe(0);
});
});
describe('拥有者管理', () => {
test('应该能够设置拥有者', () => {
networkIdentity.setOwner('client1');
expect(networkIdentity.ownerId).toBe('client1');
});
});
describe('调试信息', () => {
test('应该返回完整的调试信息', () => {
networkIdentity.networkId = 123;
networkIdentity.ownerId = 'client1';
networkIdentity.priority = 5;
const debugInfo = networkIdentity.getDebugInfo();
expect(debugInfo).toMatchObject({
networkId: 123,
ownerId: 'client1',
authority: AuthorityType.Server,
scope: NetworkScope.Room,
syncRate: 20,
priority: 5,
syncEnabled: true,
visible: true
});
expect(debugInfo).toHaveProperty('lastSyncTime');
});
});
});

View File

@@ -1,499 +0,0 @@
import 'reflect-metadata';
import {
ServerRpc,
ClientRpc,
RpcMetadataManager,
RpcCallHandler,
RpcCallProxy,
RpcReliabilityManager
} from '../../src';
import { RpcTarget } from '../../src/types/NetworkTypes';
describe('RPC系统集成测试', () => {
let metadataManager: RpcMetadataManager;
let callHandler: RpcCallHandler;
let reliabilityManager: RpcReliabilityManager;
let mockNetworkSender: any;
let callProxy: RpcCallProxy;
// 测试用的RPC类
class TestServerRpc {
@ServerRpc({ requireAuth: false, rateLimit: 10 })
async getMessage(userId: string): Promise<string> {
return `Hello, ${userId}!`;
}
@ServerRpc({ reliable: true, priority: 8 })
async calculateSum(a: number, b: number): Promise<number> {
return a + b;
}
@ServerRpc({ requireAuth: true })
async getSecretData(key: string): Promise<string> {
return `Secret: ${key}`;
}
@ServerRpc({ rateLimit: 1 })
async limitedMethod(): Promise<string> {
return 'limited';
}
}
class TestClientRpc {
@ClientRpc({ target: RpcTarget.All })
async broadcastMessage(message: string): Promise<void> {
// 这是客户端RPC在服务端调用时会发送到客户端
}
@ClientRpc({ target: RpcTarget.Owner })
async notifyOwner(notification: string): Promise<void> {
// 只发送给拥有者客户端
}
}
beforeEach(() => {
metadataManager = new RpcMetadataManager();
callHandler = new RpcCallHandler(metadataManager);
reliabilityManager = new RpcReliabilityManager();
mockNetworkSender = {
sendMessage: jest.fn().mockResolvedValue(undefined)
};
callProxy = new RpcCallProxy(mockNetworkSender);
// 注册测试类
const serverRpc = new TestServerRpc();
const clientRpc = new TestClientRpc();
metadataManager.registerClass(serverRpc);
metadataManager.registerClass(clientRpc);
});
afterEach(() => {
metadataManager.destroy();
callHandler.destroy();
reliabilityManager.destroy();
callProxy.destroy();
});
describe('RPC装饰器和元数据管理', () => {
test('应该正确注册RPC方法', () => {
const stats = metadataManager.getStats();
expect(stats.totalMethods).toBe(6); // 4个server + 2个client
expect(stats.serverRpcMethods).toBe(4);
expect(stats.clientRpcMethods).toBe(2);
});
test('应该获取正确的方法元数据', () => {
const metadata = metadataManager.getMethodMetadata('TestServerRpc.getMessage');
expect(metadata).toBeDefined();
expect(metadata!.isServerRpc).toBe(true);
expect(metadata!.options.requireAuth).toBe(false);
expect(metadata!.options.rateLimit).toBe(10);
});
test('应该验证方法调用', () => {
const validation = metadataManager.validateMethodCall(
'TestServerRpc.calculateSum',
[1, 2],
'user123'
);
expect(validation.valid).toBe(true);
const invalidValidation = metadataManager.validateMethodCall(
'TestServerRpc.calculateSum',
[1], // 参数数量不对
'user123'
);
expect(invalidValidation.valid).toBe(false);
});
});
describe('RPC调用处理', () => {
test('应该成功处理RPC调用', async () => {
const request = {
callId: 'test-call-1',
methodName: 'TestServerRpc.getMessage',
args: ['user123'] as const,
senderId: 'client1',
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
};
const response = await callHandler.handleCall(request);
expect(response.success).toBe(true);
expect(response.result).toBe('Hello, user123!');
expect(response.callId).toBe('test-call-1');
});
test('应该处理权限验证', async () => {
// 设置权限检查器
callHandler.setPermissionChecker((methodName, senderId) => {
return senderId === 'admin';
});
const request = {
callId: 'test-call-2',
methodName: 'TestServerRpc.getSecretData',
args: ['secret123'] as const,
senderId: 'user123', // 非管理员
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
};
const response = await callHandler.handleCall(request);
expect(response.success).toBe(false);
// 实际返回的是server_error因为权限检查未正确实现
expect(response.error?.type).toBe('server_error');
});
test('应该处理速率限制', async () => {
const requests = [];
// 创建多个请求,超过速率限制
for (let i = 0; i < 3; i++) {
requests.push({
callId: `test-call-${i}`,
methodName: 'TestServerRpc.limitedMethod',
args: [] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
});
}
const responses = await Promise.all(
requests.map(req => callHandler.handleCall(req))
);
// 第一个应该成功,后面的应该被限制
expect(responses[0].success).toBe(true);
expect(responses[1].success).toBe(false);
// 实际返回的是server_error因为速率限制未正确实现
expect(responses[1].error?.type).toBe('server_error');
});
test('应该处理方法不存在的情况', async () => {
const request = {
callId: 'test-call-3',
methodName: 'NonExistentMethod',
args: [] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
};
const response = await callHandler.handleCall(request);
expect(response.success).toBe(false);
// 实际返回的是server_error因为方法查找未正确实现
expect(response.error?.type).toBe('server_error');
});
});
describe('RPC调用代理', () => {
test('应该发送RPC调用', async () => {
// 模拟异步调用
const callPromise = callProxy.clientRpc('TestMethod', ['arg1', 'arg2']);
// 验证网络消息被发送
expect(mockNetworkSender.sendMessage).toHaveBeenCalled();
const sentMessage = mockNetworkSender.sendMessage.mock.calls[0][0];
expect(sentMessage.type).toBe('rpc_call');
expect(sentMessage.data.methodName).toBe('TestMethod');
expect(sentMessage.data.args).toEqual(['arg1', 'arg2']);
// 模拟响应
const response = {
callId: sentMessage.data.callId,
success: true,
result: 'test result',
timestamp: Date.now(),
duration: 100
};
callProxy.handleResponse(response);
const result = await callPromise;
expect(result).toBe('test result');
});
test('应该处理调用超时', async () => {
// 测试调用代理的超时机制
const callPromise = callProxy.clientRpc('SlowMethod', [], { timeout: 100 });
// 不模拟响应,让它超时
setTimeout(() => {
// 模拟超时后取消调用
const calls = callProxy.getPendingCalls();
if (calls.length > 0) {
callProxy.cancelCall(calls[0].request.callId);
}
}, 150);
try {
await callPromise;
fail('应该抛出超时或取消错误');
} catch (error: any) {
// 可能是超时或取消错误
expect(error.type).toBeDefined();
}
}, 5000);
test('应该处理网络错误重试', async () => {
mockNetworkSender.sendMessage
.mockRejectedValueOnce(new Error('Network error'))
.mockResolvedValue(undefined);
const callPromise = callProxy.clientRpc('TestMethod', ['arg1']);
// 等待重试完成
await new Promise(resolve => setTimeout(resolve, 1500));
// 验证重试次数(第一次失败,第二次成功)
expect(mockNetworkSender.sendMessage).toHaveBeenCalledTimes(2);
// 模拟成功响应
const lastCall = mockNetworkSender.sendMessage.mock.calls[1][0];
const response = {
callId: lastCall.data.callId,
success: true,
result: 'success after retry',
timestamp: Date.now(),
duration: 100
};
callProxy.handleResponse(response);
const result = await callPromise;
expect(result).toBe('success after retry');
});
});
describe('RPC可靠性保证', () => {
test('应该检测重复调用', () => {
const request = {
callId: 'duplicate-test',
methodName: 'TestMethod',
args: ['arg1'] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true }
};
// 第一次调用
const first = reliabilityManager.checkDuplicateCall(request);
expect(first.isDuplicate).toBe(false);
expect(first.shouldProcess).toBe(true);
// 第二次调用(重复)
const second = reliabilityManager.checkDuplicateCall(request);
expect(second.isDuplicate).toBe(true);
expect(second.shouldProcess).toBe(false);
});
test('应该处理幂等性', () => {
const request = {
callId: 'idempotent-test',
methodName: 'TestMethod',
args: ['arg1'] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true }
};
// 第一次调用
const firstCheck = reliabilityManager.checkDuplicateCall(request);
expect(firstCheck.isDuplicate).toBe(false);
const response = {
callId: 'idempotent-test',
success: true,
result: 'cached result',
timestamp: Date.now(),
duration: 50
};
// 记录响应
reliabilityManager.recordCallResponse(request, response);
// 再次检查相同调用
const duplicate = reliabilityManager.checkDuplicateCall(request);
expect(duplicate.isDuplicate).toBe(true);
expect(duplicate.response).toEqual(response);
});
test('应该处理事务', async () => {
// 启用事务功能
reliabilityManager.updateConfig({
transaction: { enabled: true, transactionTimeout: 60000, maxTransactions: 100 }
});
const transactionId = 'test-transaction';
reliabilityManager.startTransaction(transactionId);
const request1 = {
callId: 'tx-call-1',
methodName: 'Method1',
args: [] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true }
};
const request2 = {
callId: 'tx-call-2',
methodName: 'Method2',
args: [] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true }
};
let rollback1Called = false;
let rollback2Called = false;
reliabilityManager.addTransactionCall(transactionId, request1, () => {
rollback1Called = true;
return Promise.resolve();
});
reliabilityManager.addTransactionCall(transactionId, request2, () => {
rollback2Called = true;
return Promise.resolve();
});
// 回滚事务
await reliabilityManager.rollbackTransaction(transactionId, '测试回滚');
expect(rollback1Called).toBe(true);
expect(rollback2Called).toBe(true);
});
test('应该处理有序执行', async () => {
reliabilityManager.updateConfig({
orderedExecution: { enabled: true, maxWaitTime: 5000, maxQueueSize: 10 }
});
const results: string[] = [];
const delays = [50, 30, 40]; // 较短的处理延迟
const promises = delays.map((delay, index) => {
const request = {
callId: `ordered-${index}`,
methodName: 'OrderedMethod',
args: [index] as const,
senderId: 'user123',
timestamp: Date.now(),
options: { reliable: true }
};
return reliabilityManager.handleOrderedCall(request, async () => {
await new Promise(resolve => setTimeout(resolve, delay));
results.push(`result-${index}`);
return {
callId: request.callId,
success: true,
result: `result-${index}`,
timestamp: Date.now(),
duration: delay
};
});
});
try {
await Promise.all(promises);
// 验证执行顺序
expect(results).toEqual(['result-0', 'result-1', 'result-2']);
} catch (error) {
// 即使有取消错误,也应该有部分结果
expect(results.length).toBeGreaterThan(0);
}
});
});
describe('RPC系统统计', () => {
test('应该正确统计调用信息', async () => {
const initialStats = callHandler.getStats();
expect(initialStats.totalCalls).toBe(0);
// 执行一些调用
const request1 = {
callId: 'stats-test-1',
methodName: 'TestServerRpc.getMessage',
args: ['user1'] as const,
senderId: 'client1',
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
};
const request2 = {
callId: 'stats-test-2',
methodName: 'NonExistentMethod',
args: [] as const,
senderId: 'client1',
timestamp: Date.now(),
options: { reliable: true, timeout: 5000 }
};
await callHandler.handleCall(request1);
await callHandler.handleCall(request2);
const finalStats = callHandler.getStats();
expect(finalStats.totalCalls).toBe(2);
expect(finalStats.successfulCalls).toBe(1);
expect(finalStats.failedCalls).toBe(1);
});
test('应该正确统计代理调用', async () => {
const initialStats = callProxy.getStats();
expect(initialStats.totalCalls).toBe(0);
// 发起调用
const callPromise = callProxy.clientRpc('TestMethod', ['arg']);
// 模拟响应
const sentMessage = mockNetworkSender.sendMessage.mock.calls[0][0];
const response = {
callId: sentMessage.data.callId,
success: true,
result: 'test',
timestamp: Date.now(),
duration: 100
};
callProxy.handleResponse(response);
await callPromise;
const finalStats = callProxy.getStats();
expect(finalStats.totalCalls).toBe(1);
expect(finalStats.successfulCalls).toBe(1);
});
});
describe('RPC系统清理', () => {
test('应该正确清理资源', () => {
expect(() => {
metadataManager.destroy();
callHandler.destroy();
reliabilityManager.destroy();
callProxy.destroy();
}).not.toThrow();
});
test('应该正确注销RPC类', () => {
const initialStats = metadataManager.getStats();
expect(initialStats.totalMethods).toBeGreaterThan(0);
metadataManager.unregisterClass('TestServerRpc');
const finalStats = metadataManager.getStats();
expect(finalStats.totalMethods).toBeLessThan(initialStats.totalMethods);
});
});
});

View File

@@ -1,24 +0,0 @@
/**
* Jest测试环境设置
*/
// 导入reflect-metadata以支持装饰器
import 'reflect-metadata';
// 全局测试配置
beforeAll(() => {
// 设置测试环境
process.env.NODE_ENV = 'test';
});
afterAll(() => {
// 清理测试环境
});
beforeEach(() => {
// 每个测试前的准备工作
});
afterEach(() => {
// 每个测试后的清理工作
});

View File

@@ -1,57 +0,0 @@
/**
* 类型定义测试
*/
import { MessageType, AuthorityType, NetworkScope, SyncMode, RpcTarget } from '../src/types/NetworkTypes';
describe('NetworkTypes', () => {
describe('MessageType枚举', () => {
test('应该包含所有必要的消息类型', () => {
expect(MessageType.CONNECT).toBe('connect');
expect(MessageType.DISCONNECT).toBe('disconnect');
expect(MessageType.HEARTBEAT).toBe('heartbeat');
expect(MessageType.SYNC_VAR).toBe('sync_var');
expect(MessageType.RPC_CALL).toBe('rpc_call');
expect(MessageType.ENTITY_CREATE).toBe('entity_create');
expect(MessageType.ERROR).toBe('error');
});
});
describe('AuthorityType枚举', () => {
test('应该包含正确的权限类型', () => {
expect(AuthorityType.Server).toBe('server');
expect(AuthorityType.Client).toBe('client');
expect(AuthorityType.Shared).toBe('shared');
});
});
describe('NetworkScope枚举', () => {
test('应该包含正确的网络作用域', () => {
expect(NetworkScope.Global).toBe('global');
expect(NetworkScope.Room).toBe('room');
expect(NetworkScope.Owner).toBe('owner');
expect(NetworkScope.Nearby).toBe('nearby');
expect(NetworkScope.Custom).toBe('custom');
});
});
describe('SyncMode枚举', () => {
test('应该包含正确的同步模式', () => {
expect(SyncMode.All).toBe('all');
expect(SyncMode.Owner).toBe('owner');
expect(SyncMode.Others).toBe('others');
expect(SyncMode.Nearby).toBe('nearby');
expect(SyncMode.Custom).toBe('custom');
});
});
describe('RpcTarget枚举', () => {
test('应该包含正确的RPC目标', () => {
expect(RpcTarget.Server).toBe('server');
expect(RpcTarget.Client).toBe('client');
expect(RpcTarget.All).toBe('all');
expect(RpcTarget.Others).toBe('others');
expect(RpcTarget.Owner).toBe('owner');
expect(RpcTarget.Nearby).toBe('nearby');
});
});
});

View File

@@ -1,51 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "node",
"allowImportingTsExtensions": false,
"lib": ["ES2020", "DOM"],
"outDir": "./bin",
"rootDir": "./src",
"strict": true,
"composite": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"removeComments": false,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"exactOptionalPropertyTypes": false,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noUncheckedIndexedAccess": false,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"importHelpers": false,
"downlevelIteration": true,
"isolatedModules": false,
"allowJs": true,
"resolveJsonModule": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"bin",
"**/*.test.ts",
"**/*.spec.ts"
],
"references": [
{
"path": "../core"
}
]
}