更新network库及core库优化

This commit is contained in:
YHH
2025-08-12 09:39:07 +08:00
parent c178e2fbcc
commit 9f76d37a82
117 changed files with 17988 additions and 4099 deletions

View File

@@ -0,0 +1,60 @@
# @esengine/ecs-framework-network-shared
ECS Framework 网络库 - 共享组件和类型定义
## 概述
这是 ECS Framework 网络库的共享包,包含了客户端和服务端通用的:
- 装饰器定义 (`@SyncVar`, `@ClientRpc`, `@ServerRpc` 等)
- 类型定义和接口
- 序列化/反序列化工具
- Protobuf 自动生成机制
- 网络消息基类
## 特性
- **装饰器驱动**: 基于装饰器自动生成网络协议
- **类型安全**: 完整的 TypeScript 支持
- **自动序列化**: 基于 Protobuf 的高性能序列化
- **零配置**: 无需手写 .proto 文件
- **ECS 集成**: 深度集成 ECS 框架的特性
## 安装
```bash
npm install @esengine/ecs-framework-network-shared
```
## 基本用法
```typescript
import { NetworkComponent, SyncVar, ClientRpc, ServerRpc } from '@esengine/ecs-framework-network-shared';
@NetworkComponent()
class PlayerController extends Component {
@SyncVar({ onChanged: 'onHealthChanged' })
public health: number = 100;
@SyncVar()
public playerName: string = '';
@ClientRpc()
public showDamage(damage: number): void {
// 客户端显示伤害效果
}
@ServerRpc()
public movePlayer(direction: Vector3): void {
// 服务端处理玩家移动
}
private onHealthChanged(oldValue: number, newValue: number): void {
console.log(`生命值从 ${oldValue} 变为 ${newValue}`);
}
}
```
## License
MIT

View File

@@ -0,0 +1,118 @@
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
console.log('🚀 使用 Rollup 构建 network-shared 包...');
async function main() {
try {
if (fs.existsSync('./dist')) {
console.log('🧹 清理旧的构建文件...');
execSync('rimraf ./dist', { stdio: 'inherit' });
}
console.log('📦 执行 Rollup 构建...');
execSync('rollup -c rollup.config.cjs', { stdio: 'inherit' });
console.log('📋 生成 package.json...');
generatePackageJson();
console.log('📁 复制必要文件...');
copyFiles();
showBuildResults();
console.log('✅ 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',
'networking',
'shared',
'decorators',
'protobuf',
'serialization',
'game-engine',
'typescript'
],
author: sourcePackage.author,
license: sourcePackage.license,
repository: sourcePackage.repository,
dependencies: sourcePackage.dependencies,
peerDependencies: sourcePackage.peerDependencies,
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

@@ -0,0 +1,53 @@
/** @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: 60,
functions: 70,
lines: 70,
statements: 70
},
'./src/decorators/': {
branches: 70,
functions: 80,
lines: 80,
statements: 80
}
},
verbose: true,
transform: {
'^.+\\.tsx?$': ['ts-jest', {
tsconfig: 'tsconfig.json',
useESM: false,
}],
},
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
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/'
]
};

View File

@@ -0,0 +1,85 @@
{
"name": "@esengine/ecs-framework-network-shared",
"version": "1.0.15",
"description": "ECS Framework 网络库 - 共享组件和类型定义",
"type": "module",
"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",
"networking",
"shared",
"decorators",
"protobuf",
"serialization",
"game-engine",
"typescript"
],
"scripts": {
"clean": "rimraf bin dist",
"build:ts": "tsc",
"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",
"test:ci": "jest --ci --coverage --config jest.config.cjs",
"test:clear": "jest --clearCache"
},
"author": "yhh",
"license": "MIT",
"dependencies": {
"reflect-metadata": "^0.2.2",
"protobufjs": "^7.5.3"
},
"peerDependencies": {
"@esengine/ecs-framework": ">=2.1.29"
},
"devDependencies": {
"@esengine/ecs-framework": "*",
"@rollup/plugin-commonjs": "^28.0.3",
"@rollup/plugin-node-resolve": "^16.0.1",
"@rollup/plugin-terser": "^0.4.4",
"@types/jest": "^29.5.14",
"@types/node": "^20.19.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"rimraf": "^5.0.0",
"rollup": "^4.42.0",
"rollup-plugin-dts": "^6.2.1",
"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

@@ -0,0 +1,129 @@
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/ecs-framework-network-shared v${pkg.version}
* ECS Framework 网络库 - 共享组件和类型定义
*
* @author ${pkg.author}
* @license ${pkg.license}
*/`;
const external = ['reflect-metadata', 'protobufjs', 'uuid', '@esengine/ecs-framework'];
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: {
'reflect-metadata': 'ReflectMetadata',
'protobufjs': 'protobuf',
'uuid': 'uuid',
'@esengine/ecs-framework': 'ECS'
}
},
plugins: [
...commonPlugins,
terser({
format: {
comments: /^!/
}
})
],
external,
treeshake: {
moduleSideEffects: false
}
},
// 类型定义构建
{
input: 'bin/index.d.ts',
output: {
file: 'dist/index.d.ts',
format: 'es',
banner: `/**
* @esengine/ecs-framework-network-shared v${pkg.version}
* TypeScript definitions
*/`
},
plugins: [
dts({
respectExternal: true
})
],
external
}
];

View File

@@ -0,0 +1,261 @@
/**
* NetworkBehaviour 基类
*
* 所有网络组件的基类,提供网络功能的基础实现
*/
import { Component } from '@esengine/ecs-framework';
import { INetworkComponent, INetworkObject, SyncVarMetadata, RpcMetadata, Constructor } from '../types/NetworkTypes';
import { getSyncVarMetadata, getDirtySyncVars, clearAllDirtySyncVars } from '../decorators/SyncVar';
import { getClientRpcMetadata } from '../decorators/ClientRpc';
import { getServerRpcMetadata } from '../decorators/ServerRpc';
/**
* NetworkBehaviour 基类
*
* 提供网络组件的基础功能:
* - SyncVar 支持
* - RPC 调用支持
* - 网络身份管理
* - 权限控制
*/
export abstract class NetworkBehaviour extends Component implements INetworkComponent {
/** 索引签名以支持动态属性访问 */
[key: string]: unknown;
/** 网络对象引用 */
public networkObject: INetworkObject | null = null;
/** 网络ID */
public get networkId(): number {
return this.networkObject?.networkId || 0;
}
/** 是否拥有权威 */
public get hasAuthority(): boolean {
return this.networkObject?.hasAuthority || false;
}
/** 组件类型名 */
public get componentType(): string {
return this.constructor.name;
}
/** 是否为服务端 */
public get isServer(): boolean {
// 这个方法会被具体的客户端/服务端库重写
return false;
}
/** 是否为客户端 */
public get isClient(): boolean {
// 这个方法会被具体的客户端/服务端库重写
return false;
}
/** 是否为本地对象 */
public get isLocal(): boolean {
return this.networkObject?.isLocal || false;
}
/** 所有者客户端ID */
public get ownerId(): number {
return this.networkObject?.ownerId || 0;
}
constructor() {
super();
this.setupSyncVarNotification();
}
/**
* 设置 SyncVar 变化通知
*/
private setupSyncVarNotification(): void {
// 添加 SyncVar 变化通知方法
(this as any).notifySyncVarChanged = (
propertyName: string,
oldValue: any,
newValue: any,
metadata: SyncVarMetadata
) => {
this.onSyncVarChanged(propertyName, oldValue, newValue, metadata);
};
}
/**
* SyncVar 变化处理
*/
protected onSyncVarChanged(
propertyName: string,
oldValue: any,
newValue: any,
metadata: SyncVarMetadata
): void {
// 权限检查
if (metadata.authorityOnly && !this.hasAuthority) {
console.warn(`Authority required for SyncVar: ${this.componentType}.${propertyName}`);
return;
}
// 通知网络管理器
this.notifyNetworkManager('syncvar-changed', {
networkId: this.networkId,
componentType: this.componentType,
propertyName,
oldValue,
newValue,
metadata
});
}
/**
* 发送客户端 RPC
*/
protected sendClientRpc(methodName: string, args: any[], options?: any, metadata?: RpcMetadata): any {
if (!this.hasAuthority && !this.isServer) {
console.warn(`Authority required for ClientRpc: ${this.componentType}.${methodName}`);
return;
}
return this.notifyNetworkManager('client-rpc', {
networkId: this.networkId,
componentType: this.componentType,
methodName,
args,
options,
metadata
});
}
/**
* 发送服务端 RPC
*/
protected sendServerRpc(methodName: string, args: any[], options?: any, metadata?: RpcMetadata): any {
if (!this.isClient) {
console.warn(`ServerRpc can only be called from client: ${this.componentType}.${methodName}`);
return;
}
return this.notifyNetworkManager('server-rpc', {
networkId: this.networkId,
componentType: this.componentType,
methodName,
args,
options,
metadata
});
}
/**
* 通知网络管理器
*/
private notifyNetworkManager(eventType: string, data: any): any {
// 这个方法会被具体的客户端/服务端库重写
// 用于与网络管理器通信
if (typeof (globalThis as any).NetworkManager !== 'undefined') {
return (globalThis as any).NetworkManager.handleNetworkEvent?.(eventType, data);
}
console.warn(`NetworkManager not found for event: ${eventType}`);
return null;
}
/**
* 获取所有 SyncVar 元数据
*/
public getSyncVars(): SyncVarMetadata[] {
return getSyncVarMetadata(this.constructor as Constructor);
}
/**
* 获取所有客户端 RPC 元数据
*/
public getClientRpcs(): RpcMetadata[] {
return getClientRpcMetadata(this.constructor as Constructor);
}
/**
* 获取所有服务端 RPC 元数据
*/
public getServerRpcs(): RpcMetadata[] {
return getServerRpcMetadata(this.constructor as Constructor);
}
/**
* 获取所有脏的 SyncVar
*/
public getDirtySyncVars() {
return getDirtySyncVars(this);
}
/**
* 清除所有脏标记
*/
public clearDirtySyncVars(): void {
clearAllDirtySyncVars(this);
}
/**
* 序列化组件状态
*/
public serializeState(): any {
const syncVars = this.getSyncVars();
const state: any = {};
for (const syncVar of syncVars) {
const value = (this as any)[`_${syncVar.propertyName}`];
if (value !== undefined) {
state[syncVar.propertyName] = value;
}
}
return state;
}
/**
* 反序列化组件状态
*/
public deserializeState(state: any): void {
const syncVars = this.getSyncVars();
for (const syncVar of syncVars) {
if (state.hasOwnProperty(syncVar.propertyName)) {
// 直接设置内部值,跳过权限检查
(this as any)[`_${syncVar.propertyName}`] = state[syncVar.propertyName];
// 调用变化回调
if (syncVar.onChanged && typeof (this as any)[syncVar.onChanged] === 'function') {
(this as any)[syncVar.onChanged](undefined, state[syncVar.propertyName]);
}
}
}
}
/**
* 检查是否有权限执行操作
*/
protected checkAuthority(requiresOwnership = false): boolean {
if (requiresOwnership && this.ownerId !== this.getLocalClientId()) {
return false;
}
return this.hasAuthority;
}
/**
* 获取本地客户端ID
* 这个方法会被具体实现重写
*/
protected getLocalClientId(): number {
return 0;
}
/**
* 组件销毁时的清理
*/
public onDestroy(): void {
this.networkObject = null;
// 清理网络资源(基类销毁由框架处理)
}
}

View File

@@ -0,0 +1,324 @@
/**
* NetworkIdentity 类
*
* 标识网络对象的唯一身份,管理网络组件和权威性
*/
import { Component } from '@esengine/ecs-framework';
import { INetworkObject, INetworkComponent } from '../types/NetworkTypes';
import { NetworkBehaviour } from './NetworkBehaviour';
/**
* NetworkIdentity 组件
*
* 所有需要网络同步的实体都必须拥有此组件
*/
export class NetworkIdentity extends Component implements INetworkObject {
/** 网络对象的唯一标识符 */
public networkId: number = 0;
/** 所有者客户端ID0 表示服务端拥有 */
public ownerId: number = 0;
/** 是否拥有权威,权威端可以修改 SyncVar 和发送 RPC */
public hasAuthority: boolean = false;
/** 是否为本地对象 */
public isLocal: boolean = false;
/** 是否为本地玩家对象 */
public isLocalPlayer: boolean = false;
/** 预制体名称(用于网络生成) */
public prefabName: string = '';
/** 场景对象ID用于场景中已存在的对象 */
public sceneId: number = 0;
/** 挂载的网络组件列表 */
public networkComponents: INetworkComponent[] = [];
/** 是否已在网络中生成 */
public isSpawned: boolean = false;
/** 可见性距离用于网络LOD */
public visibilityDistance: number = 100;
/** 网络更新频率覆盖0 = 使用全局设置) */
public updateRate: number = 0;
/** 是否总是相关(不受距离限制) */
public alwaysRelevant: boolean = false;
constructor() {
super();
}
/**
* 组件启动时初始化
*/
public override onEnabled(): void {
super.onEnabled();
this.gatherNetworkComponents();
this.registerToNetworkManager();
}
/**
* 收集实体上的所有网络组件
*/
private gatherNetworkComponents(): void {
if (!this.entity) {
return;
}
// 清空现有列表
this.networkComponents = [];
// 获取实体上的所有组件
// 获取实体上的所有组件,简化类型处理
const components = (this.entity as any).getComponents();
for (const component of components) {
if (component instanceof NetworkBehaviour) {
this.addNetworkComponent(component);
}
}
}
/**
* 添加网络组件
*/
public addNetworkComponent(component: INetworkComponent): void {
if (this.networkComponents.includes(component)) {
return;
}
this.networkComponents.push(component);
component.networkObject = this;
// 如果已经注册到网络,通知网络管理器
if (this.isSpawned) {
this.notifyNetworkManager('component-added', {
networkId: this.networkId,
componentType: component.componentType,
component
});
}
}
/**
* 移除网络组件
*/
public removeNetworkComponent(component: INetworkComponent): void {
const index = this.networkComponents.indexOf(component);
if (index === -1) {
return;
}
this.networkComponents.splice(index, 1);
component.networkObject = null;
// 如果已经注册到网络,通知网络管理器
if (this.isSpawned) {
this.notifyNetworkManager('component-removed', {
networkId: this.networkId,
componentType: component.componentType,
component
});
}
}
/**
* 设置权威性
*/
public setAuthority(hasAuthority: boolean, ownerId: number = 0): void {
const oldAuthority = this.hasAuthority;
const oldOwner = this.ownerId;
this.hasAuthority = hasAuthority;
this.ownerId = ownerId;
this.isLocal = this.checkIsLocal();
// 如果权威性发生变化,通知相关系统
if (oldAuthority !== hasAuthority || oldOwner !== ownerId) {
this.onAuthorityChanged(oldAuthority, hasAuthority, oldOwner, ownerId);
}
}
/**
* 设置为本地玩家
*/
public setAsLocalPlayer(): void {
this.isLocalPlayer = true;
this.hasAuthority = true;
this.isLocal = true;
}
/**
* 检查是否为本地对象
*/
private checkIsLocal(): boolean {
const localClientId = this.getLocalClientId();
return this.ownerId === localClientId;
}
/**
* 获取本地客户端ID
*/
private getLocalClientId(): number {
// 这个方法会被具体实现重写
if (typeof (globalThis as any).NetworkManager !== 'undefined') {
return (globalThis as any).NetworkManager.getLocalClientId?.() || 0;
}
return 0;
}
/**
* 权威性变化处理
*/
private onAuthorityChanged(
oldAuthority: boolean,
newAuthority: boolean,
oldOwner: number,
newOwner: number
): void {
// 通知网络管理器
this.notifyNetworkManager('authority-changed', {
networkId: this.networkId,
oldAuthority,
newAuthority,
oldOwner,
newOwner
});
// 通知所有网络组件
for (const component of this.networkComponents) {
if ('onAuthorityChanged' in component && typeof component.onAuthorityChanged === 'function') {
component.onAuthorityChanged(newAuthority);
}
}
}
/**
* 获取指定类型的网络组件
*/
public getNetworkComponent<T extends INetworkComponent>(type: new (...args: any[]) => T): T | null {
return this.networkComponents.find(c => c instanceof type) as T || null;
}
/**
* 获取所有指定类型的网络组件
*/
public getNetworkComponents<T extends INetworkComponent>(type: new (...args: any[]) => T): T[] {
return this.networkComponents.filter(c => c instanceof type) as T[];
}
/**
* 序列化网络身份
*/
public serialize(): any {
return {
networkId: this.networkId,
ownerId: this.ownerId,
hasAuthority: this.hasAuthority,
isLocal: this.isLocal,
isLocalPlayer: this.isLocalPlayer,
prefabName: this.prefabName,
sceneId: this.sceneId,
visibilityDistance: this.visibilityDistance,
updateRate: this.updateRate,
alwaysRelevant: this.alwaysRelevant
};
}
/**
* 反序列化网络身份
*/
public deserialize(data: any): void {
this.networkId = data.networkId || 0;
this.ownerId = data.ownerId || 0;
this.hasAuthority = data.hasAuthority || false;
this.isLocal = data.isLocal || false;
this.isLocalPlayer = data.isLocalPlayer || false;
this.prefabName = data.prefabName || '';
this.sceneId = data.sceneId || 0;
this.visibilityDistance = data.visibilityDistance || 100;
this.updateRate = data.updateRate || 0;
this.alwaysRelevant = data.alwaysRelevant || false;
}
/**
* 注册到网络管理器
*/
private registerToNetworkManager(): void {
this.notifyNetworkManager('register-network-object', {
networkIdentity: this,
networkId: this.networkId,
components: this.networkComponents
});
this.isSpawned = true;
}
/**
* 从网络管理器注销
*/
private unregisterFromNetworkManager(): void {
this.notifyNetworkManager('unregister-network-object', {
networkIdentity: this,
networkId: this.networkId
});
this.isSpawned = false;
}
/**
* 通知网络管理器
*/
private notifyNetworkManager(eventType: string, data: any): void {
if (typeof (globalThis as any).NetworkManager !== 'undefined') {
(globalThis as any).NetworkManager.handleNetworkEvent?.(eventType, data);
}
}
/**
* 检查对象是否对指定客户端可见
*/
public isVisibleTo(clientId: number, clientPosition?: { x: number; y: number; z?: number }): boolean {
// 如果总是相关,则对所有客户端可见
if (this.alwaysRelevant) {
return true;
}
// 如果没有提供客户端位置,默认可见
// 简单的可见性检查暂时不依赖Transform组件
if (!clientPosition) {
return true;
}
// 基于距离的可见性检查(需要自定义位置获取逻辑)
const position = { x: 0, y: 0, z: 0 }; // 占位符
const dx = position.x - clientPosition.x;
const dy = position.y - clientPosition.y;
const dz = (position.z || 0) - (clientPosition.z || 0);
const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
return distance <= this.visibilityDistance;
}
/**
* 组件销毁时的清理
*/
public destroy(): void {
// 从网络管理器注销
if (this.isSpawned) {
this.unregisterFromNetworkManager();
}
// 清理所有网络组件的引用
for (const component of this.networkComponents) {
component.networkObject = null;
}
this.networkComponents = [];
// 清理网络资源(基类销毁由框架处理)
}
}

View File

@@ -0,0 +1,6 @@
/**
* 核心类导出
*/
export * from './NetworkBehaviour';
export * from './NetworkIdentity';

View File

@@ -0,0 +1,177 @@
/**
* ClientRpc 装饰器
*
* 用于标记可以在服务端调用,在客户端执行的方法
*/
import 'reflect-metadata';
import { RpcMetadata, DecoratorTarget, Constructor, RpcParameterType, RpcReturnType } from '../types/NetworkTypes';
import { getNetworkComponentMetadata } from './NetworkComponent';
/**
* ClientRpc 装饰器选项
*/
export interface ClientRpcOptions {
/** 是否需要权限验证 */
requiresAuth?: boolean;
/** 是否可靠传输,默认为 true */
reliable?: boolean;
/** 是否需要响应 */
requiresResponse?: boolean;
/** 目标客户端筛选器 */
targetFilter?: 'all' | 'others' | 'owner' | 'specific';
}
/**
* 存储 ClientRpc 元数据的 Symbol
*/
export const CLIENT_RPC_METADATA_KEY = Symbol('client_rpc_metadata');
/**
* ClientRpc 装饰器
*
* @param options 装饰器选项
* @returns 方法装饰器函数
*
* @example
* ```typescript
* @NetworkComponent()
* class PlayerController extends Component {
* @ClientRpc({ targetFilter: 'all' })
* public showDamageEffect(damage: number, position: Vector3): void {
* // 在所有客户端显示伤害效果
* console.log(`Showing damage: ${damage} at ${position}`);
* }
*
* @ClientRpc({ targetFilter: 'owner', reliable: false })
* public updateUI(data: UIData): void {
* // 只在拥有者客户端更新UI使用不可靠传输
* }
*
* @ClientRpc({ requiresResponse: true })
* public requestClientData(): ClientData {
* // 请求客户端数据并等待响应
* return this.getClientData();
* }
* }
* ```
*/
export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
return function (target: unknown, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
if (typeof propertyKey !== 'string') {
throw new Error('ClientRpc can only be applied to string method names');
}
// 获取或创建元数据数组
let metadata: RpcMetadata[] = Reflect.getMetadata(CLIENT_RPC_METADATA_KEY, (target as { constructor: Constructor }).constructor);
if (!metadata) {
metadata = [];
Reflect.defineMetadata(CLIENT_RPC_METADATA_KEY, metadata, (target as { constructor: Constructor }).constructor);
}
// 创建 RPC 元数据
const rpcMetadata: RpcMetadata = {
methodName: propertyKey,
rpcType: 'client-rpc',
requiresAuth: options.requiresAuth || false,
reliable: options.reliable !== false,
requiresResponse: options.requiresResponse || false
};
metadata.push(rpcMetadata);
// 更新 NetworkComponent 元数据
const componentMetadata = getNetworkComponentMetadata((target as { constructor: Constructor }).constructor);
if (componentMetadata) {
const existingIndex = componentMetadata.rpcs.findIndex(rpc => rpc.methodName === propertyKey);
if (existingIndex >= 0) {
componentMetadata.rpcs[existingIndex] = rpcMetadata;
} else {
componentMetadata.rpcs.push(rpcMetadata);
}
}
// 保存原方法
const originalMethod = descriptor.value;
if (typeof originalMethod !== 'function') {
throw new Error(`ClientRpc can only be applied to methods, got ${typeof originalMethod}`);
}
// 包装方法以添加网络调用逻辑
descriptor.value = function (this: Record<string, unknown> & {
isServer?: () => boolean;
sendClientRpc?: (methodName: string, args: RpcParameterType[], options: ClientRpcOptions, metadata: RpcMetadata) => RpcReturnType;
}, ...args: RpcParameterType[]): RpcReturnType {
// 如果在服务端调用,发送到客户端
const isServer = this.isServer?.() || (typeof window === 'undefined' && typeof process !== 'undefined');
if (isServer) {
return this.sendClientRpc?.(propertyKey, args, options, rpcMetadata) as RpcReturnType;
}
// 如果在客户端,直接执行本地方法
return (originalMethod as (...args: RpcParameterType[]) => RpcReturnType).apply(this, args);
};
// 保存原方法的引用,供直接调用
const decoratedFunction = descriptor.value as typeof descriptor.value & {
__originalMethod: typeof originalMethod;
__rpcMetadata: RpcMetadata;
__rpcOptions: ClientRpcOptions;
};
decoratedFunction.__originalMethod = originalMethod;
decoratedFunction.__rpcMetadata = rpcMetadata;
decoratedFunction.__rpcOptions = options;
return descriptor;
};
}
/**
* 获取类的 ClientRpc 元数据
*/
export function getClientRpcMetadata(target: Constructor): RpcMetadata[] {
return Reflect.getMetadata(CLIENT_RPC_METADATA_KEY, target) || [];
}
/**
* 检查方法是否为 ClientRpc
*/
export function isClientRpc(target: Constructor, methodName: string): boolean {
const metadata = getClientRpcMetadata(target);
return metadata.some(m => m.methodName === methodName);
}
/**
* 获取特定方法的 ClientRpc 元数据
*/
export function getClientRpcMethodMetadata(target: Constructor, methodName: string): RpcMetadata | null {
const metadata = getClientRpcMetadata(target);
return metadata.find(m => m.methodName === methodName) || null;
}
/**
* 直接调用原方法(跳过网络逻辑)
*/
export function invokeClientRpcLocally(instance: Record<string, unknown>, methodName: string, args: RpcParameterType[]): RpcReturnType {
const method = instance[methodName] as { __originalMethod?: (...args: RpcParameterType[]) => RpcReturnType } | undefined;
if (method && typeof method.__originalMethod === 'function') {
return method.__originalMethod.apply(instance, args);
}
throw new Error(`Method ${methodName} is not a valid ClientRpc or original method not found`);
}
/**
* 检查 ClientRpc 是否需要响应
*/
export function clientRpcRequiresResponse(instance: Record<string, unknown>, methodName: string): boolean {
const method = instance[methodName] as { __rpcMetadata?: RpcMetadata } | undefined;
return method?.__rpcMetadata?.requiresResponse || false;
}
/**
* 获取 ClientRpc 的选项
*/
export function getClientRpcOptions(instance: Record<string, unknown>, methodName: string): ClientRpcOptions | null {
const method = instance[methodName] as { __rpcOptions?: ClientRpcOptions } | undefined;
return method?.__rpcOptions || null;
}

View File

@@ -0,0 +1,138 @@
/**
* NetworkComponent 装饰器
*
* 用于标记网络组件,自动注册到网络系统
*/
import 'reflect-metadata';
import { NetworkComponentMetadata } from '../types/NetworkTypes';
/**
* NetworkComponent 装饰器选项
*/
export interface NetworkComponentOptions {
/** 是否自动生成 protobuf 协议 */
autoGenerateProtocol?: boolean;
/** 自定义组件类型名 */
typeName?: string;
/** 是否仅服务端存在 */
serverOnly?: boolean;
/** 是否仅客户端存在 */
clientOnly?: boolean;
}
/**
* 存储 NetworkComponent 元数据的 Symbol
*/
export const NETWORK_COMPONENT_METADATA_KEY = Symbol('network_component_metadata');
/**
* NetworkComponent 装饰器
*
* @param options 装饰器选项
* @returns 类装饰器函数
*
* @example
* ```typescript
* @NetworkComponent({ autoGenerateProtocol: true })
* class PlayerController extends Component implements INetworkComponent {
* networkObject: INetworkObject | null = null;
* networkId: number = 0;
* hasAuthority: boolean = false;
* componentType: string = 'PlayerController';
*
* @SyncVar()
* public health: number = 100;
*
* @ClientRpc()
* public showDamage(damage: number): void {
* // 显示伤害效果
* }
* }
* ```
*/
export function NetworkComponent(options: NetworkComponentOptions = {}): ClassDecorator {
return function <T extends Function>(target: T) {
const metadata: NetworkComponentMetadata = {
componentType: options.typeName || target.name,
syncVars: [],
rpcs: [],
autoGenerateProtocol: options.autoGenerateProtocol !== false,
};
// 存储元数据
Reflect.defineMetadata(NETWORK_COMPONENT_METADATA_KEY, metadata, target);
// 注册到全局组件注册表
NetworkComponentRegistry.register(target as any, metadata);
return target;
};
}
/**
* 获取类的 NetworkComponent 元数据
*/
export function getNetworkComponentMetadata(target: any): NetworkComponentMetadata | null {
return Reflect.getMetadata(NETWORK_COMPONENT_METADATA_KEY, target) || null;
}
/**
* 检查类是否为 NetworkComponent
*/
export function isNetworkComponent(target: any): boolean {
return Reflect.hasMetadata(NETWORK_COMPONENT_METADATA_KEY, target);
}
/**
* 网络组件注册表
*/
class NetworkComponentRegistry {
private static components = new Map<string, {
constructor: any;
metadata: NetworkComponentMetadata;
}>();
/**
* 注册网络组件
*/
static register(constructor: any, metadata: NetworkComponentMetadata): void {
this.components.set(metadata.componentType, {
constructor,
metadata
});
}
/**
* 获取组件信息
*/
static getComponent(typeName: string) {
return this.components.get(typeName);
}
/**
* 获取所有组件
*/
static getAllComponents() {
return Array.from(this.components.entries()).map(([typeName, info]) => ({
typeName,
...info
}));
}
/**
* 检查组件是否已注册
*/
static hasComponent(typeName: string): boolean {
return this.components.has(typeName);
}
/**
* 清空注册表 (主要用于测试)
*/
static clear(): void {
this.components.clear();
}
}
export { NetworkComponentRegistry };

View File

@@ -0,0 +1,178 @@
/**
* ServerRpc 装饰器
*
* 用于标记可以在客户端调用,在服务端执行的方法
*/
import 'reflect-metadata';
import { RpcMetadata, DecoratorTarget, Constructor, RpcParameterType, RpcReturnType } from '../types/NetworkTypes';
import { getNetworkComponentMetadata } from './NetworkComponent';
/**
* ServerRpc 装饰器选项
*/
export interface ServerRpcOptions {
/** 是否需要权限验证 */
requiresAuth?: boolean;
/** 是否可靠传输,默认为 true */
reliable?: boolean;
/** 是否需要响应 */
requiresResponse?: boolean;
/** 是否需要拥有者权限 */
requiresOwnership?: boolean;
/** 调用频率限制 (调用/秒) */
rateLimit?: number;
}
/**
* 存储 ServerRpc 元数据的 Symbol
*/
export const SERVER_RPC_METADATA_KEY = Symbol('server_rpc_metadata');
/**
* ServerRpc 装饰器
*
* @param options 装饰器选项
* @returns 方法装饰器函数
*
* @example
* ```typescript
* @NetworkComponent()
* class PlayerController extends Component {
* @ServerRpc({ requiresOwnership: true, rateLimit: 10 })
* public movePlayer(direction: Vector3): void {
* // 在服务端处理玩家移动需要拥有者权限限制每秒10次调用
* this.transform.position.add(direction);
* }
*
* @ServerRpc({ requiresAuth: true })
* public purchaseItem(itemId: string): boolean {
* // 购买物品,需要认证
* return this.inventory.tryPurchase(itemId);
* }
*
* @ServerRpc({ requiresResponse: true })
* public getPlayerStats(): PlayerStats {
* // 获取玩家统计数据并返回给客户端
* return this.stats.toObject();
* }
* }
* ```
*/
export function ServerRpc(options: ServerRpcOptions = {}): MethodDecorator {
return function (target: unknown, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
if (typeof propertyKey !== 'string') {
throw new Error('ServerRpc can only be applied to string method names');
}
// 获取或创建元数据数组
const targetConstructor = (target as { constructor: Constructor }).constructor;
let metadata: RpcMetadata[] = Reflect.getMetadata(SERVER_RPC_METADATA_KEY, targetConstructor);
if (!metadata) {
metadata = [];
Reflect.defineMetadata(SERVER_RPC_METADATA_KEY, metadata, targetConstructor);
}
// 创建 RPC 元数据
const rpcMetadata: RpcMetadata = {
methodName: propertyKey,
rpcType: 'server-rpc',
requiresAuth: options.requiresAuth || false,
reliable: options.reliable !== false,
requiresResponse: options.requiresResponse || false
};
metadata.push(rpcMetadata);
// 更新 NetworkComponent 元数据
const componentMetadata = getNetworkComponentMetadata(targetConstructor);
if (componentMetadata) {
const existingIndex = componentMetadata.rpcs.findIndex(rpc => rpc.methodName === propertyKey);
if (existingIndex >= 0) {
componentMetadata.rpcs[existingIndex] = rpcMetadata;
} else {
componentMetadata.rpcs.push(rpcMetadata);
}
}
// 保存原方法
const originalMethod = descriptor.value;
if (typeof originalMethod !== 'function') {
throw new Error(`ServerRpc can only be applied to methods, got ${typeof originalMethod}`);
}
// 包装方法以添加网络调用逻辑
descriptor.value = function (this: any, ...args: any[]) {
// 如果在客户端调用,发送到服务端
const isClient = this.isClient?.() || (typeof window !== 'undefined');
if (isClient) {
return this.sendServerRpc?.(propertyKey, args, options, rpcMetadata);
}
// 如果在服务端,直接执行本地方法
return originalMethod.apply(this, args);
};
// 保存原方法的引用,供直接调用
(descriptor.value as any).__originalMethod = originalMethod;
(descriptor.value as any).__rpcMetadata = rpcMetadata;
(descriptor.value as any).__rpcOptions = options;
return descriptor;
};
}
/**
* Command 装饰器 (ServerRpc 的别名,用于兼容性)
*/
export const Command = ServerRpc;
/**
* 获取类的 ServerRpc 元数据
*/
export function getServerRpcMetadata(target: Constructor): RpcMetadata[] {
return Reflect.getMetadata(SERVER_RPC_METADATA_KEY, target) || [];
}
/**
* 检查方法是否为 ServerRpc
*/
export function isServerRpc(target: Constructor, methodName: string): boolean {
const metadata = getServerRpcMetadata(target);
return metadata.some(m => m.methodName === methodName);
}
/**
* 获取特定方法的 ServerRpc 元数据
*/
export function getServerRpcMethodMetadata(target: Constructor, methodName: string): RpcMetadata | null {
const metadata = getServerRpcMetadata(target);
return metadata.find(m => m.methodName === methodName) || null;
}
/**
* 直接调用原方法(跳过网络逻辑)
*/
export function invokeServerRpcLocally(instance: any, methodName: string, args: any[]): any {
const method = instance[methodName];
if (method && typeof method.__originalMethod === 'function') {
return method.__originalMethod.apply(instance, args);
}
throw new Error(`Method ${methodName} is not a valid ServerRpc or original method not found`);
}
/**
* 检查 ServerRpc 是否需要响应
*/
export function serverRpcRequiresResponse(instance: any, methodName: string): boolean {
const method = instance[methodName];
return method?.__rpcMetadata?.requiresResponse || false;
}
/**
* 获取 ServerRpc 的选项
*/
export function getServerRpcOptions(instance: any, methodName: string): ServerRpcOptions | null {
const method = instance[methodName];
return method?.__rpcOptions || null;
}

View File

@@ -0,0 +1,249 @@
/**
* SyncVar 装饰器
*
* 用于标记需要在网络间自动同步的属性
*/
import 'reflect-metadata';
import { SyncVarMetadata, NetworkValue, DecoratorTarget, Constructor } from '../types/NetworkTypes';
import { getNetworkComponentMetadata } from './NetworkComponent';
/**
* SyncVar 装饰器选项
*/
export interface SyncVarOptions {
/** 是否仅权威端可修改,默认为 true */
authorityOnly?: boolean;
/** 变化回调函数名 */
onChanged?: string;
/** 序列化类型提示 */
serializeType?: string;
/** 是否使用增量同步 */
deltaSync?: boolean;
/** 同步优先级,数值越大优先级越高 */
priority?: number;
}
/**
* 存储 SyncVar 元数据的 Symbol
*/
export const SYNCVAR_METADATA_KEY = Symbol('syncvar_metadata');
/**
* SyncVar 装饰器
*
* @param options 装饰器选项
* @returns 属性装饰器函数
*
* @example
* ```typescript
* @NetworkComponent()
* class PlayerController extends Component {
* @SyncVar({ onChanged: 'onHealthChanged', priority: 10 })
* public health: number = 100;
*
* @SyncVar({ authorityOnly: false })
* public playerName: string = '';
*
* @SyncVar({ deltaSync: true })
* public inventory: Item[] = [];
*
* private onHealthChanged(oldValue: number, newValue: number): void {
* console.log(`Health changed from ${oldValue} to ${newValue}`);
* }
* }
* ```
*/
export function SyncVar<T extends NetworkValue = NetworkValue>(options: SyncVarOptions = {}): PropertyDecorator {
return function (target: unknown, propertyKey: string | symbol) {
if (typeof propertyKey !== 'string') {
throw new Error('SyncVar can only be applied to string property keys');
}
// 获取或创建元数据数组
const targetConstructor = (target as { constructor: Constructor }).constructor;
let metadata: SyncVarMetadata[] = Reflect.getMetadata(SYNCVAR_METADATA_KEY, targetConstructor);
if (!metadata) {
metadata = [];
Reflect.defineMetadata(SYNCVAR_METADATA_KEY, metadata, targetConstructor);
}
// 创建 SyncVar 元数据
const syncVarMetadata: SyncVarMetadata = {
propertyName: propertyKey,
authorityOnly: options.authorityOnly !== false,
onChanged: options.onChanged,
serializeType: options.serializeType,
deltaSync: options.deltaSync || false,
priority: options.priority || 0
};
metadata.push(syncVarMetadata);
// 更新 NetworkComponent 元数据
const componentMetadata = getNetworkComponentMetadata(targetConstructor);
if (componentMetadata) {
const existingIndex = componentMetadata.syncVars.findIndex(sv => sv.propertyName === propertyKey);
if (existingIndex >= 0) {
componentMetadata.syncVars[existingIndex] = syncVarMetadata;
} else {
componentMetadata.syncVars.push(syncVarMetadata);
}
}
// 创建属性的内部存储和变化跟踪
const internalKey = `_${propertyKey}`;
const dirtyKey = `_${propertyKey}_dirty`;
const previousKey = `_${propertyKey}_previous`;
// 重新定义属性的 getter 和 setter
Object.defineProperty(target, propertyKey, {
get: function (this: Record<string, unknown>): T {
return this[internalKey] as T;
},
set: function (this: Record<string, unknown>, newValue: T) {
const oldValue = this[internalKey] as T;
// 检查值是否真的发生了变化
if (oldValue === newValue) {
return;
}
// 对于复杂对象,进行深度比较
if (typeof newValue === 'object' && newValue !== null &&
typeof oldValue === 'object' && oldValue !== null) {
if (JSON.stringify(oldValue) === JSON.stringify(newValue)) {
return;
}
}
// 保存旧值用于回调
this[previousKey] = oldValue;
this[internalKey] = newValue;
this[dirtyKey] = true;
// 调用变化回调
if (options.onChanged && typeof (this[options.onChanged] as unknown) === 'function') {
(this[options.onChanged] as (oldValue: T, newValue: T) => void)(oldValue, newValue);
}
// 通知网络同步系统
(this as { notifySyncVarChanged?: (key: string, oldValue: T, newValue: T, metadata: SyncVarMetadata) => void }).notifySyncVarChanged?.(propertyKey, oldValue, newValue, syncVarMetadata);
},
enumerable: true,
configurable: true
});
// 初始化内部属性
const targetRecord = target as Record<string, unknown>;
if (targetRecord[internalKey] === undefined) {
targetRecord[internalKey] = targetRecord[propertyKey];
}
targetRecord[dirtyKey] = false;
};
}
/**
* 获取类的 SyncVar 元数据
*/
export function getSyncVarMetadata(target: Constructor): SyncVarMetadata[] {
return Reflect.getMetadata(SYNCVAR_METADATA_KEY, target) || [];
}
/**
* 检查属性是否为 SyncVar
*/
export function isSyncVar(target: Constructor, propertyName: string): boolean {
const metadata = getSyncVarMetadata(target);
return metadata.some(m => m.propertyName === propertyName);
}
/**
* 获取 SyncVar 的脏标记
*/
export function isSyncVarDirty(instance: Record<string, unknown>, propertyName: string): boolean {
return (instance[`_${propertyName}_dirty`] as boolean) || false;
}
/**
* 清除 SyncVar 的脏标记
*/
export function clearSyncVarDirty(instance: Record<string, unknown>, propertyName: string): void {
instance[`_${propertyName}_dirty`] = false;
}
/**
* 获取 SyncVar 的前一个值
*/
export function getSyncVarPreviousValue<T extends NetworkValue = NetworkValue>(instance: Record<string, unknown>, propertyName: string): T | undefined {
return instance[`_${propertyName}_previous`] as T | undefined;
}
/**
* 强制设置 SyncVar 值(跳过权限检查和变化检测)
*/
export function setSyncVarValue<T extends NetworkValue = NetworkValue>(instance: Record<string, unknown>, propertyName: string, value: T, skipCallback = false): void {
const internalKey = `_${propertyName}`;
const dirtyKey = `_${propertyName}_dirty`;
const previousKey = `_${propertyName}_previous`;
const oldValue = instance[internalKey] as T;
instance[previousKey] = oldValue;
instance[internalKey] = value;
instance[dirtyKey] = false; // 网络接收的值不标记为脏
// 可选择性调用回调
if (!skipCallback) {
const metadata = getSyncVarMetadata((instance as { constructor: Constructor }).constructor);
const syncVarMeta = metadata.find(m => m.propertyName === propertyName);
if (syncVarMeta?.onChanged && typeof (instance[syncVarMeta.onChanged] as unknown) === 'function') {
(instance[syncVarMeta.onChanged] as (oldValue: T, newValue: T) => void)(oldValue, value);
}
}
}
/**
* 批量获取所有脏的 SyncVar
*/
export function getDirtySyncVars(instance: Record<string, unknown>): Array<{
propertyName: string;
oldValue: NetworkValue;
newValue: NetworkValue;
metadata: SyncVarMetadata;
}> {
const metadata = getSyncVarMetadata((instance as { constructor: Constructor }).constructor);
const dirtyVars: Array<{
propertyName: string;
oldValue: NetworkValue;
newValue: NetworkValue;
metadata: SyncVarMetadata;
}> = [];
for (const syncVar of metadata) {
if (isSyncVarDirty(instance, syncVar.propertyName)) {
const oldValue = getSyncVarPreviousValue(instance, syncVar.propertyName);
const newValue = instance[`_${syncVar.propertyName}`] as NetworkValue;
dirtyVars.push({
propertyName: syncVar.propertyName,
oldValue: oldValue ?? newValue, // 使用空合并运算符处理undefined
newValue: newValue,
metadata: syncVar
});
}
}
// 按优先级排序,优先级高的先处理
return dirtyVars.sort((a, b) => (b.metadata.priority || 0) - (a.metadata.priority || 0));
}
/**
* 批量清除所有脏标记
*/
export function clearAllDirtySyncVars(instance: Record<string, unknown>): void {
const metadata = getSyncVarMetadata((instance as { constructor: Constructor }).constructor);
for (const syncVar of metadata) {
clearSyncVarDirty(instance, syncVar.propertyName);
}
}

View File

@@ -0,0 +1,8 @@
/**
* 装饰器导出
*/
export * from './NetworkComponent';
export * from './SyncVar';
export * from './ClientRpc';
export * from './ServerRpc';

View File

@@ -0,0 +1,43 @@
/**
* ECS Framework Network Shared
*
* 共享的网络组件、装饰器和类型定义
*/
// 确保 reflect-metadata 被导入
import 'reflect-metadata';
// 类型定义
export * from './types';
// 装饰器
export * from './decorators';
// 核心类
export * from './core';
// 序列化工具
export * from './serialization';
// 协议编译器
export * from './protocol';
// 工具函数
export * from './utils';
// 版本信息
export const VERSION = '1.0.0';
// 默认配置
export const DEFAULT_NETWORK_CONFIG = {
port: 7777,
host: 'localhost',
maxConnections: 100,
syncRate: 20,
snapshotRate: 5,
compression: true,
encryption: false,
timeout: 30000,
maxReconnectAttempts: 3,
reconnectInterval: 5000
};

View File

@@ -0,0 +1,663 @@
/**
* TypeScript 协议分析器
*
* 负责解析 TypeScript 代码中的网络组件装饰器,
* 提取类型信息并构建协议定义
*/
// TypeScript编译器API - 开发时依赖
declare const require: any;
let ts: any;
let path: any;
let fs: any;
try {
ts = require('typescript');
path = require('path');
fs = require('fs');
} catch (e) {
// 在运行时如果没有这些依赖,使用占位符
ts = {
ScriptTarget: { ES2020: 99 },
ModuleKind: { ES2020: 99 },
createProgram: () => ({ getSourceFiles: () => [] }),
isClassDeclaration: () => false,
isDecorator: () => false,
isIdentifier: () => false,
isCallExpression: () => false,
forEachChild: () => {}
};
path = { join: (...args: string[]) => args.join('/') };
fs = { existsSync: () => false, readFileSync: () => '{}' };
}
import {
ComponentProtocol,
ProtocolField,
ProtocolRpc,
RpcParameter,
SerializeType,
ProtocolAnalysisResult,
ProtocolError,
ProtocolWarning,
ProtocolCompilerConfig
} from '../types/ProtocolTypes';
/**
* TypeScript 协议分析器
*/
export class TypeScriptAnalyzer {
private program: ts.Program;
private typeChecker: ts.TypeChecker;
private config: ProtocolCompilerConfig;
private components: ComponentProtocol[] = [];
private errors: ProtocolError[] = [];
private warnings: ProtocolWarning[] = [];
private dependencies: Map<string, string[]> = new Map();
constructor(config: ProtocolCompilerConfig) {
this.config = config;
this.initializeTypeScript();
}
/**
* 初始化 TypeScript 编译器
*/
private initializeTypeScript(): void {
if (!ts || !path || !fs) {
throw new Error('TypeScript analyzer requires typescript, path, and fs modules');
}
const configPath = this.config.tsconfigPath || path.join(this.config.inputDir, 'tsconfig.json');
let compilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ES2020,
module: ts.ModuleKind.ES2020,
lib: ['ES2020'],
experimentalDecorators: true,
emitDecoratorMetadata: true,
strict: true
};
// 加载 tsconfig.json
if (fs.existsSync(configPath)) {
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
if (configFile.error) {
this.addError('syntax', `Failed to read tsconfig.json: ${configFile.error.messageText}`);
} else {
const parsedConfig = ts.parseJsonConfigFileContent(
configFile.config,
ts.sys,
path.dirname(configPath)
);
compilerOptions = { ...compilerOptions, ...parsedConfig.options };
}
}
// 收集所有 TypeScript 文件
const files = this.collectTypeScriptFiles(this.config.inputDir);
this.program = ts.createProgram(files, compilerOptions);
this.typeChecker = this.program.getTypeChecker();
}
/**
* 收集 TypeScript 文件
*/
private collectTypeScriptFiles(dir: string): string[] {
const files: string[] = [];
const excludePatterns = this.config.excludePatterns || ['**/*.test.ts', '**/*.spec.ts', '**/node_modules/**'];
function collectFiles(currentDir: string): void {
const items = fs.readdirSync(currentDir);
for (const item of items) {
const fullPath = path.join(currentDir, item);
const stat = fs.statSync(fullPath);
if (stat.isDirectory()) {
// 检查是否应该排除此目录
const shouldExclude = excludePatterns.some(pattern =>
fullPath.includes(pattern.replace('**/', '').replace('/**', ''))
);
if (!shouldExclude) {
collectFiles(fullPath);
}
} else if (item.endsWith('.ts') || item.endsWith('.tsx')) {
// 检查是否应该排除此文件
const shouldExclude = excludePatterns.some(pattern => {
if (pattern.includes('**')) {
const regex = new RegExp(pattern.replace('**/', '.*').replace('*', '.*'));
return regex.test(fullPath);
}
return fullPath.endsWith(pattern.replace('*', ''));
});
if (!shouldExclude) {
files.push(fullPath);
}
}
}
}
collectFiles(dir);
return files;
}
/**
* 分析网络协议
*/
public analyze(): ProtocolAnalysisResult {
this.components = [];
this.errors = [];
this.warnings = [];
this.dependencies.clear();
const sourceFiles = this.program.getSourceFiles().filter(sf =>
!sf.isDeclarationFile && sf.fileName.includes(this.config.inputDir)
);
// 分析每个源文件
for (const sourceFile of sourceFiles) {
this.analyzeSourceFile(sourceFile);
}
// 检查依赖关系
this.validateDependencies();
return {
files: sourceFiles.map(sf => sf.fileName),
components: this.components,
dependencies: this.dependencies,
errors: this.errors,
warnings: this.warnings
};
}
/**
* 分析单个源文件
*/
private analyzeSourceFile(sourceFile: any): void {
const visit = (node: any): void => {
if (ts.isClassDeclaration(node) && this.isNetworkComponent(node)) {
this.analyzeNetworkComponent(node, sourceFile);
}
ts.forEachChild(node, visit);
};
visit(sourceFile);
}
/**
* 检查是否为网络组件
*/
private isNetworkComponent(node: any): boolean {
if (!node.modifiers) return false;
return node.modifiers.some((modifier: any) => {
if (ts.isDecorator(modifier)) {
const expression = modifier.expression;
if (ts.isCallExpression(expression) || ts.isIdentifier(expression)) {
const decoratorName = this.getDecoratorName(expression);
return decoratorName === 'NetworkComponent';
}
}
return false;
});
}
/**
* 获取装饰器名称
*/
private getDecoratorName(expression: ts.Expression): string | null {
if (ts.isIdentifier(expression)) {
return expression.text;
}
if (ts.isCallExpression(expression) && ts.isIdentifier(expression.expression)) {
return expression.expression.text;
}
return null;
}
/**
* 分析网络组件
*/
private analyzeNetworkComponent(node: any, sourceFile: any): void {
const className = node.name?.text;
if (!className) {
this.addError('syntax', 'NetworkComponent class must have a name', sourceFile, node);
return;
}
const componentProtocol: ComponentProtocol = {
typeName: className,
version: 1,
syncVars: [],
rpcs: [],
batchEnabled: false,
deltaEnabled: false
};
// 分析类成员
for (const member of node.members) {
if (ts.isPropertyDeclaration(member)) {
const syncVar = this.analyzeSyncVar(member, sourceFile);
if (syncVar) {
componentProtocol.syncVars.push(syncVar);
}
} else if (ts.isMethodDeclaration(member)) {
const rpc = this.analyzeRpc(member, sourceFile);
if (rpc) {
componentProtocol.rpcs.push(rpc);
}
}
}
// 分析装饰器选项
this.analyzeComponentDecorator(node, componentProtocol, sourceFile);
this.components.push(componentProtocol);
}
/**
* 分析 SyncVar 属性
*/
private analyzeSyncVar(node: ts.PropertyDeclaration, sourceFile: ts.SourceFile): ProtocolField | null {
if (!this.hasSyncVarDecorator(node)) {
return null;
}
const propertyName = this.getPropertyName(node);
if (!propertyName) {
this.addError('syntax', 'SyncVar property must have a name', sourceFile, node);
return null;
}
const type = this.typeChecker.getTypeAtLocation(node);
const serializeType = this.inferSerializeType(type, node, sourceFile);
if (!serializeType) {
this.addError('type', `Cannot infer serialize type for property: ${propertyName}`, sourceFile, node);
return null;
}
const field: ProtocolField = {
name: propertyName,
type: serializeType,
id: this.generateFieldId(propertyName),
optional: this.isOptionalProperty(node),
repeated: this.isArrayType(type)
};
// 分析装饰器选项
this.analyzeSyncVarDecorator(node, field, sourceFile);
return field;
}
/**
* 分析 RPC 方法
*/
private analyzeRpc(node: ts.MethodDeclaration, sourceFile: ts.SourceFile): ProtocolRpc | null {
const rpcType = this.getRpcType(node);
if (!rpcType) {
return null;
}
const methodName = this.getMethodName(node);
if (!methodName) {
this.addError('syntax', 'RPC method must have a name', sourceFile, node);
return null;
}
const parameters: RpcParameter[] = [];
// 分析参数
if (node.parameters) {
for (const param of node.parameters) {
const paramName = param.name.getText();
const paramType = this.typeChecker.getTypeAtLocation(param);
const serializeType = this.inferSerializeType(paramType, param, sourceFile);
if (serializeType === null) {
this.addError('type', `Cannot infer type for parameter: ${paramName}`, sourceFile, param);
continue;
}
parameters.push({
name: paramName,
type: serializeType,
optional: param.questionToken !== undefined,
isArray: this.isArrayType(paramType)
});
}
}
// 分析返回类型
let returnType: SerializeType | undefined;
if (node.type && !this.isVoidType(node.type)) {
const returnTypeNode = this.typeChecker.getTypeAtLocation(node.type);
returnType = this.inferSerializeType(returnTypeNode, node.type, sourceFile);
}
const rpc: ProtocolRpc = {
name: methodName,
id: this.generateRpcId(methodName),
type: rpcType,
parameters,
returnType
};
// 分析装饰器选项
this.analyzeRpcDecorator(node, rpc, sourceFile);
return rpc;
}
/**
* 检查是否有 SyncVar 装饰器
*/
private hasSyncVarDecorator(node: ts.PropertyDeclaration): boolean {
return this.hasDecorator(node, 'SyncVar');
}
/**
* 获取 RPC 类型
*/
private getRpcType(node: ts.MethodDeclaration): 'client-rpc' | 'server-rpc' | null {
if (this.hasDecorator(node, 'ClientRpc')) {
return 'client-rpc';
}
if (this.hasDecorator(node, 'ServerRpc') || this.hasDecorator(node, 'Command')) {
return 'server-rpc';
}
return null;
}
/**
* 检查是否有特定装饰器
*/
private hasDecorator(node: ts.Node, decoratorName: string): boolean {
if (!ts.canHaveModifiers(node) || !ts.getModifiers(node)) return false;
const modifiers = ts.getModifiers(node)!;
return modifiers.some(modifier => {
if (ts.isDecorator(modifier)) {
const name = this.getDecoratorName(modifier.expression);
return name === decoratorName;
}
return false;
});
}
/**
* 推导序列化类型
*/
private inferSerializeType(type: ts.Type, node: ts.Node, sourceFile: ts.SourceFile): SerializeType | null {
const typeString = this.typeChecker.typeToString(type);
// 自定义类型映射
if (this.config.typeMapping?.has(typeString)) {
return this.config.typeMapping.get(typeString)!;
}
// 基础类型推导
if (type.flags & ts.TypeFlags.Boolean) return SerializeType.BOOLEAN;
if (type.flags & ts.TypeFlags.Number) return SerializeType.FLOAT64;
if (type.flags & ts.TypeFlags.String) return SerializeType.STRING;
// 对象类型推导
if (type.flags & ts.TypeFlags.Object) {
// 检查是否为数组
if (this.typeChecker.isArrayType(type)) {
return SerializeType.ARRAY;
}
// 检查常见游戏类型
if (typeString.includes('Vector2')) return SerializeType.VECTOR2;
if (typeString.includes('Vector3')) return SerializeType.VECTOR3;
if (typeString.includes('Quaternion')) return SerializeType.QUATERNION;
if (typeString.includes('Color')) return SerializeType.COLOR;
// 默认为对象类型
return SerializeType.OBJECT;
}
this.addWarning('performance', `Unknown type: ${typeString}, falling back to JSON`, sourceFile, node);
return SerializeType.JSON;
}
/**
* 获取属性名
*/
private getPropertyName(node: ts.PropertyDeclaration): string | null {
if (ts.isIdentifier(node.name)) {
return node.name.text;
}
return null;
}
/**
* 获取方法名
*/
private getMethodName(node: ts.MethodDeclaration): string | null {
if (ts.isIdentifier(node.name)) {
return node.name.text;
}
return null;
}
/**
* 检查是否为可选属性
*/
private isOptionalProperty(node: ts.PropertyDeclaration): boolean {
return node.questionToken !== undefined;
}
/**
* 检查是否为数组类型
*/
private isArrayType(type: ts.Type): boolean {
return this.typeChecker.isArrayType(type);
}
/**
* 检查是否为 void 类型
*/
private isVoidType(node: ts.TypeNode): boolean {
return ts.isTypeReferenceNode(node) && node.typeName.getText() === 'void';
}
/**
* 生成字段 ID
*/
private generateFieldId(fieldName: string): number {
// 简单的哈希函数生成字段 ID
let hash = 0;
for (let i = 0; i < fieldName.length; i++) {
const char = fieldName.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为 32位整数
}
return Math.abs(hash) % 10000 + 1; // 确保 ID 为正数且在合理范围内
}
/**
* 生成 RPC ID
*/
private generateRpcId(rpcName: string): number {
return this.generateFieldId(rpcName) + 10000; // RPC ID 从 10000 开始
}
/**
* 分析组件装饰器选项
*/
private analyzeComponentDecorator(
node: ts.ClassDeclaration,
protocol: ComponentProtocol,
sourceFile: ts.SourceFile
): void {
const decorator = this.findDecorator(node, 'NetworkComponent');
if (decorator && ts.isCallExpression(decorator.expression)) {
const args = decorator.expression.arguments;
if (args.length > 0 && ts.isObjectLiteralExpression(args[0])) {
const options = args[0];
for (const prop of options.properties) {
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
const propName = prop.name.text;
if (propName === 'batchEnabled' && this.isBooleanLiteral(prop.initializer)) {
protocol.batchEnabled = (prop.initializer as ts.BooleanLiteral).token === ts.SyntaxKind.TrueKeyword;
}
if (propName === 'deltaEnabled' && this.isBooleanLiteral(prop.initializer)) {
protocol.deltaEnabled = (prop.initializer as ts.BooleanLiteral).token === ts.SyntaxKind.TrueKeyword;
}
}
}
}
}
}
/**
* 分析 SyncVar 装饰器选项
*/
private analyzeSyncVarDecorator(
node: ts.PropertyDeclaration,
field: ProtocolField,
sourceFile: ts.SourceFile
): void {
const decorator = this.findDecorator(node, 'SyncVar');
if (decorator && ts.isCallExpression(decorator.expression)) {
const args = decorator.expression.arguments;
if (args.length > 0 && ts.isObjectLiteralExpression(args[0])) {
const options = args[0];
for (const prop of options.properties) {
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
const propName = prop.name.text;
if (propName === 'serialize' && ts.isStringLiteral(prop.initializer)) {
const serializeType = prop.initializer.text as SerializeType;
if (Object.values(SerializeType).includes(serializeType)) {
field.type = serializeType;
}
}
}
}
}
}
}
/**
* 分析 RPC 装饰器选项
*/
private analyzeRpcDecorator(
node: ts.MethodDeclaration,
rpc: ProtocolRpc,
sourceFile: ts.SourceFile
): void {
const decoratorName = rpc.type === 'client-rpc' ? 'ClientRpc' : 'ServerRpc';
const decorator = this.findDecorator(node, decoratorName) || this.findDecorator(node, 'Command');
if (decorator && ts.isCallExpression(decorator.expression)) {
const args = decorator.expression.arguments;
if (args.length > 0 && ts.isObjectLiteralExpression(args[0])) {
const options = args[0];
for (const prop of options.properties) {
if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) {
const propName = prop.name.text;
if (propName === 'requiresAuth' && this.isBooleanLiteral(prop.initializer)) {
rpc.requiresAuth = (prop.initializer as ts.BooleanLiteral).token === ts.SyntaxKind.TrueKeyword;
}
if (propName === 'reliable' && this.isBooleanLiteral(prop.initializer)) {
rpc.reliable = (prop.initializer as ts.BooleanLiteral).token === ts.SyntaxKind.TrueKeyword;
}
if (propName === 'rateLimit' && ts.isNumericLiteral(prop.initializer)) {
rpc.rateLimit = parseInt(prop.initializer.text);
}
}
}
}
}
}
/**
* 查找装饰器
*/
private findDecorator(node: ts.Node, decoratorName: string): ts.Decorator | null {
if (!ts.canHaveModifiers(node) || !ts.getModifiers(node)) return null;
const modifiers = ts.getModifiers(node)!;
for (const modifier of modifiers) {
if (ts.isDecorator(modifier)) {
const name = this.getDecoratorName(modifier.expression);
if (name === decoratorName) {
return modifier;
}
}
}
return null;
}
/**
* 检查是否为布尔字面量
*/
private isBooleanLiteral(node: ts.Node): node is ts.BooleanLiteral {
return node.kind === ts.SyntaxKind.TrueKeyword || node.kind === ts.SyntaxKind.FalseKeyword;
}
/**
* 验证依赖关系
*/
private validateDependencies(): void {
// 检查循环依赖等问题
// 这里可以添加更复杂的依赖分析逻辑
}
/**
* 添加错误
*/
private addError(
type: ProtocolError['type'],
message: string,
sourceFile?: ts.SourceFile,
node?: ts.Node
): void {
const error: ProtocolError = {
type,
message,
file: sourceFile?.fileName,
line: node ? ts.getLineAndCharacterOfPosition(sourceFile!, node.getStart()).line + 1 : undefined,
column: node ? ts.getLineAndCharacterOfPosition(sourceFile!, node.getStart()).character + 1 : undefined
};
this.errors.push(error);
}
/**
* 添加警告
*/
private addWarning(
type: ProtocolWarning['type'],
message: string,
sourceFile?: ts.SourceFile,
node?: ts.Node
): void {
const warning: ProtocolWarning = {
type,
message,
file: sourceFile?.fileName,
line: node ? ts.getLineAndCharacterOfPosition(sourceFile!, node.getStart()).line + 1 : undefined,
column: node ? ts.getLineAndCharacterOfPosition(sourceFile!, node.getStart()).character + 1 : undefined
};
this.warnings.push(warning);
}
}

View File

@@ -0,0 +1,5 @@
/**
* 协议分析器导出
*/
export * from './TypeScriptAnalyzer';

View File

@@ -0,0 +1,576 @@
/**
* 协议推导引擎
*
* 负责从分析结果推导出最优的序列化协议,
* 包括类型优化、字段重排序、兼容性检查等
*/
import {
ComponentProtocol,
ProtocolField,
ProtocolRpc,
SerializeType,
ProtocolSchema,
ProtocolError,
ProtocolWarning
} from '../types/ProtocolTypes';
/**
* 优化选项
*/
export interface InferenceOptions {
/** 是否启用字段重排序优化 */
enableFieldReordering?: boolean;
/** 是否启用类型提升优化 */
enableTypePromotion?: boolean;
/** 是否启用批量处理优化 */
enableBatchOptimization?: boolean;
/** 是否启用向后兼容检查 */
enableCompatibilityCheck?: boolean;
/** 最大字段数量限制 */
maxFieldCount?: number;
/** 最大 RPC 数量限制 */
maxRpcCount?: number;
}
/**
* 协议推导引擎
*/
export class ProtocolInferenceEngine {
private options: Required<InferenceOptions>;
private errors: ProtocolError[] = [];
private warnings: ProtocolWarning[] = [];
constructor(options: InferenceOptions = {}) {
this.options = {
enableFieldReordering: true,
enableTypePromotion: true,
enableBatchOptimization: true,
enableCompatibilityCheck: true,
maxFieldCount: 100,
maxRpcCount: 50,
...options
};
}
/**
* 推导协议模式
*/
public inferSchema(components: ComponentProtocol[], version: string = '1.0.0'): ProtocolSchema {
this.errors = [];
this.warnings = [];
const optimizedComponents = new Map<string, ComponentProtocol>();
const globalTypes = new Map<string, ProtocolField[]>();
// 第一遍:基础优化和验证
for (const component of components) {
const optimized = this.optimizeComponent(component);
if (optimized) {
optimizedComponents.set(component.typeName, optimized);
}
}
// 第二遍:跨组件优化
this.performCrossComponentOptimizations(optimizedComponents);
// 提取全局类型
this.extractGlobalTypes(optimizedComponents, globalTypes);
const schema: ProtocolSchema = {
version,
components: optimizedComponents,
types: globalTypes,
compatibility: {
minVersion: version,
maxVersion: version
}
};
// 最终验证
this.validateSchema(schema);
return schema;
}
/**
* 优化单个组件
*/
private optimizeComponent(component: ComponentProtocol): ComponentProtocol | null {
// 验证组件
if (!this.validateComponent(component)) {
return null;
}
const optimized: ComponentProtocol = {
...component,
syncVars: [...component.syncVars],
rpcs: [...component.rpcs]
};
// 优化 SyncVar 字段
if (this.options.enableFieldReordering) {
optimized.syncVars = this.optimizeFieldOrdering(optimized.syncVars);
}
if (this.options.enableTypePromotion) {
optimized.syncVars = this.optimizeFieldTypes(optimized.syncVars);
}
// 优化 RPC 方法
optimized.rpcs = this.optimizeRpcs(optimized.rpcs);
// 启用批量处理优化
if (this.options.enableBatchOptimization) {
this.inferBatchOptimization(optimized);
}
return optimized;
}
/**
* 验证组件
*/
private validateComponent(component: ComponentProtocol): boolean {
let isValid = true;
// 检查字段数量限制
if (component.syncVars.length > this.options.maxFieldCount) {
this.addError('semantic', `Component ${component.typeName} has too many SyncVars (${component.syncVars.length}/${this.options.maxFieldCount})`);
isValid = false;
}
// 检查 RPC 数量限制
if (component.rpcs.length > this.options.maxRpcCount) {
this.addError('semantic', `Component ${component.typeName} has too many RPCs (${component.rpcs.length}/${this.options.maxRpcCount})`);
isValid = false;
}
// 检查字段名冲突
const fieldNames = new Set<string>();
for (const field of component.syncVars) {
if (fieldNames.has(field.name)) {
this.addError('semantic', `Duplicate SyncVar name: ${field.name} in ${component.typeName}`);
isValid = false;
}
fieldNames.add(field.name);
}
// 检查 RPC 名冲突
const rpcNames = new Set<string>();
for (const rpc of component.rpcs) {
if (rpcNames.has(rpc.name)) {
this.addError('semantic', `Duplicate RPC name: ${rpc.name} in ${component.typeName}`);
isValid = false;
}
rpcNames.add(rpc.name);
}
return isValid;
}
/**
* 优化字段顺序
* 将频繁变化的字段和固定大小的字段排在前面,以提高序列化效率
*/
private optimizeFieldOrdering(fields: ProtocolField[]): ProtocolField[] {
const optimized = [...fields];
// 按照优化策略排序
optimized.sort((a, b) => {
// 优先级高的在前
const priorityA = this.getFieldPriority(a);
const priorityB = this.getFieldPriority(b);
if (priorityA !== priorityB) {
return priorityB - priorityA; // 优先级高的在前
}
// 固定大小类型在前
const fixedA = this.isFixedSizeType(a.type) ? 1 : 0;
const fixedB = this.isFixedSizeType(b.type) ? 1 : 0;
if (fixedA !== fixedB) {
return fixedB - fixedA;
}
// 按类型大小排序,小的在前
const sizeA = this.getTypeSize(a.type);
const sizeB = this.getTypeSize(b.type);
return sizeA - sizeB;
});
// 重新分配字段 ID保持顺序
optimized.forEach((field, index) => {
field.id = index + 1;
});
return optimized;
}
/**
* 优化字段类型
* 将通用类型提升为更高效的序列化类型
*/
private optimizeFieldTypes(fields: ProtocolField[]): ProtocolField[] {
return fields.map(field => {
const optimized = { ...field };
// 类型提升规则
switch (field.type) {
case SerializeType.FLOAT64:
// 检查是否可以使用 float32
if (this.canUseFloat32(field)) {
optimized.type = SerializeType.FLOAT32;
this.addWarning('performance', `Promoted field ${field.name} from float64 to float32`);
}
break;
case SerializeType.INT64:
// 检查是否可以使用 int32
if (this.canUseInt32(field)) {
optimized.type = SerializeType.INT32;
this.addWarning('performance', `Promoted field ${field.name} from int64 to int32`);
}
break;
case SerializeType.JSON:
// 检查是否可以使用更高效的类型
const betterType = this.inferBetterType(field);
if (betterType && betterType !== SerializeType.JSON) {
optimized.type = betterType;
this.addWarning('performance', `Promoted field ${field.name} from JSON to ${betterType}`);
}
break;
}
return optimized;
});
}
/**
* 优化 RPC 方法
*/
private optimizeRpcs(rpcs: ProtocolRpc[]): ProtocolRpc[] {
return rpcs.map(rpc => {
const optimized = { ...rpc };
// 优化参数类型
optimized.parameters = rpc.parameters.map(param => ({
...param,
type: this.optimizeParameterType(param.type)
}));
// 设置默认选项
if (optimized.reliable === undefined) {
optimized.reliable = rpc.type === 'server-rpc'; // 服务端 RPC 默认可靠
}
return optimized;
});
}
/**
* 推导批量处理优化
*/
private inferBatchOptimization(component: ComponentProtocol): void {
// 检查是否适合批量处理
const hasManyInstances = this.estimateInstanceCount(component) > 10;
const hasSimpleTypes = component.syncVars.every(field =>
this.isSimpleType(field.type) && !field.repeated
);
if (hasManyInstances && hasSimpleTypes) {
component.batchEnabled = true;
this.addWarning('performance', `Enabled batch optimization for ${component.typeName}`);
}
// 检查是否适合增量同步
const hasLargeData = component.syncVars.some(field =>
this.isLargeDataType(field.type) || field.repeated
);
if (hasLargeData) {
component.deltaEnabled = true;
this.addWarning('performance', `Enabled delta synchronization for ${component.typeName}`);
}
}
/**
* 跨组件优化
*/
private performCrossComponentOptimizations(components: Map<string, ComponentProtocol>): void {
// 检查重复字段模式,提取为全局类型
const fieldPatterns = this.findCommonFieldPatterns(Array.from(components.values()));
for (const [pattern, count] of fieldPatterns) {
if (count >= 3) { // 如果有3个或更多组件使用相同模式
this.addWarning('style', `Common field pattern found: ${pattern} (used ${count} times). Consider extracting to a shared type.`);
}
}
// 检查 ID 冲突
this.validateIdUniqueness(Array.from(components.values()));
}
/**
* 提取全局类型
*/
private extractGlobalTypes(
components: Map<string, ComponentProtocol>,
globalTypes: Map<string, ProtocolField[]>
): void {
// 预定义常用游戏类型
globalTypes.set('Vector2', [
{ name: 'x', type: SerializeType.FLOAT32, id: 1 },
{ name: 'y', type: SerializeType.FLOAT32, id: 2 }
]);
globalTypes.set('Vector3', [
{ name: 'x', type: SerializeType.FLOAT32, id: 1 },
{ name: 'y', type: SerializeType.FLOAT32, id: 2 },
{ name: 'z', type: SerializeType.FLOAT32, id: 3 }
]);
globalTypes.set('Quaternion', [
{ name: 'x', type: SerializeType.FLOAT32, id: 1 },
{ name: 'y', type: SerializeType.FLOAT32, id: 2 },
{ name: 'z', type: SerializeType.FLOAT32, id: 3 },
{ name: 'w', type: SerializeType.FLOAT32, id: 4 }
]);
globalTypes.set('Color', [
{ name: 'r', type: SerializeType.FLOAT32, id: 1 },
{ name: 'g', type: SerializeType.FLOAT32, id: 2 },
{ name: 'b', type: SerializeType.FLOAT32, id: 3 },
{ name: 'a', type: SerializeType.FLOAT32, id: 4, optional: true, defaultValue: 1.0 }
]);
}
/**
* 验证协议模式
*/
private validateSchema(schema: ProtocolSchema): void {
// 检查版本兼容性
if (this.options.enableCompatibilityCheck) {
this.validateCompatibility(schema);
}
// 检查全局一致性
this.validateGlobalConsistency(schema);
}
// 辅助方法
private getFieldPriority(field: ProtocolField): number {
// 根据字段名推断优先级
const highPriorityNames = ['position', 'rotation', 'health', 'transform'];
const mediumPriorityNames = ['velocity', 'speed', 'direction'];
const fieldName = field.name.toLowerCase();
if (highPriorityNames.some(name => fieldName.includes(name))) {
return 10;
}
if (mediumPriorityNames.some(name => fieldName.includes(name))) {
return 5;
}
return 1;
}
private isFixedSizeType(type: SerializeType): boolean {
const fixedTypes = [
SerializeType.BOOLEAN,
SerializeType.INT8, SerializeType.UINT8,
SerializeType.INT16, SerializeType.UINT16,
SerializeType.INT32, SerializeType.UINT32,
SerializeType.INT64, SerializeType.UINT64,
SerializeType.FLOAT32, SerializeType.FLOAT64,
SerializeType.VECTOR2, SerializeType.VECTOR3,
SerializeType.QUATERNION, SerializeType.COLOR
];
return fixedTypes.includes(type);
}
private getTypeSize(type: SerializeType): number {
const sizes = {
[SerializeType.BOOLEAN]: 1,
[SerializeType.INT8]: 1,
[SerializeType.UINT8]: 1,
[SerializeType.INT16]: 2,
[SerializeType.UINT16]: 2,
[SerializeType.INT32]: 4,
[SerializeType.UINT32]: 4,
[SerializeType.INT64]: 8,
[SerializeType.UINT64]: 8,
[SerializeType.FLOAT32]: 4,
[SerializeType.FLOAT64]: 8,
[SerializeType.VECTOR2]: 8,
[SerializeType.VECTOR3]: 12,
[SerializeType.QUATERNION]: 16,
[SerializeType.COLOR]: 16,
[SerializeType.STRING]: 100, // 估算
[SerializeType.BYTES]: 100,
[SerializeType.ARRAY]: 200,
[SerializeType.MAP]: 200,
[SerializeType.OBJECT]: 500,
[SerializeType.JSON]: 1000
};
return sizes[type] || 100;
}
private canUseFloat32(field: ProtocolField): boolean {
// 简单启发式:位置、旋转等游戏相关字段通常可以使用 float32
const float32FriendlyNames = ['position', 'rotation', 'scale', 'velocity', 'speed'];
return float32FriendlyNames.some(name => field.name.toLowerCase().includes(name));
}
private canUseInt32(field: ProtocolField): boolean {
// 大多数游戏中的整数值都可以用 int32 表示
const int32FriendlyNames = ['id', 'count', 'level', 'score', 'health', 'mana'];
return int32FriendlyNames.some(name => field.name.toLowerCase().includes(name));
}
private inferBetterType(field: ProtocolField): SerializeType | null {
// 根据字段名推断更好的类型
const fieldName = field.name.toLowerCase();
if (fieldName.includes('position') || fieldName.includes('vector')) {
return SerializeType.VECTOR3;
}
if (fieldName.includes('rotation') || fieldName.includes('quaternion')) {
return SerializeType.QUATERNION;
}
if (fieldName.includes('color')) {
return SerializeType.COLOR;
}
return null;
}
private optimizeParameterType(type: SerializeType): SerializeType {
// RPC 参数类型优化
if (type === SerializeType.FLOAT64) {
return SerializeType.FLOAT32; // RPC 通常不需要高精度
}
if (type === SerializeType.INT64) {
return SerializeType.INT32;
}
return type;
}
private estimateInstanceCount(component: ComponentProtocol): number {
// 基于组件名称估算实例数量
const highVolumeNames = ['transform', 'position', 'movement', 'particle'];
const mediumVolumeNames = ['player', 'enemy', 'bullet', 'item'];
const typeName = component.typeName.toLowerCase();
if (highVolumeNames.some(name => typeName.includes(name))) {
return 100;
}
if (mediumVolumeNames.some(name => typeName.includes(name))) {
return 20;
}
return 5;
}
private isSimpleType(type: SerializeType): boolean {
const simpleTypes = [
SerializeType.BOOLEAN,
SerializeType.INT32, SerializeType.UINT32,
SerializeType.FLOAT32,
SerializeType.VECTOR2, SerializeType.VECTOR3,
SerializeType.QUATERNION
];
return simpleTypes.includes(type);
}
private isLargeDataType(type: SerializeType): boolean {
const largeTypes = [
SerializeType.STRING,
SerializeType.BYTES,
SerializeType.ARRAY,
SerializeType.MAP,
SerializeType.OBJECT,
SerializeType.JSON
];
return largeTypes.includes(type);
}
private findCommonFieldPatterns(components: ComponentProtocol[]): Map<string, number> {
const patterns = new Map<string, number>();
for (const component of components) {
const pattern = component.syncVars
.map(field => `${field.name}:${field.type}`)
.sort()
.join(',');
patterns.set(pattern, (patterns.get(pattern) || 0) + 1);
}
return patterns;
}
private validateIdUniqueness(components: ComponentProtocol[]): void {
const fieldIds = new Map<number, string>();
const rpcIds = new Map<number, string>();
for (const component of components) {
// 检查字段 ID 冲突
for (const field of component.syncVars) {
const existing = fieldIds.get(field.id);
if (existing && existing !== `${component.typeName}.${field.name}`) {
this.addError('semantic', `Field ID conflict: ${field.id} used by both ${existing} and ${component.typeName}.${field.name}`);
}
fieldIds.set(field.id, `${component.typeName}.${field.name}`);
}
// 检查 RPC ID 冲突
for (const rpc of component.rpcs) {
const existing = rpcIds.get(rpc.id);
if (existing && existing !== `${component.typeName}.${rpc.name}`) {
this.addError('semantic', `RPC ID conflict: ${rpc.id} used by both ${existing} and ${component.typeName}.${rpc.name}`);
}
rpcIds.set(rpc.id, `${component.typeName}.${rpc.name}`);
}
}
}
private validateCompatibility(schema: ProtocolSchema): void {
// 这里可以添加向后兼容性检查逻辑
// 比如检查字段删除、类型变更等
}
private validateGlobalConsistency(schema: ProtocolSchema): void {
// 检查全局类型的一致性使用
for (const [typeName, fields] of schema.types) {
const usageCount = Array.from(schema.components.values())
.flatMap(comp => comp.syncVars)
.filter(field => field.type === typeName as SerializeType)
.length;
if (usageCount === 0) {
this.addWarning('style', `Global type ${typeName} is defined but not used`);
}
}
}
private addError(type: ProtocolError['type'], message: string): void {
this.errors.push({ type, message });
}
private addWarning(type: ProtocolWarning['type'], message: string): void {
this.warnings.push({ type, message });
}
public getErrors(): ProtocolError[] {
return [...this.errors];
}
public getWarnings(): ProtocolWarning[] {
return [...this.warnings];
}
}

View File

@@ -0,0 +1,5 @@
/**
* 协议编译器导出
*/
export * from './ProtocolInferenceEngine';

View File

@@ -0,0 +1,8 @@
/**
* 协议编译器模块导出
*/
export * from './types';
// 协议分析器需要开发时依赖,暂时禁用
// export * from './analyzer';
export * from './compiler';

View File

@@ -0,0 +1,289 @@
/**
* 网络协议编译系统类型定义
*/
/**
* 序列化类型枚举
*/
export enum SerializeType {
// 基础类型
BOOLEAN = 'boolean',
INT8 = 'int8',
UINT8 = 'uint8',
INT16 = 'int16',
UINT16 = 'uint16',
INT32 = 'int32',
UINT32 = 'uint32',
INT64 = 'int64',
UINT64 = 'uint64',
FLOAT32 = 'float32',
FLOAT64 = 'float64',
STRING = 'string',
BYTES = 'bytes',
// 常用游戏类型
VECTOR2 = 'Vector2',
VECTOR3 = 'Vector3',
QUATERNION = 'Quaternion',
COLOR = 'Color',
// 容器类型
ARRAY = 'array',
MAP = 'map',
// 复杂类型
OBJECT = 'object',
JSON = 'json'
}
/**
* 字段定义
*/
export interface ProtocolField {
/** 字段名 */
name: string;
/** 序列化类型 */
type: SerializeType;
/** 字段ID用于向后兼容 */
id: number;
/** 是否可选 */
optional?: boolean;
/** 是否重复(数组) */
repeated?: boolean;
/** 元素类型(用于数组和映射) */
elementType?: SerializeType;
/** 键类型(用于映射) */
keyType?: SerializeType;
/** 值类型(用于映射) */
valueType?: SerializeType;
/** 默认值 */
defaultValue?: any;
/** 自定义序列化器 */
customSerializer?: string;
}
/**
* RPC 参数定义
*/
export interface RpcParameter {
/** 参数名 */
name: string;
/** 参数类型 */
type: SerializeType;
/** 是否可选 */
optional?: boolean;
/** 是否为数组 */
isArray?: boolean;
}
/**
* RPC 定义
*/
export interface ProtocolRpc {
/** 方法名 */
name: string;
/** RPC ID */
id: number;
/** RPC 类型 */
type: 'client-rpc' | 'server-rpc';
/** 参数列表 */
parameters: RpcParameter[];
/** 返回类型 */
returnType?: SerializeType;
/** 是否需要权限 */
requiresAuth?: boolean;
/** 是否可靠传输 */
reliable?: boolean;
/** 频率限制 */
rateLimit?: number;
}
/**
* 网络组件协议定义
*/
export interface ComponentProtocol {
/** 组件类型名 */
typeName: string;
/** 协议版本 */
version: number;
/** SyncVar 字段 */
syncVars: ProtocolField[];
/** RPC 方法 */
rpcs: ProtocolRpc[];
/** 是否启用批量处理 */
batchEnabled?: boolean;
/** 是否启用增量同步 */
deltaEnabled?: boolean;
}
/**
* 协议模式定义
*/
export interface ProtocolSchema {
/** 模式版本 */
version: string;
/** 组件协议映射 */
components: Map<string, ComponentProtocol>;
/** 全局类型定义 */
types: Map<string, ProtocolField[]>;
/** 协议兼容性信息 */
compatibility: {
minVersion: string;
maxVersion: string;
};
}
/**
* 序列化器接口
*/
export interface IProtocolSerializer {
/** 序列化单个对象 */
serialize(obj: any, type: SerializeType): Uint8Array;
/** 反序列化单个对象 */
deserialize(data: Uint8Array, type: SerializeType): any;
/** 批量序列化 */
serializeBatch(objects: any[], type: SerializeType): Uint8Array;
/** 批量反序列化 */
deserializeBatch(data: Uint8Array, type: SerializeType): any[];
/** 增量序列化 */
serializeDelta(oldObj: any, newObj: any, type: SerializeType): Uint8Array | null;
/** 应用增量 */
applyDelta(baseObj: any, delta: Uint8Array, type: SerializeType): any;
}
/**
* 协议编译器配置
*/
export interface ProtocolCompilerConfig {
/** 输入目录 */
inputDir: string;
/** 输出目录 */
outputDir: string;
/** TypeScript 配置文件路径 */
tsconfigPath?: string;
/** 是否启用优化 */
optimize?: boolean;
/** 是否生成调试信息 */
debug?: boolean;
/** 自定义类型映射 */
typeMapping?: Map<string, SerializeType>;
/** 排除的文件模式 */
excludePatterns?: string[];
}
/**
* 协议分析结果
*/
export interface ProtocolAnalysisResult {
/** 分析的文件列表 */
files: string[];
/** 发现的网络组件 */
components: ComponentProtocol[];
/** 类型依赖图 */
dependencies: Map<string, string[]>;
/** 分析错误 */
errors: ProtocolError[];
/** 分析警告 */
warnings: ProtocolWarning[];
}
/**
* 协议错误
*/
export interface ProtocolError {
/** 错误类型 */
type: 'syntax' | 'type' | 'semantic' | 'compatibility';
/** 错误消息 */
message: string;
/** 文件路径 */
file?: string;
/** 行号 */
line?: number;
/** 列号 */
column?: number;
}
/**
* 协议警告
*/
export interface ProtocolWarning {
/** 警告类型 */
type: 'performance' | 'compatibility' | 'style';
/** 警告消息 */
message: string;
/** 文件路径 */
file?: string;
/** 行号 */
line?: number;
/** 列号 */
column?: number;
}
/**
* 代码生成选项
*/
export interface CodeGenerationOptions {
/** 目标平台 */
platform: 'node' | 'browser' | 'universal';
/** 代码风格 */
style: 'typescript' | 'javascript';
/** 是否生成类型定义 */
generateTypes?: boolean;
/** 是否生成文档 */
generateDocs?: boolean;
/** 模块格式 */
moduleFormat?: 'es' | 'cjs' | 'umd';
/** 压缩级别 */
minification?: 'none' | 'basic' | 'aggressive';
}
/**
* 运行时协议信息
*/
export interface RuntimeProtocolInfo {
/** 协议版本 */
version: string;
/** 组件数量 */
componentCount: number;
/** 总字段数 */
fieldCount: number;
/** 总 RPC 数 */
rpcCount: number;
/** 内存使用情况 */
memoryUsage: {
schemas: number;
serializers: number;
cache: number;
};
/** 性能统计 */
performance: {
serializeTime: number;
deserializeTime: number;
cacheHits: number;
cacheMisses: number;
};
}
/**
* 协议事件类型
*/
export type ProtocolEventType =
| 'protocol-loaded'
| 'protocol-updated'
| 'serializer-registered'
| 'compatibility-check'
| 'performance-warning';
/**
* 协议事件数据
*/
export interface ProtocolEventData {
type: ProtocolEventType;
timestamp: number;
data: any;
}
/**
* 协议事件处理器
*/
export type ProtocolEventHandler = (event: ProtocolEventData) => void;

View File

@@ -0,0 +1,5 @@
/**
* 协议类型定义导出
*/
export * from './ProtocolTypes';

View File

@@ -0,0 +1,355 @@
/**
* 网络序列化器
*
* 提供高效的网络消息序列化和反序列化
*/
import { INetworkSerializer, NetworkValue, SerializationSchema } from '../types/NetworkTypes';
/**
* 序列化类型映射
*/
interface SerializationTypeMap {
[typeName: string]: SerializationSchema<any>;
}
/**
* 基础网络序列化器实现
*/
export class NetworkSerializer implements INetworkSerializer {
private typeMap: SerializationTypeMap = {};
constructor() {
this.registerBuiltinTypes();
}
/**
* 注册内置类型
*/
private registerBuiltinTypes(): void {
// 基础类型
this.registerType<string>('string', {
serialize: (str: string) => new TextEncoder().encode(str),
deserialize: (data: Uint8Array) => new TextDecoder().decode(data),
getSize: (str: string) => new TextEncoder().encode(str).length
});
this.registerType<number>('number', {
serialize: (num: number) => {
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setFloat64(0, num);
return new Uint8Array(buffer);
},
deserialize: (data: Uint8Array) => {
const view = new DataView(data.buffer);
return view.getFloat64(0);
},
getSize: () => 8
});
this.registerType<boolean>('boolean', {
serialize: (bool: boolean) => new Uint8Array([bool ? 1 : 0]),
deserialize: (data: Uint8Array) => data[0] === 1,
getSize: () => 1
});
this.registerType<number>('int32', {
serialize: (num: number) => {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setInt32(0, num);
return new Uint8Array(buffer);
},
deserialize: (data: Uint8Array) => {
const view = new DataView(data.buffer);
return view.getInt32(0);
},
getSize: () => 4
});
this.registerType<number>('uint32', {
serialize: (num: number) => {
const buffer = new ArrayBuffer(4);
const view = new DataView(buffer);
view.setUint32(0, num);
return new Uint8Array(buffer);
},
deserialize: (data: Uint8Array) => {
const view = new DataView(data.buffer);
return view.getUint32(0);
},
getSize: () => 4
});
// Vector3 类型
this.registerType<{x: number, y: number, z?: number}>('Vector3', {
serialize: (vec: { x: number; y: number; z?: number }) => {
const buffer = new ArrayBuffer(12);
const view = new DataView(buffer);
view.setFloat32(0, vec.x);
view.setFloat32(4, vec.y);
view.setFloat32(8, vec.z || 0);
return new Uint8Array(buffer);
},
deserialize: (data: Uint8Array) => {
const view = new DataView(data.buffer);
return {
x: view.getFloat32(0),
y: view.getFloat32(4),
z: view.getFloat32(8)
};
},
getSize: () => 12
});
// JSON 类型(用于复杂对象)
this.registerType('json', {
serialize: (obj: any) => {
const jsonStr = JSON.stringify(obj);
return new TextEncoder().encode(jsonStr);
},
deserialize: (data: Uint8Array) => {
const jsonStr = new TextDecoder().decode(data);
return JSON.parse(jsonStr);
},
getSize: (obj: any) => {
const jsonStr = JSON.stringify(obj);
return new TextEncoder().encode(jsonStr).length;
}
});
}
/**
* 注册序列化类型
*/
public registerType<T = NetworkValue>(typeName: string, typeSchema: SerializationSchema<T>): void {
if (typeof typeSchema.serialize !== 'function' ||
typeof typeSchema.deserialize !== 'function') {
throw new Error(`Invalid type schema for ${typeName}: must have serialize and deserialize methods`);
}
this.typeMap[typeName] = {
serialize: typeSchema.serialize as any,
deserialize: typeSchema.deserialize as any,
getSize: typeSchema.getSize as any || ((obj: any) => this.serialize(obj, typeName).length)
};
}
/**
* 序列化对象
*/
public serialize(obj: any, type?: string): Uint8Array {
if (type && this.typeMap[type]) {
return this.typeMap[type].serialize(obj);
}
// 自动类型检测
const detectedType = this.detectType(obj);
if (this.typeMap[detectedType]) {
return this.typeMap[detectedType].serialize(obj);
}
// 默认使用 JSON 序列化
const jsonHandler = this.typeMap['json'];
if (jsonHandler?.serialize) {
return jsonHandler.serialize(obj);
}
// 最终回退方案
return new TextEncoder().encode(JSON.stringify(obj));
}
/**
* 反序列化对象
*/
public deserialize<T = any>(data: Uint8Array, type?: string): T {
if (type && this.typeMap[type]) {
return this.typeMap[type].deserialize(data);
}
// 如果没有指定类型,尝试使用 JSON 反序列化
try {
const jsonHandler = this.typeMap['json'];
if (jsonHandler?.deserialize) {
return jsonHandler.deserialize(data);
}
// 最终回退方案
const jsonString = new TextDecoder().decode(data);
return JSON.parse(jsonString);
} catch (error) {
throw new Error(`Failed to deserialize data: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* 获取序列化后的大小
*/
public getSerializedSize(obj: any, type?: string): number {
if (type && this.typeMap[type]?.getSize) {
return this.typeMap[type].getSize(obj);
}
const detectedType = this.detectType(obj);
if (this.typeMap[detectedType]?.getSize) {
return this.typeMap[detectedType].getSize(obj);
}
const jsonHandler = this.typeMap['json'];
return jsonHandler?.getSize ? jsonHandler.getSize(obj) : JSON.stringify(obj).length;
}
/**
* 自动检测对象类型
*/
private detectType(obj: any): string {
if (typeof obj === 'string') return 'string';
if (typeof obj === 'number') return 'number';
if (typeof obj === 'boolean') return 'boolean';
if (obj && typeof obj === 'object') {
// 检测 Vector3 类型
if ('x' in obj && 'y' in obj && typeof obj.x === 'number' && typeof obj.y === 'number') {
return 'Vector3';
}
}
return 'json';
}
/**
* 批量序列化多个值
*/
public serializeBatch(values: Array<{ value: any; type?: string }>): Uint8Array {
const serializedParts: Uint8Array[] = [];
let totalSize = 0;
// 序列化每个值
for (const item of values) {
const serialized = this.serialize(item.value, item.type);
serializedParts.push(serialized);
totalSize += serialized.length + 4; // +4 为长度信息
}
// 创建总缓冲区
const result = new Uint8Array(totalSize + 4); // +4 为值的数量
const view = new DataView(result.buffer);
let offset = 0;
// 写入值的数量
view.setUint32(offset, values.length);
offset += 4;
// 写入每个序列化的值
for (const serialized of serializedParts) {
// 写入长度
view.setUint32(offset, serialized.length);
offset += 4;
// 写入数据
result.set(serialized, offset);
offset += serialized.length;
}
return result;
}
/**
* 批量反序列化
*/
public deserializeBatch(data: Uint8Array, types?: string[]): any[] {
const view = new DataView(data.buffer);
let offset = 0;
// 读取值的数量
const count = view.getUint32(offset);
offset += 4;
const results: any[] = [];
// 读取每个值
for (let i = 0; i < count; i++) {
// 读取长度
const length = view.getUint32(offset);
offset += 4;
// 读取数据
const valueData = data.slice(offset, offset + length);
offset += length;
// 反序列化
const type = types?.[i];
const value = this.deserialize(valueData, type);
results.push(value);
}
return results;
}
/**
* 压缩序列化数据
*/
public compress(data: Uint8Array): Uint8Array {
// 这里可以集成压缩算法,如 LZ4、gzip 等
// 目前返回原数据
return data;
}
/**
* 解压缩数据
*/
public decompress(data: Uint8Array): Uint8Array {
// 这里可以集成解压缩算法
// 目前返回原数据
return data;
}
/**
* 创建增量序列化数据
*/
public serializeDelta(oldValue: any, newValue: any, type?: string): Uint8Array | null {
// 基础实现:如果值相同则返回 null否则序列化新值
if (this.isEqual(oldValue, newValue)) {
return null;
}
return this.serialize(newValue, type);
}
/**
* 应用增量数据
*/
public applyDelta(_baseValue: any, deltaData: Uint8Array, type?: string): any {
// 基础实现:直接反序列化增量数据
// baseValue 在更复杂的增量实现中会被使用
return this.deserialize(deltaData, type);
}
/**
* 检查两个值是否相等
*/
private isEqual(a: any, b: any): boolean {
if (a === b) return true;
if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
return JSON.stringify(a) === JSON.stringify(b);
}
return false;
}
/**
* 获取已注册的类型列表
*/
public getRegisteredTypes(): string[] {
return Object.keys(this.typeMap);
}
/**
* 检查类型是否已注册
*/
public hasType(typeName: string): boolean {
return typeName in this.typeMap;
}
}

View File

@@ -0,0 +1,5 @@
/**
* 序列化工具导出
*/
export * from './NetworkSerializer';

View File

@@ -0,0 +1,375 @@
/**
* 网络库核心类型定义
*/
// 通用类型定义
export type NetworkValue = string | number | boolean | NetworkValue[] | { [key: string]: NetworkValue };
export type SerializableObject = Record<string, NetworkValue>;
export type Constructor<T = {}> = new (...args: unknown[]) => T;
export type MethodDecorator<T = unknown> = (target: unknown, propertyKey: string | symbol, descriptor: PropertyDescriptor) => void;
// 装饰器目标类型 - 使用更灵活的定义
export interface DecoratorTarget extends Record<string, unknown> {
constructor: Constructor;
}
// 网络数据类型约束
export interface SerializedData {
type: string;
data: Uint8Array;
checksum?: string;
}
// RPC参数类型
export type RpcParameterType = NetworkValue;
export type RpcReturnType = NetworkValue | void | Promise<NetworkValue | void>;
// 序列化模式接口 - 使用泛型支持特定类型
export interface SerializationSchema<T = NetworkValue> {
serialize: (obj: T) => Uint8Array;
deserialize: (data: Uint8Array) => T;
getSize?: (obj: T) => number;
}
/**
* 网络端类型
*/
export type NetworkSide = 'client' | 'server' | 'host';
/**
* 网络连接状态
*/
export type NetworkConnectionState =
| 'disconnected'
| 'connecting'
| 'connected'
| 'disconnecting'
| 'reconnecting'
| 'failed';
/**
* 网络消息类型
*/
export type NetworkMessageType =
| 'syncvar'
| 'client-rpc'
| 'server-rpc'
| 'spawn'
| 'destroy'
| 'ownership'
| 'scene-change'
| 'snapshot'
| 'ping'
| 'custom';
/**
* 网络配置
*/
export interface NetworkConfig {
/** 端口号 */
port: number;
/** 主机地址 */
host: string;
/** 最大连接数 */
maxConnections: number;
/** 同步频率 (Hz) */
syncRate: number;
/** 快照频率 (Hz) */
snapshotRate: number;
/** 是否启用压缩 */
compression: boolean;
/** 是否启用加密 */
encryption: boolean;
/** 网络超时时间 (ms) */
timeout: number;
/** 重连尝试次数 */
maxReconnectAttempts: number;
/** 重连间隔 (ms) */
reconnectInterval: number;
}
/**
* 网络统计信息
*/
export interface NetworkStats {
/** 连接数量 */
connectionCount: number;
/** 已发送字节数 */
bytesSent: number;
/** 已接收字节数 */
bytesReceived: number;
/** 已发送消息数 */
messagesSent: number;
/** 已接收消息数 */
messagesReceived: number;
/** 平均延迟 (ms) */
averageLatency: number;
/** 丢包率 (%) */
packetLoss: number;
/** 带宽使用率 (bytes/s) */
bandwidth: number;
}
/**
* 网络消息基类
*/
export interface NetworkMessage {
/** 消息类型 */
type: NetworkMessageType;
/** 网络对象ID */
networkId: number;
/** 消息数据 */
data: SerializableObject;
/** 时间戳 */
timestamp: number;
/** 消息ID */
messageId?: string;
/** 发送者ID */
senderId?: number;
/** 接收者ID (可选,用于定向发送) */
targetId?: number;
/** 是否可靠传输 */
reliable?: boolean;
/** 优先级 */
priority?: number;
}
/**
* SyncVar 消息
*/
export interface SyncVarMessage extends NetworkMessage {
type: 'syncvar';
/** 组件类型名 */
componentType: string;
/** 属性名 */
propertyName: string;
/** 属性值 */
value: NetworkValue;
/** 变化类型 */
changeType?: 'set' | 'add' | 'remove' | 'clear';
}
/**
* RPC 消息
*/
export interface RpcMessage extends NetworkMessage {
type: 'client-rpc' | 'server-rpc';
/** 组件类型名 */
componentType: string;
/** 方法名 */
methodName: string;
/** 参数列表 */
args: RpcParameterType[];
/** RPC ID (用于响应) */
rpcId?: string;
/** 是否需要响应 */
requiresResponse?: boolean;
}
/**
* 对象生成消息
*/
export interface SpawnMessage extends NetworkMessage {
type: 'spawn';
/** 预制体名称或ID */
prefabName: string;
/** 生成位置 */
position?: { x: number; y: number; z?: number };
/** 生成旋转 */
rotation?: { x: number; y: number; z: number; w: number };
/** 所有者ID */
ownerId: number;
/** 初始数据 */
initData?: SerializableObject;
}
/**
* 对象销毁消息
*/
export interface DestroyMessage extends NetworkMessage {
type: 'destroy';
/** 销毁原因 */
reason?: string;
}
/**
* 所有权转移消息
*/
export interface OwnershipMessage extends NetworkMessage {
type: 'ownership';
/** 新所有者ID */
newOwnerId: number;
/** 旧所有者ID */
oldOwnerId: number;
}
/**
* 快照消息
*/
export interface SnapshotMessage extends NetworkMessage {
type: 'snapshot';
/** 快照ID */
snapshotId: number;
/** 快照数据 */
snapshot: SerializableObject;
/** 包含的网络对象ID列表 */
networkIds: number[];
}
/**
* SyncVar 元数据
*/
export interface SyncVarMetadata {
/** 属性名 */
propertyName: string;
/** 是否仅权威端可修改 */
authorityOnly: boolean;
/** 变化回调函数名 */
onChanged?: string;
/** 序列化类型 */
serializeType?: string;
/** 是否使用增量同步 */
deltaSync?: boolean;
/** 同步优先级 */
priority?: number;
}
/**
* RPC 元数据
*/
export interface RpcMetadata {
/** 方法名 */
methodName: string;
/** RPC 类型 */
rpcType: 'client-rpc' | 'server-rpc';
/** 是否需要权限验证 */
requiresAuth?: boolean;
/** 是否可靠传输 */
reliable?: boolean;
/** 是否需要响应 */
requiresResponse?: boolean;
}
/**
* 网络组件元数据
*/
export interface NetworkComponentMetadata {
/** 组件类型名 */
componentType: string;
/** SyncVar 列表 */
syncVars: SyncVarMetadata[];
/** RPC 列表 */
rpcs: RpcMetadata[];
/** 是否自动生成协议 */
autoGenerateProtocol?: boolean;
}
/**
* 网络对象接口
*/
export interface INetworkObject {
/** 网络ID */
networkId: number;
/** 所有者客户端ID */
ownerId: number;
/** 是否拥有权威 */
hasAuthority: boolean;
/** 是否为本地对象 */
isLocal: boolean;
/** 网络组件列表 */
networkComponents: INetworkComponent[];
}
/**
* 网络组件接口
*/
export interface INetworkComponent {
/** 网络对象引用 */
networkObject: INetworkObject | null;
/** 网络ID */
networkId: number;
/** 是否拥有权威 */
hasAuthority: boolean;
/** 组件类型名 */
componentType: string;
}
/**
* 网络传输层接口
*/
export interface INetworkTransport {
/** 启动服务端 */
startServer(config: NetworkConfig): Promise<void>;
/** 连接到服务端 */
connectToServer(host: string, port: number): Promise<void>;
/** 断开连接 */
disconnect(): Promise<void>;
/** 发送消息 */
sendMessage(message: NetworkMessage, targetId?: number): Promise<void>;
/** 广播消息 */
broadcastMessage(message: NetworkMessage, excludeIds?: number[]): Promise<void>;
/** 设置消息处理器 */
onMessage(handler: (message: NetworkMessage, fromId?: number) => void): void;
/** 设置连接事件处理器 */
onConnection(handler: (clientId: number, isConnected: boolean) => void): void;
}
/**
* 序列化器接口
*/
export interface INetworkSerializer {
/** 序列化对象 */
serialize(obj: NetworkValue, type?: string): Uint8Array;
/** 反序列化对象 */
deserialize<T extends NetworkValue = NetworkValue>(data: Uint8Array, type?: string): T;
/** 注册类型 */
registerType<T = NetworkValue>(typeName: string, typeSchema: SerializationSchema<T>): void;
/** 获取序列化后的大小 */
getSerializedSize(obj: NetworkValue, type?: string): number;
}
/**
* 网络事件处理器
*/
export interface NetworkEventHandlers {
/** 连接成功 */
onConnected?: () => void;
/** 连接断开 */
onDisconnected?: (reason?: string) => void;
/** 客户端连接 */
onClientConnected?: (clientId: number) => void;
/** 客户端断开 */
onClientDisconnected?: (clientId: number, reason?: string) => void;
/** 网络错误 */
onError?: (error: Error) => void;
/** 延迟变化 */
onLatencyUpdate?: (latency: number) => void;
}
/**
* 网络调试信息
*/
export interface NetworkDebugInfo {
/** 连接信息 */
connections: {
[clientId: number]: {
id: number;
address: string;
latency: number;
connected: boolean;
lastSeen: number;
};
};
/** 网络对象列表 */
networkObjects: {
[networkId: number]: {
id: number;
ownerId: number;
componentTypes: string[];
syncVarCount: number;
rpcCount: number;
};
};
/** 统计信息 */
stats: NetworkStats;
}

View File

@@ -0,0 +1,4 @@
/**
* 类型定义导出
*/
export * from './NetworkTypes';

View File

@@ -0,0 +1,16 @@
/**
* reflect-metadata 类型扩展
*/
/// <reference types="reflect-metadata" />
declare namespace Reflect {
function defineMetadata(metadataKey: any, metadataValue: any, target: any, propertyKey?: string | symbol): void;
function getMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): any;
function getOwnMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): any;
function hasMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): boolean;
function hasOwnMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): boolean;
function deleteMetadata(metadataKey: any, target: any, propertyKey?: string | symbol): boolean;
function getMetadataKeys(target: any, propertyKey?: string | symbol): any[];
function getOwnMetadataKeys(target: any, propertyKey?: string | symbol): any[];
}

View File

@@ -0,0 +1,242 @@
/**
* 网络工具函数
*/
/**
* 生成网络ID
*/
export function generateNetworkId(): number {
return Math.floor(Math.random() * 0x7FFFFFFF) + 1;
}
/**
* 生成消息ID
*/
export function generateMessageId(): string {
return Date.now().toString(36) + Math.random().toString(36).substr(2);
}
/**
* 计算两点之间的距离
*/
export function calculateDistance(
pos1: { x: number; y: number; z?: number },
pos2: { x: number; y: number; z?: number }
): number {
const dx = pos1.x - pos2.x;
const dy = pos1.y - pos2.y;
const dz = (pos1.z || 0) - (pos2.z || 0);
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
/**
* 检查环境是否为 Node.js
*/
export function isNodeEnvironment(): boolean {
return typeof process !== 'undefined' && process.versions && !!process.versions.node;
}
/**
* 检查环境是否为浏览器
*/
export function isBrowserEnvironment(): boolean {
return typeof window !== 'undefined' && typeof window.document !== 'undefined';
}
/**
* 获取时间戳(毫秒)
*/
export function getTimestamp(): number {
return Date.now();
}
/**
* 获取高精度时间戳(如果可用)
*/
export function getHighResTimestamp(): number {
if (typeof performance !== 'undefined' && performance.now) {
return performance.now();
}
return Date.now();
}
/**
* 限制调用频率
*/
export function throttle<T extends (...args: any[]) => any>(
func: T,
limit: number
): T {
let inThrottle: boolean;
let context: any;
return (function(this: any, ...args: any[]) {
context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
}
}) as T;
}
/**
* 防抖函数
*/
export function debounce<T extends (...args: any[]) => any>(
func: T,
delay: number
): T {
let timeoutId: NodeJS.Timeout;
let context: any;
return (function(this: any, ...args: any[]) {
context = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => func.apply(context, args), delay);
}) as T;
}
/**
* 深拷贝对象
*/
export function deepClone<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime()) as T;
}
if (obj instanceof Array) {
return obj.map(item => deepClone(item)) as T;
}
if (typeof obj === 'object') {
const cloned = {} as T;
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = deepClone(obj[key]);
}
}
return cloned;
}
return obj;
}
/**
* 检查对象是否为空
*/
export function isEmpty(obj: any): boolean {
if (obj === null || obj === undefined) return true;
if (typeof obj === 'string' || Array.isArray(obj)) return obj.length === 0;
if (typeof obj === 'object') return Object.keys(obj).length === 0;
return false;
}
/**
* 格式化字节大小
*/
export function formatBytes(bytes: number, decimals = 2): string {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
/**
* 格式化延迟时间
*/
export function formatLatency(milliseconds: number): string {
if (milliseconds < 1000) {
return `${Math.round(milliseconds)}ms`;
} else {
return `${(milliseconds / 1000).toFixed(1)}s`;
}
}
/**
* 获取网络质量描述
*/
export function getNetworkQuality(latency: number, packetLoss: number): string {
if (latency < 50 && packetLoss < 1) return 'Excellent';
if (latency < 100 && packetLoss < 2) return 'Good';
if (latency < 200 && packetLoss < 5) return 'Fair';
if (latency < 500 && packetLoss < 10) return 'Poor';
return 'Very Poor';
}
/**
* 计算网络统计平均值
*/
export function calculateNetworkAverage(values: number[], maxSamples = 100): number {
if (values.length === 0) return 0;
// 保留最近的样本
const samples = values.slice(-maxSamples);
const sum = samples.reduce((acc, val) => acc + val, 0);
return sum / samples.length;
}
/**
* 验证网络配置
*/
export function validateNetworkConfig(config: any): { valid: boolean; errors: string[] } {
const errors: string[] = [];
if (typeof config.port !== 'number' || config.port <= 0 || config.port > 65535) {
errors.push('Port must be a number between 1 and 65535');
}
if (typeof config.host !== 'string' || config.host.length === 0) {
errors.push('Host must be a non-empty string');
}
if (typeof config.maxConnections !== 'number' || config.maxConnections <= 0) {
errors.push('Max connections must be a positive number');
}
if (typeof config.syncRate !== 'number' || config.syncRate <= 0) {
errors.push('Sync rate must be a positive number');
}
return {
valid: errors.length === 0,
errors
};
}
/**
* 重试函数
*/
export async function retry<T>(
fn: () => Promise<T>,
maxAttempts: number,
delay: number = 1000
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt === maxAttempts) {
throw lastError;
}
await new Promise(resolve => setTimeout(resolve, delay * attempt));
}
}
throw lastError!;
}

View File

@@ -0,0 +1,5 @@
/**
* 工具函数导出
*/
export * from './NetworkUtils';

View File

@@ -0,0 +1,9 @@
import 'reflect-metadata';
global.beforeEach(() => {
jest.clearAllMocks();
});
global.afterEach(() => {
jest.restoreAllMocks();
});

View File

@@ -0,0 +1,46 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2020",
"moduleResolution": "node",
"allowImportingTsExtensions": false,
"lib": ["ES2020", "DOM"],
"outDir": "./bin",
"rootDir": "./src",
"strict": 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",
"src/protocol/analyzer/**/*"
]
}