diff --git a/package-lock.json b/package-lock.json index 44104bc1..1a73ce79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9701,8 +9701,7 @@ "node_modules/reflect-metadata": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==" }, "node_modules/require-directory": { "version": "2.1.1", diff --git a/packages/network/package.json b/packages/network/package.json index b858cf2f..2cf21af6 100644 --- a/packages/network/package.json +++ b/packages/network/package.json @@ -32,17 +32,20 @@ "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" + "test:clear": "jest --clearCache", + "publish:patch": "npm version patch && npm publish", + "publish:minor": "npm version minor && npm publish", + "publish:major": "npm version major && npm publish" }, "author": "yhh", "license": "MIT", "dependencies": { - "reflect-metadata": "^0.2.2", - "ws": "^8.18.0", "isomorphic-ws": "^5.0.0", - "uuid": "^10.0.0", + "reflect-metadata": "^0.2.2", + "tsbuffer": "^2.2.10", "tsrpc": "^3.4.19", - "tsbuffer": "^2.2.10" + "uuid": "^10.0.0", + "ws": "^8.18.0" }, "peerDependencies": { "@esengine/ecs-framework": ">=2.1.29" @@ -54,8 +57,8 @@ "@rollup/plugin-terser": "^0.4.4", "@types/jest": "^29.5.14", "@types/node": "^20.19.0", - "@types/ws": "^8.5.13", "@types/uuid": "^10.0.0", + "@types/ws": "^8.5.13", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "rimraf": "^5.0.0", diff --git a/packages/network/src/Config/NetworkConfigManager.ts b/packages/network/src/Config/NetworkConfigManager.ts deleted file mode 100644 index bea4eb28..00000000 --- a/packages/network/src/Config/NetworkConfigManager.ts +++ /dev/null @@ -1,478 +0,0 @@ -/** - * 网络配置管理器 - */ - -import { - NETWORK_CONFIG, - SYNCVAR_CONFIG, - MESSAGE_CONFIG, - SERIALIZATION_CONFIG, - TSRPC_CONFIG, - AUTHORITY_CONFIG, - PERFORMANCE_CONFIG -} from '../constants/NetworkConstants'; - -/** - * 网络配置接口 - */ -export interface INetworkConfig { - [key: string]: unknown; - /** 连接配置 */ - connection: { - timeout: number; - maxReconnectAttempts: number; - reconnectDelay: number; - }; - - /** 心跳配置 */ - heartbeat: { - interval: number; - timeout: number; - maxConsecutiveLoss: number; - packetSize: number; - enableAdaptiveInterval: boolean; - rttHistorySize: number; - }; - - /** SyncVar配置 */ - syncVar: { - cacheTimeout: number; - defaultThrottleMs: number; - maxFieldNumber: number; - minFieldNumber: number; - }; - - /** 消息配置 */ - message: { - maxSequenceNumber: number; - maxHeaderSize: number; - maxPayloadSize: number; - defaultTimeout: number; - maxBatchSize: number; - }; - - /** 序列化配置 */ - serialization: { - defaultCompressionLevel: number; - minCompressionSize: number; - initialBufferSize: number; - maxBufferSize: number; - }; - - /** TSRPC配置 */ - tsrpc: { - defaultServerUrl: string; - defaultTimeout: number; - heartbeatInterval: number; - heartbeatTimeout: number; - poolConfig: { - minConnections: number; - maxConnections: number; - idleTimeout: number; - }; - }; - - /** 权限配置 */ - authority: { - minPriority: number; - maxPriority: number; - defaultRulePriority: number; - }; - - /** 性能监控配置 */ - performance: { - statsCollectionInterval: number; - statsRetentionTime: number; - warningThresholds: { - rtt: number; - packetLoss: number; - jitter: number; - cpuUsage: number; - memoryUsage: number; - }; - }; -} - -/** - * 配置更新事件接口 - */ -export interface IConfigUpdateEvent { - path: string; - oldValue: T; - newValue: T; - timestamp: number; -} - -/** - * 配置管理器 - * - * 提供类型安全的配置管理,支持配置更新监听和验证 - */ -export class NetworkConfigManager { - private static _instance: NetworkConfigManager | null = null; - private _config: INetworkConfig; - private _updateListeners: Map void>> = new Map(); - - private constructor() { - this._config = this.createDefaultConfig(); - } - - public static get Instance(): NetworkConfigManager { - if (!NetworkConfigManager._instance) { - NetworkConfigManager._instance = new NetworkConfigManager(); - } - return NetworkConfigManager._instance; - } - - /** - * 创建默认配置 - */ - private createDefaultConfig(): INetworkConfig { - return { - connection: { - timeout: NETWORK_CONFIG.DEFAULT_CONNECTION_TIMEOUT, - maxReconnectAttempts: NETWORK_CONFIG.DEFAULT_MAX_RECONNECT_ATTEMPTS, - reconnectDelay: NETWORK_CONFIG.DEFAULT_RECONNECT_DELAY - }, - heartbeat: { - interval: NETWORK_CONFIG.DEFAULT_HEARTBEAT_INTERVAL, - timeout: NETWORK_CONFIG.DEFAULT_HEARTBEAT_TIMEOUT, - maxConsecutiveLoss: NETWORK_CONFIG.DEFAULT_MAX_CONSECUTIVE_LOSS, - packetSize: NETWORK_CONFIG.DEFAULT_HEARTBEAT_PACKET_SIZE, - enableAdaptiveInterval: true, - rttHistorySize: NETWORK_CONFIG.DEFAULT_RTT_HISTORY_SIZE - }, - syncVar: { - cacheTimeout: SYNCVAR_CONFIG.DEFAULT_CACHE_TIMEOUT, - defaultThrottleMs: SYNCVAR_CONFIG.DEFAULT_THROTTLE_MS, - maxFieldNumber: SYNCVAR_CONFIG.MAX_FIELD_NUMBER, - minFieldNumber: SYNCVAR_CONFIG.MIN_FIELD_NUMBER - }, - message: { - maxSequenceNumber: MESSAGE_CONFIG.MAX_SEQUENCE_NUMBER, - maxHeaderSize: MESSAGE_CONFIG.MAX_HEADER_SIZE, - maxPayloadSize: MESSAGE_CONFIG.MAX_PAYLOAD_SIZE, - defaultTimeout: MESSAGE_CONFIG.DEFAULT_MESSAGE_TIMEOUT, - maxBatchSize: MESSAGE_CONFIG.MAX_BATCH_SIZE - }, - serialization: { - defaultCompressionLevel: SERIALIZATION_CONFIG.DEFAULT_COMPRESSION_LEVEL, - minCompressionSize: SERIALIZATION_CONFIG.MIN_COMPRESSION_SIZE, - initialBufferSize: SERIALIZATION_CONFIG.INITIAL_BUFFER_SIZE, - maxBufferSize: SERIALIZATION_CONFIG.MAX_BUFFER_SIZE - }, - tsrpc: { - defaultServerUrl: TSRPC_CONFIG.DEFAULT_SERVER_URL, - defaultTimeout: TSRPC_CONFIG.DEFAULT_TIMEOUT, - heartbeatInterval: TSRPC_CONFIG.DEFAULT_HEARTBEAT.interval, - heartbeatTimeout: TSRPC_CONFIG.DEFAULT_HEARTBEAT.timeout, - poolConfig: { - minConnections: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.minConnections, - maxConnections: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.maxConnections, - idleTimeout: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.idleTimeout - } - }, - authority: { - minPriority: AUTHORITY_CONFIG.MIN_PRIORITY, - maxPriority: AUTHORITY_CONFIG.MAX_PRIORITY, - defaultRulePriority: AUTHORITY_CONFIG.DEFAULT_RULE_PRIORITY - }, - performance: { - statsCollectionInterval: PERFORMANCE_CONFIG.STATS_COLLECTION_INTERVAL, - statsRetentionTime: PERFORMANCE_CONFIG.STATS_RETENTION_TIME, - warningThresholds: { - rtt: PERFORMANCE_CONFIG.WARNING_THRESHOLDS.RTT, - packetLoss: PERFORMANCE_CONFIG.WARNING_THRESHOLDS.PACKET_LOSS, - jitter: PERFORMANCE_CONFIG.WARNING_THRESHOLDS.JITTER, - cpuUsage: PERFORMANCE_CONFIG.WARNING_THRESHOLDS.CPU_USAGE, - memoryUsage: PERFORMANCE_CONFIG.WARNING_THRESHOLDS.MEMORY_USAGE - } - } - }; - } - - /** - * 获取配置 - */ - public getConfig(): Readonly { - return this._config; - } - - /** - * 获取指定路径的配置值 - * - * @param path - 配置路径,使用点号分隔 - * @returns 配置值 - */ - public get(path: string): T { - const keys = path.split('.'); - let current: unknown = this._config; - - for (const key of keys) { - if (typeof current !== 'object' || current === null || !(key in current)) { - throw new Error(`配置路径不存在: ${path}`); - } - current = (current as Record)[key]; - } - - return current as T; - } - - /** - * 设置指定路径的配置值 - * - * @param path - 配置路径 - * @param value - 新值 - */ - public set(path: string, value: T): void { - const keys = path.split('.'); - const lastKey = keys.pop()!; - let current = this._config as Record; - - // 导航到父对象 - for (const key of keys) { - if (typeof current[key] !== 'object' || current[key] === null) { - throw new Error(`配置路径无效: ${path}`); - } - current = current[key] as Record; - } - - const oldValue = current[lastKey]; - - // 验证新值 - this.validateConfigValue(path, value); - - // 设置新值 - current[lastKey] = value; - - // 触发更新事件 - this.emitConfigUpdate({ - path, - oldValue, - newValue: value, - timestamp: Date.now() - }); - } - - /** - * 批量更新配置 - * - * @param updates - 配置更新对象 - */ - public update(updates: Partial): void { - const flatUpdates = this.flattenObject(updates as unknown as Record); - - for (const [path, value] of Object.entries(flatUpdates)) { - this.set(path, value); - } - } - - /** - * 重置配置为默认值 - */ - public reset(): void { - const oldConfig = { ...this._config }; - this._config = this.createDefaultConfig(); - - this.emitConfigUpdate({ - path: '', - oldValue: oldConfig, - newValue: this._config, - timestamp: Date.now() - }); - } - - /** - * 添加配置更新监听器 - * - * @param path - 监听的配置路径(空字符串监听所有) - * @param listener - 监听器函数 - */ - public addUpdateListener(path: string, listener: (event: IConfigUpdateEvent) => void): void { - if (!this._updateListeners.has(path)) { - this._updateListeners.set(path, []); - } - this._updateListeners.get(path)!.push(listener); - } - - /** - * 移除配置更新监听器 - * - * @param path - 配置路径 - * @param listener - 监听器函数 - */ - public removeUpdateListener(path: string, listener: (event: IConfigUpdateEvent) => void): void { - const listeners = this._updateListeners.get(path); - if (listeners) { - const index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - } - } - - /** - * 从环境变量加载配置 - */ - public loadFromEnv(): void { - const envConfig = this.parseEnvConfig(); - if (Object.keys(envConfig).length > 0) { - this.update(envConfig); - } - } - - /** - * 从JSON对象加载配置 - * - * @param config - 配置对象 - */ - public loadFromObject(config: Partial): void { - this.validateConfig(config); - this.update(config); - } - - /** - * 导出配置为JSON - */ - public exportConfig(): INetworkConfig { - return JSON.parse(JSON.stringify(this._config)); - } - - /** - * 验证配置值 - */ - private validateConfigValue(path: string, value: unknown): void { - // 基本类型检查 - if (path.includes('timeout') || path.includes('interval') || path.includes('delay')) { - if (typeof value !== 'number' || value < 0) { - throw new Error(`配置值必须是非负数: ${path}`); - } - } - - if (path.includes('size') && typeof value === 'number' && value <= 0) { - throw new Error(`大小配置必须是正数: ${path}`); - } - - if (path.includes('url') && typeof value !== 'string') { - throw new Error(`URL配置必须是字符串: ${path}`); - } - - // 特定路径验证 - if (path === 'syncVar.maxFieldNumber' && typeof value === 'number' && value > SYNCVAR_CONFIG.MAX_FIELD_NUMBER) { - throw new Error(`字段编号不能超过 ${SYNCVAR_CONFIG.MAX_FIELD_NUMBER}`); - } - - if (path === 'syncVar.minFieldNumber' && typeof value === 'number' && value < SYNCVAR_CONFIG.MIN_FIELD_NUMBER) { - throw new Error(`字段编号不能小于 ${SYNCVAR_CONFIG.MIN_FIELD_NUMBER}`); - } - } - - /** - * 验证整个配置对象 - */ - private validateConfig(config: Partial): void { - // 递归验证配置结构 - const flatConfig = this.flattenObject(config); - for (const [path, value] of Object.entries(flatConfig)) { - this.validateConfigValue(path, value); - } - } - - /** - * 扁平化对象 - */ - private flattenObject(obj: Record, prefix = ''): Record { - const flattened: Record = {}; - - for (const [key, value] of Object.entries(obj)) { - const newKey = prefix ? `${prefix}.${key}` : key; - - if (typeof value === 'object' && value !== null && !Array.isArray(value)) { - Object.assign(flattened, this.flattenObject(value as Record, newKey)); - } else { - flattened[newKey] = value; - } - } - - return flattened; - } - - /** - * 解析环境变量配置 - */ - private parseEnvConfig(): Partial { - const config: Partial = {}; - - // 连接配置 - if (process.env.NETWORK_CONNECTION_TIMEOUT) { - config.connection = { - timeout: parseInt(process.env.NETWORK_CONNECTION_TIMEOUT, 10), - maxReconnectAttempts: NETWORK_CONFIG.DEFAULT_MAX_RECONNECT_ATTEMPTS, - reconnectDelay: NETWORK_CONFIG.DEFAULT_RECONNECT_DELAY - }; - } - - // 心跳配置 - if (process.env.NETWORK_HEARTBEAT_INTERVAL) { - config.heartbeat = { - interval: parseInt(process.env.NETWORK_HEARTBEAT_INTERVAL, 10), - timeout: NETWORK_CONFIG.DEFAULT_HEARTBEAT_TIMEOUT, - maxConsecutiveLoss: NETWORK_CONFIG.DEFAULT_MAX_CONSECUTIVE_LOSS, - packetSize: NETWORK_CONFIG.DEFAULT_HEARTBEAT_PACKET_SIZE, - enableAdaptiveInterval: true, - rttHistorySize: NETWORK_CONFIG.DEFAULT_RTT_HISTORY_SIZE - }; - } - - // TSRPC配置 - if (process.env.TSRPC_SERVER_URL) { - config.tsrpc = { - defaultServerUrl: process.env.TSRPC_SERVER_URL, - defaultTimeout: TSRPC_CONFIG.DEFAULT_TIMEOUT, - heartbeatInterval: TSRPC_CONFIG.DEFAULT_HEARTBEAT.interval, - heartbeatTimeout: TSRPC_CONFIG.DEFAULT_HEARTBEAT.timeout, - poolConfig: { - minConnections: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.minConnections, - maxConnections: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.maxConnections, - idleTimeout: TSRPC_CONFIG.DEFAULT_POOL_CONFIG.idleTimeout - } - }; - } - - return config; - } - - /** - * 触发配置更新事件 - */ - private emitConfigUpdate(event: IConfigUpdateEvent): void { - // 触发具体路径的监听器 - const listeners = this._updateListeners.get(event.path); - if (listeners) { - listeners.forEach(listener => { - try { - listener(event); - } catch (error) { - console.error(`配置更新监听器错误 (${event.path}):`, error); - } - }); - } - - // 触发全局监听器 - const globalListeners = this._updateListeners.get(''); - if (globalListeners) { - globalListeners.forEach(listener => { - try { - listener(event); - } catch (error) { - console.error('全局配置更新监听器错误:', error); - } - }); - } - } -} - -/** - * 配置管理器单例实例 - */ -export const NetworkConfig = NetworkConfigManager.Instance; \ No newline at end of file diff --git a/packages/network/src/Core/HeartbeatManager.ts b/packages/network/src/Core/HeartbeatManager.ts deleted file mode 100644 index 6324eb95..00000000 --- a/packages/network/src/Core/HeartbeatManager.ts +++ /dev/null @@ -1,477 +0,0 @@ -import { NetworkConnection } from './NetworkConnection'; -import { MessageData } from '../types/NetworkTypes'; -import { NETWORK_CONFIG } from '../constants/NetworkConstants'; - -// Logger akan diimport dari framework logger -const logger = { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug -}; - -/** - * 心跳包数据 - */ -export interface HeartbeatPacket { - id: string; - timestamp: number; - sequenceNumber: number; - type: 'ping' | 'pong'; - payload?: MessageData; -} - -/** - * 心跳统计信息 - */ -export interface HeartbeatStats { - /** 总发送数 */ - totalSent: number; - /** 总接收数 */ - totalReceived: number; - /** 丢包数 */ - lostPackets: number; - /** 丢包率 */ - packetLossRate: number; - /** 平均RTT */ - averageRtt: number; - /** 最小RTT */ - minRtt: number; - /** 最大RTT */ - maxRtt: number; - /** 抖动 */ - jitter: number; - /** 连续丢包数 */ - consecutiveLoss: number; - /** 最后心跳时间 */ - lastHeartbeat: number; - /** 连接状态 */ - isAlive: boolean; -} - -/** - * 心跳配置 - */ -export interface HeartbeatConfig { - /** 心跳间隔(毫秒) */ - interval: number; - /** 超时时间(毫秒) */ - timeout: number; - /** 最大连续丢包数 */ - maxConsecutiveLoss: number; - /** 心跳包大小(字节) */ - packetSize: number; - /** 是否启用自适应间隔 */ - enableAdaptiveInterval: boolean; - /** RTT历史记录数量 */ - rttHistorySize: number; -} - -/** - * 心跳管理器 - * - * 提供精确的包丢失检测和连接质量监控 - */ -export class HeartbeatManager { - private static readonly DEFAULT_CONFIG: HeartbeatConfig = { - interval: NETWORK_CONFIG.DEFAULT_HEARTBEAT_INTERVAL, - timeout: NETWORK_CONFIG.DEFAULT_HEARTBEAT_TIMEOUT, - maxConsecutiveLoss: NETWORK_CONFIG.DEFAULT_MAX_CONSECUTIVE_LOSS, - packetSize: NETWORK_CONFIG.DEFAULT_HEARTBEAT_PACKET_SIZE, - enableAdaptiveInterval: true, - rttHistorySize: NETWORK_CONFIG.DEFAULT_RTT_HISTORY_SIZE - }; - - private _config: HeartbeatConfig; - private _connection: NetworkConnection; - private _isRunning: boolean = false; - private _intervalId: NodeJS.Timeout | null = null; - private _sequenceNumber: number = 0; - - // 统计数据 - private _stats: HeartbeatStats = { - totalSent: 0, - totalReceived: 0, - lostPackets: 0, - packetLossRate: 0, - averageRtt: 0, - minRtt: Number.MAX_VALUE, - maxRtt: 0, - jitter: 0, - consecutiveLoss: 0, - lastHeartbeat: 0, - isAlive: true - }; - - // 心跳记录 - private _pendingPings: Map = new Map(); - private _rttHistory: number[] = []; - private _lastRtt: number = 0; - - constructor(connection: NetworkConnection, config?: Partial) { - this._connection = connection; - this._config = { ...HeartbeatManager.DEFAULT_CONFIG, ...config }; - this.setupConnectionHandlers(); - } - - /** - * 设置连接处理器 - */ - private setupConnectionHandlers(): void { - this._connection.on('message', (data) => { - this.handleMessage(data); - }); - - this._connection.on('disconnected', () => { - this.stop(); - }); - } - - /** - * 开始心跳监控 - */ - public start(): void { - if (this._isRunning) { - return; - } - - this._isRunning = true; - this.scheduleNextHeartbeat(); - logger.info('心跳管理器已启动'); - } - - /** - * 停止心跳监控 - */ - public stop(): void { - if (!this._isRunning) { - return; - } - - this._isRunning = false; - - if (this._intervalId) { - clearTimeout(this._intervalId); - this._intervalId = null; - } - - this._pendingPings.clear(); - logger.info('心跳管理器已停止'); - } - - /** - * 安排下次心跳 - */ - private scheduleNextHeartbeat(): void { - if (!this._isRunning) { - return; - } - - let interval = this._config.interval; - - // 自适应间隔调整 - if (this._config.enableAdaptiveInterval) { - interval = this.calculateAdaptiveInterval(); - } - - this._intervalId = setTimeout(() => { - this.sendHeartbeat(); - this.scheduleNextHeartbeat(); - }, interval); - } - - /** - * 计算自适应间隔 - */ - private calculateAdaptiveInterval(): number { - const baseInterval = this._config.interval; - - // 根据网络质量调整间隔 - if (this._stats.packetLossRate > 0.05) { - // 丢包率高时增加心跳频率 - return Math.max(baseInterval * 0.5, 1000); - } else if (this._stats.packetLossRate < 0.01 && this._stats.averageRtt < 50) { - // 网络质量好时减少心跳频率 - return Math.min(baseInterval * 1.5, 15000); - } - - return baseInterval; - } - - /** - * 发送心跳包 - */ - private sendHeartbeat(): void { - const packet: HeartbeatPacket = { - id: this.generatePacketId(), - timestamp: Date.now(), - sequenceNumber: ++this._sequenceNumber, - type: 'ping', - payload: this.generateHeartbeatPayload() - }; - - const data = this.serializePacket(packet); - const success = this._connection.send(data); - - if (success) { - this._pendingPings.set(packet.id, { - packet, - sentAt: Date.now() - }); - this._stats.totalSent++; - - // 清理超时的心跳包 - this.cleanupTimeoutPings(); - } else { - logger.warn('心跳包发送失败'); - this._stats.consecutiveLoss++; - this.updateConnectionStatus(); - } - } - - /** - * 处理接收到的消息 - */ - private handleMessage(data: Uint8Array): void { - try { - const packet = this.deserializePacket(data); - if (!packet || !this.isHeartbeatPacket(packet)) { - return; - } - - if (packet.type === 'ping') { - this.handlePingPacket(packet); - } else if (packet.type === 'pong') { - this.handlePongPacket(packet); - } - } catch (error) { - logger.debug('处理心跳包时出错:', error); - } - } - - /** - * 处理Ping包 - */ - private handlePingPacket(packet: HeartbeatPacket): void { - const pongPacket: HeartbeatPacket = { - id: packet.id, - timestamp: Date.now(), - sequenceNumber: packet.sequenceNumber, - type: 'pong', - payload: packet.payload - }; - - const data = this.serializePacket(pongPacket); - this._connection.send(data); - } - - /** - * 处理Pong包 - */ - private handlePongPacket(packet: HeartbeatPacket): void { - const pendingPing = this._pendingPings.get(packet.id); - if (!pendingPing) { - return; // 可能是超时的包 - } - - // 计算RTT - const now = Date.now(); - const rtt = now - pendingPing.sentAt; - - // 更新统计信息 - this._stats.totalReceived++; - this._stats.lastHeartbeat = now; - this._stats.consecutiveLoss = 0; - this._stats.isAlive = true; - - // 更新RTT统计 - this.updateRttStats(rtt); - - // 移除已确认的ping - this._pendingPings.delete(packet.id); - - // 更新丢包统计 - this.updatePacketLossStats(); - - logger.debug(`心跳RTT: ${rtt}ms`); - } - - /** - * 更新RTT统计 - */ - private updateRttStats(rtt: number): void { - this._rttHistory.push(rtt); - if (this._rttHistory.length > this._config.rttHistorySize) { - this._rttHistory.shift(); - } - - // 计算平均RTT - this._stats.averageRtt = this._rttHistory.reduce((sum, r) => sum + r, 0) / this._rttHistory.length; - - // 更新最小/最大RTT - this._stats.minRtt = Math.min(this._stats.minRtt, rtt); - this._stats.maxRtt = Math.max(this._stats.maxRtt, rtt); - - // 计算抖动 - if (this._lastRtt > 0) { - const jitterSample = Math.abs(rtt - this._lastRtt); - this._stats.jitter = (this._stats.jitter * 0.9) + (jitterSample * 0.1); - } - this._lastRtt = rtt; - } - - /** - * 更新丢包统计 - */ - private updatePacketLossStats(): void { - const now = Date.now(); - let lostPackets = 0; - - // 计算超时的包数量 - for (const [id, pingInfo] of this._pendingPings) { - if (now - pingInfo.sentAt > this._config.timeout) { - lostPackets++; - this._pendingPings.delete(id); - } - } - - this._stats.lostPackets += lostPackets; - - if (this._stats.totalSent > 0) { - this._stats.packetLossRate = this._stats.lostPackets / this._stats.totalSent; - } - } - - /** - * 清理超时的ping包 - */ - private cleanupTimeoutPings(): void { - const now = Date.now(); - const timeoutIds: string[] = []; - - for (const [id, pingInfo] of this._pendingPings) { - if (now - pingInfo.sentAt > this._config.timeout) { - timeoutIds.push(id); - } - } - - if (timeoutIds.length > 0) { - this._stats.consecutiveLoss += timeoutIds.length; - this._stats.lostPackets += timeoutIds.length; - - for (const id of timeoutIds) { - this._pendingPings.delete(id); - } - - this.updateConnectionStatus(); - logger.debug(`清理了 ${timeoutIds.length} 个超时心跳包`); - } - } - - /** - * 更新连接状态 - */ - private updateConnectionStatus(): void { - const now = Date.now(); - const timeSinceLastHeartbeat = now - this._stats.lastHeartbeat; - - // 检查连接是否还活着 - this._stats.isAlive = - this._stats.consecutiveLoss < this._config.maxConsecutiveLoss && - timeSinceLastHeartbeat < this._config.timeout * 2; - - if (!this._stats.isAlive) { - logger.warn('连接被判定为不活跃', { - consecutiveLoss: this._stats.consecutiveLoss, - timeSinceLastHeartbeat - }); - } - } - - /** - * 生成包ID - */ - private generatePacketId(): string { - return `heartbeat_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - } - - /** - * 生成心跳载荷 - */ - private generateHeartbeatPayload(): MessageData { - const payloadSize = this._config.packetSize - 50; // 减去头部大小 - return { - data: 'x'.repeat(Math.max(0, payloadSize)), - timestamp: Date.now() - }; - } - - /** - * 序列化包 - */ - private serializePacket(packet: HeartbeatPacket): Uint8Array { - const jsonString = JSON.stringify(packet); - return new TextEncoder().encode(jsonString); - } - - /** - * 反序列化包 - */ - private deserializePacket(data: Uint8Array): HeartbeatPacket | null { - try { - const jsonString = new TextDecoder().decode(data); - return JSON.parse(jsonString) as HeartbeatPacket; - } catch { - return null; - } - } - - /** - * 检查是否为心跳包 - */ - private isHeartbeatPacket(packet: unknown): packet is HeartbeatPacket { - return typeof packet === 'object' && - packet !== null && - typeof (packet as Record).id === 'string' && - typeof (packet as Record).timestamp === 'number' && - typeof (packet as Record).sequenceNumber === 'number' && - ((packet as Record).type === 'ping' || (packet as Record).type === 'pong'); - } - - /** - * 获取统计信息 - */ - public getStats(): HeartbeatStats { - return { ...this._stats }; - } - - /** - * 重置统计信息 - */ - public resetStats(): void { - this._stats = { - totalSent: 0, - totalReceived: 0, - lostPackets: 0, - packetLossRate: 0, - averageRtt: 0, - minRtt: Number.MAX_VALUE, - maxRtt: 0, - jitter: 0, - consecutiveLoss: 0, - lastHeartbeat: 0, - isAlive: true - }; - this._rttHistory = []; - this._pendingPings.clear(); - logger.debug('心跳统计信息已重置'); - } - - /** - * 更新配置 - */ - public updateConfig(newConfig: Partial): void { - this._config = { ...this._config, ...newConfig }; - logger.debug('心跳配置已更新'); - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkClient.ts b/packages/network/src/Core/NetworkClient.ts deleted file mode 100644 index 87dd57f9..00000000 --- a/packages/network/src/Core/NetworkClient.ts +++ /dev/null @@ -1,512 +0,0 @@ -import WebSocket from 'isomorphic-ws'; -import { NetworkConnection } from './NetworkConnection'; -import { SyncVarUpdateMessage } from '../Messaging/MessageTypes'; -import { SyncVarMessageHandler } from '../SyncVar/SyncVarMessageHandler'; -import { MessageHandler } from '../Messaging/MessageHandler'; -import { NetworkPerformanceMonitor } from './NetworkPerformanceMonitor'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 客户端事件接口 - */ -export interface NetworkClientEvents { - connected: () => void; - disconnected: (reason?: string) => void; - message: (data: Uint8Array) => void; - error: (error: Error) => void; - reconnecting: (attempt: number) => void; - reconnected: () => void; -} - -/** - * 网络客户端 - * - * 管理与服务端的WebSocket连接,支持自动重连 - * 提供消息发送和接收功能 - */ -export class NetworkClient { - private static readonly logger = createLogger('NetworkClient'); - private _connection: NetworkConnection | null = null; - private _url: string = ''; - private _isConnected: boolean = false; - private _isConnecting: boolean = false; - private _connectTime: number = 0; - private _eventHandlers: Map = new Map(); - - // SyncVar相关组件 - private _syncVarHandler: SyncVarMessageHandler; - private _messageHandler: MessageHandler; - - // 性能监控 - private _performanceMonitor: NetworkPerformanceMonitor; - - // 重连配置 - private _autoReconnect: boolean = true; - private _reconnectAttempts: number = 0; - private _maxReconnectAttempts: number = 5; - private _reconnectDelay: number = 1000; // 初始重连延迟1秒 - private _maxReconnectDelay: number = 30000; // 最大重连延迟30秒 - private _reconnectTimer: NodeJS.Timeout | null = null; - - constructor() { - // 初始化SyncVar组件 - this._syncVarHandler = new SyncVarMessageHandler(); - this._messageHandler = MessageHandler.Instance; - this._performanceMonitor = NetworkPerformanceMonitor.Instance; - - // 注册SyncVar消息处理器 - this._messageHandler.registerHandler( - 400, // MessageType.SYNC_VAR_UPDATE - SyncVarUpdateMessage, - this._syncVarHandler, - 0 - ); - } - - /** - * 连接到服务端 - * - * @param url - 服务端WebSocket地址 - * @param autoReconnect - 是否启用自动重连 - * @returns Promise - */ - public async connect(url: string, autoReconnect: boolean = true): Promise { - if (this._isConnected || this._isConnecting) { - throw new Error('客户端已连接或正在连接中'); - } - - this._url = url; - this._autoReconnect = autoReconnect; - this._isConnecting = true; - this._reconnectAttempts = 0; - - return this.attemptConnection(); - } - - /** - * 尝试建立连接 - * - * @returns Promise - */ - private async attemptConnection(): Promise { - return new Promise((resolve, reject) => { - try { - const ws = new WebSocket(this._url); - - // 设置连接超时 - const connectTimeout = setTimeout(() => { - ws.close(); - this.handleConnectionFailed(new Error('连接超时'), resolve, reject); - }, 10000); - - ws.onopen = () => { - clearTimeout(connectTimeout); - this.handleConnectionSuccess(ws, resolve); - }; - - ws.onerror = (event) => { - clearTimeout(connectTimeout); - const error = new Error(`连接失败: ${event.toString()}`); - this.handleConnectionFailed(error, resolve, reject); - }; - - } catch (error) { - this.handleConnectionFailed(error as Error, resolve, reject); - } - }); - } - - /** - * 处理连接成功 - * - * @param ws - WebSocket连接 - * @param resolve - Promise resolve函数 - */ - private handleConnectionSuccess(ws: WebSocket, resolve: () => void): void { - this._connection = new NetworkConnection(ws, 'client', this._url); - this._isConnected = true; - this._isConnecting = false; - this._connectTime = Date.now(); - this._reconnectAttempts = 0; - - // 设置连接事件监听 - this._connection.on('connected', () => { - NetworkClient.logger.info(`连接成功: ${this._url}`); - this.emit('connected'); - - // 如果这是重连,触发重连成功事件 - if (this._reconnectAttempts > 0) { - this.emit('reconnected'); - } - }); - - this._connection.on('disconnected', (reason) => { - NetworkClient.logger.info(`连接断开: ${reason}`); - this.handleDisconnection(reason); - }); - - this._connection.on('message', async (data) => { - this.recordMessagePerformance(data, false); - this.emit('message', data); - - // 自动处理消息 - await this._messageHandler.handleRawMessage(data); - }); - - this._connection.on('error', (error) => { - NetworkClient.logger.error('连接错误:', error); - this.emit('error', error); - }); - - resolve(); - } - - /** - * 处理连接失败 - * - * @param error - 错误对象 - * @param resolve - Promise resolve函数 - * @param reject - Promise reject函数 - */ - private handleConnectionFailed(error: Error, resolve: () => void, reject: (error: Error) => void): void { - this._isConnecting = false; - - if (this._autoReconnect && this._reconnectAttempts < this._maxReconnectAttempts) { - this.scheduleReconnection(resolve, reject); - } else { - this._autoReconnect = false; - this.emit('error', error); - reject(error); - } - } - - /** - * 处理连接断开 - * - * @param reason - 断开原因 - */ - private handleDisconnection(reason?: string): void { - const wasConnected = this._isConnected; - - this._isConnected = false; - this._connection = null; - this._connectTime = 0; - - this.emit('disconnected', reason); - - // 如果启用自动重连且之前是连接状态 - if (this._autoReconnect && wasConnected && this._reconnectAttempts < this._maxReconnectAttempts) { - this.scheduleReconnection(); - } - } - - /** - * 安排重连 - * - * @param resolve - Promise resolve函数(可选) - * @param reject - Promise reject函数(可选) - */ - private scheduleReconnection(resolve?: () => void, reject?: (error: Error) => void): void { - this._reconnectAttempts++; - - // 计算重连延迟(指数退避) - const delay = Math.min( - this._reconnectDelay * Math.pow(2, this._reconnectAttempts - 1), - this._maxReconnectDelay - ); - - NetworkClient.logger.info(`${delay}ms后尝试重连 (${this._reconnectAttempts}/${this._maxReconnectAttempts})`); - this.emit('reconnecting', this._reconnectAttempts); - - this._reconnectTimer = setTimeout(async () => { - this._reconnectTimer = null; - this._isConnecting = true; - - try { - await this.attemptConnection(); - if (resolve) resolve(); - } catch (error) { - if (reject) reject(error as Error); - } - }, delay); - } - - /** - * 断开连接 - * - * @param reason - 断开原因 - */ - public async disconnect(reason: string = 'Disconnected by client'): Promise { - // 停止自动重连 - this._autoReconnect = false; - - // 清除重连定时器 - if (this._reconnectTimer) { - clearTimeout(this._reconnectTimer); - this._reconnectTimer = null; - } - - // 关闭连接 - if (this._connection) { - this._connection.close(reason); - } - - // 重置状态 - this._isConnected = false; - this._isConnecting = false; - this._connection = null; - this._connectTime = 0; - this._reconnectAttempts = 0; - } - - /** - * 发送消息 - * - * @param data - 要发送的数据 - * @returns 是否发送成功 - */ - public send(data: Uint8Array): boolean { - if (!this._connection || !this._isConnected) { - NetworkClient.logger.warn('未连接,无法发送数据'); - return false; - } - - const success = this._connection.send(data); - if (success) { - this.recordMessagePerformance(data, true); - } - return success; - } - - /** - * 手动触发重连 - * - * @returns Promise - */ - public async reconnect(): Promise { - if (this._isConnected) { - await this.disconnect('Manual reconnect'); - } - - this._autoReconnect = true; - this._reconnectAttempts = 0; - - return this.connect(this._url, this._autoReconnect); - } - - /** - * 设置自动重连配置 - * - * @param enabled - 是否启用自动重连 - * @param maxAttempts - 最大重连次数 - * @param initialDelay - 初始重连延迟(毫秒) - * @param maxDelay - 最大重连延迟(毫秒) - */ - public setReconnectConfig( - enabled: boolean = true, - maxAttempts: number = 5, - initialDelay: number = 1000, - maxDelay: number = 30000 - ): void { - this._autoReconnect = enabled; - this._maxReconnectAttempts = maxAttempts; - this._reconnectDelay = initialDelay; - this._maxReconnectDelay = maxDelay; - } - - /** - * 添加事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public on( - event: K, - handler: NetworkClientEvents[K] - ): void { - if (!this._eventHandlers.has(event)) { - this._eventHandlers.set(event, []); - } - this._eventHandlers.get(event)!.push(handler); - } - - /** - * 移除事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public off( - event: K, - handler: NetworkClientEvents[K] - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - const index = handlers.indexOf(handler); - if (index !== -1) { - handlers.splice(index, 1); - } - } - } - - /** - * 触发事件 - * - * @param event - 事件名称 - * @param args - 事件参数 - */ - private emit( - event: K, - ...args: Parameters - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - handlers.forEach(handler => { - try { - handler(...args); - } catch (error) { - NetworkClient.logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 检查是否已连接 - */ - public get isConnected(): boolean { - return this._isConnected; - } - - /** - * 检查是否正在连接 - */ - public get isConnecting(): boolean { - return this._isConnecting; - } - - /** - * 获取服务端URL - */ - public get url(): string { - return this._url; - } - - /** - * 获取连接时长(毫秒) - */ - public get connectedTime(): number { - return this._connectTime > 0 ? Date.now() - this._connectTime : 0; - } - - /** - * 获取重连次数 - */ - public get reconnectAttempts(): number { - return this._reconnectAttempts; - } - - /** - * 获取连接对象 - */ - public get connection(): NetworkConnection | null { - return this._connection; - } - - /** - * 获取客户端统计信息 - */ - public getStats(): { - isConnected: boolean; - isConnecting: boolean; - url: string; - connectedTime: number; - reconnectAttempts: number; - maxReconnectAttempts: number; - autoReconnect: boolean; - connectionStats?: ReturnType; - } { - return { - isConnected: this._isConnected, - isConnecting: this._isConnecting, - url: this._url, - connectedTime: this.connectedTime, - reconnectAttempts: this._reconnectAttempts, - maxReconnectAttempts: this._maxReconnectAttempts, - autoReconnect: this._autoReconnect, - connectionStats: this._connection ? this._connection.getStats() : undefined - }; - } - - /** - * 发送SyncVar更新消息到服务端 - * - * @param message - SyncVar更新消息 - * @returns 是否发送成功 - */ - public sendSyncVarMessage(message: SyncVarUpdateMessage): boolean { - try { - const serializedMessage = message.serialize(); - const success = this.send(serializedMessage); - - if (success) { - NetworkClient.logger.debug(`发送SyncVar消息: ${message.networkId}.${message.componentType}`); - } else { - NetworkClient.logger.warn(`SyncVar消息发送失败: ${message.networkId}.${message.componentType}`); - } - - return success; - } catch (error) { - NetworkClient.logger.error('发送SyncVar消息失赅:', error); - return false; - } - } - - /** - * 批量发送SyncVar更新消息 - * - * @param messages - SyncVar更新消息数组 - * @returns 成功发送的消息数量 - */ - public sendSyncVarMessages(messages: SyncVarUpdateMessage[]): number { - let successCount = 0; - - for (const message of messages) { - if (this.sendSyncVarMessage(message)) { - successCount++; - } - } - - NetworkClient.logger.debug(`批量发送SyncVar消息: ${successCount}/${messages.length} 成功`); - return successCount; - } - - /** - * 记录消息传输性能 - */ - private recordMessagePerformance(data: Uint8Array, sent: boolean): void { - const size = data.length; - if (sent) { - this._performanceMonitor.recordDataTransfer(size, 0); - this._performanceMonitor.recordMessageTransfer(1, 0); - } else { - this._performanceMonitor.recordDataTransfer(0, size); - this._performanceMonitor.recordMessageTransfer(0, 1); - } - this._performanceMonitor.updateActiveConnections(this._isConnected ? 1 : 0); - } - - /** - * 获取性能监控数据 - */ - public getPerformanceMetrics(): any { - return this._performanceMonitor.getCurrentMetrics(); - } - - /** - * 获取性能报告 - */ - public getPerformanceReport(timeRangeMs?: number): any { - return this._performanceMonitor.generateReport(timeRangeMs); - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkConnection.ts b/packages/network/src/Core/NetworkConnection.ts deleted file mode 100644 index f90af929..00000000 --- a/packages/network/src/Core/NetworkConnection.ts +++ /dev/null @@ -1,484 +0,0 @@ -import WebSocket from 'isomorphic-ws'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 网络连接状态 - */ -export enum ConnectionState { - Disconnected = 'disconnected', - Connecting = 'connecting', - Connected = 'connected', - Disconnecting = 'disconnecting' -} - -/** - * 网络连接事件 - */ -export interface NetworkConnectionEvents { - connected: () => void; - disconnected: (reason?: string, code?: number) => void; - message: (data: Uint8Array) => void; - error: (error: Error) => void; - reconnecting: (attempt: number) => void; - reconnected: () => void; - reconnectFailed: (error: Error) => void; -} - -/** - * 网络连接抽象类 - * - * 封装WebSocket连接,提供统一的连接管理接口 - * 支持二进制消息传输,集成心跳检测 - */ -export class NetworkConnection { - private static readonly logger = createLogger('NetworkConnection'); - private _ws: WebSocket | null = null; - private _state: ConnectionState = ConnectionState.Disconnected; - private _connectionId: string = ''; - private _address: string = ''; - private _connectedTime: number = 0; - private _lastPingTime: number = 0; - private _pingInterval: NodeJS.Timeout | null = null; - private _eventHandlers: Map = new Map(); - - // 重连相关属性 - private _reconnectAttempts: number = 0; - private _maxReconnectAttempts: number = 5; - private _reconnectInterval: number = 1000; // 1秒 - private _maxReconnectInterval: number = 30000; // 30秒 - private _reconnectTimer: NodeJS.Timeout | null = null; - private _enableAutoReconnect: boolean = true; - private _originalUrl: string = ''; - - // 心跳配置 - private static readonly PING_INTERVAL = 30000; // 30秒 - private static readonly PING_TIMEOUT = 5000; // 5秒超时 - - // 错误分类 - private static readonly RECOVERABLE_CODES = [1006, 1011, 1012, 1013, 1014]; - - /** - * 构造函数 - * - * @param ws - WebSocket实例 - * @param connectionId - 连接ID - * @param address - 连接地址 - * @param options - 连接选项 - */ - constructor( - ws: WebSocket, - connectionId: string, - address: string = '', - options?: { - enableAutoReconnect?: boolean; - maxReconnectAttempts?: number; - reconnectInterval?: number; - maxReconnectInterval?: number; - } - ) { - this._ws = ws; - this._connectionId = connectionId; - this._address = address; - this._originalUrl = address; - this._connectedTime = Date.now(); - - // 设置重连选项 - if (options) { - this._enableAutoReconnect = options.enableAutoReconnect ?? true; - this._maxReconnectAttempts = options.maxReconnectAttempts ?? 5; - this._reconnectInterval = options.reconnectInterval ?? 1000; - this._maxReconnectInterval = options.maxReconnectInterval ?? 30000; - } - - this.setupWebSocket(); - this.startPingInterval(); - } - - /** - * 设置WebSocket事件监听 - */ - private setupWebSocket(): void { - if (!this._ws) return; - - this._ws.onopen = () => { - this._state = ConnectionState.Connected; - - // 重连成功,重置重连状态 - if (this._reconnectAttempts > 0) { - NetworkConnection.logger.info(`重连成功 (第 ${this._reconnectAttempts} 次尝试)`); - this.resetReconnectState(); - this.emit('reconnected'); - } - - this.emit('connected'); - }; - - this._ws.onclose = (event) => { - this._state = ConnectionState.Disconnected; - this.stopPingInterval(); - - const reason = event.reason || '连接已关闭'; - const code = event.code; - - NetworkConnection.logger.warn(`连接断开: ${reason} (code: ${code})`); - this.emit('disconnected', reason, code); - - // 判断是否需要自动重连 - if (this._enableAutoReconnect && this.shouldReconnect(code)) { - this.scheduleReconnect(); - } - }; - - this._ws.onerror = (event) => { - const error = new Error(`WebSocket error: ${event.toString()}`); - this.emit('error', error); - }; - - this._ws.onmessage = (event) => { - try { - let data: Uint8Array; - - if (event.data instanceof ArrayBuffer) { - data = new Uint8Array(event.data); - } else if (event.data instanceof Uint8Array) { - data = event.data; - } else if (typeof event.data === 'string') { - // 处理字符串消息(如心跳) - if (event.data === 'pong') { - this._lastPingTime = Date.now(); - return; - } - // 将字符串转换为Uint8Array - data = new TextEncoder().encode(event.data); - } else { - NetworkConnection.logger.warn(' 收到未知类型的消息:', typeof event.data); - return; - } - - this.emit('message', data); - } catch (error) { - NetworkConnection.logger.error(' 消息处理错误:', error); - } - }; - } - - /** - * 启动心跳检测 - */ - private startPingInterval(): void { - this._pingInterval = setInterval(() => { - if (this._state === ConnectionState.Connected) { - this.ping(); - } - }, NetworkConnection.PING_INTERVAL); - } - - /** - * 停止心跳检测 - */ - private stopPingInterval(): void { - if (this._pingInterval) { - clearInterval(this._pingInterval); - this._pingInterval = null; - } - } - - /** - * 发送心跳包 - */ - private ping(): void { - if (this._ws && this._state === ConnectionState.Connected) { - try { - this._ws.send('ping'); - } catch (error) { - NetworkConnection.logger.error(' 心跳发送失败:', error); - } - } - } - - /** - * 发送二进制数据 - * - * @param data - 要发送的数据 - * @returns 是否发送成功 - */ - public send(data: Uint8Array): boolean { - if (!this._ws || this._state !== ConnectionState.Connected) { - NetworkConnection.logger.warn(' 连接未就绪,无法发送数据'); - return false; - } - - try { - this._ws.send(data); - return true; - } catch (error) { - NetworkConnection.logger.error(' 数据发送失败:', error); - return false; - } - } - - /** - * 判断是否应该重连 - */ - private shouldReconnect(code: number): boolean { - // 正常关闭不需要重连 - if (code === 1000) { - return false; - } - - // 检查是否是可恢复的错误代码 - return NetworkConnection.RECOVERABLE_CODES.includes(code) || code === 1006; - } - - /** - * 安排重连 - */ - private scheduleReconnect(): void { - if (this._reconnectAttempts >= this._maxReconnectAttempts) { - NetworkConnection.logger.error(`重连失败,已达到最大重试次数 ${this._maxReconnectAttempts}`); - this.emit('reconnectFailed', new Error(`重连失败,已达到最大重试次数`)); - return; - } - - this._reconnectAttempts++; - const delay = Math.min( - this._reconnectInterval * Math.pow(2, this._reconnectAttempts - 1), - this._maxReconnectInterval - ); - - NetworkConnection.logger.info(`将在 ${delay}ms 后尝试第 ${this._reconnectAttempts} 次重连`); - this.emit('reconnecting', this._reconnectAttempts); - - this._reconnectTimer = setTimeout(() => { - this.attemptReconnect(); - }, delay); - } - - /** - * 尝试重连 - */ - private attemptReconnect(): void { - if (this._state === ConnectionState.Connected) { - return; - } - - try { - NetworkConnection.logger.info(`正在尝试重连到 ${this._originalUrl}`); - - // 创建新的WebSocket连接 - this._ws = new WebSocket(this._originalUrl); - this._state = ConnectionState.Connecting; - - // 重新设置事件处理 - this.setupWebSocket(); - - } catch (error) { - NetworkConnection.logger.error(`重连失败:`, error); - - // 继续下一次重连尝试 - setTimeout(() => { - this.scheduleReconnect(); - }, 1000); - } - } - - /** - * 重置重连状态 - */ - private resetReconnectState(): void { - this._reconnectAttempts = 0; - - if (this._reconnectTimer) { - clearTimeout(this._reconnectTimer); - this._reconnectTimer = null; - } - } - - /** - * 设置重连配置 - */ - public setReconnectOptions(options: { - enableAutoReconnect?: boolean; - maxReconnectAttempts?: number; - reconnectInterval?: number; - maxReconnectInterval?: number; - }): void { - if (options.enableAutoReconnect !== undefined) { - this._enableAutoReconnect = options.enableAutoReconnect; - } - if (options.maxReconnectAttempts !== undefined) { - this._maxReconnectAttempts = options.maxReconnectAttempts; - } - if (options.reconnectInterval !== undefined) { - this._reconnectInterval = options.reconnectInterval; - } - if (options.maxReconnectInterval !== undefined) { - this._maxReconnectInterval = options.maxReconnectInterval; - } - } - - /** - * 手动重连 - */ - public reconnect(): void { - if (this._state === ConnectionState.Connected) { - NetworkConnection.logger.warn('连接已存在,无需重连'); - return; - } - - this.resetReconnectState(); - this.attemptReconnect(); - } - - /** - * 关闭连接 - * - * @param reason - 关闭原因 - */ - public close(reason: string = 'Connection closed by local'): void { - if (this._state === ConnectionState.Disconnected) { - return; - } - - this._state = ConnectionState.Disconnecting; - - // 主动关闭时禁用自动重连 - this._enableAutoReconnect = false; - this.resetReconnectState(); - - this.stopPingInterval(); - - if (this._ws) { - try { - this._ws.close(1000, reason); - } catch (error) { - NetworkConnection.logger.error(' 连接关闭失败:', error); - } - this._ws = null; - } - } - - /** - * 添加事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public on( - event: K, - handler: NetworkConnectionEvents[K] - ): void { - if (!this._eventHandlers.has(event)) { - this._eventHandlers.set(event, []); - } - this._eventHandlers.get(event)!.push(handler); - } - - /** - * 移除事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public off( - event: K, - handler: NetworkConnectionEvents[K] - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - const index = handlers.indexOf(handler); - if (index !== -1) { - handlers.splice(index, 1); - } - } - } - - /** - * 触发事件 - * - * @param event - 事件名称 - * @param args - 事件参数 - */ - private emit( - event: K, - ...args: Parameters - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - handlers.forEach(handler => { - try { - handler(...args); - } catch (error) { - NetworkConnection.logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 获取连接ID - */ - public get connectionId(): string { - return this._connectionId; - } - - /** - * 获取连接地址 - */ - public get address(): string { - return this._address; - } - - /** - * 获取连接状态 - */ - public get state(): ConnectionState { - return this._state; - } - - /** - * 检查是否已连接 - */ - public get isConnected(): boolean { - return this._state === ConnectionState.Connected; - } - - /** - * 获取连接时长(毫秒) - */ - public get connectedTime(): number { - return this._connectedTime > 0 ? Date.now() - this._connectedTime : 0; - } - - /** - * 获取最后一次心跳时间 - */ - public get lastPingTime(): number { - return this._lastPingTime; - } - - /** - * 获取连接统计信息 - */ - public getStats(): { - connectionId: string; - address: string; - state: ConnectionState; - connectedTime: number; - lastPingTime: number; - isAlive: boolean; - } { - const now = Date.now(); - const isAlive = this._state === ConnectionState.Connected && - (this._lastPingTime === 0 || (now - this._lastPingTime) < NetworkConnection.PING_TIMEOUT * 2); - - return { - connectionId: this._connectionId, - address: this._address, - state: this._state, - connectedTime: this.connectedTime, - lastPingTime: this._lastPingTime, - isAlive - }; - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkEnvironment.ts b/packages/network/src/Core/NetworkEnvironment.ts deleted file mode 100644 index 001ddd7c..00000000 --- a/packages/network/src/Core/NetworkEnvironment.ts +++ /dev/null @@ -1,248 +0,0 @@ -import { NetworkRole } from '../NetworkRole'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 网络环境状态 - */ -export enum NetworkEnvironmentState { - /** 未初始化状态 */ - None = 'none', - /** 服务端模式 */ - Server = 'server', - /** 客户端模式 */ - Client = 'client', - /** 混合模式(既是服务端又是客户端,用于特殊场景) */ - Hybrid = 'hybrid' -} - -/** - * 网络环境管理器 - * - * 全局管理当前网络环境状态,让NetworkComponent能够自动检测角色 - * 避免在构造函数中传递角色参数,保持与核心ECS框架的兼容性 - */ -export class NetworkEnvironment { - private static readonly logger = createLogger('NetworkEnvironment'); - private static _instance: NetworkEnvironment | null = null; - private _state: NetworkEnvironmentState = NetworkEnvironmentState.None; - private _serverStartTime: number = 0; - private _clientConnectTime: number = 0; - - /** - * 获取NetworkEnvironment单例实例 - */ - public static get Instance(): NetworkEnvironment { - if (!NetworkEnvironment._instance) { - NetworkEnvironment._instance = new NetworkEnvironment(); - } - return NetworkEnvironment._instance; - } - - private constructor() {} - - /** - * 设置为服务端模式 - */ - public static SetServerMode(): void { - const instance = NetworkEnvironment.Instance; - - if (instance._state === NetworkEnvironmentState.Client) { - // 如果已经是客户端,则变为混合模式 - instance._state = NetworkEnvironmentState.Hybrid; - } else { - instance._state = NetworkEnvironmentState.Server; - } - - instance._serverStartTime = Date.now(); - NetworkEnvironment.logger.info(`环境设置为: ${instance._state}`); - } - - /** - * 设置为客户端模式 - */ - public static SetClientMode(): void { - const instance = NetworkEnvironment.Instance; - - if (instance._state === NetworkEnvironmentState.Server) { - // 如果已经是服务端,则变为混合模式 - instance._state = NetworkEnvironmentState.Hybrid; - } else { - instance._state = NetworkEnvironmentState.Client; - } - - instance._clientConnectTime = Date.now(); - NetworkEnvironment.logger.info(`环境设置为: ${instance._state}`); - } - - /** - * 清除服务端模式 - */ - public static ClearServerMode(): void { - const instance = NetworkEnvironment.Instance; - - if (instance._state === NetworkEnvironmentState.Server) { - instance._state = NetworkEnvironmentState.None; - } else if (instance._state === NetworkEnvironmentState.Hybrid) { - instance._state = NetworkEnvironmentState.Client; - } - - instance._serverStartTime = 0; - NetworkEnvironment.logger.info(`服务端模式已清除,当前状态: ${instance._state}`); - } - - /** - * 清除客户端模式 - */ - public static ClearClientMode(): void { - const instance = NetworkEnvironment.Instance; - - if (instance._state === NetworkEnvironmentState.Client) { - instance._state = NetworkEnvironmentState.None; - } else if (instance._state === NetworkEnvironmentState.Hybrid) { - instance._state = NetworkEnvironmentState.Server; - } - - instance._clientConnectTime = 0; - NetworkEnvironment.logger.info(`客户端模式已清除,当前状态: ${instance._state}`); - } - - /** - * 重置环境状态 - */ - public static Reset(): void { - const instance = NetworkEnvironment.Instance; - instance._state = NetworkEnvironmentState.None; - instance._serverStartTime = 0; - instance._clientConnectTime = 0; - NetworkEnvironment.logger.info('环境状态已重置'); - } - - /** - * 检查是否为服务端环境 - */ - public static get isServer(): boolean { - const instance = NetworkEnvironment.Instance; - return instance._state === NetworkEnvironmentState.Server || - instance._state === NetworkEnvironmentState.Hybrid; - } - - /** - * 检查是否为客户端环境 - */ - public static get isClient(): boolean { - const instance = NetworkEnvironment.Instance; - return instance._state === NetworkEnvironmentState.Client || - instance._state === NetworkEnvironmentState.Hybrid; - } - - /** - * 检查是否为混合环境 - */ - public static get isHybrid(): boolean { - const instance = NetworkEnvironment.Instance; - return instance._state === NetworkEnvironmentState.Hybrid; - } - - /** - * 获取当前环境状态 - */ - public static get state(): NetworkEnvironmentState { - return NetworkEnvironment.Instance._state; - } - - /** - * 获取主要角色(用于NetworkComponent) - * - * 在混合模式下,优先返回服务端角色 - * @returns 当前主要网络角色 - */ - public static getPrimaryRole(): NetworkRole { - const instance = NetworkEnvironment.Instance; - - switch (instance._state) { - case NetworkEnvironmentState.Server: - case NetworkEnvironmentState.Hybrid: - return NetworkRole.SERVER; - case NetworkEnvironmentState.Client: - return NetworkRole.CLIENT; - case NetworkEnvironmentState.None: - default: - // 默认返回客户端角色,避免抛出异常 - return NetworkRole.CLIENT; - } - } - - /** - * 检查环境是否已初始化 - */ - public static get isInitialized(): boolean { - return NetworkEnvironment.Instance._state !== NetworkEnvironmentState.None; - } - - /** - * 获取服务端运行时间(毫秒) - */ - public static get serverUptime(): number { - const instance = NetworkEnvironment.Instance; - return instance._serverStartTime > 0 ? Date.now() - instance._serverStartTime : 0; - } - - /** - * 获取客户端连接时间(毫秒) - */ - public static get clientConnectTime(): number { - const instance = NetworkEnvironment.Instance; - return instance._clientConnectTime > 0 ? Date.now() - instance._clientConnectTime : 0; - } - - /** - * 获取环境统计信息 - */ - public static getStats(): { - state: NetworkEnvironmentState; - isServer: boolean; - isClient: boolean; - isHybrid: boolean; - isInitialized: boolean; - primaryRole: NetworkRole; - serverUptime: number; - clientConnectTime: number; - } { - return { - state: NetworkEnvironment.state, - isServer: NetworkEnvironment.isServer, - isClient: NetworkEnvironment.isClient, - isHybrid: NetworkEnvironment.isHybrid, - isInitialized: NetworkEnvironment.isInitialized, - primaryRole: NetworkEnvironment.getPrimaryRole(), - serverUptime: NetworkEnvironment.serverUptime, - clientConnectTime: NetworkEnvironment.clientConnectTime - }; - } - - /** - * 强制设置环境状态(用于测试) - * - * @param state - 要设置的环境状态 - * @param serverStartTime - 服务端启动时间(可选) - * @param clientConnectTime - 客户端连接时间(可选) - */ - public static forceSetState( - state: NetworkEnvironmentState, - serverStartTime?: number, - clientConnectTime?: number - ): void { - const instance = NetworkEnvironment.Instance; - instance._state = state; - - if (serverStartTime !== undefined) { - instance._serverStartTime = serverStartTime; - } - - if (clientConnectTime !== undefined) { - instance._clientConnectTime = clientConnectTime; - } - - NetworkEnvironment.logger.debug(`强制设置环境状态为: ${state}`); - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkIdentity.ts b/packages/network/src/Core/NetworkIdentity.ts deleted file mode 100644 index 14b766f3..00000000 --- a/packages/network/src/Core/NetworkIdentity.ts +++ /dev/null @@ -1,481 +0,0 @@ -import { Component, createLogger } from '@esengine/ecs-framework'; -import { v4 as uuidv4 } from 'uuid'; - -/** - * 网络对象身份组件 - * - * 为ECS实体提供网络身份标识,支持网络对象的唯一识别和管理 - * 每个需要网络同步的实体都必须拥有此组件 - */ -export class NetworkIdentity extends Component { - private static readonly logger = createLogger('NetworkIdentity'); - - /** - * 网络对象唯一ID - */ - public readonly networkId: string; - - /** - * 是否为服务端权威对象 - */ - public hasAuthority: boolean = false; - - /** - * 对象拥有者的连接ID(客户端ID) - */ - public ownerId: string = ''; - - /** - * 网络对象是否处于激活状态 - */ - public isNetworkActive: boolean = false; - - /** - * 对象创建时间 - */ - public readonly createdAt: number; - - /** - * 最后同步时间 - */ - public lastSyncTime: number = 0; - - /** - * 同步序列号(用于确保消息顺序) - */ - public syncSequence: number = 0; - - /** - * 网络对象场景标识(用于场景管理) - */ - public sceneId: string = ''; - - /** - * 是否为预制体实例 - */ - public isPrefabInstance: boolean = false; - - /** - * 预制体名称(如果是预制体实例) - */ - public prefabName: string = ''; - - /** - * 构造函数 - * - * @param networkId - 指定网络ID,如果不提供则自动生成 - * @param hasAuthority - 是否有权威,默认false - */ - constructor(networkId?: string, hasAuthority: boolean = false) { - super(); - this.networkId = networkId || uuidv4(); - this.hasAuthority = hasAuthority; - this.createdAt = Date.now(); - this.lastSyncTime = this.createdAt; - - // 自动注册到NetworkIdentityRegistry - NetworkIdentityRegistry.Instance.register(this); - - NetworkIdentity.logger.debug(` 创建网络对象: ${this.networkId}, 权威: ${hasAuthority}`); - } - - /** - * 设置对象拥有者 - * - * @param ownerId - 拥有者连接ID - */ - public setOwner(ownerId: string): void { - const oldOwnerId = this.ownerId; - this.ownerId = ownerId; - - NetworkIdentity.logger.debug(` 对象 ${this.networkId} 拥有者变更: ${oldOwnerId} -> ${ownerId}`); - } - - /** - * 设置权威状态 - * - * @param hasAuthority - 是否有权威 - */ - public setAuthority(hasAuthority: boolean): void { - if (this.hasAuthority !== hasAuthority) { - this.hasAuthority = hasAuthority; - NetworkIdentity.logger.debug(` 对象 ${this.networkId} 权威状态变更: ${hasAuthority}`); - } - } - - /** - * 激活网络对象 - */ - public activate(): void { - if (!this.isNetworkActive) { - this.isNetworkActive = true; - NetworkIdentity.logger.debug(` 激活网络对象: ${this.networkId}`); - } - } - - /** - * 停用网络对象 - */ - public deactivate(): void { - if (this.isNetworkActive) { - this.isNetworkActive = false; - NetworkIdentity.logger.debug(` 停用网络对象: ${this.networkId}`); - } - } - - /** - * 获取下一个同步序列号 - */ - public getNextSyncSequence(): number { - return ++this.syncSequence; - } - - /** - * 更新最后同步时间 - */ - public updateSyncTime(): void { - this.lastSyncTime = Date.now(); - } - - /** - * 检查是否需要同步 - * - * @param maxInterval - 最大同步间隔(毫秒) - */ - public needsSync(maxInterval: number = 1000): boolean { - return Date.now() - this.lastSyncTime > maxInterval; - } - - /** - * 获取网络对象统计信息 - */ - public getStats(): { - networkId: string; - hasAuthority: boolean; - ownerId: string; - isActive: boolean; - createdAt: number; - lastSyncTime: number; - syncSequence: number; - age: number; - timeSinceLastSync: number; - } { - const now = Date.now(); - return { - networkId: this.networkId, - hasAuthority: this.hasAuthority, - ownerId: this.ownerId, - isActive: this.isNetworkActive, - createdAt: this.createdAt, - lastSyncTime: this.lastSyncTime, - syncSequence: this.syncSequence, - age: now - this.createdAt, - timeSinceLastSync: now - this.lastSyncTime - }; - } - - /** - * 清理网络对象 - */ - public cleanup(): void { - NetworkIdentityRegistry.Instance.unregister(this.networkId); - this.deactivate(); - NetworkIdentity.logger.debug(` 清理网络对象: ${this.networkId}`); - } -} - -/** - * 网络身份注册表 - * - * 管理所有网络对象的注册和查找 - */ -export class NetworkIdentityRegistry { - private static readonly logger = createLogger('NetworkIdentityRegistry'); - private static _instance: NetworkIdentityRegistry | null = null; - - /** - * 网络对象注册表 - * Key: networkId, Value: NetworkIdentity实例 - */ - private _identities: Map = new Map(); - - /** - * 按拥有者分组的对象 - * Key: ownerId, Value: NetworkIdentity集合 - */ - private _ownerObjects: Map> = new Map(); - - /** - * 权威对象集合 - */ - private _authorityObjects: Set = new Set(); - - /** - * 获取注册表单例 - */ - public static get Instance(): NetworkIdentityRegistry { - if (!NetworkIdentityRegistry._instance) { - NetworkIdentityRegistry._instance = new NetworkIdentityRegistry(); - } - return NetworkIdentityRegistry._instance; - } - - private constructor() {} - - /** - * 注册网络对象 - * - * @param identity - 网络身份组件 - */ - public register(identity: NetworkIdentity): void { - if (this._identities.has(identity.networkId)) { - NetworkIdentityRegistry.logger.warn(` 网络对象ID重复: ${identity.networkId}`); - return; - } - - this._identities.set(identity.networkId, identity); - - // 按拥有者分组 - if (identity.ownerId) { - this.addToOwnerGroup(identity); - } - - // 权威对象管理 - if (identity.hasAuthority) { - this._authorityObjects.add(identity); - } - - NetworkIdentityRegistry.logger.debug(` 注册网络对象: ${identity.networkId}`); - } - - /** - * 注销网络对象 - * - * @param networkId - 网络对象ID - */ - public unregister(networkId: string): boolean { - const identity = this._identities.get(networkId); - if (!identity) { - return false; - } - - this._identities.delete(networkId); - - // 从拥有者分组中移除 - if (identity.ownerId) { - this.removeFromOwnerGroup(identity); - } - - // 从权威对象集合中移除 - this._authorityObjects.delete(identity); - - NetworkIdentityRegistry.logger.debug(` 注销网络对象: ${networkId}`); - return true; - } - - /** - * 通过网络ID查找对象 - * - * @param networkId - 网络对象ID - * @returns 网络身份组件或undefined - */ - public find(networkId: string): NetworkIdentity | undefined { - return this._identities.get(networkId); - } - - /** - * 获取指定拥有者的所有对象 - * - * @param ownerId - 拥有者ID - * @returns 网络身份组件数组 - */ - public getObjectsByOwner(ownerId: string): NetworkIdentity[] { - const ownerObjects = this._ownerObjects.get(ownerId); - return ownerObjects ? Array.from(ownerObjects) : []; - } - - /** - * 获取所有权威对象 - * - * @returns 权威对象数组 - */ - public getAuthorityObjects(): NetworkIdentity[] { - return Array.from(this._authorityObjects); - } - - /** - * 获取所有激活的网络对象 - * - * @returns 激活的网络对象数组 - */ - public getActiveObjects(): NetworkIdentity[] { - return Array.from(this._identities.values()).filter(identity => identity.isNetworkActive); - } - - /** - * 获取需要同步的对象 - * - * @param maxInterval - 最大同步间隔 - * @returns 需要同步的对象数组 - */ - public getObjectsNeedingSync(maxInterval: number = 1000): NetworkIdentity[] { - return this.getActiveObjects().filter(identity => identity.needsSync(maxInterval)); - } - - /** - * 更新对象拥有者 - * - * @param networkId - 网络对象ID - * @param newOwnerId - 新拥有者ID - */ - public updateObjectOwner(networkId: string, newOwnerId: string): boolean { - const identity = this._identities.get(networkId); - if (!identity) { - return false; - } - - // 从旧拥有者分组中移除 - if (identity.ownerId) { - this.removeFromOwnerGroup(identity); - } - - // 设置新拥有者 - identity.setOwner(newOwnerId); - - // 添加到新拥有者分组 - if (newOwnerId) { - this.addToOwnerGroup(identity); - } - - return true; - } - - /** - * 更新对象权威状态 - * - * @param networkId - 网络对象ID - * @param hasAuthority - 是否有权威 - */ - public updateObjectAuthority(networkId: string, hasAuthority: boolean): boolean { - const identity = this._identities.get(networkId); - if (!identity) { - return false; - } - - const oldAuthority = identity.hasAuthority; - identity.setAuthority(hasAuthority); - - // 更新权威对象集合 - if (hasAuthority && !oldAuthority) { - this._authorityObjects.add(identity); - } else if (!hasAuthority && oldAuthority) { - this._authorityObjects.delete(identity); - } - - return true; - } - - /** - * 清理断开连接客户端的对象 - * - * @param disconnectedOwnerId - 断开连接的客户端ID - */ - public cleanupDisconnectedOwner(disconnectedOwnerId: string): NetworkIdentity[] { - const ownerObjects = this.getObjectsByOwner(disconnectedOwnerId); - - for (const identity of ownerObjects) { - // 移除拥有权,但保留对象(由服务端接管) - this.updateObjectOwner(identity.networkId, ''); - identity.setAuthority(false); - } - - NetworkIdentityRegistry.logger.debug(` 清理断开连接客户端 ${disconnectedOwnerId} 的 ${ownerObjects.length} 个对象`); - return ownerObjects; - } - - /** - * 添加到拥有者分组 - */ - private addToOwnerGroup(identity: NetworkIdentity): void { - if (!identity.ownerId) return; - - let ownerSet = this._ownerObjects.get(identity.ownerId); - if (!ownerSet) { - ownerSet = new Set(); - this._ownerObjects.set(identity.ownerId, ownerSet); - } - ownerSet.add(identity); - } - - /** - * 从拥有者分组中移除 - */ - private removeFromOwnerGroup(identity: NetworkIdentity): void { - if (!identity.ownerId) return; - - const ownerSet = this._ownerObjects.get(identity.ownerId); - if (ownerSet) { - ownerSet.delete(identity); - if (ownerSet.size === 0) { - this._ownerObjects.delete(identity.ownerId); - } - } - } - - /** - * 获取注册表统计信息 - */ - public getStats(): { - totalObjects: number; - activeObjects: number; - authorityObjects: number; - ownerCount: number; - averageAge: number; - oldestObject?: string; - newestObject?: string; - } { - const all = Array.from(this._identities.values()); - const active = all.filter(i => i.isNetworkActive); - - let totalAge = 0; - let oldestTime = Date.now(); - let newestTime = 0; - let oldestId = ''; - let newestId = ''; - - for (const identity of all) { - const age = Date.now() - identity.createdAt; - totalAge += age; - - if (identity.createdAt < oldestTime) { - oldestTime = identity.createdAt; - oldestId = identity.networkId; - } - - if (identity.createdAt > newestTime) { - newestTime = identity.createdAt; - newestId = identity.networkId; - } - } - - return { - totalObjects: all.length, - activeObjects: active.length, - authorityObjects: this._authorityObjects.size, - ownerCount: this._ownerObjects.size, - averageAge: all.length > 0 ? totalAge / all.length : 0, - oldestObject: oldestId || undefined, - newestObject: newestId || undefined - }; - } - - /** - * 清空注册表 - */ - public clear(): void { - this._identities.clear(); - this._ownerObjects.clear(); - this._authorityObjects.clear(); - NetworkIdentityRegistry.logger.info(' 已清空注册表'); - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkManager.ts b/packages/network/src/Core/NetworkManager.ts deleted file mode 100644 index e7929d9a..00000000 --- a/packages/network/src/Core/NetworkManager.ts +++ /dev/null @@ -1,207 +0,0 @@ -import { NetworkServer } from './NetworkServer'; -import { NetworkClient } from './NetworkClient'; -import { NetworkEnvironment } from './NetworkEnvironment'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 网络管理器 - 网络框架的核心入口 - * - * 负责管理整个网络生命周期 - * 支持启动服务端、客户端,管理连接状态 - */ -export class NetworkManager { - private static readonly _logger = createLogger('NetworkManager'); - private static _instance: NetworkManager | null = null; - private _server: NetworkServer | null = null; - private _client: NetworkClient | null = null; - private _isServer: boolean = false; - private _isClient: boolean = false; - - /** - * 获取NetworkManager单例实例 - */ - public static get Instance(): NetworkManager { - if (!NetworkManager._instance) { - NetworkManager._instance = new NetworkManager(); - } - return NetworkManager._instance; - } - - private constructor() {} - - /** - * 启动服务端 - * - * @param port - 监听端口 - * @param host - 监听地址,默认为 '0.0.0.0' - * @returns Promise 启动是否成功 - */ - public static async StartServer(port: number, host: string = '0.0.0.0'): Promise { - const instance = NetworkManager.Instance; - - if (instance._isServer) { - NetworkManager._logger.warn('服务端已经在运行'); - return false; - } - - try { - instance._server = new NetworkServer(); - await instance._server.start(port, host); - instance._isServer = true; - - // 自动设置网络环境为服务端模式 - NetworkEnvironment.SetServerMode(); - - NetworkManager._logger.info(`服务端启动成功,监听 ${host}:${port}`); - return true; - } catch (error) { - NetworkManager._logger.error('服务端启动失败:', error); - instance._server = null; - return false; - } - } - - /** - * 启动客户端 - * - * @param url - 服务端WebSocket地址 - * @returns Promise 连接是否成功 - */ - public static async StartClient(url: string): Promise { - const instance = NetworkManager.Instance; - - if (instance._isClient) { - NetworkManager._logger.warn('客户端已经在运行'); - return false; - } - - try { - instance._client = new NetworkClient(); - await instance._client.connect(url); - instance._isClient = true; - - // 自动设置网络环境为客户端模式 - NetworkEnvironment.SetClientMode(); - - NetworkManager._logger.info(`客户端连接成功: ${url}`); - return true; - } catch (error) { - NetworkManager._logger.error('客户端连接失败:', error); - instance._client = null; - return false; - } - } - - /** - * 停止服务端 - */ - public static async StopServer(): Promise { - const instance = NetworkManager.Instance; - - if (instance._server && instance._isServer) { - await instance._server.stop(); - instance._server = null; - instance._isServer = false; - - // 清除服务端环境模式 - NetworkEnvironment.ClearServerMode(); - - NetworkManager._logger.info('服务端已停止'); - } - } - - /** - * 断开客户端连接 - */ - public static async StopClient(): Promise { - const instance = NetworkManager.Instance; - - if (instance._client && instance._isClient) { - await instance._client.disconnect(); - instance._client = null; - instance._isClient = false; - - // 清除客户端环境模式 - NetworkEnvironment.ClearClientMode(); - - NetworkManager._logger.info('客户端已断开连接'); - } - } - - /** - * 完全停止网络管理器 - */ - public static async Stop(): Promise { - await NetworkManager.StopServer(); - await NetworkManager.StopClient(); - - // 重置网络环境 - NetworkEnvironment.Reset(); - - NetworkManager._instance = null; - } - - /** - * 检查是否为服务端 - */ - public static get isServer(): boolean { - return NetworkManager.Instance._isServer; - } - - /** - * 检查是否为客户端 - */ - public static get isClient(): boolean { - return NetworkManager.Instance._isClient; - } - - /** - * 获取服务端实例 - */ - public static get server(): NetworkServer | null { - return NetworkManager.Instance._server; - } - - /** - * 获取服务端实例 (方法形式,用于动态调用) - */ - public static GetServer(): NetworkServer | null { - return NetworkManager.Instance._server; - } - - /** - * 获取客户端实例 - */ - public static get client(): NetworkClient | null { - return NetworkManager.Instance._client; - } - - /** - * 获取当前连接数(仅服务端有效) - */ - public static get connectionCount(): number { - const instance = NetworkManager.Instance; - return instance._server ? instance._server.connectionCount : 0; - } - - /** - * 获取网络统计信息 - */ - public static getNetworkStats(): { - isServer: boolean; - isClient: boolean; - connectionCount: number; - serverUptime?: number; - clientConnectedTime?: number; - } { - const instance = NetworkManager.Instance; - - return { - isServer: instance._isServer, - isClient: instance._isClient, - connectionCount: instance._server ? instance._server.connectionCount : 0, - serverUptime: instance._server ? instance._server.uptime : undefined, - clientConnectedTime: instance._client ? instance._client.connectedTime : undefined - }; - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkPerformanceMonitor.ts b/packages/network/src/Core/NetworkPerformanceMonitor.ts deleted file mode 100644 index 91f7b167..00000000 --- a/packages/network/src/Core/NetworkPerformanceMonitor.ts +++ /dev/null @@ -1,858 +0,0 @@ -/** - * 网络性能监控器 - * - * 监控网络连接的性能指标,包括延迟、吞吐量、包丢失率等 - */ -import { createLogger } from '@esengine/ecs-framework'; -import { HeartbeatManager, HeartbeatStats } from './HeartbeatManager'; -import { NetworkConnection } from './NetworkConnection'; - -export interface NetworkMetrics { - /** 往返时延 (ms) */ - rtt: number; - /** 延迟 (ms) */ - latency: number; - /** 上行带宽 (bytes/s) */ - uploadBandwidth: number; - /** 下行带宽 (bytes/s) */ - downloadBandwidth: number; - /** 包丢失率 (0-1) */ - packetLoss: number; - /** 抖动 (ms) */ - jitter: number; - /** 连接质量评分 (0-100) */ - connectionQuality: number; -} - -export interface PerformanceSnapshot { - /** 时间戳 */ - timestamp: number; - /** 网络指标 */ - metrics: NetworkMetrics; - /** 连接统计 */ - connectionStats: { - /** 发送的总字节数 */ - bytesSent: number; - /** 接收的总字节数 */ - bytesReceived: number; - /** 发送的总消息数 */ - messagesSent: number; - /** 接收的总消息数 */ - messagesReceived: number; - /** 活跃连接数 */ - activeConnections: number; - }; - /** SyncVar同步统计 */ - syncVarStats?: { - /** 同步的组件数 */ - syncedComponents: number; - /** 同步的字段数 */ - syncedFields: number; - /** 平均同步频率 (Hz) */ - averageSyncRate: number; - /** 同步数据大小 (bytes) */ - syncDataSize: number; - }; -} - -/** - * 网络性能监控器 - */ -export class NetworkPerformanceMonitor { - private static readonly logger = createLogger('NetworkPerformanceMonitor'); - private static _instance: NetworkPerformanceMonitor | null = null; - - /** 性能快照历史 */ - private _snapshots: PerformanceSnapshot[] = []; - - /** 最大历史记录数量 */ - private _maxSnapshots: number = 100; - - /** 监控间隔 (ms) */ - private _monitoringInterval: number = 1000; - - /** 监控定时器 */ - private _monitoringTimer: NodeJS.Timeout | null = null; - - /** 是否正在监控 */ - private _isMonitoring: boolean = false; - - /** RTT测量历史 */ - private _rttHistory: number[] = []; - - /** 最大RTT历史长度 */ - private _maxRttHistory: number = 20; - - /** 带宽测量窗口 */ - private _bandwidthWindow: { - timestamp: number; - uploadBytes: number; - downloadBytes: number; - }[] = []; - - /** 带宽测量窗口大小 */ - private _bandwidthWindowSize: number = 10; - - /** 连接统计 */ - private _connectionStats = { - bytesSent: 0, - bytesReceived: 0, - messagesSent: 0, - messagesReceived: 0, - activeConnections: 0 - }; - - /** 事件监听器 */ - private _eventListeners: Map = new Map(); - - /** 心跳管理器映射 */ - private _heartbeatManagers: Map = new Map(); - - public static get Instance(): NetworkPerformanceMonitor { - if (!NetworkPerformanceMonitor._instance) { - NetworkPerformanceMonitor._instance = new NetworkPerformanceMonitor(); - } - return NetworkPerformanceMonitor._instance; - } - - private constructor() {} - - /** - * 开始性能监控 - * - * @param interval - 监控间隔 (ms) - */ - public startMonitoring(interval: number = 1000): void { - if (this._isMonitoring) { - NetworkPerformanceMonitor.logger.warn('监控已在运行'); - return; - } - - this._monitoringInterval = interval; - this._isMonitoring = true; - - this._monitoringTimer = setInterval(() => { - this.collectMetrics(); - }, this._monitoringInterval); - - NetworkPerformanceMonitor.logger.info(`开始性能监控,间隔: ${interval}ms`); - } - - /** - * 停止性能监控 - */ - public stopMonitoring(): void { - if (!this._isMonitoring) { - return; - } - - if (this._monitoringTimer) { - clearInterval(this._monitoringTimer); - this._monitoringTimer = null; - } - - this._isMonitoring = false; - NetworkPerformanceMonitor.logger.info('停止性能监控'); - } - - /** - * 收集网络性能指标 - */ - private collectMetrics(): void { - const timestamp = Date.now(); - - // 计算网络指标 - const metrics = this.calculateNetworkMetrics(); - - // 获取SyncVar统计 - const syncVarStats = this.getSyncVarStatistics(); - - // 创建性能快照 - const snapshot: PerformanceSnapshot = { - timestamp, - metrics, - connectionStats: { ...this._connectionStats }, - syncVarStats - }; - - // 添加到历史记录 - this._snapshots.push(snapshot); - - // 限制历史记录数量 - if (this._snapshots.length > this._maxSnapshots) { - this._snapshots.shift(); - } - - // 触发监控事件 - this.emit('metricsCollected', snapshot); - - // 检查性能警告 - this.checkPerformanceWarnings(metrics); - } - - /** - * 计算网络指标 - */ - private calculateNetworkMetrics(): NetworkMetrics { - // 计算RTT - const avgRtt = this._rttHistory.length > 0 - ? this._rttHistory.reduce((a, b) => a + b, 0) / this._rttHistory.length - : 0; - - // 计算抖动 - const jitter = this.calculateJitter(); - - // 计算带宽 - const { uploadBandwidth, downloadBandwidth } = this.calculateBandwidth(); - - // 获取真实的包丢失率(优先使用心跳管理器数据) - const packetLoss = this.getAccuratePacketLoss(); - - // 计算连接质量评分 - const connectionQuality = this.calculateConnectionQuality(avgRtt, jitter, packetLoss); - - return { - rtt: avgRtt, - latency: avgRtt / 2, // 单向延迟近似为RTT的一半 - uploadBandwidth, - downloadBandwidth, - packetLoss, - jitter, - connectionQuality - }; - } - - /** - * 计算抖动 - */ - private calculateJitter(): number { - if (this._rttHistory.length < 2) { - return 0; - } - - let totalVariation = 0; - for (let i = 1; i < this._rttHistory.length; i++) { - totalVariation += Math.abs(this._rttHistory[i] - this._rttHistory[i - 1]); - } - - return totalVariation / (this._rttHistory.length - 1); - } - - /** - * 计算带宽 - */ - private calculateBandwidth(): { uploadBandwidth: number; downloadBandwidth: number } { - if (this._bandwidthWindow.length < 2) { - return { uploadBandwidth: 0, downloadBandwidth: 0 }; - } - - const first = this._bandwidthWindow[0]; - const last = this._bandwidthWindow[this._bandwidthWindow.length - 1]; - const timeDiff = (last.timestamp - first.timestamp) / 1000; // 转换为秒 - - if (timeDiff <= 0) { - return { uploadBandwidth: 0, downloadBandwidth: 0 }; - } - - const uploadBandwidth = (last.uploadBytes - first.uploadBytes) / timeDiff; - const downloadBandwidth = (last.downloadBytes - first.downloadBytes) / timeDiff; - - return { uploadBandwidth, downloadBandwidth }; - } - - /** - * 估算包丢失率 - * 使用多种指标进行更精确的丢包检测 - */ - private estimatePacketLoss(): number { - const recentRtt = this._rttHistory.slice(-10); // 增加样本数量 - if (recentRtt.length < 3) return 0; // 需要足够的样本 - - // 1. 基于RTT标准差的检测 - const avgRtt = recentRtt.reduce((a, b) => a + b, 0) / recentRtt.length; - const variance = recentRtt.reduce((sum, rtt) => sum + Math.pow(rtt - avgRtt, 2), 0) / recentRtt.length; - const stdDev = Math.sqrt(variance); - const coefficientOfVariation = stdDev / avgRtt; - - // 2. 异常RTT值检测(可能是重传导致) - const threshold = avgRtt + 2 * stdDev; - const abnormalRttCount = recentRtt.filter(rtt => rtt > threshold).length; - const abnormalRttRatio = abnormalRttCount / recentRtt.length; - - // 3. 基于连续超时的检测 - let consecutiveHighRtt = 0; - let maxConsecutive = 0; - for (const rtt of recentRtt) { - if (rtt > avgRtt * 1.5) { - consecutiveHighRtt++; - maxConsecutive = Math.max(maxConsecutive, consecutiveHighRtt); - } else { - consecutiveHighRtt = 0; - } - } - const consecutiveImpact = Math.min(maxConsecutive / recentRtt.length * 2, 1); - - // 综合评估丢包率 - const basePacketLoss = Math.min(coefficientOfVariation * 0.3, 0.15); - const abnormalAdjustment = abnormalRttRatio * 0.1; - const consecutiveAdjustment = consecutiveImpact * 0.05; - - const totalPacketLoss = basePacketLoss + abnormalAdjustment + consecutiveAdjustment; - return Math.min(totalPacketLoss, 0.2); // 最大20%丢包率 - } - - /** - * 计算连接质量评分 - */ - private calculateConnectionQuality(rtt: number, jitter: number, packetLoss: number): number { - let quality = 100; - - // RTT影响(大于100ms开始扣分) - if (rtt > 100) { - quality -= Math.min((rtt - 100) / 10, 30); - } - - // 抖动影响(大于20ms开始扣分) - if (jitter > 20) { - quality -= Math.min((jitter - 20) / 5, 25); - } - - // 丢包影响 - quality -= packetLoss * 100; - - return Math.max(Math.min(quality, 100), 0); - } - - /** - * 获取SyncVar统计信息 - * 改进异常处理,提供更详细的错误信息和降级策略 - * 使用懒加载避免循环依赖问题 - */ - private getSyncVarStatistics(): PerformanceSnapshot['syncVarStats'] { - try { - // 使用懒加载获取SyncVarSyncScheduler,避免循环依赖 - const scheduler = this.getSyncVarScheduler(); - - if (!scheduler) { - NetworkPerformanceMonitor.logger.debug('SyncVarSyncScheduler实例不存在,可能尚未初始化'); - return { - syncedComponents: 0, - syncedFields: 0, - averageSyncRate: 0, - syncDataSize: 0 - }; - } - - // 检查getStats方法是否存在 - if (typeof scheduler.getStats !== 'function') { - NetworkPerformanceMonitor.logger.warn('SyncVarSyncScheduler缺少getStats方法'); - return { - syncedComponents: 0, - syncedFields: 0, - averageSyncRate: 0, - syncDataSize: 0 - }; - } - - const stats = scheduler.getStats(); - - // 验证统计数据的有效性 - const validatedStats = { - syncedComponents: (typeof stats.totalComponents === 'number') ? Math.max(0, stats.totalComponents) : 0, - syncedFields: (typeof stats.totalFields === 'number') ? Math.max(0, stats.totalFields) : 0, - averageSyncRate: (typeof stats.averageFrequency === 'number') ? Math.max(0, stats.averageFrequency) : 0, - syncDataSize: (typeof stats.totalDataSize === 'number') ? Math.max(0, stats.totalDataSize) : 0 - }; - - return validatedStats; - - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - NetworkPerformanceMonitor.logger.warn(`获取SyncVar统计失败: ${errorMsg}, 返回默认统计数据`); - - // 返回安全的默认值而不是undefined - return { - syncedComponents: 0, - syncedFields: 0, - averageSyncRate: 0, - syncDataSize: 0 - }; - } - } - - /** - * 懒加载获取SyncVarSyncScheduler实例,避免循环依赖 - */ - private getSyncVarScheduler(): any | null { - try { - // 检查全局对象中是否已有实例 - const globalObj = (globalThis as any); - if (globalObj.SyncVarSyncScheduler && globalObj.SyncVarSyncScheduler.Instance) { - return globalObj.SyncVarSyncScheduler.Instance; - } - - // 尝试动态导入(仅在必要时) - try { - const SyncVarModule = require('../SyncVar/SyncVarSyncScheduler'); - if (SyncVarModule.SyncVarSyncScheduler && SyncVarModule.SyncVarSyncScheduler.Instance) { - return SyncVarModule.SyncVarSyncScheduler.Instance; - } - } catch (requireError) { - // 如果动态require失败,返回null而不是抛出错误 - NetworkPerformanceMonitor.logger.debug('无法动态加载SyncVarSyncScheduler模块'); - } - - return null; - } catch (error) { - NetworkPerformanceMonitor.logger.debug('获取SyncVarScheduler实例失败'); - return null; - } - } - - /** - * 检查性能警告 - */ - private checkPerformanceWarnings(metrics: NetworkMetrics): void { - const warnings: string[] = []; - - if (metrics.rtt > 200) { - warnings.push(`高延迟: ${metrics.rtt.toFixed(1)}ms`); - } - - if (metrics.jitter > 50) { - warnings.push(`高抖动: ${metrics.jitter.toFixed(1)}ms`); - } - - if (metrics.packetLoss > 0.05) { - warnings.push(`高丢包率: ${(metrics.packetLoss * 100).toFixed(1)}%`); - } - - if (metrics.connectionQuality < 70) { - warnings.push(`连接质量低: ${metrics.connectionQuality.toFixed(0)}/100`); - } - - if (warnings.length > 0) { - this.emit('performanceWarning', warnings); - NetworkPerformanceMonitor.logger.warn('性能警告:', warnings.join(', ')); - } - } - - /** - * 记录RTT测量 - */ - public recordRtt(rtt: number): void { - this._rttHistory.push(rtt); - - if (this._rttHistory.length > this._maxRttHistory) { - this._rttHistory.shift(); - } - } - - /** - * 记录数据传输 - */ - public recordDataTransfer(sent: number, received: number): void { - this._connectionStats.bytesSent += sent; - this._connectionStats.bytesReceived += received; - - // 更新带宽测量窗口 - const timestamp = Date.now(); - this._bandwidthWindow.push({ - timestamp, - uploadBytes: this._connectionStats.bytesSent, - downloadBytes: this._connectionStats.bytesReceived - }); - - if (this._bandwidthWindow.length > this._bandwidthWindowSize) { - this._bandwidthWindow.shift(); - } - } - - /** - * 记录消息传输 - */ - public recordMessageTransfer(sent: number, received: number): void { - this._connectionStats.messagesSent += sent; - this._connectionStats.messagesReceived += received; - } - - /** - * 更新活跃连接数 - */ - public updateActiveConnections(count: number): void { - this._connectionStats.activeConnections = count; - } - - /** - * 获取当前网络指标 - */ - public getCurrentMetrics(): NetworkMetrics { - return this.calculateNetworkMetrics(); - } - - /** - * 获取性能快照历史 - */ - public getSnapshots(count?: number): PerformanceSnapshot[] { - if (count && count > 0) { - return this._snapshots.slice(-count); - } - return [...this._snapshots]; - } - - /** - * 获取最新的性能快照 - */ - public getLatestSnapshot(): PerformanceSnapshot | null { - return this._snapshots.length > 0 ? this._snapshots[this._snapshots.length - 1] : null; - } - - /** - * 清除历史数据 - */ - public clearHistory(): void { - this._snapshots = []; - this._rttHistory = []; - this._bandwidthWindow = []; - NetworkPerformanceMonitor.logger.debug('清除历史数据'); - } - - /** - * 生成性能报告 - */ - public generateReport(timeRangeMs?: number): { - summary: { - averageRtt: number; - averageJitter: number; - averagePacketLoss: number; - averageQuality: number; - totalBytesSent: number; - totalBytesReceived: number; - totalMessages: number; - }; - snapshots: PerformanceSnapshot[]; - } { - let snapshots = this._snapshots; - - if (timeRangeMs && timeRangeMs > 0) { - const cutoffTime = Date.now() - timeRangeMs; - snapshots = snapshots.filter(s => s.timestamp >= cutoffTime); - } - - if (snapshots.length === 0) { - return { - summary: { - averageRtt: 0, - averageJitter: 0, - averagePacketLoss: 0, - averageQuality: 0, - totalBytesSent: 0, - totalBytesReceived: 0, - totalMessages: 0 - }, - snapshots: [] - }; - } - - const summary = { - averageRtt: snapshots.reduce((sum, s) => sum + s.metrics.rtt, 0) / snapshots.length, - averageJitter: snapshots.reduce((sum, s) => sum + s.metrics.jitter, 0) / snapshots.length, - averagePacketLoss: snapshots.reduce((sum, s) => sum + s.metrics.packetLoss, 0) / snapshots.length, - averageQuality: snapshots.reduce((sum, s) => sum + s.metrics.connectionQuality, 0) / snapshots.length, - totalBytesSent: this._connectionStats.bytesSent, - totalBytesReceived: this._connectionStats.bytesReceived, - totalMessages: this._connectionStats.messagesSent + this._connectionStats.messagesReceived - }; - - return { summary, snapshots }; - } - - /** - * 添加事件监听器 - */ - public on(event: string, listener: Function): void { - if (!this._eventListeners.has(event)) { - this._eventListeners.set(event, []); - } - this._eventListeners.get(event)!.push(listener); - } - - /** - * 移除事件监听器 - */ - public off(event: string, listener: Function): void { - const listeners = this._eventListeners.get(event); - if (listeners) { - const index = listeners.indexOf(listener); - if (index !== -1) { - listeners.splice(index, 1); - } - } - } - - /** - * 触发事件 - */ - private emit(event: string, ...args: any[]): void { - const listeners = this._eventListeners.get(event); - if (listeners) { - listeners.forEach(listener => { - try { - listener(...args); - } catch (error) { - NetworkPerformanceMonitor.logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 配置监控参数 - */ - public configure(options: { - maxSnapshots?: number; - maxRttHistory?: number; - bandwidthWindowSize?: number; - }): void { - if (options.maxSnapshots && options.maxSnapshots > 0) { - this._maxSnapshots = options.maxSnapshots; - } - - if (options.maxRttHistory && options.maxRttHistory > 0) { - this._maxRttHistory = options.maxRttHistory; - } - - if (options.bandwidthWindowSize && options.bandwidthWindowSize > 0) { - this._bandwidthWindowSize = options.bandwidthWindowSize; - } - - NetworkPerformanceMonitor.logger.info('配置已更新:', options); - } - - /** - * 获取监控器统计信息 - */ - public getMonitorStats(): { - isMonitoring: boolean; - interval: number; - snapshotCount: number; - rttHistoryLength: number; - bandwidthWindowSize: number; - } { - return { - isMonitoring: this._isMonitoring, - interval: this._monitoringInterval, - snapshotCount: this._snapshots.length, - rttHistoryLength: this._rttHistory.length, - bandwidthWindowSize: this._bandwidthWindow.length - }; - } - - /** - * 网络质量自适应机制 - * 根据网络状况动态调整同步策略 - */ - public adaptNetworkStrategy(): { - suggestedSyncInterval: number; - suggestedBatchSize: number; - suggestedCompressionLevel: number; - prioritizeUpdate: boolean; - } { - const metrics = this.getCurrentMetrics(); - - // 基础设置 - let syncInterval = 50; // 默认50ms - let batchSize = 10; // 默认批大小 - let compressionLevel = 1; // 默认压缩级别 - let prioritizeUpdate = false; - - // 根据连接质量调整 - if (metrics.connectionQuality >= 80) { - // 高质量网络: 高频更新,小批次 - syncInterval = 33; // 30fps - batchSize = 5; - compressionLevel = 0; // 不压缩 - } else if (metrics.connectionQuality >= 60) { - // 中等质量网络: 标准设置 - syncInterval = 50; // 20fps - batchSize = 10; - compressionLevel = 1; - } else if (metrics.connectionQuality >= 40) { - // 低质量网络: 降低频率,增加批处理 - syncInterval = 100; // 10fps - batchSize = 20; - compressionLevel = 2; - prioritizeUpdate = true; - } else { - // 极低质量网络: 最保守设置 - syncInterval = 200; // 5fps - batchSize = 50; - compressionLevel = 3; - prioritizeUpdate = true; - } - - // 根据RTT进一步调整 - if (metrics.rtt > 300) { - syncInterval = Math.max(syncInterval * 1.5, 200); - batchSize = Math.min(batchSize * 2, 100); - } - - // 根据丢包率调整 - if (metrics.packetLoss > 0.1) { - syncInterval = Math.max(syncInterval * 1.2, 150); - compressionLevel = Math.min(compressionLevel + 1, 3); - prioritizeUpdate = true; - } - - return { - suggestedSyncInterval: Math.round(syncInterval), - suggestedBatchSize: Math.round(batchSize), - suggestedCompressionLevel: compressionLevel, - prioritizeUpdate - }; - } - - /** - * 检测网络拥塞状态 - */ - public detectNetworkCongestion(): { - isCongested: boolean; - congestionLevel: 'none' | 'light' | 'moderate' | 'severe'; - suggestedAction: string; - } { - const recentSnapshots = this._snapshots.slice(-5); - if (recentSnapshots.length < 3) { - return { - isCongested: false, - congestionLevel: 'none', - suggestedAction: '数据不足,继续监控' - }; - } - - // 计算趋势 - const rttTrend = this.calculateTrend(recentSnapshots.map(s => s.metrics.rtt)); - const packetLossTrend = this.calculateTrend(recentSnapshots.map(s => s.metrics.packetLoss)); - const qualityTrend = this.calculateTrend(recentSnapshots.map(s => s.metrics.connectionQuality)); - - // 检测拥塞指标 - const avgRtt = recentSnapshots.reduce((sum, s) => sum + s.metrics.rtt, 0) / recentSnapshots.length; - const avgPacketLoss = recentSnapshots.reduce((sum, s) => sum + s.metrics.packetLoss, 0) / recentSnapshots.length; - const avgQuality = recentSnapshots.reduce((sum, s) => sum + s.metrics.connectionQuality, 0) / recentSnapshots.length; - - // 拥塞判定 - let congestionLevel: 'none' | 'light' | 'moderate' | 'severe' = 'none'; - let suggestedAction = '网络状况良好'; - - if (avgRtt > 500 || avgPacketLoss > 0.15 || avgQuality < 30) { - congestionLevel = 'severe'; - suggestedAction = '严重拥塞,建议降低同步频率至最低,启用高压缩'; - } else if (avgRtt > 300 || avgPacketLoss > 0.08 || avgQuality < 50) { - congestionLevel = 'moderate'; - suggestedAction = '中等拥塞,建议减少同步频率,启用压缩'; - } else if (avgRtt > 150 || avgPacketLoss > 0.03 || avgQuality < 70) { - congestionLevel = 'light'; - suggestedAction = '轻微拥塞,建议适度降低同步频率'; - } - - const isCongested = congestionLevel !== 'none'; - - return { - isCongested, - congestionLevel, - suggestedAction - }; - } - - /** - * 计算数据趋势(斜率) - */ - private calculateTrend(values: number[]): number { - if (values.length < 2) return 0; - - const n = values.length; - let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0; - - for (let i = 0; i < n; i++) { - sumX += i; - sumY += values[i]; - sumXY += i * values[i]; - sumX2 += i * i; - } - - const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX); - return slope; - } - - /** - * 为连接添加心跳监控 - */ - public addHeartbeatMonitoring(connectionId: string, connection: NetworkConnection): void { - if (!this._heartbeatManagers.has(connectionId)) { - const heartbeatManager = new HeartbeatManager(connection); - this._heartbeatManagers.set(connectionId, heartbeatManager); - heartbeatManager.start(); - NetworkPerformanceMonitor.logger.info(`为连接 ${connectionId} 启动心跳监控`); - } - } - - /** - * 移除连接的心跳监控 - */ - public removeHeartbeatMonitoring(connectionId: string): void { - const heartbeatManager = this._heartbeatManagers.get(connectionId); - if (heartbeatManager) { - heartbeatManager.stop(); - this._heartbeatManagers.delete(connectionId); - NetworkPerformanceMonitor.logger.info(`移除连接 ${connectionId} 的心跳监控`); - } - } - - /** - * 获取精确的包丢失率(优先使用心跳数据) - */ - private getAccuratePacketLoss(): number { - let totalPacketLoss = 0; - let count = 0; - - // 从心跳管理器获取真实丢包率 - for (const heartbeatManager of this._heartbeatManagers.values()) { - const stats = heartbeatManager.getStats(); - totalPacketLoss += stats.packetLossRate; - count++; - } - - if (count > 0) { - return totalPacketLoss / count; - } - - // 回退到估算方法 - return this.estimatePacketLoss(); - } - - /** - * 获取心跳统计信息 - */ - public getHeartbeatStats(): Map { - const stats = new Map(); - - for (const [connectionId, manager] of this._heartbeatManagers) { - stats.set(connectionId, manager.getStats()); - } - - return stats; - } - - /** - * 获取所有连接的健康状态 - */ - public getConnectionHealth(): Map { - const health = new Map(); - - for (const [connectionId, manager] of this._heartbeatManagers) { - const stats = manager.getStats(); - health.set(connectionId, stats.isAlive); - } - - return health; - } -} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkRegistry.ts b/packages/network/src/Core/NetworkRegistry.ts new file mode 100644 index 00000000..f70409a7 --- /dev/null +++ b/packages/network/src/Core/NetworkRegistry.ts @@ -0,0 +1,185 @@ +/** + * 网络注册表 + * + * 管理所有网络对象的注册和查找 + */ + +import { createLogger } from '@esengine/ecs-framework'; +import { NetworkIdentity } from '../NetworkIdentity'; + +const logger = createLogger('NetworkRegistry'); + +export class NetworkRegistry { + private static _instance: NetworkRegistry | null = null; + + /** 网络对象映射表 networkId -> NetworkIdentity */ + private networkObjects: Map = new Map(); + + /** 下一个可用的网络ID */ + private nextNetworkId: number = 1; + + /** 本地玩家对象 */ + private localPlayer: NetworkIdentity | null = null; + + public static get instance(): NetworkRegistry { + if (!NetworkRegistry._instance) { + NetworkRegistry._instance = new NetworkRegistry(); + } + return NetworkRegistry._instance; + } + + private constructor() {} + + /** + * 注册网络对象 + * @param identity 网络身份组件 + * @param networkId 指定的网络ID,如果不提供则自动分配 + * @returns 分配的网络ID + */ + public register(identity: NetworkIdentity, networkId?: number): number { + // 使用指定ID或自动分配 + const assignedId = networkId || this.nextNetworkId++; + + // 检查ID是否已被占用 + if (this.networkObjects.has(assignedId)) { + logger.error(`网络ID ${assignedId} 已被占用`); + throw new Error(`Network ID ${assignedId} is already in use`); + } + + // 注册对象 + identity.networkId = assignedId; + this.networkObjects.set(assignedId, identity); + + // 确保下一个ID不冲突 + if (assignedId >= this.nextNetworkId) { + this.nextNetworkId = assignedId + 1; + } + + logger.debug(`注册网络对象: ID=${assignedId}, Type=${identity.entity?.name || 'Unknown'}`); + return assignedId; + } + + /** + * 注销网络对象 + * @param networkId 网络ID + */ + public unregister(networkId: number): void { + const identity = this.networkObjects.get(networkId); + if (identity) { + this.networkObjects.delete(networkId); + + // 如果是本地玩家,清除引用 + if (this.localPlayer === identity) { + this.localPlayer = null; + } + + logger.debug(`注销网络对象: ID=${networkId}`); + } + } + + /** + * 根据网络ID查找对象 + * @param networkId 网络ID + * @returns 网络身份组件 + */ + public find(networkId: number): NetworkIdentity | null { + return this.networkObjects.get(networkId) || null; + } + + /** + * 获取所有网络对象 + */ + public getAllNetworkObjects(): NetworkIdentity[] { + return Array.from(this.networkObjects.values()); + } + + /** + * 获取所有拥有权威的对象 + */ + public getAuthorityObjects(): NetworkIdentity[] { + return Array.from(this.networkObjects.values()).filter(identity => identity.hasAuthority); + } + + /** + * 获取指定客户端拥有的对象 + * @param ownerId 客户端ID + */ + public getObjectsByOwner(ownerId: number): NetworkIdentity[] { + return Array.from(this.networkObjects.values()).filter(identity => identity.ownerId === ownerId); + } + + /** + * 设置本地玩家 + * @param identity 本地玩家的网络身份组件 + */ + public setLocalPlayer(identity: NetworkIdentity): void { + // 清除之前的本地玩家标记 + if (this.localPlayer) { + this.localPlayer.isLocalPlayer = false; + } + + // 设置新的本地玩家 + this.localPlayer = identity; + identity.setAsLocalPlayer(); + + logger.info(`设置本地玩家: ID=${identity.networkId}`); + } + + /** + * 获取本地玩家 + */ + public getLocalPlayer(): NetworkIdentity | null { + return this.localPlayer; + } + + /** + * 清理指定客户端断开连接后的对象 + * @param ownerId 断开连接的客户端ID + */ + public cleanupDisconnectedClient(ownerId: number): void { + const ownedObjects = this.getObjectsByOwner(ownerId); + + for (const identity of ownedObjects) { + // 移除权威,转移给服务端 + identity.setAuthority(false, 0); + + // 如果是本地玩家,清除引用 + if (identity === this.localPlayer) { + this.localPlayer = null; + } + } + + logger.info(`清理断开连接客户端 ${ownerId} 的 ${ownedObjects.length} 个对象`); + } + + /** + * 检查网络ID是否存在 + * @param networkId 网络ID + */ + public exists(networkId: number): boolean { + return this.networkObjects.has(networkId); + } + + /** + * 重置注册表(用于测试) + */ + public reset(): void { + this.networkObjects.clear(); + this.nextNetworkId = 1; + this.localPlayer = null; + logger.info('网络注册表已重置'); + } + + /** + * 获取统计信息 + */ + public getStats() { + const objects = Array.from(this.networkObjects.values()); + return { + totalObjects: objects.length, + authorityObjects: objects.filter(o => o.hasAuthority).length, + localPlayerCount: this.localPlayer ? 1 : 0, + nextNetworkId: this.nextNetworkId + }; + } +} \ No newline at end of file diff --git a/packages/network/src/Core/NetworkServer.ts b/packages/network/src/Core/NetworkServer.ts deleted file mode 100644 index bf493ee6..00000000 --- a/packages/network/src/Core/NetworkServer.ts +++ /dev/null @@ -1,594 +0,0 @@ -import WebSocket, { WebSocketServer } from 'ws'; -import { NetworkConnection } from './NetworkConnection'; -import { v4 as uuidv4 } from 'uuid'; -import { SyncVarUpdateMessage } from '../Messaging/MessageTypes'; -import { SyncVarMessageHandler } from '../SyncVar/SyncVarMessageHandler'; -import { SyncVarSyncScheduler } from '../SyncVar/SyncVarSyncScheduler'; -import { MessageHandler } from '../Messaging/MessageHandler'; -import { NetworkPerformanceMonitor } from './NetworkPerformanceMonitor'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 服务端事件接口 - */ -export interface NetworkServerEvents { - clientConnected: (connection: NetworkConnection) => void; - clientDisconnected: (connection: NetworkConnection, reason?: string) => void; - clientMessage: (connection: NetworkConnection, data: Uint8Array) => void; - serverStarted: (port: number, host: string) => void; - serverStopped: () => void; - error: (error: Error) => void; -} - -/** - * 网络服务端 - * - * 管理WebSocket服务器,处理客户端连接和消息分发 - * 支持多客户端连接,提供广播和单播功能 - */ -export class NetworkServer { - private static readonly _logger = createLogger('NetworkServer'); - private _wss: WebSocketServer | null = null; - private _connections: Map = new Map(); - private _isRunning: boolean = false; - private _port: number = 0; - private _host: string = ''; - private _startTime: number = 0; - private _eventHandlers: Map = new Map(); - - // SyncVar相关组件 - private _syncVarHandler: SyncVarMessageHandler; - private _syncScheduler: SyncVarSyncScheduler; - private _messageHandler: MessageHandler; - - // 性能监控 - private _performanceMonitor: NetworkPerformanceMonitor; - - // 服务器配置 - private static readonly MAX_CONNECTIONS = 100; - private static readonly CONNECTION_TIMEOUT = 60000; // 60秒 - - constructor() { - // 初始化SyncVar组件 - this._syncVarHandler = new SyncVarMessageHandler(); - this._syncScheduler = SyncVarSyncScheduler.Instance; - this._messageHandler = MessageHandler.Instance; - this._performanceMonitor = NetworkPerformanceMonitor.Instance; - - // 注册SyncVar消息处理器 - this._messageHandler.registerHandler( - 400, // MessageType.SYNC_VAR_UPDATE - SyncVarUpdateMessage, - this._syncVarHandler, - 0 - ); - - // 设置SyncVar消息发送回调 - this._syncScheduler.setMessageSendCallback(async (message: SyncVarUpdateMessage) => { - await this.broadcastSyncVarMessage(message); - }); - } - - /** - * 启动服务器 - * - * @param port - 监听端口 - * @param host - 监听地址 - */ - public async start(port: number, host: string = '0.0.0.0'): Promise { - if (this._isRunning) { - throw new Error('服务器已经在运行'); - } - - return new Promise((resolve, reject) => { - try { - this._wss = new WebSocketServer({ - port, - host, - maxPayload: 16 * 1024 * 1024, // 16MB - perMessageDeflate: true, - clientTracking: true - }); - - this._wss.on('connection', (ws: WebSocket, request) => { - this.handleNewConnection(ws, request); - }); - - this._wss.on('listening', () => { - this._isRunning = true; - this._port = port; - this._host = host; - this._startTime = Date.now(); - - // 启动SyncVar同步调度器 - this.startSyncVarScheduler(); - - // 启动性能监控 - this.startPerformanceMonitoring(); - - NetworkServer._logger.info(`服务器启动成功: ${host}:${port}`); - this.emit('serverStarted', port, host); - resolve(); - }); - - this._wss.on('error', (error) => { - NetworkServer._logger.error('服务器错误:', error); - this.emit('error', error); - - if (!this._isRunning) { - reject(error); - } - }); - - } catch (error) { - reject(error); - } - }); - } - - /** - * 停止服务器 - */ - public async stop(): Promise { - if (!this._isRunning || !this._wss) { - return; - } - - return new Promise((resolve) => { - // 关闭所有客户端连接 - const connections = Array.from(this._connections.values()); - connections.forEach(connection => { - connection.close('Server shutting down'); - }); - this._connections.clear(); - - // 停止SyncVar同步调度器 - this.stopSyncVarScheduler(); - - // 停止性能监控 - this.stopPerformanceMonitoring(); - - // 关闭WebSocket服务器 - this._wss!.close(() => { - this._isRunning = false; - this._wss = null; - this._port = 0; - this._host = ''; - this._startTime = 0; - - NetworkServer._logger.info('服务器已停止'); - this.emit('serverStopped'); - resolve(); - }); - }); - } - - /** - * 处理新的客户端连接 - * - * @param ws - WebSocket连接 - * @param request - HTTP请求对象 - */ - private handleNewConnection(ws: WebSocket, request: any): void { - // 检查连接数限制 - if (this._connections.size >= NetworkServer.MAX_CONNECTIONS) { - NetworkServer._logger.warn('达到最大连接数限制,拒绝新连接'); - ws.close(1013, 'Server full'); - return; - } - - // 生成连接ID和获取客户端地址 - const connectionId = uuidv4(); - const clientAddress = request.socket.remoteAddress || 'unknown'; - - // 创建连接对象 - const connection = new NetworkConnection(ws, connectionId, clientAddress); - - // 设置连接事件监听 - connection.on('connected', () => { - this._connections.set(connectionId, connection); - NetworkServer._logger.info(`客户端连接: ${connectionId} (${clientAddress})`); - this.emit('clientConnected', connection); - }); - - connection.on('disconnected', (reason) => { - this._connections.delete(connectionId); - NetworkServer._logger.info(`客户端断开: ${connectionId} (${reason})`); - this.emit('clientDisconnected', connection, reason); - }); - - connection.on('message', async (data) => { - this.recordMessagePerformance(data, false); - this.emit('clientMessage', connection, data); - - // 自动处理消息 - await this._messageHandler.handleRawMessage(data, connection); - }); - - connection.on('error', (error) => { - NetworkServer._logger.error(`连接错误 ${connectionId}:`, error); - this.emit('error', error); - }); - } - - /** - * 向指定客户端发送消息 - * - * @param connectionId - 连接ID - * @param data - 消息数据 - * @returns 是否发送成功 - */ - public sendToClient(connectionId: string, data: Uint8Array): boolean { - const connection = this._connections.get(connectionId); - if (!connection) { - NetworkServer._logger.warn(`连接不存在: ${connectionId}`); - return false; - } - - const success = connection.send(data); - if (success) { - this.recordMessagePerformance(data, true); - } - return success; - } - - /** - * 广播消息给所有客户端 - * - * @param data - 消息数据 - * @param excludeConnection - 排除的连接ID(可选) - * @returns 成功发送的连接数 - */ - public broadcast(data: Uint8Array, excludeConnection?: string): number { - let successCount = 0; - - for (const [connectionId, connection] of this._connections) { - if (excludeConnection && connectionId === excludeConnection) { - continue; - } - - if (connection.send(data)) { - successCount++; - this.recordMessagePerformance(data, true); - } - } - - return successCount; - } - - /** - * 向多个指定客户端发送消息 - * - * @param connectionIds - 连接ID数组 - * @param data - 消息数据 - * @returns 成功发送的连接数 - */ - public sendToMultipleClients(connectionIds: string[], data: Uint8Array): number { - let successCount = 0; - - connectionIds.forEach(connectionId => { - if (this.sendToClient(connectionId, data)) { - successCount++; - } - }); - - return successCount; - } - - /** - * 断开指定客户端连接 - * - * @param connectionId - 连接ID - * @param reason - 断开原因 - * @returns 是否成功断开 - */ - public disconnectClient(connectionId: string, reason: string = 'Disconnected by server'): boolean { - const connection = this._connections.get(connectionId); - if (!connection) { - return false; - } - - connection.close(reason); - return true; - } - - /** - * 获取指定客户端连接 - * - * @param connectionId - 连接ID - * @returns 连接对象 - */ - public getConnection(connectionId: string): NetworkConnection | null { - return this._connections.get(connectionId) || null; - } - - /** - * 获取所有活跃连接 - * - * @returns 连接数组 - */ - public getAllConnections(): NetworkConnection[] { - return Array.from(this._connections.values()); - } - - /** - * 获取活跃连接的ID列表 - * - * @returns 连接ID数组 - */ - public getConnectionIds(): string[] { - return Array.from(this._connections.keys()); - } - - /** - * 添加事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public on( - event: K, - handler: NetworkServerEvents[K] - ): void { - if (!this._eventHandlers.has(event)) { - this._eventHandlers.set(event, []); - } - this._eventHandlers.get(event)!.push(handler); - } - - /** - * 移除事件监听器 - * - * @param event - 事件名称 - * @param handler - 事件处理函数 - */ - public off( - event: K, - handler: NetworkServerEvents[K] - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - const index = handlers.indexOf(handler); - if (index !== -1) { - handlers.splice(index, 1); - } - } - } - - /** - * 触发事件 - * - * @param event - 事件名称 - * @param args - 事件参数 - */ - private emit( - event: K, - ...args: Parameters - ): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - handlers.forEach(handler => { - try { - handler(...args); - } catch (error) { - NetworkServer._logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 检查服务器是否正在运行 - */ - public get isRunning(): boolean { - return this._isRunning; - } - - /** - * 获取当前连接数 - */ - public get connectionCount(): number { - return this._connections.size; - } - - /** - * 获取监听端口 - */ - public get port(): number { - return this._port; - } - - /** - * 获取监听地址 - */ - public get host(): string { - return this._host; - } - - /** - * 获取服务器运行时间(毫秒) - */ - public get uptime(): number { - return this._startTime > 0 ? Date.now() - this._startTime : 0; - } - - /** - * 启动SyncVar同步调度器 - */ - private startSyncVarScheduler(): void { - try { - this._syncScheduler.start(); - NetworkServer._logger.info('SyncVar同步调度器已启动'); - } catch (error) { - NetworkServer._logger.error('启动SyncVar调度器失败:', error); - } - } - - /** - * 停止SyncVar同步调度器 - */ - private stopSyncVarScheduler(): void { - try { - this._syncScheduler.stop(); - NetworkServer._logger.info('SyncVar同步调度器已停止'); - } catch (error) { - NetworkServer._logger.error('停止SyncVar调度器失败:', error); - } - } - - /** - * 广播SyncVar更新消息 - * - * @param message - SyncVar更新消息 - */ - public async broadcastSyncVarMessage(message: SyncVarUpdateMessage): Promise { - try { - const serializedMessage = message.serialize(); - const successCount = this.broadcast(serializedMessage); - - NetworkServer._logger.info(`广播SyncVar消息: ${message.networkId}.${message.componentType}, 成功发送到 ${successCount} 个客户端`); - } catch (error) { - NetworkServer._logger.error('广播SyncVar消息失败:', error); - } - } - - /** - * 发送SyncVar消息到指定客户端 - * - * @param connectionId - 连接ID - * @param message - SyncVar更新消息 - */ - public async sendSyncVarMessage(connectionId: string, message: SyncVarUpdateMessage): Promise { - try { - const serializedMessage = message.serialize(); - return this.sendToClient(connectionId, serializedMessage); - } catch (error) { - NetworkServer._logger.error(`发送SyncVar消息到客户端 ${connectionId} 失败:`, error); - return false; - } - } - - /** - * 发送SyncVar消息到指定客户端列表(排除某个客户端) - * - * @param message - SyncVar更新消息 - * @param excludeConnectionId - 要排除的连接ID - */ - public async broadcastSyncVarMessageExcept(message: SyncVarUpdateMessage, excludeConnectionId: string): Promise { - try { - const serializedMessage = message.serialize(); - const allConnections = Array.from(this._connections.keys()); - const targetConnections = allConnections.filter(id => id !== excludeConnectionId); - - return this.sendToMultipleClients(targetConnections, serializedMessage); - } catch (error) { - NetworkServer._logger.error('广播SyncVar消息(排除指定客户端)失败:', error); - return 0; - } - } - - /** - * 获取SyncVar调度器统计信息 - */ - public getSyncVarStats(): any { - return this._syncScheduler.getStats(); - } - - /** - * 配置SyncVar同步调度器 - */ - public configureSyncVarScheduler(config: any): void { - this._syncScheduler.configure(config); - } - - /** - * 启动性能监控 - */ - private startPerformanceMonitoring(): void { - try { - this._performanceMonitor.startMonitoring(); - NetworkServer._logger.info('性能监控已启动'); - } catch (error) { - NetworkServer._logger.error('启动性能监控失败:', error); - } - } - - /** - * 停止性能监控 - */ - private stopPerformanceMonitoring(): void { - try { - this._performanceMonitor.stopMonitoring(); - NetworkServer._logger.info('性能监控已停止'); - } catch (error) { - NetworkServer._logger.error('停止性能监控失败:', error); - } - } - - /** - * 记录消息传输性能 - */ - private recordMessagePerformance(data: Uint8Array, sent: boolean): void { - const size = data.length; - if (sent) { - this._performanceMonitor.recordDataTransfer(size, 0); - this._performanceMonitor.recordMessageTransfer(1, 0); - } else { - this._performanceMonitor.recordDataTransfer(0, size); - this._performanceMonitor.recordMessageTransfer(0, 1); - } - this._performanceMonitor.updateActiveConnections(this._connections.size); - } - - /** - * 获取性能监控数据 - */ - public getPerformanceMetrics(): any { - return this._performanceMonitor.getCurrentMetrics(); - } - - /** - * 获取性能报告 - */ - public getPerformanceReport(timeRangeMs?: number): any { - return this._performanceMonitor.generateReport(timeRangeMs); - } - - /** - * 获取服务器统计信息 - */ - public getStats(): { - isRunning: boolean; - connectionCount: number; - maxConnections: number; - port: number; - host: string; - uptime: number; - connections: Array<{ - connectionId: string; - address: string; - connectedTime: number; - isAlive: boolean; - }>; - } { - const connectionStats = Array.from(this._connections.values()).map(conn => { - const stats = conn.getStats(); - return { - connectionId: stats.connectionId, - address: stats.address, - connectedTime: stats.connectedTime, - isAlive: stats.isAlive - }; - }); - - return { - isRunning: this._isRunning, - connectionCount: this._connections.size, - maxConnections: NetworkServer.MAX_CONNECTIONS, - port: this._port, - host: this._host, - uptime: this.uptime, - connections: connectionStats - }; - } -} \ No newline at end of file diff --git a/packages/network/src/Core/RpcManager.ts b/packages/network/src/Core/RpcManager.ts new file mode 100644 index 00000000..d9f24d4f --- /dev/null +++ b/packages/network/src/Core/RpcManager.ts @@ -0,0 +1,223 @@ +/** + * RPC 管理器 + * + * 负责处理客户端 RPC 和命令的注册、调用和消息路由 + */ + +import { createLogger } from '@esengine/ecs-framework'; +import { NetworkBehaviour } from '../NetworkBehaviour'; +import { NetworkRegistry } from './NetworkRegistry'; +import { RpcMessage, RpcMetadata } from '../types/NetworkTypes'; +import { getClientRpcMetadata } from '../decorators/ClientRpc'; +import { getCommandMetadata } from '../decorators/Command'; + +const logger = createLogger('RpcManager'); + +/** + * RPC 调用信息 + */ +interface RpcCall { + networkId: number; + componentType: string; + methodName: string; + args: any[]; + timestamp: number; + isClientRpc: boolean; +} + +export class RpcManager { + private static _instance: RpcManager | null = null; + + /** 已注册的网络组件类型 */ + private registeredComponents: Set = new Set(); + + /** 待发送的 RPC 调用队列 */ + private pendingRpcCalls: RpcCall[] = []; + + public static get instance(): RpcManager { + if (!RpcManager._instance) { + RpcManager._instance = new RpcManager(); + } + return RpcManager._instance; + } + + private constructor() {} + + /** + * 注册网络组件的 RPC 方法 + */ + public registerComponent(component: NetworkBehaviour): void { + const componentType = component.constructor.name; + + if (this.registeredComponents.has(componentType)) { + return; // 已经注册过了 + } + + // 获取 ClientRpc 和 Command 元数据 + const clientRpcMetadata = getClientRpcMetadata(component.constructor); + const commandMetadata = getCommandMetadata(component.constructor); + + if (clientRpcMetadata.length === 0 && commandMetadata.length === 0) { + return; // 没有 RPC 方法 + } + + this.registeredComponents.add(componentType); + + logger.debug(`注册 RPC 组件: ${componentType}, ClientRpc: ${clientRpcMetadata.length}, Commands: ${commandMetadata.length}`); + } + + /** + * 添加 ClientRpc 调用到队列 + */ + public addClientRpcCall( + networkId: number, + componentType: string, + methodName: string, + args: any[] = [] + ): void { + const rpcCall: RpcCall = { + networkId, + componentType, + methodName, + args, + timestamp: Date.now(), + isClientRpc: true + }; + + this.pendingRpcCalls.push(rpcCall); + logger.debug(`添加 ClientRpc 调用: ${componentType}.${methodName}`); + } + + /** + * 添加 Command 调用到队列 + */ + public addCommandCall( + networkId: number, + componentType: string, + methodName: string, + args: any[] = [] + ): void { + const rpcCall: RpcCall = { + networkId, + componentType, + methodName, + args, + timestamp: Date.now(), + isClientRpc: false + }; + + this.pendingRpcCalls.push(rpcCall); + logger.debug(`添加 Command 调用: ${componentType}.${methodName}`); + } + + /** + * 获取待发送的 RPC 消息 + */ + public getPendingRpcMessages(): RpcMessage[] { + const messages: RpcMessage[] = []; + + for (const rpcCall of this.pendingRpcCalls) { + messages.push({ + type: 'rpc', + networkId: rpcCall.networkId, + data: { + componentType: rpcCall.componentType, + methodName: rpcCall.methodName, + args: rpcCall.args + }, + methodName: rpcCall.methodName, + args: rpcCall.args, + isClientRpc: rpcCall.isClientRpc, + timestamp: rpcCall.timestamp + }); + } + + // 清空待发送队列 + this.pendingRpcCalls.length = 0; + return messages; + } + + /** + * 处理收到的 RPC 消息 + */ + public handleRpcMessage(message: RpcMessage): void { + const networkIdentity = NetworkRegistry.instance.find(message.networkId); + if (!networkIdentity) { + logger.warn(`找不到网络ID为 ${message.networkId} 的对象`); + return; + } + + // 找到对应的组件 + const targetComponent = networkIdentity.networkBehaviours + .find(b => b.constructor.name === message.data.componentType); + + if (!targetComponent) { + logger.warn(`找不到组件: ${message.data.componentType} (网络ID: ${message.networkId})`); + return; + } + + // 验证方法是否存在 + const method = (targetComponent as any)[message.methodName]; + if (typeof method !== 'function') { + logger.warn(`方法不存在: ${message.data.componentType}.${message.methodName}`); + return; + } + + // 验证权限 + if (!this.validateRpcPermission(targetComponent as NetworkBehaviour, message)) { + return; + } + + try { + // 执行方法 + method.apply(targetComponent, message.args); + logger.debug(`执行 RPC: ${message.data.componentType}.${message.methodName}`); + } catch (error) { + logger.error(`RPC 执行失败: ${message.data.componentType}.${message.methodName}`, error); + } + } + + /** + * 验证 RPC 调用权限 + */ + private validateRpcPermission(component: NetworkBehaviour, message: RpcMessage): boolean { + let metadata: RpcMetadata[] = []; + + if (message.isClientRpc) { + metadata = getClientRpcMetadata(component.constructor); + } else { + metadata = getCommandMetadata(component.constructor); + } + + const rpcMeta = metadata.find(m => m.methodName === message.methodName); + if (!rpcMeta) { + logger.warn(`未找到 RPC 元数据: ${message.data.componentType}.${message.methodName}`); + return false; + } + + // 检查权限要求 + if (rpcMeta.requiresAuthority && !component.hasAuthority) { + logger.warn(`RPC 权限不足: ${message.data.componentType}.${message.methodName}`); + return false; + } + + return true; + } + + /** + * 清理所有待发送的 RPC 调用 + */ + public clearPendingCalls(): void { + this.pendingRpcCalls.length = 0; + } + + /** + * 获取统计信息 + */ + public getStats() { + return { + registeredComponents: this.registeredComponents.size, + pendingRpcCalls: this.pendingRpcCalls.length + }; + } +} \ No newline at end of file diff --git a/packages/network/src/Core/SyncVarManager.ts b/packages/network/src/Core/SyncVarManager.ts new file mode 100644 index 00000000..f2093ae4 --- /dev/null +++ b/packages/network/src/Core/SyncVarManager.ts @@ -0,0 +1,222 @@ +/** + * 同步变量管理器 + * + * 负责管理 SyncVar 的同步逻辑,包括变化检测、权限验证和消息发送 + */ + +import { createLogger } from '@esengine/ecs-framework'; +import { NetworkBehaviour } from '../NetworkBehaviour'; +import { getSyncVarMetadata, SYNCVAR_METADATA_KEY } from '../decorators/SyncVar'; +import { SyncVarMessage, SyncVarMetadata } from '../types/NetworkTypes'; + +const logger = createLogger('SyncVarManager'); + +/** + * SyncVar 变化记录 + */ +interface SyncVarChange { + networkId: number; + componentType: string; + propertyName: string; + oldValue: any; + newValue: any; + timestamp: number; +} + +export class SyncVarManager { + private static _instance: SyncVarManager | null = null; + + /** 待同步的变化列表 */ + private pendingChanges: SyncVarChange[] = []; + + /** 已注册的网络组件实例 */ + private registeredComponents: Map = new Map(); + + public static get instance(): SyncVarManager { + if (!SyncVarManager._instance) { + SyncVarManager._instance = new SyncVarManager(); + } + return SyncVarManager._instance; + } + + private constructor() {} + + /** + * 注册网络组件,为其设置 SyncVar 支持 + */ + public registerComponent(component: NetworkBehaviour): void { + const componentType = component.constructor.name; + + // 获取 SyncVar 元数据 + const metadata = getSyncVarMetadata(component.constructor); + if (metadata.length === 0) { + return; // 没有 SyncVar,无需处理 + } + + // 添加到注册列表 + if (!this.registeredComponents.has(componentType)) { + this.registeredComponents.set(componentType, []); + } + this.registeredComponents.get(componentType)!.push(component); + + // 为组件添加变化通知方法 + this.addSyncVarSupport(component, metadata); + + logger.debug(`注册网络组件: ${componentType},包含 ${metadata.length} 个 SyncVar`); + } + + /** + * 注销网络组件 + */ + public unregisterComponent(component: NetworkBehaviour): void { + const componentType = component.constructor.name; + const components = this.registeredComponents.get(componentType); + + if (components) { + const index = components.indexOf(component); + if (index !== -1) { + components.splice(index, 1); + } + + if (components.length === 0) { + this.registeredComponents.delete(componentType); + } + } + + logger.debug(`注销网络组件: ${componentType}`); + } + + /** + * 处理收到的 SyncVar 消息 + */ + public handleSyncVarMessage(message: SyncVarMessage): void { + const components = this.registeredComponents.get(message.componentType); + if (!components) { + logger.warn(`收到未知组件类型的 SyncVar 消息: ${message.componentType}`); + return; + } + + // 找到对应的网络对象 + const targetComponent = components.find(c => c.networkId === message.networkId); + if (!targetComponent) { + logger.warn(`找不到网络ID为 ${message.networkId} 的组件: ${message.componentType}`); + return; + } + + // 应用同步值 + this.applySyncVar(targetComponent, message.propertyName, message.value); + } + + /** + * 获取待发送的 SyncVar 消息 + */ + public getPendingMessages(): SyncVarMessage[] { + const messages: SyncVarMessage[] = []; + + for (const change of this.pendingChanges) { + messages.push({ + type: 'syncvar', + networkId: change.networkId, + componentType: change.componentType, + propertyName: change.propertyName, + value: change.newValue, + data: change.newValue, + timestamp: change.timestamp + }); + } + + // 清空待同步列表 + this.pendingChanges.length = 0; + return messages; + } + + /** + * 为网络组件添加 SyncVar 支持 + */ + private addSyncVarSupport(component: NetworkBehaviour, metadata: SyncVarMetadata[]): void { + // 添加变化通知方法 + (component as any).notifySyncVarChanged = (propertyName: string, oldValue: any, newValue: any) => { + this.onSyncVarChanged(component, propertyName, oldValue, newValue, metadata); + }; + } + + /** + * 处理 SyncVar 变化 + */ + private onSyncVarChanged( + component: NetworkBehaviour, + propertyName: string, + oldValue: any, + newValue: any, + metadata: SyncVarMetadata[] + ): void { + // 找到对应的元数据 + const syncVarMeta = metadata.find(m => m.propertyName === propertyName); + if (!syncVarMeta) { + return; + } + + // 权限检查 + if (syncVarMeta.authorityOnly && !component.hasAuthority) { + logger.warn(`权限不足,无法修改 SyncVar: ${component.constructor.name}.${propertyName}`); + // 回滚值 + (component as any)[`_${propertyName}`] = oldValue; + return; + } + + // 记录变化 + const change: SyncVarChange = { + networkId: component.networkId, + componentType: component.constructor.name, + propertyName, + oldValue, + newValue, + timestamp: Date.now() + }; + + this.pendingChanges.push(change); + + logger.debug(`SyncVar 变化: ${change.componentType}.${propertyName} = ${newValue}`); + } + + /** + * 应用同步变量值 + */ + private applySyncVar(component: NetworkBehaviour, propertyName: string, value: any): void { + try { + // 直接设置内部值,跳过 setter 的权限检查 + (component as any)[`_${propertyName}`] = value; + + // 获取并调用变化回调 + const metadata = getSyncVarMetadata(component.constructor); + const syncVarMeta = metadata.find(m => m.propertyName === propertyName); + + if (syncVarMeta?.onChanged && typeof (component as any)[syncVarMeta.onChanged] === 'function') { + (component as any)[syncVarMeta.onChanged](undefined, value); + } + + logger.debug(`应用 SyncVar: ${component.constructor.name}.${propertyName} = ${value}`); + } catch (error) { + logger.error(`应用 SyncVar 失败: ${component.constructor.name}.${propertyName}`, error); + } + } + + /** + * 清理所有待同步变化 + */ + public clearPendingChanges(): void { + this.pendingChanges.length = 0; + } + + /** + * 获取统计信息 + */ + public getStats() { + return { + registeredComponents: this.registeredComponents.size, + pendingChanges: this.pendingChanges.length, + totalInstances: Array.from(this.registeredComponents.values()) + .reduce((sum, components) => sum + components.length, 0) + }; + } +} \ No newline at end of file diff --git a/packages/network/src/Core/index.ts b/packages/network/src/Core/index.ts deleted file mode 100644 index 6576af6c..00000000 --- a/packages/network/src/Core/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 网络核心模块导出 - * - * 提供网络管理、连接和通信的核心功能 - */ - -export { NetworkManager } from './NetworkManager'; -export { NetworkServer } from './NetworkServer'; -export { NetworkClient } from './NetworkClient'; -export { NetworkConnection, ConnectionState } from './NetworkConnection'; -export { NetworkEnvironment, NetworkEnvironmentState } from './NetworkEnvironment'; -export { NetworkIdentity, NetworkIdentityRegistry } from './NetworkIdentity'; -export { NetworkPerformanceMonitor } from './NetworkPerformanceMonitor'; -// 事件接口导出 -export type { NetworkServerEvents } from './NetworkServer'; -export type { NetworkClientEvents } from './NetworkClient'; -export type { NetworkConnectionEvents } from './NetworkConnection'; - -// 性能监控类型导出 -export type { NetworkMetrics, PerformanceSnapshot } from './NetworkPerformanceMonitor'; \ No newline at end of file diff --git a/packages/network/src/Error/NetworkErrorHandler.ts b/packages/network/src/Error/NetworkErrorHandler.ts deleted file mode 100644 index f724a6a6..00000000 --- a/packages/network/src/Error/NetworkErrorHandler.ts +++ /dev/null @@ -1,409 +0,0 @@ -/** - * 网络错误处理系统 - */ - -import { ERROR_CODES, LOG_LEVELS } from '../constants/NetworkConstants'; -import { NetworkErrorType, INetworkError, createNetworkError } from '../types/NetworkTypes'; - -/** - * 错误处理器接口 - */ -export interface IErrorHandler { - /** 处理器名称 */ - readonly name: string; - /** 是否可以处理该错误 */ - canHandle(error: INetworkError): boolean; - /** 处理错误 */ - handle(error: INetworkError): Promise | boolean; - /** 优先级(数值越大优先级越高) */ - readonly priority: number; -} - -/** - * 错误处理选项 - */ -export interface ErrorHandlingOptions { - /** 是否自动重试 */ - autoRetry?: boolean; - /** 最大重试次数 */ - maxRetries?: number; - /** 重试延迟(毫秒) */ - retryDelay?: number; - /** 是否记录错误日志 */ - logError?: boolean; - /** 日志级别 */ - logLevel?: 'error' | 'warn' | 'info' | 'debug'; - /** 是否通知用户 */ - notifyUser?: boolean; - /** 自定义处理函数 */ - customHandler?: ((error: INetworkError) => Promise | boolean) | undefined; -} - -/** - * 错误统计信息 - */ -export interface ErrorStats { - /** 总错误数 */ - totalErrors: number; - /** 按类型分组的错误数 */ - errorsByType: Map; - /** 按错误代码分组的错误数 */ - errorsByCode: Map; - /** 最近的错误 */ - recentErrors: INetworkError[]; - /** 错误趋势(每小时) */ - hourlyTrend: number[]; -} - -/** - * 网络错误处理器 - * - * 提供统一的错误处理、重试机制和错误统计功能 - */ -export class NetworkErrorHandler { - private static _instance: NetworkErrorHandler | null = null; - private _handlers: IErrorHandler[] = []; - private _stats: ErrorStats; - private _options: Omit, 'customHandler'> & { customHandler?: (error: INetworkError) => Promise | boolean }; - - private readonly logger = { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug - }; - - private constructor() { - this._stats = { - totalErrors: 0, - errorsByType: new Map(), - errorsByCode: new Map(), - recentErrors: [], - hourlyTrend: new Array(24).fill(0) - }; - - this._options = { - autoRetry: true, - maxRetries: 3, - retryDelay: 1000, - logError: true, - logLevel: 'error' as const, - notifyUser: false, - customHandler: undefined - }; - - this.initializeDefaultHandlers(); - } - - public static get Instance(): NetworkErrorHandler { - if (!NetworkErrorHandler._instance) { - NetworkErrorHandler._instance = new NetworkErrorHandler(); - } - return NetworkErrorHandler._instance; - } - - /** - * 处理网络错误 - */ - public async handleError( - error: Error | INetworkError, - options?: Partial - ): Promise { - const networkError = this.ensureNetworkError(error); - const handlingOptions = { ...this._options, ...options }; - - // 更新统计信息 - this.updateStats(networkError); - - // 记录错误日志 - if (handlingOptions.logError) { - this.logError(networkError, handlingOptions.logLevel); - } - - // 执行自定义处理器 - if (handlingOptions.customHandler) { - try { - const handled = await handlingOptions.customHandler(networkError); - if (handled) { - return true; - } - } catch (customError) { - this.logger.error('自定义错误处理器失败:', customError); - } - } - - // 查找合适的处理器 - const handlers = this._handlers - .filter(handler => handler.canHandle(networkError)) - .sort((a, b) => b.priority - a.priority); - - for (const handler of handlers) { - try { - const handled = await handler.handle(networkError); - if (handled) { - this.logger.debug(`错误已被处理器 "${handler.name}" 处理`); - return true; - } - } catch (handlerError) { - this.logger.error(`处理器 "${handler.name}" 执行失败:`, handlerError); - } - } - - // 自动重试机制 - if (handlingOptions.autoRetry && this.shouldRetry(networkError)) { - return this.attemptRetry(networkError, handlingOptions); - } - - // 通知用户 - if (handlingOptions.notifyUser) { - this.notifyUser(networkError); - } - - return false; - } - - /** - * 添加错误处理器 - */ - public addHandler(handler: IErrorHandler): void { - this._handlers.push(handler); - this._handlers.sort((a, b) => b.priority - a.priority); - this.logger.debug(`添加错误处理器: ${handler.name} (优先级: ${handler.priority})`); - } - - /** - * 移除错误处理器 - */ - public removeHandler(name: string): boolean { - const index = this._handlers.findIndex(handler => handler.name === name); - if (index !== -1) { - this._handlers.splice(index, 1); - this.logger.debug(`移除错误处理器: ${name}`); - return true; - } - return false; - } - - /** - * 设置默认处理选项 - */ - public setDefaultOptions(options: Partial): void { - this._options = { ...this._options, ...options }; - } - - /** - * 获取错误统计信息 - */ - public getStats(): ErrorStats { - return { - totalErrors: this._stats.totalErrors, - errorsByType: new Map(this._stats.errorsByType), - errorsByCode: new Map(this._stats.errorsByCode), - recentErrors: [...this._stats.recentErrors], - hourlyTrend: [...this._stats.hourlyTrend] - }; - } - - /** - * 重置统计信息 - */ - public resetStats(): void { - this._stats = { - totalErrors: 0, - errorsByType: new Map(), - errorsByCode: new Map(), - recentErrors: [], - hourlyTrend: new Array(24).fill(0) - }; - } - - /** - * 创建网络错误 - */ - public createError( - type: NetworkErrorType, - message: string, - context?: Record - ): INetworkError { - return createNetworkError(type, message, context); - } - - /** - * 初始化默认错误处理器 - */ - private initializeDefaultHandlers(): void { - // 连接错误处理器 - this.addHandler({ - name: 'connection-error-handler', - priority: 80, - canHandle: (error) => error.type === NetworkErrorType.CONNECTION_FAILED, - handle: (error) => { - this.logger.warn('连接失败,尝试重新连接:', error.message); - return false; // 让重试机制处理 - } - }); - - // 序列化错误处理器 - this.addHandler({ - name: 'serialization-error-handler', - priority: 70, - canHandle: (error) => - error.type === NetworkErrorType.SERIALIZATION_FAILED || - error.type === NetworkErrorType.DESERIALIZATION_FAILED, - handle: (error) => { - this.logger.error('序列化/反序列化失败:', error.message); - // 序列化错误通常不需要重试 - return true; - } - }); - - // 权限错误处理器 - this.addHandler({ - name: 'permission-error-handler', - priority: 60, - canHandle: (error) => error.type === NetworkErrorType.PERMISSION_DENIED, - handle: (error) => { - this.logger.warn('权限被拒绝:', error.message); - // 权限错误不应该重试 - return true; - } - }); - - // 默认错误处理器 - this.addHandler({ - name: 'default-error-handler', - priority: 0, - canHandle: () => true, - handle: (error) => { - this.logger.error('未处理的网络错误:', error.message, error.context); - return false; - } - }); - } - - /** - * 确保错误是NetworkError类型 - */ - private ensureNetworkError(error: Error | INetworkError): INetworkError { - if ('type' in error && 'timestamp' in error) { - return error as INetworkError; - } - - // 转换普通错误为网络错误 - return createNetworkError( - NetworkErrorType.INVALID_DATA, - error.message, - { originalError: error.name } - ); - } - - /** - * 更新错误统计 - */ - private updateStats(error: INetworkError): void { - this._stats.totalErrors++; - - // 按类型统计 - const typeCount = this._stats.errorsByType.get(error.type) || 0; - this._stats.errorsByType.set(error.type, typeCount + 1); - - // 按错误代码统计 - if (error.code !== undefined) { - const codeCount = this._stats.errorsByCode.get(error.code) || 0; - this._stats.errorsByCode.set(error.code, codeCount + 1); - } - - // 记录最近错误(保留最近100个) - this._stats.recentErrors.push(error); - if (this._stats.recentErrors.length > 100) { - this._stats.recentErrors.shift(); - } - - // 更新小时趋势 - const currentHour = new Date().getHours(); - this._stats.hourlyTrend[currentHour]++; - } - - /** - * 记录错误日志 - */ - private logError(error: INetworkError, level: 'error' | 'warn' | 'info' | 'debug'): void { - const logMessage = `[${error.type}] ${error.message}`; - const logData = { - error: error.message, - type: error.type, - code: error.code, - context: error.context, - timestamp: new Date(error.timestamp).toISOString() - }; - - switch (level) { - case 'error': - this.logger.error(logMessage, logData); - break; - case 'warn': - this.logger.warn(logMessage, logData); - break; - case 'info': - this.logger.info(logMessage, logData); - break; - case 'debug': - this.logger.debug(logMessage, logData); - break; - } - } - - /** - * 判断是否应该重试 - */ - private shouldRetry(error: INetworkError): boolean { - // 这些错误类型不应该重试 - const noRetryTypes = [ - NetworkErrorType.PERMISSION_DENIED, - NetworkErrorType.SERIALIZATION_FAILED, - NetworkErrorType.DESERIALIZATION_FAILED - ]; - - return !noRetryTypes.includes(error.type); - } - - /** - * 尝试重试 - */ - private async attemptRetry( - error: INetworkError, - options: Omit, 'customHandler'> & { customHandler?: (error: INetworkError) => Promise | boolean } - ): Promise { - for (let attempt = 1; attempt <= options.maxRetries; attempt++) { - this.logger.info(`重试处理错误 (${attempt}/${options.maxRetries}):`, error.message); - - // 等待重试延迟 - await new Promise(resolve => setTimeout(resolve, options.retryDelay * attempt)); - - try { - // 这里应该有具体的重试逻辑,取决于错误类型 - // 目前返回false表示重试失败 - return false; - } catch (retryError) { - this.logger.error(`重试失败 (${attempt}):`, retryError); - } - } - - this.logger.error(`重试${options.maxRetries}次后仍然失败:`, error.message); - return false; - } - - /** - * 通知用户 - */ - private notifyUser(error: INetworkError): void { - // 这里可以实现用户通知逻辑 - // 例如显示弹窗、发送邮件等 - console.warn(`用户通知: ${error.message}`); - } -} - -/** - * 错误处理器单例实例 - */ -export const ErrorHandler = NetworkErrorHandler.Instance; \ No newline at end of file diff --git a/packages/network/src/INetworkSyncable.ts b/packages/network/src/INetworkSyncable.ts deleted file mode 100644 index bba2c551..00000000 --- a/packages/network/src/INetworkSyncable.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * 网络同步接口 - * - * 为帧同步框架提供网络状态管理 - */ -export interface INetworkSyncable { - /** - * 获取网络同步状态 - * - * @returns 序列化的网络状态数据 - */ - getNetworkState(): Uint8Array; - - /** - * 应用网络状态 - * - * @param data - 网络状态数据 - */ - applyNetworkState(data: Uint8Array): void; - - /** - * 获取变化的字段编号列表 - * - * @returns 变化字段的编号数组 - */ - getDirtyFields(): number[]; - - /** - * 标记所有字段为干净状态 - */ - markClean(): void; - - /** - * 标记字段为脏状态 - * - * @param fieldNumber - 字段编号 - */ - markFieldDirty(fieldNumber: number): void; - - /** - * 检查字段是否为脏状态 - * - * @param fieldNumber - 字段编号 - * @returns 是否为脏状态 - */ - isFieldDirty(fieldNumber: number): boolean; -} \ No newline at end of file diff --git a/packages/network/src/Messaging/MessageHandler.ts b/packages/network/src/Messaging/MessageHandler.ts deleted file mode 100644 index aab1b2ba..00000000 --- a/packages/network/src/Messaging/MessageHandler.ts +++ /dev/null @@ -1,305 +0,0 @@ -import { NetworkMessage } from './NetworkMessage'; -import { NetworkConnection } from '../Core/NetworkConnection'; -import { IBasicNetworkMessage, MessageData } from '../types/NetworkTypes'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * 消息处理器接口 - */ -export interface IMessageHandler { - /** - * 处理消息 - * - * @param message - 网络消息 - * @param connection - 发送消息的连接(服务端有效) - */ - handle(message: T, connection?: NetworkConnection): Promise | void; -} - -/** - * 消息处理器注册信息 - */ -interface MessageHandlerInfo { - handler: IMessageHandler>; - messageClass: new (...args: any[]) => IBasicNetworkMessage; - priority: number; -} - -/** - * 消息处理器管理器 - * - * 负责注册、查找和调用消息处理器 - * 支持消息优先级和类型匹配 - */ -export class MessageHandler { - private static readonly logger = createLogger('MessageHandler'); - private static _instance: MessageHandler | null = null; - private _handlers: Map = new Map(); - private _messageClasses: Map IBasicNetworkMessage> = new Map(); - - /** - * 获取消息处理器单例 - */ - public static get Instance(): MessageHandler { - if (!MessageHandler._instance) { - MessageHandler._instance = new MessageHandler(); - } - return MessageHandler._instance; - } - - private constructor() {} - - /** - * 注册消息处理器 - * - * @param messageType - 消息类型ID - * @param messageClass - 消息类构造函数 - * @param handler - 消息处理器 - * @param priority - 处理优先级(数字越小优先级越高) - */ - public registerHandler>( - messageType: number, - messageClass: new (...args: any[]) => T, - handler: IMessageHandler, - priority: number = 0 - ): void { - // 注册消息类 - this._messageClasses.set(messageType, messageClass); - - // 获取或创建处理器列表 - if (!this._handlers.has(messageType)) { - this._handlers.set(messageType, []); - } - - const handlers = this._handlers.get(messageType)!; - - // 检查是否已经注册了相同的处理器 - const existingIndex = handlers.findIndex(h => h.handler === handler); - if (existingIndex !== -1) { - MessageHandler.logger.warn(`消息类型 ${messageType} 的处理器已存在,将替换优先级`); - handlers[existingIndex].priority = priority; - } else { - // 添加新处理器 - handlers.push({ - handler: handler as IMessageHandler, - messageClass: messageClass as new (...args: any[]) => IBasicNetworkMessage, - priority - }); - } - - // 按优先级排序(数字越小优先级越高) - handlers.sort((a, b) => a.priority - b.priority); - - MessageHandler.logger.debug(`注册消息处理器: 类型=${messageType}, 优先级=${priority}`); - } - - /** - * 注销消息处理器 - * - * @param messageType - 消息类型ID - * @param handler - 消息处理器 - */ - public unregisterHandler(messageType: number, handler: IMessageHandler): void { - const handlers = this._handlers.get(messageType); - if (!handlers) { - return; - } - - const index = handlers.findIndex(h => h.handler === handler); - if (index !== -1) { - handlers.splice(index, 1); - MessageHandler.logger.debug(`注销消息处理器: 类型=${messageType}`); - } - - // 如果没有处理器了,清理映射 - if (handlers.length === 0) { - this._handlers.delete(messageType); - this._messageClasses.delete(messageType); - } - } - - /** - * 处理原始消息数据 - * - * @param data - 原始消息数据 - * @param connection - 发送消息的连接(服务端有效) - * @returns 是否成功处理 - */ - public async handleRawMessage(data: Uint8Array, connection?: NetworkConnection): Promise { - if (data.length < 4) { - MessageHandler.logger.error('消息数据长度不足,至少需要4字节消息类型'); - return false; - } - - // 读取消息类型(前4字节) - const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - const messageType = view.getUint32(0, true); - - // 查找消息类 - const MessageClass = this._messageClasses.get(messageType); - if (!MessageClass) { - MessageHandler.logger.warn(`未知的消息类型: ${messageType}`); - return false; - } - - // 创建消息实例并反序列化 - try { - const message = new MessageClass(); - message.deserialize(data); - - return await this.handleMessage(message, connection); - } catch (error) { - MessageHandler.logger.error(`消息反序列化失败 (类型=${messageType}):`, error); - return false; - } - } - - /** - * 处理网络消息 - * - * @param message - 网络消息 - * @param connection - 发送消息的连接(服务端有效) - * @returns 是否成功处理 - */ - public async handleMessage(message: IBasicNetworkMessage, connection?: NetworkConnection): Promise { - const messageType = message.messageType; - const handlers = this._handlers.get(messageType); - - if (!handlers || handlers.length === 0) { - MessageHandler.logger.warn(`没有找到消息类型 ${messageType} 的处理器`); - return false; - } - - let handledCount = 0; - - // 按优先级顺序执行所有处理器 - for (const handlerInfo of handlers) { - try { - const result = handlerInfo.handler.handle(message, connection); - - // 支持异步处理器 - if (result instanceof Promise) { - await result; - } - - handledCount++; - } catch (error) { - MessageHandler.logger.error(`处理器执行错误 (类型=${messageType}, 优先级=${handlerInfo.priority}):`, error); - // 继续执行其他处理器 - } - } - - return handledCount > 0; - } - - /** - * 获取已注册的消息类型列表 - * - * @returns 消息类型数组 - */ - public getRegisteredMessageTypes(): number[] { - return Array.from(this._messageClasses.keys()); - } - - /** - * 检查消息类型是否已注册 - * - * @param messageType - 消息类型ID - * @returns 是否已注册 - */ - public isMessageTypeRegistered(messageType: number): boolean { - return this._messageClasses.has(messageType); - } - - /** - * 获取消息类型的处理器数量 - * - * @param messageType - 消息类型ID - * @returns 处理器数量 - */ - public getHandlerCount(messageType: number): number { - const handlers = this._handlers.get(messageType); - return handlers ? handlers.length : 0; - } - - /** - * 清除所有处理器 - */ - public clear(): void { - this._handlers.clear(); - this._messageClasses.clear(); - MessageHandler.logger.info('已清除所有消息处理器'); - } - - /** - * 获取消息处理器统计信息 - * - * @returns 统计信息 - */ - public getStats(): { - totalMessageTypes: number; - totalHandlers: number; - messageTypes: Array<{ - type: number; - handlerCount: number; - className: string; - }>; - } { - let totalHandlers = 0; - const messageTypes: Array<{ type: number; handlerCount: number; className: string }> = []; - - for (const [type, handlers] of this._handlers) { - const handlerCount = handlers.length; - totalHandlers += handlerCount; - - const MessageClass = this._messageClasses.get(type); - const className = MessageClass ? MessageClass.name : 'Unknown'; - - messageTypes.push({ - type, - handlerCount, - className - }); - } - - return { - totalMessageTypes: this._messageClasses.size, - totalHandlers, - messageTypes - }; - } -} - -/** - * 消息处理器装饰器 - * - * 用于自动注册消息处理器 - * - * @param messageType - 消息类型ID - * @param messageClass - 消息类构造函数 - * @param priority - 处理优先级 - */ -export function MessageHandlerDecorator>( - messageType: number, - messageClass: new (...args: any[]) => T, - priority: number = 0 -) { - return function(target: unknown, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - if (typeof originalMethod !== 'function') { - throw new Error(`[MessageHandlerDecorator] ${propertyKey} is not a function`); - } - - // 注册处理器 - const handler: IMessageHandler = { - handle: async (message: T, connection?: NetworkConnection) => { - return await originalMethod.call(target, message, connection); - } - }; - - MessageHandler.Instance.registerHandler(messageType, messageClass, handler, priority); - - return descriptor; - }; -} \ No newline at end of file diff --git a/packages/network/src/Messaging/MessageTypes.ts b/packages/network/src/Messaging/MessageTypes.ts deleted file mode 100644 index d76fac4f..00000000 --- a/packages/network/src/Messaging/MessageTypes.ts +++ /dev/null @@ -1,513 +0,0 @@ -import { NetworkMessage, JsonMessage } from './NetworkMessage'; -import { MessageType as CoreMessageType } from '../types/MessageTypes'; -import { MESSAGE_CONFIG } from '../constants/NetworkConstants'; - -/** - * 内置消息类型枚举 - */ -export enum MessageType { - // 基础消息类型 (0-99) - RAW = 0, - JSON = 1, - PROTOBUF = 2, - - // 连接管理消息 (100-199) - CONNECT_REQUEST = 100, - CONNECT_RESPONSE = 101, - DISCONNECT = 102, - PING = 103, - PONG = 104, - - // 身份验证消息 (200-299) - AUTH_REQUEST = 200, - AUTH_RESPONSE = 201, - - // 网络对象管理 (300-399) - SPAWN_OBJECT = 300, - DESTROY_OBJECT = 301, - TRANSFER_AUTHORITY = 302, - - // 组件同步消息 (400-499) - SYNC_VAR_UPDATE = 400, - COMPONENT_STATE = 401, - BATCH_UPDATE = 402, - - // RPC调用消息 (500-599) - CLIENT_RPC = 500, - SERVER_RPC = 501, - RPC_RESPONSE = 502, - - // 场景管理消息 (600-699) - SCENE_LOAD = 600, - SCENE_LOADED = 601, - SCENE_UNLOAD = 602, - - // 自定义消息 (1000+) - CUSTOM = 1000 -} - -/** - * 连接请求消息 - */ -export class ConnectRequestMessage extends JsonMessage<{ - clientVersion: string; - protocolVersion: number; - clientId?: string; -}> { - public override readonly messageType: number = MessageType.CONNECT_REQUEST; - - constructor(clientVersion: string = '1.0.0', protocolVersion: number = 1, clientId?: string) { - super({ - clientVersion, - protocolVersion, - clientId - }); - } -} - -/** - * 连接响应消息 - */ -export class ConnectResponseMessage extends JsonMessage<{ - success: boolean; - clientId: string; - serverVersion: string; - protocolVersion: number; - errorMessage?: string; -}> { - public override readonly messageType: number = MessageType.CONNECT_RESPONSE; - - constructor( - success: boolean, - clientId: string, - serverVersion: string = '1.0.0', - protocolVersion: number = 1, - errorMessage?: string - ) { - super({ - success, - clientId, - serverVersion, - protocolVersion, - errorMessage - }); - } -} - -/** - * 断开连接消息 - */ -export class DisconnectMessage extends JsonMessage<{ - reason: string; - code?: number; -}> { - public override readonly messageType: number = MessageType.DISCONNECT; - - constructor(reason: string, code?: number) { - super({ - reason, - code - }); - } -} - -/** - * 心跳消息 - */ -export class PingMessage extends NetworkMessage<{ pingId: number }> { - public readonly messageType: number = MessageType.PING; - private _data: { pingId: number }; - - public get data(): { pingId: number } { - return this._data; - } - - public get pingId(): number { - return this._data.pingId; - } - - public set pingId(value: number) { - this._data.pingId = value; - } - - constructor(pingId: number = Date.now()) { - super(); - this._data = { pingId }; - } - - public serialize(): Uint8Array { - const buffer = new ArrayBuffer(12); // 4字节时间戳 + 4字节pingId + 4字节消息类型 - const view = new DataView(buffer); - - view.setUint32(0, this.messageType, true); - view.setUint32(4, this.timestamp, true); - view.setUint32(8, this._data.pingId, true); - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 12) { - throw new Error('Ping消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - // messageType在第0-3字节已经被外部处理 - this.timestamp = view.getUint32(4, true); - this._data.pingId = view.getUint32(8, true); - } -} - -/** - * 心跳响应消息 - */ -export class PongMessage extends NetworkMessage<{ pingId: number; serverTime: number }> { - public readonly messageType: number = MessageType.PONG; - private _data: { pingId: number; serverTime: number }; - - public get data(): { pingId: number; serverTime: number } { - return this._data; - } - - public get pingId(): number { - return this._data.pingId; - } - - public set pingId(value: number) { - this._data.pingId = value; - } - - public get serverTime(): number { - return this._data.serverTime; - } - - public set serverTime(value: number) { - this._data.serverTime = value; - } - - constructor(pingId: number = 0, serverTime: number = Date.now()) { - super(); - this._data = { pingId, serverTime }; - } - - public serialize(): Uint8Array { - const buffer = new ArrayBuffer(16); // 4字节消息类型 + 4字节时间戳 + 4字节pingId + 4字节服务器时间 - const view = new DataView(buffer); - - view.setUint32(0, this.messageType, true); - view.setUint32(4, this.timestamp, true); - view.setUint32(8, this._data.pingId, true); - view.setUint32(12, this._data.serverTime, true); - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 16) { - throw new Error('Pong消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - // messageType在第0-3字节已经被外部处理 - this.timestamp = view.getUint32(4, true); - this._data.pingId = view.getUint32(8, true); - this._data.serverTime = view.getUint32(12, true); - } -} - -/** - * 网络对象生成消息 - */ -export class SpawnObjectMessage extends JsonMessage<{ - networkId: string; - prefabName: string; - position: { x: number; y: number; z?: number }; - rotation?: { x: number; y: number; z: number; w: number }; - ownerId: string; - hasAuthority: boolean; - components?: Array<{ - type: string; - data: string; // base64编码的protobuf数据 - }>; -}> { - public override readonly messageType: number = MessageType.SPAWN_OBJECT; - - constructor( - networkId: string, - prefabName: string, - position: { x: number; y: number; z?: number }, - ownerId: string, - hasAuthority: boolean = false, - rotation?: { x: number; y: number; z: number; w: number }, - components?: Array<{ type: string; data: string }> - ) { - super({ - networkId, - prefabName, - position, - rotation, - ownerId, - hasAuthority, - components - }); - } -} - -/** - * 网络对象销毁消息 - */ -export class DestroyObjectMessage extends JsonMessage<{ - networkId: string; - reason?: string; -}> { - public override readonly messageType: number = MessageType.DESTROY_OBJECT; - - constructor(networkId: string, reason?: string) { - super({ - networkId, - reason - }); - } -} - -/** - * 权限转移消息 - */ -export class TransferAuthorityMessage extends JsonMessage<{ - networkId: string; - newOwnerId: string; - previousOwnerId: string; -}> { - public override readonly messageType: number = MessageType.TRANSFER_AUTHORITY; - - constructor(networkId: string, newOwnerId: string, previousOwnerId: string) { - super({ - networkId, - newOwnerId, - previousOwnerId - }); - } -} - -/** - * SyncVar字段更新数据 - */ -export interface SyncVarFieldUpdate { - /** 字段编号 */ - fieldNumber: number; - /** 字段名称(用于调试) */ - propertyKey: string; - /** 序列化后的新值 */ - newValue: string | number | boolean | null | undefined | Date | Uint8Array | Record | unknown[]; - /** 序列化后的旧值(用于回滚或调试) */ - oldValue?: string | number | boolean | null | undefined | Date | Uint8Array | Record | unknown[]; - /** 字段变化时间戳 */ - timestamp: number; - /** 是否是权威字段(只有权威端可以修改) */ - authorityOnly?: boolean; -} - -/** - * SyncVar更新消息数据结构 - */ -export interface SyncVarUpdateData extends Record { - /** 网络对象ID */ - networkId: string; - /** 组件类型名称 */ - componentType: string; - /** 字段更新列表 */ - fieldUpdates: SyncVarFieldUpdate[]; - /** 是否是完整状态同步(而非增量更新) */ - isFullSync: boolean; - /** 发送者ID */ - senderId: string; - /** 同步序号(用于确保顺序) */ - syncSequence: number; -} - -/** - * SyncVar更新消息 - * - * 支持增量同步和批处理 - */ -export class SyncVarUpdateMessage extends JsonMessage { - public override readonly messageType: number = MessageType.SYNC_VAR_UPDATE; - - /** 网络对象ID */ - public get networkId(): string { - return this.payload.networkId; - } - - public set networkId(value: string) { - this.payload.networkId = value; - } - - /** 组件类型名称 */ - public get componentType(): string { - return this.payload.componentType; - } - - public set componentType(value: string) { - this.payload.componentType = value; - } - - /** 字段更新列表 */ - public get fieldUpdates(): SyncVarFieldUpdate[] { - return this.payload.fieldUpdates; - } - - public set fieldUpdates(value: SyncVarFieldUpdate[]) { - this.payload.fieldUpdates = value; - } - - /** 是否是完整状态同步(而非增量更新) */ - public get isFullSync(): boolean { - return this.payload.isFullSync; - } - - public set isFullSync(value: boolean) { - this.payload.isFullSync = value; - } - - /** 同步序号(用于确保顺序) */ - public get syncSequence(): number { - return this.payload.syncSequence; - } - - public set syncSequence(value: number) { - this.payload.syncSequence = value; - } - - constructor( - networkId: string = '', - componentType: string = '', - fieldUpdates: SyncVarFieldUpdate[] = [], - isFullSync: boolean = false, - senderId: string = '', - syncSequence: number = 0 - ) { - const data: SyncVarUpdateData = { - networkId, - componentType, - fieldUpdates, - isFullSync, - senderId, - syncSequence - }; - super(data, senderId, syncSequence); - } - - /** - * 添加字段更新 - */ - public addFieldUpdate(update: SyncVarFieldUpdate): void { - this.payload.fieldUpdates.push(update); - } - - /** - * 获取指定字段的更新 - */ - public getFieldUpdate(fieldNumber: number): SyncVarFieldUpdate | undefined { - return this.payload.fieldUpdates.find(update => update.fieldNumber === fieldNumber); - } - - /** - * 移除指定字段的更新 - */ - public removeFieldUpdate(fieldNumber: number): boolean { - const index = this.payload.fieldUpdates.findIndex(update => update.fieldNumber === fieldNumber); - if (index !== -1) { - this.payload.fieldUpdates.splice(index, 1); - return true; - } - return false; - } - - /** - * 清空所有字段更新 - */ - public clearFieldUpdates(): void { - this.payload.fieldUpdates = []; - } - - /** - * 获取更新的字段数量 - */ - public getUpdateCount(): number { - return this.payload.fieldUpdates.length; - } - - /** - * 检查是否有字段更新 - */ - public hasUpdates(): boolean { - return this.payload.fieldUpdates.length > 0; - } - - - - /** - * 创建消息副本 - */ - public override clone(): SyncVarUpdateMessage { - return new SyncVarUpdateMessage( - this.payload.networkId, - this.payload.componentType, - [...this.payload.fieldUpdates], // 深拷贝字段更新数组 - this.payload.isFullSync, - this.payload.senderId, - this.payload.syncSequence - ); - } - - /** - * 获取消息统计信息 - */ - public getStats(): { - updateCount: number; - estimatedSize: number; - hasAuthorityOnlyFields: boolean; - oldestUpdateTime: number; - newestUpdateTime: number; - } { - if (this.payload.fieldUpdates.length === 0) { - return { - updateCount: 0, - estimatedSize: this.getSize(), - hasAuthorityOnlyFields: false, - oldestUpdateTime: 0, - newestUpdateTime: 0 - }; - } - - const timestamps = this.payload.fieldUpdates.map(u => u.timestamp); - const hasAuthorityOnlyFields = this.payload.fieldUpdates.some(u => u.authorityOnly); - - return { - updateCount: this.payload.fieldUpdates.length, - estimatedSize: this.getSize(), - hasAuthorityOnlyFields, - oldestUpdateTime: Math.min(...timestamps), - newestUpdateTime: Math.max(...timestamps) - }; - } -} - -/** - * 批量更新消息 - * - * 用于一次性发送多个对象的状态更新 - */ -export class BatchUpdateMessage extends JsonMessage<{ - updates: Array<{ - networkId: string; - componentType: string; - data: string; // base64编码的完整组件状态 - }>; -}> { - public override readonly messageType: number = MessageType.BATCH_UPDATE; - - constructor(updates: Array<{ networkId: string; componentType: string; data: string }>) { - super({ updates }); - } -} \ No newline at end of file diff --git a/packages/network/src/Messaging/NetworkMessage.ts b/packages/network/src/Messaging/NetworkMessage.ts deleted file mode 100644 index e74eb04b..00000000 --- a/packages/network/src/Messaging/NetworkMessage.ts +++ /dev/null @@ -1,675 +0,0 @@ -import { IBasicNetworkMessage, MessageData } from '../types/NetworkTypes'; - -/** - * 网络消息基类 - * - * 所有网络消息都应该继承此类 - * 提供消息的序列化和反序列化功能 - */ -export abstract class NetworkMessage implements IBasicNetworkMessage { - /** - * 消息类型ID - * 每个消息类型都应该有唯一的ID - */ - public abstract readonly messageType: number; - - /** - * 消息数据 - */ - public abstract readonly data: TData; - - /** - * 消息时间戳 - */ - public timestamp: number = Date.now(); - - /** - * 发送者ID - */ - public senderId?: string; - - /** - * 消息序列号 - */ - public sequence?: number; - - /** - * 序列化消息为二进制数据 - * - * @returns 序列化后的数据 - */ - public abstract serialize(): Uint8Array; - - /** - * 从二进制数据反序列化消息 - * - * @param data - 二进制数据 - */ - public abstract deserialize(data: Uint8Array): void; - - /** - * 创建消息实例 - */ - protected constructor( - senderId?: string, - sequence?: number - ) { - this.senderId = senderId; - this.sequence = sequence; - } - - /** - * 获取消息大小(字节) - * - * @returns 消息大小 - */ - public getSize(): number { - return this.serialize().length; - } - - /** - * 创建消息副本 - * - * @returns 消息副本 - */ - public clone(): NetworkMessage { - const Constructor = this.constructor as new (senderId?: string, sequence?: number) => NetworkMessage; - const cloned = new Constructor(this.senderId, this.sequence); - const data = this.serialize(); - cloned.deserialize(data); - return cloned; - } -} - -/** - * 原始二进制消息 - * - * 用于传输原始二进制数据,不进行额外的序列化处理 - */ -export class RawMessage extends NetworkMessage { - public readonly messageType: number = 0; - private _data: Uint8Array; - - public get data(): Uint8Array { - return this._data; - } - - constructor( - data: Uint8Array = new Uint8Array(0), - senderId?: string, - sequence?: number - ) { - super(senderId, sequence); - this._data = data; - } - - public serialize(): Uint8Array { - // 创建包含消息类型的完整消息格式:[4字节消息类型][原始数据] - const buffer = new ArrayBuffer(4 + this._data.length); - const view = new DataView(buffer); - const uint8Array = new Uint8Array(buffer); - - // 写入消息类型 - view.setUint32(0, this.messageType, true); - - // 写入原始数据 - uint8Array.set(this._data, 4); - - return uint8Array; - } - - public deserialize(data: Uint8Array): void { - // 原始数据从第4字节开始(前4字节是消息类型) - this._data = data.subarray(4); - } -} - -/** - * JSON消息 - * - * 用于传输JSON数据,自动进行JSON序列化和反序列化 - */ -export class JsonMessage> extends NetworkMessage> { - public readonly messageType: number = 1; - private _data: Record; - - public get data(): Record { - return this._data; - } - - constructor( - payload: T = {} as T, - senderId?: string, - sequence?: number - ) { - super(senderId, sequence); - this._data = { payload }; - } - - public get payload(): T { - return this._data.payload as T; - } - - public serialize(): Uint8Array { - const payloadBytes = this.serializePayload(this._data.payload); - const senderIdBytes = new TextEncoder().encode(this.senderId || ''); - - const buffer = new ArrayBuffer( - 4 + // messageType - 8 + // timestamp - 4 + // sequence - 4 + // senderId length - senderIdBytes.length + // senderId - payloadBytes.length - ); - - const view = new DataView(buffer); - const uint8Array = new Uint8Array(buffer); - let offset = 0; - - view.setUint32(offset, this.messageType, true); - offset += 4; - - view.setBigUint64(offset, BigInt(this.timestamp), true); - offset += 8; - - view.setUint32(offset, this.sequence || 0, true); - offset += 4; - - view.setUint32(offset, senderIdBytes.length, true); - offset += 4; - - uint8Array.set(senderIdBytes, offset); - offset += senderIdBytes.length; - - uint8Array.set(payloadBytes, offset); - - return uint8Array; - } - - public deserialize(data: Uint8Array): void { - const view = new DataView(data.buffer, data.byteOffset); - let offset = 4; // 跳过messageType - - this.timestamp = Number(view.getBigUint64(offset, true)); - offset += 8; - - this.sequence = view.getUint32(offset, true); - offset += 4; - - const senderIdLength = view.getUint32(offset, true); - offset += 4; - - this.senderId = new TextDecoder().decode(data.subarray(offset, offset + senderIdLength)); - offset += senderIdLength; - - const payloadBytes = data.subarray(offset); - this._data = { payload: this.deserializePayload(payloadBytes) }; - } - - /** - * 序列化payload,子类可以重写以使用不同的序列化策略 - */ - protected serializePayload(payload: unknown): Uint8Array { - const jsonString = JSON.stringify(payload); - return new TextEncoder().encode(jsonString); - } - - /** - * 反序列化payload,子类可以重写以使用不同的反序列化策略 - */ - protected deserializePayload(data: Uint8Array): unknown { - const jsonString = new TextDecoder().decode(data); - return JSON.parse(jsonString); - } -} - -/** - * Protobuf消息包装器 - * - * 用于包装已经序列化的Protobuf数据 - */ -export class ProtobufMessage extends NetworkMessage { - public readonly messageType: number = 2; - private _componentType: string; - private _data: Uint8Array; - - public get componentType(): string { - return this._componentType; - } - - public get data(): Uint8Array { - return this._data; - } - - constructor( - componentType: string = '', - data: Uint8Array = new Uint8Array(0), - senderId?: string, - sequence?: number - ) { - super(senderId, sequence); - this._componentType = componentType; - this._data = data; - } - - public serialize(): Uint8Array { - // 创建包含头部信息的消息格式: - // [4字节消息类型][4字节时间戳][1字节组件类型长度][组件类型字符串][protobuf数据] - const typeBytes = new TextEncoder().encode(this._componentType); - const buffer = new ArrayBuffer(4 + 4 + 1 + typeBytes.length + this._data.length); - const view = new DataView(buffer); - const uint8Array = new Uint8Array(buffer); - - let offset = 0; - - // 写入消息类型(4字节) - view.setUint32(offset, this.messageType, true); - offset += 4; - - // 写入时间戳(4字节) - view.setUint32(offset, this.timestamp, true); - offset += 4; - - // 写入组件类型长度(1字节) - view.setUint8(offset, typeBytes.length); - offset += 1; - - // 写入组件类型字符串 - uint8Array.set(typeBytes, offset); - offset += typeBytes.length; - - // 写入protobuf数据 - uint8Array.set(this._data, offset); - - return uint8Array; - } - - public deserialize(data: Uint8Array): void { - if (data.length < 9) { // 4+4+1 = 9字节最小长度 - throw new Error('Protobuf消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset, data.byteLength); - let offset = 4; // 跳过前4字节消息类型 - - // 读取时间戳(4字节) - this.timestamp = view.getUint32(offset, true); - offset += 4; - - // 读取组件类型长度(1字节) - const typeLength = view.getUint8(offset); - offset += 1; - - if (data.length < offset + typeLength) { - throw new Error('Protobuf消息组件类型数据不足'); - } - - // 读取组件类型字符串 - const typeBytes = data.subarray(offset, offset + typeLength); - this._componentType = new TextDecoder().decode(typeBytes); - offset += typeLength; - - // 读取protobuf数据 - this._data = data.subarray(offset); - } -} - -/** - * 心跳消息 - * 用于维持连接和检测网络延迟 - */ -export class HeartbeatMessage extends NetworkMessage<{ ping: boolean; timestamp: number }> { - public readonly messageType: number = 4; - private _isPing: boolean; - private _pingTimestamp: number; - - public get data(): { ping: boolean; timestamp: number } { - return { - ping: this._isPing, - timestamp: this._pingTimestamp - }; - } - - constructor(isPing: boolean = true, timestamp: number = Date.now(), senderId?: string, sequence?: number) { - super(senderId, sequence); - this._isPing = isPing; - this._pingTimestamp = timestamp; - } - - public serialize(): Uint8Array { - const buffer = new ArrayBuffer(13); // 4(type) + 1(ping) + 8(timestamp) - const view = new DataView(buffer); - - view.setUint32(0, this.messageType, true); - view.setUint8(4, this._isPing ? 1 : 0); - // 使用BigUint64将timestamp存储为64位整数 - view.setBigUint64(5, BigInt(this._pingTimestamp), true); - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 13) { - throw new Error('心跳消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset); - this._isPing = view.getUint8(4) === 1; - this._pingTimestamp = Number(view.getBigUint64(5, true)); - } - - /** - * 创建ping消息 - */ - public static createPing(senderId?: string): HeartbeatMessage { - return new HeartbeatMessage(true, Date.now(), senderId); - } - - /** - * 创建pong消息 - */ - public static createPong(originalTimestamp: number, senderId?: string): HeartbeatMessage { - return new HeartbeatMessage(false, originalTimestamp, senderId); - } -} - -/** - * 错误消息 - * 用于传递错误信息 - */ -export class ErrorMessage extends NetworkMessage<{ code: number; message: string; details?: any }> { - public readonly messageType: number = 5; - private _errorCode: number; - private _errorMessage: string; - private _details?: any; - - public get data(): { code: number; message: string; details?: any } { - return { - code: this._errorCode, - message: this._errorMessage, - details: this._details - }; - } - - constructor(code: number, message: string, details?: any, senderId?: string, sequence?: number) { - super(senderId, sequence); - this._errorCode = code; - this._errorMessage = message; - this._details = details; - } - - public serialize(): Uint8Array { - const messageBytes = new TextEncoder().encode(this._errorMessage); - const detailsBytes = this._details ? new TextEncoder().encode(JSON.stringify(this._details)) : new Uint8Array(0); - - const buffer = new ArrayBuffer(4 + 4 + 4 + messageBytes.length + 4 + detailsBytes.length); - const view = new DataView(buffer); - - let offset = 0; - view.setUint32(offset, this.messageType, true); - offset += 4; - - view.setUint32(offset, this._errorCode, true); - offset += 4; - - view.setUint32(offset, messageBytes.length, true); - offset += 4; - - new Uint8Array(buffer, offset, messageBytes.length).set(messageBytes); - offset += messageBytes.length; - - view.setUint32(offset, detailsBytes.length, true); - offset += 4; - - if (detailsBytes.length > 0) { - new Uint8Array(buffer, offset, detailsBytes.length).set(detailsBytes); - } - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 16) { // 至少4+4+4+4字节 - throw new Error('错误消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset); - let offset = 4; // 跳过消息类型 - - this._errorCode = view.getUint32(offset, true); - offset += 4; - - const messageLength = view.getUint32(offset, true); - offset += 4; - - if (data.length < offset + messageLength + 4) { - throw new Error('错误消息数据不足'); - } - - const messageBytes = data.subarray(offset, offset + messageLength); - this._errorMessage = new TextDecoder().decode(messageBytes); - offset += messageLength; - - const detailsLength = view.getUint32(offset, true); - offset += 4; - - if (detailsLength > 0) { - if (data.length < offset + detailsLength) { - throw new Error('错误消息详情数据不足'); - } - const detailsBytes = data.subarray(offset, offset + detailsLength); - try { - this._details = JSON.parse(new TextDecoder().decode(detailsBytes)); - } catch { - this._details = new TextDecoder().decode(detailsBytes); - } - } - } -} - -/** - * 状态同步消息 - * 用于同步游戏对象状态 - */ -export interface StateData extends Record { - entityId: number; - position?: { x: number; y: number; z?: number }; - rotation?: { x: number; y: number; z: number; w: number }; - velocity?: { x: number; y: number; z?: number }; - health?: number; - customData?: Record; -} - -export class StateSyncMessage extends NetworkMessage { - public readonly messageType: number = 6; - private _stateData: StateData; - - public get data(): StateData { - return this._stateData; - } - - constructor(stateData: StateData, senderId?: string, sequence?: number) { - super(senderId, sequence); - this._stateData = stateData; - } - - public serialize(): Uint8Array { - // 使用JSON序列化(可以替换为更高效的二进制协议) - const jsonString = JSON.stringify(this._stateData); - const jsonBytes = new TextEncoder().encode(jsonString); - - const buffer = new ArrayBuffer(8 + jsonBytes.length); - const view = new DataView(buffer); - - view.setUint32(0, this.messageType, true); - view.setUint32(4, jsonBytes.length, true); - - new Uint8Array(buffer, 8, jsonBytes.length).set(jsonBytes); - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 8) { - throw new Error('状态同步消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset); - const jsonLength = view.getUint32(4, true); - - if (data.length < 8 + jsonLength) { - throw new Error('状态同步消息JSON数据不足'); - } - - const jsonBytes = data.subarray(8, 8 + jsonLength); - const jsonString = new TextDecoder().decode(jsonBytes); - - try { - this._stateData = JSON.parse(jsonString); - } catch (error) { - throw new Error(`状态同步消息JSON解析失败: ${error}`); - } - } -} - -/** - * 房间消息 - * 用于房间相关的操作和通知 - */ -export interface RoomMessageData extends Record { - action: 'join' | 'leave' | 'kick' | 'message' | 'update'; - roomId: string; - playerId?: number; - playerName?: string; - message?: string; - metadata?: Record; -} - -export class RoomMessage extends NetworkMessage { - public readonly messageType: number = 7; - private _roomData: RoomMessageData; - - public get data(): RoomMessageData { - return this._roomData; - } - - constructor(roomData: RoomMessageData, senderId?: string, sequence?: number) { - super(senderId, sequence); - this._roomData = roomData; - } - - public serialize(): Uint8Array { - const jsonString = JSON.stringify(this._roomData); - const jsonBytes = new TextEncoder().encode(jsonString); - - const buffer = new ArrayBuffer(8 + jsonBytes.length); - const view = new DataView(buffer); - - view.setUint32(0, this.messageType, true); - view.setUint32(4, jsonBytes.length, true); - - new Uint8Array(buffer, 8, jsonBytes.length).set(jsonBytes); - - return new Uint8Array(buffer); - } - - public deserialize(data: Uint8Array): void { - if (data.length < 8) { - throw new Error('房间消息数据长度不足'); - } - - const view = new DataView(data.buffer, data.byteOffset); - const jsonLength = view.getUint32(4, true); - - if (data.length < 8 + jsonLength) { - throw new Error('房间消息JSON数据不足'); - } - - const jsonBytes = data.subarray(8, 8 + jsonLength); - const jsonString = new TextDecoder().decode(jsonBytes); - - try { - this._roomData = JSON.parse(jsonString); - } catch (error) { - throw new Error(`房间消息JSON解析失败: ${error}`); - } - } -} - -/** - * 消息工厂 - * 根据消息类型创建相应的消息实例 - */ -export class NetworkMessageFactory { - private static messageConstructors = new Map NetworkMessage>([ - [0, RawMessage], - [1, JsonMessage], - [2, ProtobufMessage], - [4, HeartbeatMessage], - [5, ErrorMessage], - [6, StateSyncMessage], - [7, RoomMessage] - ]); - - /** - * 根据消息类型创建消息实例 - */ - public static createMessage(messageType: number): NetworkMessage | null { - const Constructor = this.messageConstructors.get(messageType); - if (!Constructor) { - return null; - } - // 为不同消息类型提供默认参数 - try { - switch (messageType) { - case 4: // HeartbeatMessage - return new Constructor(); - case 5: // ErrorMessage - return new Constructor(0, ''); - case 6: // StateSyncMessage - return new Constructor({ entityId: 0 }); - case 7: // RoomMessage - return new Constructor({ action: 'message', roomId: '' }); - default: - return new Constructor(); - } - } catch { - return null; - } - } - - /** - * 从二进制数据反序列化消息 - */ - public static deserializeMessage(data: Uint8Array): NetworkMessage | null { - if (data.length < 4) { - return null; - } - - const view = new DataView(data.buffer, data.byteOffset); - const messageType = view.getUint32(0, true); - - const message = this.createMessage(messageType); - if (!message) { - return null; - } - - try { - message.deserialize(data); - return message; - } catch { - return null; - } - } - - /** - * 注册新的消息类型 - */ - public static registerMessageType( - messageType: number, - constructor: new (...args: any[]) => T - ): void { - this.messageConstructors.set(messageType, constructor as new (...args: any[]) => NetworkMessage); - } -} \ No newline at end of file diff --git a/packages/network/src/Messaging/index.ts b/packages/network/src/Messaging/index.ts deleted file mode 100644 index ba466239..00000000 --- a/packages/network/src/Messaging/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * 消息系统导出 - * - * 提供网络消息的定义、处理和管理功能 - */ - -// 消息基类和类型 -export * from './NetworkMessage'; -export * from './MessageTypes'; -export * from './MessageHandler'; - -// 导出SyncVar相关的接口和类型 -export type { SyncVarFieldUpdate } from './MessageTypes'; \ No newline at end of file diff --git a/packages/network/src/NetworkBehaviour.ts b/packages/network/src/NetworkBehaviour.ts new file mode 100644 index 00000000..795e5c13 --- /dev/null +++ b/packages/network/src/NetworkBehaviour.ts @@ -0,0 +1,141 @@ +/** + * 网络组件基类 + * + * 所有需要网络同步功能的组件都应继承此类 + * 提供 SyncVar 和 RPC 功能的基础实现 + */ + +import { Component } from '@esengine/ecs-framework'; +import { INetworkBehaviour } from './types/NetworkTypes'; +import { NetworkIdentity } from './NetworkIdentity'; +import { NetworkManager } from './NetworkManager'; + +export abstract class NetworkBehaviour extends Component implements INetworkBehaviour { + /** 网络身份组件引用 */ + public networkIdentity: NetworkIdentity | null = null; + + /** + * 是否拥有权威 + * 权威端可以修改带有 authorityOnly 标记的 SyncVar + */ + public get hasAuthority(): boolean { + return this.networkIdentity?.hasAuthority || false; + } + + /** + * 是否为本地玩家 + */ + public get isLocalPlayer(): boolean { + return this.networkIdentity?.isLocalPlayer || false; + } + + /** + * 是否在服务端运行 + */ + public get isServer(): boolean { + return NetworkManager.isServer; + } + + /** + * 是否在客户端运行 + */ + public get isClient(): boolean { + return NetworkManager.isClient; + } + + /** + * 网络ID + */ + public get networkId(): number { + return this.networkIdentity?.networkId || 0; + } + + /** + * 组件初始化时自动注册到网络身份 + */ + public start(): void { + this.registerNetworkBehaviour(); + } + + /** + * 组件销毁时自动从网络身份注销 + */ + public destroy(): void { + this.unregisterNetworkBehaviour(); + } + + /** + * 注册到网络身份组件 + */ + private registerNetworkBehaviour(): void { + if (!this.entity) return; + + const networkIdentity = this.entity.getComponent?.(NetworkIdentity); + if (networkIdentity) { + networkIdentity.addNetworkBehaviour(this); + } + } + + /** + * 从网络身份组件注销 + */ + private unregisterNetworkBehaviour(): void { + if (this.networkIdentity) { + this.networkIdentity.removeNetworkBehaviour(this); + } + } + + /** + * 检查权威性 + * 用于验证是否可以执行需要权威的操作 + */ + protected checkAuthority(): boolean { + if (!this.hasAuthority) { + console.warn(`[NetworkBehaviour] 操作被拒绝:${this.constructor.name} 没有权威`); + return false; + } + return true; + } + + /** + * 发送客户端RPC + * 只能在服务端调用,向指定客户端或所有客户端发送消息 + */ + protected sendClientRpc(methodName: string, args: any[] = [], targetClient?: number): void { + if (!this.isServer) { + console.warn(`[NetworkBehaviour] ClientRpc 只能在服务端调用: ${methodName}`); + return; + } + + NetworkManager.instance?.sendClientRpc( + this.networkId, + this.constructor.name, + methodName, + args, + targetClient + ); + } + + /** + * 发送命令到服务端 + * 只能在客户端调用,向服务端发送命令 + */ + protected sendCommand(methodName: string, args: any[] = []): void { + if (!this.isClient) { + console.warn(`[NetworkBehaviour] Command 只能在客户端调用: ${methodName}`); + return; + } + + if (!this.hasAuthority) { + console.warn(`[NetworkBehaviour] Command 需要权威才能调用: ${methodName}`); + return; + } + + NetworkManager.instance?.sendCommand( + this.networkId, + this.constructor.name, + methodName, + args + ); + } +} \ No newline at end of file diff --git a/packages/network/src/NetworkComponent.ts b/packages/network/src/NetworkComponent.ts deleted file mode 100644 index 61f6d356..00000000 --- a/packages/network/src/NetworkComponent.ts +++ /dev/null @@ -1,395 +0,0 @@ -import { Component } from '@esengine/ecs-framework'; -import { INetworkSyncable } from './INetworkSyncable'; -import { NetworkRole } from './NetworkRole'; -import { NetworkEnvironment } from './Core/NetworkEnvironment'; -import { SyncVarManager } from './SyncVar/SyncVarManager'; -import { getSyncVarMetadata } from './SyncVar/SyncVarDecorator'; -import { ComponentRegistry } from '@esengine/ecs-framework'; -import { isTsrpcSerializable } from './Serialization/TsrpcDecorators'; -import { TsrpcSerializer } from './Serialization/TsrpcSerializer'; - -/** - * 网络组件基类 - * - * 继承核心ECS的Component类,添加网络同步功能。 - * 用于需要网络同步的组件,提供帧同步框架所需的网络状态管理。 - * - * @example - * ```typescript - * import { NetworkComponent } from '@esengine/ecs-framework-network'; - * import { TsrpcSerializable, SyncField } from '@esengine/ecs-framework-network'; - * - * @TsrpcSerializable() - * class PositionComponent extends NetworkComponent { - * @SyncField() - * public x: number = 0; - * - * @SyncField() - * public y: number = 0; - * - * constructor(x: number = 0, y: number = 0) { - * super(); - * this.x = x; - * this.y = y; - * } - * - * // 客户端特有逻辑 - * public onClientUpdate(): void { - * if (__CLIENT__) { - * // 客户端构建时才包含此逻辑 - * this.handleInputPrediction(); - * this.interpolatePosition(); - * } - * } - * - * // 服务端特有逻辑 - * public onServerUpdate(): void { - * if (__SERVER__) { - * // 服务端构建时才包含此逻辑 - * this.validateMovement(); - * this.broadcastToClients(); - * } - * } - * } - * - * // 使用方式(角色由环境自动检测) - * const position = new PositionComponent(10, 20); - * // 角色由 NetworkManager.StartServer() 或 StartClient() 决定 - * ``` - */ -export abstract class NetworkComponent extends Component implements INetworkSyncable { - /** SyncVar内部ID */ - _syncVarId?: string; - /** SyncVar监听禁用标志 */ - _syncVarDisabled?: boolean; - - /** 允许通过字符串键访问属性 */ - [propertyKey: string]: unknown; - /** - * 脏字段标记集合 - * - * 记录已修改但尚未同步的字段编号 - */ - private _dirtyFields: Set = new Set(); - - /** - * 字段变化时间戳 - * - * 记录每个字段最后修改的时间 - */ - private _fieldTimestamps: Map = new Map(); - - /** - * 构造函数 - * - * 角色信息通过NetworkEnvironment自动获取,无需手动传入 - */ - constructor() { - super(); - this.initializeSyncVar(); - this.ensureComponentRegistered(); - } - - /** - * 确保当前组件类型已注册到ComponentRegistry - */ - private ensureComponentRegistered(): void { - try { - - // 检查当前组件类型是否已注册 - if (!ComponentRegistry.isRegistered(this.constructor as any)) { - // 如果未注册,自动注册 - ComponentRegistry.register(this.constructor as any); - const logger = { info: console.log, warn: console.warn, error: console.error, debug: console.debug }; - logger.debug(`自动注册组件类型: ${this.constructor.name}`); - } - } catch (error) { - const logger = { info: console.log, warn: console.warn, error: console.error, debug: console.debug }; - logger.warn(`无法注册组件类型 ${this.constructor.name}:`, error); - } - } - - /** - * 初始化SyncVar系统 - * - * 如果组件有SyncVar字段,自动创建代理来监听变化 - */ - private initializeSyncVar(): void { - const metadata = getSyncVarMetadata(this.constructor); - if (metadata.length > 0) { - const logger = { info: console.log, warn: console.warn, error: console.error, debug: console.debug }; - logger.debug(`${this.constructor.name} 发现 ${metadata.length} 个SyncVar字段,将启用代理监听`); - } - } - - /** - * 获取网络角色 - * - * 从全局网络环境获取当前角色 - * @returns 当前组件的网络角色 - */ - public getRole(): NetworkRole { - return NetworkEnvironment.getPrimaryRole(); - } - - /** - * 检查是否为客户端角色 - * - * @returns 是否为客户端 - */ - public isClient(): boolean { - return NetworkEnvironment.isClient; - } - - /** - * 检查是否为服务端角色 - * - * @returns 是否为服务端 - */ - public isServer(): boolean { - return NetworkEnvironment.isServer; - } - - /** - * 获取网络同步状态 - * - * 序列化当前组件状态为网络传输格式 - * @returns 序列化的网络状态数据 - */ - public getNetworkState(): Uint8Array { - if (!isTsrpcSerializable(this)) { - throw new Error(`组件 ${this.constructor.name} 不支持网络同步,请添加@TsrpcSerializable装饰器`); - } - - try { - const serializer = TsrpcSerializer.getInstance(); - const serializedData = serializer.serialize(this); - if (!serializedData) { - throw new Error(`序列化失败: 组件=${this.constructor.name}`); - } - return serializedData.data; - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - throw new Error(`获取网络状态失败: 组件=${this.constructor.name}, 错误=${errorMsg}, 可能原因: 序列化字段格式错误或网络连接问题`); - } - } - - /** - * 应用网络状态 - * - * 从网络数据恢复组件状态 - * @param data - 网络状态数据 - */ - public applyNetworkState(data: Uint8Array): void { - if (!isTsrpcSerializable(this)) { - throw new Error(`组件 ${this.constructor.name} 不支持网络同步,请添加@TsrpcSerializable装饰器`); - } - - try { - const serializer = TsrpcSerializer.getInstance(); - const serializedData = { - type: 'tsrpc' as const, - componentType: this.constructor.name, - data: data, - size: data.length - }; - - // 反序列化并应用到当前组件实例 - const deserializedComponent = serializer.deserialize(serializedData, this.constructor as any); - if (!deserializedComponent) { - throw new Error(`反序列化失败: 组件=${this.constructor.name}`); - } - - // 将反序列化的数据应用到当前实例 - Object.assign(this, deserializedComponent); - - // 应用后清理脏字段标记 - this.markClean(); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : String(error); - // 记录错误但不完全阻止组件运行 - console.warn(`应用网络状态失败: 组件=${this.constructor.name}, 错误=${errorMsg}, 尝试恢复到安全状态`); - - // 尝试恢复到安全状态 - try { - this.markClean(); // 至少清理脏字段标记 - } catch (cleanupError) { - // 如果连清理都失败,则抛出原始错误 - throw new Error(`应用网络状态失败: 组件=${this.constructor.name}, 原始错误=${errorMsg}, 清理失败=${cleanupError}, 组件可能处于不一致状态`); - } - - throw new Error(`应用网络状态失败: 组件=${this.constructor.name}, 错误=${errorMsg}, 已恢复到安全状态`); - } - } - - /** - * 获取变化的字段编号列表 - * - * @returns 变化字段的编号数组 - */ - public getDirtyFields(): number[] { - return Array.from(this._dirtyFields); - } - - /** - * 标记所有字段为干净状态 - * - * 清除所有脏字段标记 - */ - public markClean(): void { - this._dirtyFields.clear(); - } - - /** - * 标记字段为脏状态 - * - * 用于标记字段已修改,需要网络同步 - * @param fieldNumber - 字段编号 - */ - public markFieldDirty(fieldNumber: number): void { - this._dirtyFields.add(fieldNumber); - this._fieldTimestamps.set(fieldNumber, Date.now()); - } - - /** - * 检查字段是否为脏状态 - * - * @param fieldNumber - 字段编号 - * @returns 是否为脏状态 - */ - public isFieldDirty(fieldNumber: number): boolean { - return this._dirtyFields.has(fieldNumber); - } - - /** - * 获取字段最后修改时间 - * - * @param fieldNumber - 字段编号 - * @returns 最后修改时间戳,如果字段从未修改则返回0 - */ - public getFieldTimestamp(fieldNumber: number): number { - return this._fieldTimestamps.get(fieldNumber) || 0; - } - - /** - * 获取所有脏字段及其时间戳 - * - * @returns 脏字段和时间戳的映射 - */ - public getDirtyFieldsWithTimestamps(): Map { - const result = new Map(); - for (const fieldNumber of this._dirtyFields) { - result.set(fieldNumber, this._fieldTimestamps.get(fieldNumber) || 0); - } - return result; - } - - /** - * 获取待同步的SyncVar变化 - * - * @returns 待同步的变化数组 - */ - public getSyncVarChanges(): any[] { - const syncVarManager = SyncVarManager.Instance; - return syncVarManager.getPendingChanges(this); - } - - /** - * 创建SyncVar同步数据 - * - * @returns 同步数据,如果没有变化则返回null - */ - public createSyncVarData(): any { - const syncVarManager = SyncVarManager.Instance; - return syncVarManager.createSyncData(this); - } - - /** - * 应用SyncVar同步数据 - * - * @param syncData - 同步数据 - */ - public applySyncVarData(syncData: any): void { - const syncVarManager = SyncVarManager.Instance; - syncVarManager.applySyncData(this, syncData); - } - - /** - * 清除SyncVar变化记录 - * - * @param propertyKeys - 要清除的属性名数组,如果不提供则清除所有 - */ - public clearSyncVarChanges(propertyKeys?: string[]): void { - const syncVarManager = SyncVarManager.Instance; - syncVarManager.clearChanges(this, propertyKeys); - } - - /** - * 检查组件是否有SyncVar字段 - * - * @returns 是否有SyncVar字段 - */ - public hasSyncVars(): boolean { - const metadata = getSyncVarMetadata(this.constructor); - return metadata.length > 0; - } - - /** - * 获取SyncVar统计信息 - * - * @returns 统计信息 - */ - public getSyncVarStats(): any { - const metadata = getSyncVarMetadata(this.constructor); - const syncVarManager = SyncVarManager.Instance; - const changes = syncVarManager.getPendingChanges(this); - - return { - totalSyncVars: metadata.length, - pendingChanges: changes.length, - syncVarFields: metadata.map(m => ({ - propertyKey: m.propertyKey, - fieldNumber: m.fieldNumber, - hasHook: !!m.options.hook, - authorityOnly: !!m.options.authorityOnly - })) - }; - } - - /** - * 客户端更新逻辑 - * - * 子类可以重写此方法实现客户端特有的逻辑,如: - * - 输入预测 - * - 状态插值 - * - 回滚重放 - */ - public onClientUpdate(): void { - // 默认空实现,子类可根据需要重写 - } - - /** - * 服务端更新逻辑 - * - * 子类可以重写此方法实现服务端特有的逻辑,如: - * - 输入验证 - * - 权威状态计算 - * - 状态广播 - */ - public onServerUpdate(): void { - // 默认空实现,子类可根据需要重写 - } - - /** - * 统一的更新入口 - * - * 根据角色调用相应的更新方法 - */ - public override update(): void { - if (this.isClient()) { - this.onClientUpdate(); - } else if (this.isServer()) { - this.onServerUpdate(); - } - } -} \ No newline at end of file diff --git a/packages/network/src/NetworkIdentity.ts b/packages/network/src/NetworkIdentity.ts new file mode 100644 index 00000000..c7d39db3 --- /dev/null +++ b/packages/network/src/NetworkIdentity.ts @@ -0,0 +1,105 @@ +/** + * 网络身份组件 + * + * 标识网络对象的唯一身份,管理网络组件和权威性 + * 所有需要网络同步的实体都必须拥有此组件 + */ + +import { Component } from '@esengine/ecs-framework'; +import { INetworkBehaviour } from './types/NetworkTypes'; + +export class NetworkIdentity extends Component { + /** 网络对象的唯一标识符 */ + public networkId: number = 0; + + /** 所有者客户端ID,0 表示服务端拥有 */ + public ownerId: number = 0; + + /** 是否拥有权威,权威端可以修改 SyncVar 和发送 RPC */ + public hasAuthority: boolean = false; + + /** 是否为本地玩家对象 */ + public isLocalPlayer: boolean = false; + + /** 挂载的网络组件列表 */ + public networkBehaviours: INetworkBehaviour[] = []; + + /** + * 添加网络组件 + * @param behaviour 要添加的网络组件 + */ + public addNetworkBehaviour(behaviour: INetworkBehaviour): void { + if (this.networkBehaviours.includes(behaviour)) { + return; // 已经添加过了 + } + + this.networkBehaviours.push(behaviour); + behaviour.networkIdentity = this; + } + + /** + * 移除网络组件 + * @param behaviour 要移除的网络组件 + */ + public removeNetworkBehaviour(behaviour: INetworkBehaviour): void { + const index = this.networkBehaviours.indexOf(behaviour); + if (index !== -1) { + this.networkBehaviours.splice(index, 1); + behaviour.networkIdentity = null; + } + } + + /** + * 设置权威性 + * @param hasAuthority 是否拥有权威 + * @param ownerId 所有者客户端ID + */ + public setAuthority(hasAuthority: boolean, ownerId: number = 0): void { + this.hasAuthority = hasAuthority; + this.ownerId = ownerId; + } + + /** + * 设置为本地玩家 + */ + public setAsLocalPlayer(): void { + this.isLocalPlayer = true; + this.hasAuthority = true; + } + + /** + * 获取指定类型的网络组件 + */ + public getNetworkBehaviour(type: new (...args: any[]) => T): T | null { + return this.networkBehaviours.find(b => b instanceof type) as T || null; + } + + /** + * 获取所有指定类型的网络组件 + */ + public getNetworkBehaviours(type: new (...args: any[]) => T): T[] { + return this.networkBehaviours.filter(b => b instanceof type) as T[]; + } + + /** + * 组件启动时的处理 + */ + public start(): void { + // 如果没有分配网络ID,从网络管理器获取 + if (this.networkId === 0) { + // 这里需要从 NetworkManager 获取新的网络ID + // 实现延后到 NetworkManager 完成 + } + } + + /** + * 组件销毁时的清理 + */ + public destroy(): void { + // 清理所有网络组件的引用 + this.networkBehaviours.forEach(behaviour => { + behaviour.networkIdentity = null; + }); + this.networkBehaviours.length = 0; + } +} \ No newline at end of file diff --git a/packages/network/src/NetworkManager.ts b/packages/network/src/NetworkManager.ts new file mode 100644 index 00000000..af750ba8 --- /dev/null +++ b/packages/network/src/NetworkManager.ts @@ -0,0 +1,449 @@ +/** + * 网络管理器 + * + * 网络库的核心管理类,负责: + * - 客户端/服务端连接管理 + * - 网络消息路由和处理 + * - SyncVar 同步调度 + * - RPC 调用管理 + */ + +import { createLogger, Component } from '@esengine/ecs-framework'; +import { + NetworkConfig, + NetworkSide, + NetworkConnectionState, + NetworkStats, + NetworkEventHandlers, + SyncVarMessage, + RpcMessage, + NetworkMessage +} from './types/NetworkTypes'; +import { NetworkRegistry } from './core/NetworkRegistry'; +import { SyncVarManager } from './core/SyncVarManager'; +import { RpcManager } from './core/RpcManager'; +import { NetworkIdentity } from './NetworkIdentity'; +import { NetworkBehaviour } from './NetworkBehaviour'; +import { TsrpcTransport, TransportEventHandlers } from './transport/TsrpcTransport'; + +const logger = createLogger('NetworkManager'); + +export class NetworkManager extends Component { + private static _instance: NetworkManager | null = null; + + /** 当前网络端类型 */ + private networkSide: NetworkSide = 'client'; + + /** 连接状态 */ + private connectionState: NetworkConnectionState = 'disconnected'; + + /** 网络配置 */ + private config: NetworkConfig = { + port: 7777, + host: 'localhost', + maxConnections: 100, + syncRate: 20, + compression: false + }; + + /** 网络统计信息 */ + private stats: NetworkStats = { + connectionCount: 0, + bytesSent: 0, + bytesReceived: 0, + messagesSent: 0, + messagesReceived: 0, + averageLatency: 0 + }; + + /** 事件处理器 */ + private eventHandlers: Partial = {}; + + /** 同步定时器 */ + private syncTimer: NodeJS.Timeout | null = null; + + /** TSRPC传输层 */ + private transport: TsrpcTransport | null = null; + + public static get instance(): NetworkManager | null { + return NetworkManager._instance; + } + + public static get isServer(): boolean { + return NetworkManager._instance?.networkSide === 'server' || + NetworkManager._instance?.networkSide === 'host'; + } + + public static get isClient(): boolean { + return NetworkManager._instance?.networkSide === 'client' || + NetworkManager._instance?.networkSide === 'host'; + } + + public static get isConnected(): boolean { + return NetworkManager._instance?.connectionState === 'connected'; + } + + constructor() { + super(); + if (NetworkManager._instance) { + throw new Error('NetworkManager 已存在实例,请使用 NetworkManager.instance'); + } + NetworkManager._instance = this; + } + + /** + * 启动服务端 + * @param config 网络配置 + */ + public async startServer(config?: Partial): Promise { + if (this.connectionState !== 'disconnected') { + throw new Error('网络管理器已在运行中'); + } + + this.networkSide = 'server'; + this.config = { ...this.config, ...config }; + this.connectionState = 'connecting'; + + try { + // 初始化TSRPC传输层 + await this.initializeTransport(); + + // 启动TSRPC服务端 + await this.transport!.startServer(); + + this.connectionState = 'connected'; + this.startSyncLoop(); + + logger.info(`服务端已启动,端口: ${this.config.port}`); + this.eventHandlers.onConnected?.(); + + } catch (error) { + this.connectionState = 'disconnected'; + logger.error('启动服务端失败:', error); + this.eventHandlers.onError?.(error as Error); + throw error; + } + } + + /** + * 连接到服务端 + * @param host 服务端地址 + * @param port 服务端端口 + */ + public async connectToServer(host?: string, port?: number): Promise { + if (this.connectionState !== 'disconnected') { + throw new Error('已经连接或正在连接中'); + } + + this.networkSide = 'client'; + this.config.host = host || this.config.host; + this.config.port = port || this.config.port; + this.connectionState = 'connecting'; + + try { + // 初始化TSRPC传输层 + await this.initializeTransport(); + + // 连接到TSRPC服务端 + await this.transport!.connectToServer(); + + this.connectionState = 'connected'; + this.startSyncLoop(); + + logger.info(`已连接到服务端: ${this.config.host}:${this.config.port}`); + this.eventHandlers.onConnected?.(); + + } catch (error) { + this.connectionState = 'disconnected'; + logger.error('连接服务端失败:', error); + this.eventHandlers.onError?.(error as Error); + throw error; + } + } + + /** + * 断开连接 + */ + public async disconnect(): Promise { + if (this.connectionState === 'disconnected') { + return; + } + + this.connectionState = 'disconnected'; + this.stopSyncLoop(); + + // 关闭TSRPC传输层连接 + if (this.transport) { + await this.transport.disconnect(); + } + + logger.info('网络连接已断开'); + this.eventHandlers.onDisconnected?.(); + } + + /** + * 注册网络对象 + * @param entity 包含 NetworkIdentity 的实体 + * @returns 分配的网络ID + */ + public registerNetworkObject(entity: any): number { + const networkIdentity = entity.getComponent?.(NetworkIdentity); + if (!networkIdentity) { + throw new Error('实体必须包含 NetworkIdentity 组件才能注册为网络对象'); + } + + // 注册到网络注册表 + const networkId = NetworkRegistry.instance.register(networkIdentity); + + // 注册所有网络组件到管理器 + const networkBehaviours = entity.getComponents?.()?.filter((c: any) => c instanceof NetworkBehaviour) || []; + for (const behaviour of networkBehaviours) { + SyncVarManager.instance.registerComponent(behaviour as NetworkBehaviour); + RpcManager.instance.registerComponent(behaviour as NetworkBehaviour); + } + + logger.debug(`注册网络对象: ${entity.name}, ID: ${networkId}`); + return networkId; + } + + /** + * 发送客户端 RPC(服务端调用) + */ + public sendClientRpc( + networkId: number, + componentType: string, + methodName: string, + args: any[] = [], + targetClient?: number + ): void { + if (!NetworkManager.isServer) { + logger.warn('ClientRpc 只能在服务端调用'); + return; + } + + const message: RpcMessage = { + type: 'rpc', + networkId, + data: { + componentType, + methodName, + args + }, + methodName, + args, + isClientRpc: true, + timestamp: Date.now() + }; + + this.sendMessage(message, targetClient); + } + + /** + * 发送命令到服务端(客户端调用) + */ + public sendCommand( + networkId: number, + componentType: string, + methodName: string, + args: any[] = [] + ): void { + if (!NetworkManager.isClient) { + logger.warn('Command 只能在客户端调用'); + return; + } + + const message: RpcMessage = { + type: 'rpc', + networkId, + data: { + componentType, + methodName, + args + }, + methodName, + args, + isClientRpc: false, + timestamp: Date.now() + }; + + this.sendMessage(message); + } + + /** + * 设置事件处理器 + */ + public on( + event: K, + handler: NetworkEventHandlers[K] + ): void { + this.eventHandlers[event] = handler; + } + + /** + * 获取网络统计信息 + */ + public getStats(): NetworkStats { + return { ...this.stats }; + } + + /** + * 获取连接状态 + */ + public getConnectionState(): NetworkConnectionState { + return this.connectionState; + } + + /** + * 组件销毁时清理 + */ + public destroy(): void { + this.disconnect(); + NetworkManager._instance = null; + } + + /** + * 初始化传输层 + */ + private async initializeTransport(): Promise { + if (this.transport) { + return; // 已经初始化 + } + + // 创建TSRPC传输层 + this.transport = new TsrpcTransport(this.config); + + // 设置传输层事件处理器 + const transportHandlers: TransportEventHandlers = { + onConnected: () => { + logger.debug('传输层连接已建立'); + }, + + onDisconnected: (reason) => { + logger.debug(`传输层连接已断开: ${reason}`); + if (this.connectionState === 'connected') { + this.connectionState = 'disconnected'; + this.eventHandlers.onDisconnected?.(reason); + } + }, + + onClientConnected: (clientId) => { + logger.debug(`客户端 ${clientId} 已连接到传输层`); + this.eventHandlers.onClientConnected?.(clientId); + }, + + onClientDisconnected: (clientId, reason) => { + logger.debug(`客户端 ${clientId} 已从传输层断开: ${reason}`); + this.eventHandlers.onClientDisconnected?.(clientId, reason); + + // 清理断开连接的客户端对象 + NetworkRegistry.instance.cleanupDisconnectedClient(clientId); + }, + + onMessage: (message, fromClientId) => { + this.handleMessage(message); + }, + + onError: (error) => { + logger.error('传输层错误:', error); + this.eventHandlers.onError?.(error); + } + }; + + this.transport.setEventHandlers(transportHandlers); + logger.debug('TSRPC传输层已初始化'); + } + + /** + * 启动同步循环 + */ + private startSyncLoop(): void { + if (this.syncTimer) { + return; + } + + const interval = 1000 / (this.config.syncRate || 20); + this.syncTimer = setInterval(() => { + this.processSyncVars(); + }, interval); + + logger.debug(`同步循环已启动,频率: ${this.config.syncRate} Hz`); + } + + /** + * 停止同步循环 + */ + private stopSyncLoop(): void { + if (this.syncTimer) { + clearInterval(this.syncTimer); + this.syncTimer = null; + logger.debug('同步循环已停止'); + } + } + + /** + * 处理 SyncVar 同步 + */ + private processSyncVars(): void { + if (!NetworkManager.isServer) { + return; // 只有服务端发送同步消息 + } + + const syncVarMessages = SyncVarManager.instance.getPendingMessages(); + for (const message of syncVarMessages) { + this.sendMessage(message).catch(error => { + logger.error('发送SyncVar消息失败:', error); + }); + } + + // 处理 RPC 消息 + const rpcMessages = RpcManager.instance.getPendingRpcMessages(); + for (const message of rpcMessages) { + this.sendMessage(message).catch(error => { + logger.error('发送RPC消息失败:', error); + }); + } + } + + /** + * 发送网络消息 + */ + private async sendMessage(message: NetworkMessage, targetClient?: number): Promise { + if (!this.transport) { + logger.warn('传输层未初始化,无法发送消息'); + return; + } + + try { + await this.transport.sendMessage(message, targetClient); + this.stats.messagesSent++; + logger.debug(`发送消息: ${message.type}, 网络ID: ${message.networkId}`); + } catch (error) { + logger.error('发送消息失败:', error); + } + } + + /** + * 处理收到的网络消息(供外部调用) + */ + public handleIncomingMessage(message: NetworkMessage): void { + this.handleMessage(message); + } + + /** + * 处理收到的网络消息 + */ + private handleMessage(message: NetworkMessage): void { + this.stats.messagesReceived++; + + switch (message.type) { + case 'syncvar': + SyncVarManager.instance.handleSyncVarMessage(message as SyncVarMessage); + break; + case 'rpc': + RpcManager.instance.handleRpcMessage(message as RpcMessage); + break; + default: + logger.warn(`未知消息类型: ${message.type}`); + } + } +} \ No newline at end of file diff --git a/packages/network/src/NetworkRole.ts b/packages/network/src/NetworkRole.ts deleted file mode 100644 index 77db6ff8..00000000 --- a/packages/network/src/NetworkRole.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * 网络组件角色枚举 - * - * 定义网络组件在帧同步框架中的角色 - */ -export enum NetworkRole { - /** - * 客户端角色 - * - * 组件将执行客户端特有的逻辑,如: - * - 输入预测 - * - 状态插值 - * - 回滚重放 - */ - CLIENT = 'client', - - /** - * 服务端角色 - * - * 组件将执行服务端特有的逻辑,如: - * - 输入验证 - * - 权威状态计算 - * - 状态广播 - */ - SERVER = 'server' -} \ No newline at end of file diff --git a/packages/network/src/Serialization/SerializationTypes.ts b/packages/network/src/Serialization/SerializationTypes.ts deleted file mode 100644 index 8295cc18..00000000 --- a/packages/network/src/Serialization/SerializationTypes.ts +++ /dev/null @@ -1,8 +0,0 @@ -export interface SerializedData { - type: 'tsrpc' | 'json'; - componentType: string; - data: Uint8Array | any; - size: number; - schema?: string; - version?: number; -} \ No newline at end of file diff --git a/packages/network/src/Serialization/TsrpcDecorators.ts b/packages/network/src/Serialization/TsrpcDecorators.ts deleted file mode 100644 index 3e675b3b..00000000 --- a/packages/network/src/Serialization/TsrpcDecorators.ts +++ /dev/null @@ -1,181 +0,0 @@ -import 'reflect-metadata'; -import { Component, createLogger } from '@esengine/ecs-framework'; -import { - SyncFieldOptions, - TsrpcSerializableOptions, - TsrpcFieldMetadata, - TsrpcComponentMetadata, - TsrpcSupportedTypes -} from './TsrpcTypes'; - -const logger = createLogger('TsrpcDecorators'); - -const TSRPC_COMPONENT_KEY = Symbol('tsrpc:component'); -const TSRPC_FIELDS_KEY = Symbol('tsrpc:fields'); -const TSRPC_SERIALIZABLE_KEY = Symbol('tsrpc:serializable'); - -export class TsrpcRegistry { - private static _instance: TsrpcRegistry | null = null; - private _components: Map = new Map(); - private _constructors: Map = new Map(); - - public static getInstance(): TsrpcRegistry { - if (!TsrpcRegistry._instance) { - TsrpcRegistry._instance = new TsrpcRegistry(); - } - return TsrpcRegistry._instance; - } - - public register(metadata: TsrpcComponentMetadata): void { - this._components.set(metadata.componentType, metadata); - this._constructors.set(metadata.constructor, metadata); - } - - public getByName(componentType: string): TsrpcComponentMetadata | undefined { - return this._components.get(componentType); - } - - public getByConstructor(constructor: Function): TsrpcComponentMetadata | undefined { - return this._constructors.get(constructor); - } - - public getAllComponents(): TsrpcComponentMetadata[] { - return Array.from(this._components.values()); - } - - public isRegistered(constructor: Function): boolean { - return this._constructors.has(constructor); - } - - public clear(): void { - this._components.clear(); - this._constructors.clear(); - } -} - -function getTypeInfo(target: any, propertyKey: string) { - const designType = Reflect.getMetadata('design:type', target, propertyKey); - - if (!designType) { - return { typeName: 'unknown', isArray: false, isOptional: false, isUnion: false }; - } - - const typeName = designType.name || designType.toString(); - - return { - typeName: typeName.toLowerCase(), - isArray: designType === Array || typeName === 'Array', - isOptional: false, - isUnion: false, - unionTypes: [], - genericTypes: [] - }; -} - -function createTypeChecker(typeInfo: any): (value: any) => boolean { - return (value: any): boolean => { - if (value === null || value === undefined) { - return typeInfo.isOptional; - } - - switch (typeInfo.typeName) { - case 'boolean': return typeof value === 'boolean'; - case 'number': return typeof value === 'number' && !isNaN(value); - case 'string': return typeof value === 'string'; - case 'array': return Array.isArray(value); - case 'object': return typeof value === 'object' && !Array.isArray(value); - case 'date': return value instanceof Date; - default: return true; - } - }; -} - -export function SyncField(options: SyncFieldOptions = {}): PropertyDecorator { - return (target: any, propertyKey: string | symbol) => { - if (typeof propertyKey !== 'string') { - throw new Error('SyncField只支持字符串属性名'); - } - - const existingFields: Map = - Reflect.getMetadata(TSRPC_FIELDS_KEY, target.constructor) || new Map(); - - const typeInfo = getTypeInfo(target, propertyKey); - - const fieldMetadata: TsrpcFieldMetadata = { - propertyKey, - options: { priority: 'normal', authorityOnly: false, throttle: 0, delta: false, ...options }, - typeInfo, - typeChecker: createTypeChecker(typeInfo), - fieldIndex: existingFields.size - }; - - existingFields.set(propertyKey, fieldMetadata); - Reflect.defineMetadata(TSRPC_FIELDS_KEY, existingFields, target.constructor); - }; -} - -export function TsrpcSerializable(options: TsrpcSerializableOptions = {}): ClassDecorator { - return (constructor: any) => { - Reflect.defineMetadata(TSRPC_SERIALIZABLE_KEY, true, constructor); - - const fields: Map = - Reflect.getMetadata(TSRPC_FIELDS_KEY, constructor) || new Map(); - - const componentMetadata: TsrpcComponentMetadata = { - componentType: options.name || constructor.name, - options: { version: 1, validation: false, compression: false, strategy: 'auto', ...options }, - fields, - constructor, - version: options.version || 1, - createdAt: Date.now() - }; - - Reflect.defineMetadata(TSRPC_COMPONENT_KEY, componentMetadata, constructor); - - const registry = TsrpcRegistry.getInstance(); - registry.register(componentMetadata); - }; -} - -export function isTsrpcSerializable(component: any): boolean { - return Reflect.getMetadata(TSRPC_SERIALIZABLE_KEY, component.constructor) === true; -} - -export function getTsrpcMetadata(constructor: Function): TsrpcComponentMetadata | undefined { - return Reflect.getMetadata(TSRPC_COMPONENT_KEY, constructor); -} - -export function getTsrpcFields(constructor: Function): Map { - return Reflect.getMetadata(TSRPC_FIELDS_KEY, constructor) || new Map(); -} - -export function getTsrpcName(component: any): string | undefined { - const metadata = getTsrpcMetadata(component.constructor); - return metadata?.componentType; -} - -export const TsrpcString = (options: SyncFieldOptions = {}) => SyncField(options); -export const TsrpcNumber = (options: SyncFieldOptions = {}) => SyncField(options); -export const TsrpcBoolean = (options: SyncFieldOptions = {}) => SyncField(options); -export const TsrpcDate = (options: SyncFieldOptions = {}) => SyncField(options); -export const TsrpcArray = (options: SyncFieldOptions = {}) => SyncField({ ...options, delta: true }); -export const TsrpcObject = (options: SyncFieldOptions = {}) => SyncField({ ...options, delta: true }); -export const TsrpcCritical = (options: SyncFieldOptions = {}) => SyncField({ ...options, priority: 'critical' }); -export const TsrpcAuthority = (options: SyncFieldOptions = {}) => SyncField({ ...options, authorityOnly: true }); - -export function validateTsrpcComponent(component: Component): boolean { - const metadata = getTsrpcMetadata(component.constructor); - if (!metadata) return false; - - for (const [fieldName, fieldMetadata] of metadata.fields) { - const value = (component as any)[fieldName]; - if (fieldMetadata.typeChecker && !fieldMetadata.typeChecker(value)) { - return false; - } - } - return true; -} - -export function AutoSync(target: any): any { - return target; -} \ No newline at end of file diff --git a/packages/network/src/Serialization/TsrpcSerializer.ts b/packages/network/src/Serialization/TsrpcSerializer.ts deleted file mode 100644 index e5f34591..00000000 --- a/packages/network/src/Serialization/TsrpcSerializer.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { Component, createLogger } from '@esengine/ecs-framework'; -import { SerializedData } from './SerializationTypes'; -import { - TsrpcComponentMetadata, - TsrpcFieldMetadata, - TsrpcSerializationStats, - TsrpcSerializable -} from './TsrpcTypes'; -import { - TsrpcRegistry, - isTsrpcSerializable, - getTsrpcMetadata, - validateTsrpcComponent -} from './TsrpcDecorators'; - -const logger = createLogger('TsrpcSerializer'); -export class TsrpcSerializer { - private static _instance: TsrpcSerializer | null = null; - private _registry: TsrpcRegistry; - private _stats: TsrpcSerializationStats; - - constructor() { - this._registry = TsrpcRegistry.getInstance(); - this._stats = { - serializeCount: 0, - deserializeCount: 0, - totalSerializeTime: 0, - totalDeserializeTime: 0, - averageSerializedSize: 0, - errorCount: 0, - cacheHits: 0, - cacheMisses: 0 - }; - } - - public static getInstance(): TsrpcSerializer { - if (!TsrpcSerializer._instance) { - TsrpcSerializer._instance = new TsrpcSerializer(); - } - return TsrpcSerializer._instance; - } - - public serialize(component: Component): SerializedData | null { - if (!isTsrpcSerializable(component)) return null; - - const metadata = getTsrpcMetadata(component.constructor); - if (!metadata) return null; - - try { - const data = this.extractSerializableData(component, metadata); - const jsonString = JSON.stringify(data); - const serialized = new TextEncoder().encode(jsonString); - - this._stats.serializeCount++; - this._stats.averageSerializedSize = - (this._stats.averageSerializedSize * (this._stats.serializeCount - 1) + serialized.length) - / this._stats.serializeCount; - - return { - type: 'tsrpc', - componentType: metadata.componentType, - data: serialized, - size: serialized.length, - schema: metadata.componentType, - version: metadata.version - }; - } catch (error) { - this._stats.errorCount++; - return null; - } - } - - public deserialize( - serializedData: SerializedData, - ComponentClass?: new (...args: any[]) => T - ): T | null { - if (serializedData.type !== 'tsrpc') return null; - - let metadata: TsrpcComponentMetadata | undefined; - if (ComponentClass) { - metadata = getTsrpcMetadata(ComponentClass); - } else { - metadata = this._registry.getByName(serializedData.componentType); - } - - if (!metadata) return null; - - try { - const jsonString = new TextDecoder().decode(serializedData.data as Uint8Array); - const data = JSON.parse(jsonString); - - const component = new metadata.constructor(); - this.applySerializableData(component as T, data, metadata); - this._stats.deserializeCount++; - - return component as T; - } catch (error) { - this._stats.errorCount++; - return null; - } - } - - - private extractSerializableData(component: Component, metadata: TsrpcComponentMetadata): any { - const data: any = {}; - for (const [fieldName, fieldMetadata] of metadata.fields) { - const value = (component as any)[fieldName]; - if (value !== undefined || fieldMetadata.typeInfo.isOptional) { - data[fieldName] = this.processFieldValue(value, fieldMetadata); - } - } - return data; - } - - private applySerializableData(component: Component, data: any, metadata: TsrpcComponentMetadata): void { - for (const [fieldName, fieldMetadata] of metadata.fields) { - if (fieldName in data) { - const value = this.processFieldValue(data[fieldName], fieldMetadata, true); - (component as any)[fieldName] = value; - } - } - - if (this.isTsrpcSerializableInstance(component)) { - component.applyTsrpcData(data); - } - } - - private processFieldValue(value: any, fieldMetadata: TsrpcFieldMetadata, isDeserializing = false): any { - if (value === null || value === undefined) return value; - - const { typeInfo } = fieldMetadata; - - if (['boolean', 'number', 'string'].includes(typeInfo.typeName)) { - return value; - } - - if (typeInfo.typeName === 'date') { - return isDeserializing ? new Date(value) : value.toISOString(); - } - - if (typeInfo.isArray && Array.isArray(value)) { - return value.map(item => this.processFieldValue(item, fieldMetadata, isDeserializing)); - } - - if (typeInfo.typeName === 'object' && typeof value === 'object') { - return isDeserializing ? structuredClone(value) : value; - } - - return value; - } - - private isTsrpcSerializableInstance(component: any): component is TsrpcSerializable { - return typeof component.getTsrpcData === 'function' && - typeof component.applyTsrpcData === 'function'; - } - - public getStats(): TsrpcSerializationStats { - return { ...this._stats }; - } - - public resetStats(): void { - this._stats = { - serializeCount: 0, - deserializeCount: 0, - totalSerializeTime: 0, - totalDeserializeTime: 0, - averageSerializedSize: 0, - errorCount: 0, - cacheHits: 0, - cacheMisses: 0 - }; - } - - public getSupportedTypes(): string[] { - return this._registry.getAllComponents().map(comp => comp.componentType); - } -} - -export const tsrpcSerializer = TsrpcSerializer.getInstance(); \ No newline at end of file diff --git a/packages/network/src/Serialization/TsrpcTypes.ts b/packages/network/src/Serialization/TsrpcTypes.ts deleted file mode 100644 index f663fe8a..00000000 --- a/packages/network/src/Serialization/TsrpcTypes.ts +++ /dev/null @@ -1,218 +0,0 @@ -/** - * TSRPC序列化类型定义 - */ - -import { Component } from '@esengine/ecs-framework'; - -/** - * TSRPC同步字段配置选项 - */ -export interface SyncFieldOptions { - /** - * 同步优先级 - * - 'critical': 关键字段,每帧必须同步 - * - 'high': 高优先级,频繁同步 - * - 'normal': 普通优先级,正常同步频率 - * - 'low': 低优先级,较少同步 - */ - priority?: 'critical' | 'high' | 'normal' | 'low'; - - /** - * 值变化时的回调函数名 - * 回调函数签名: (oldValue: T, newValue: T) => void - */ - hook?: string; - - /** - * 是否只有拥有权限的客户端才能修改 - */ - authorityOnly?: boolean; - - /** - * 同步频率限制(毫秒) - * 防止过于频繁的网络同步,默认为0(不限制) - */ - throttle?: number; - - /** - * 是否启用增量同步 - * 对于对象和数组类型,启用后只同步变化的部分 - */ - delta?: boolean; - - /** - * 自定义比较函数 - * 用于判断值是否发生变化,默认使用深度比较 - */ - compare?: (oldValue: any, newValue: any) => boolean; -} - -/** - * TSRPC序列化组件配置选项 - */ -export interface TsrpcSerializableOptions { - /** - * 组件版本号,用于兼容性检查 - */ - version?: number; - - /** - * 是否启用类型验证 - * 在开发模式下默认启用,生产模式下默认关闭 - */ - validation?: boolean; - - /** - * 是否启用压缩 - */ - compression?: boolean; - - /** - * 自定义序列化名称,默认使用类名 - */ - name?: string; - - /** - * 序列化策略 - * - 'full': 完整序列化所有字段 - * - 'partial': 只序列化标记为@SyncField的字段 - * - 'auto': 自动检测,推荐使用 - */ - strategy?: 'full' | 'partial' | 'auto'; -} - -/** - * TSRPC字段元数据 - */ -export interface TsrpcFieldMetadata { - /** 属性名称 */ - propertyKey: string; - - /** 字段选项 */ - options: SyncFieldOptions; - - /** TypeScript类型信息 */ - typeInfo: { - /** 基本类型名 */ - typeName: string; - /** 是否为数组 */ - isArray: boolean; - /** 是否可选 */ - isOptional: boolean; - /** 是否为联合类型 */ - isUnion: boolean; - /** 联合类型成员(如果是联合类型) */ - unionTypes?: string[]; - /** 泛型参数(如果有) */ - genericTypes?: string[]; - }; - - /** 运行时类型检查函数 */ - typeChecker?: (value: any) => boolean; - - /** 字段索引(用于二进制序列化) */ - fieldIndex: number; -} - -/** - * TSRPC组件元数据 - */ -export interface TsrpcComponentMetadata { - /** 组件类型名称 */ - componentType: string; - - /** 组件配置选项 */ - options: TsrpcSerializableOptions; - - /** 字段元数据映射 */ - fields: Map; - - /** 组件构造函数 */ - constructor: new (...args: any[]) => Component; - - /** TSBuffer schema */ - schema?: any; - - /** 序列化版本 */ - version: number; - - /** 创建时间戳 */ - createdAt: number; -} - -/** - * TSRPC可序列化组件接口 - */ -export interface TsrpcSerializable { - /** 获取TSRPC序列化数据 */ - getTsrpcData(): Record; - - /** 应用TSRPC序列化数据 */ - applyTsrpcData(data: Record): void; - - /** 获取变化的字段 */ - getDirtyFields(): string[]; - - /** 标记字段为clean状态 */ - markClean(fieldNames?: string[]): void; -} - -/** - * 支持的TypeScript基本类型 - */ -export const TsrpcSupportedTypes = { - // 基本类型 - BOOLEAN: 'boolean', - NUMBER: 'number', - BIGINT: 'bigint', - STRING: 'string', - - // 对象类型 - OBJECT: 'object', - ARRAY: 'array', - DATE: 'Date', - REGEXP: 'RegExp', - - // 特殊类型 - UNDEFINED: 'undefined', - NULL: 'null', - - // 二进制类型 - UINT8ARRAY: 'Uint8Array', - BUFFER: 'Buffer', - ARRAYBUFFER: 'ArrayBuffer', - - // 联合类型和字面量类型 - UNION: 'union', - LITERAL: 'literal', - ENUM: 'enum' -} as const; - -/** - * TSRPC序列化统计信息 - */ -export interface TsrpcSerializationStats { - /** 序列化次数 */ - serializeCount: number; - - /** 反序列化次数 */ - deserializeCount: number; - - /** 总序列化时间 */ - totalSerializeTime: number; - - /** 总反序列化时间 */ - totalDeserializeTime: number; - - /** 平均序列化大小 */ - averageSerializedSize: number; - - /** 错误次数 */ - errorCount: number; - - /** 缓存命中次数 */ - cacheHits: number; - - /** 缓存未命中次数 */ - cacheMisses: number; -} \ No newline at end of file diff --git a/packages/network/src/Serialization/index.ts b/packages/network/src/Serialization/index.ts deleted file mode 100644 index 2202694f..00000000 --- a/packages/network/src/Serialization/index.ts +++ /dev/null @@ -1,37 +0,0 @@ -export { SerializedData } from './SerializationTypes'; - -export { - SyncFieldOptions, - TsrpcSerializableOptions, - TsrpcFieldMetadata, - TsrpcComponentMetadata, - TsrpcSerializable, - TsrpcSupportedTypes, - TsrpcSerializationStats -} from './TsrpcTypes'; - -export { - TsrpcRegistry, - SyncField, - TsrpcSerializable as TsrpcSerializableDecorator, - isTsrpcSerializable, - getTsrpcMetadata, - getTsrpcFields, - getTsrpcName, - validateTsrpcComponent, - TsrpcString, - TsrpcNumber, - TsrpcBoolean, - TsrpcDate, - TsrpcArray, - TsrpcObject, - TsrpcCritical, - TsrpcAuthority, - AutoSync -} from './TsrpcDecorators'; - -export { - TsrpcSerializer, - tsrpcSerializer -} from './TsrpcSerializer'; - diff --git a/packages/network/src/Snapshot/ISnapshotable.ts b/packages/network/src/Snapshot/ISnapshotable.ts deleted file mode 100644 index 382e03c6..00000000 --- a/packages/network/src/Snapshot/ISnapshotable.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * 可序列化接口 - * - * 实现此接口的类可以被快照系统序列化和反序列化 - */ -export interface ISnapshotable { - /** - * 序列化对象到可传输的数据格式 - * - * @returns 序列化后的数据 - */ - serialize(): any; - - /** - * 从序列化数据恢复对象状态 - * - * @param data - 序列化数据 - */ - deserialize(data: any): void; -} - -/** - * 快照配置接口 - */ -export interface SnapshotConfig { - /** 是否包含在快照中 */ - includeInSnapshot: boolean; - /** 压缩级别 (0-9) */ - compressionLevel: number; - /** 同步优先级 (数字越大优先级越高) */ - syncPriority: number; - /** 是否启用增量快照 */ - enableIncremental: boolean; -} - -/** - * 组件快照数据 - */ -export interface ComponentSnapshot { - /** 组件类型名称 */ - type: string; - /** 组件ID */ - id: number; - /** 序列化数据 */ - data: any; - /** 是否启用 */ - enabled: boolean; - /** 快照配置 */ - config?: SnapshotConfig; -} - -/** - * 实体快照数据 - */ -export interface EntitySnapshot { - /** 实体ID */ - id: number; - /** 实体名称 */ - name: string; - /** 是否启用 */ - enabled: boolean; - /** 是否激活 */ - active: boolean; - /** 标签 */ - tag: number; - /** 更新顺序 */ - updateOrder: number; - /** 组件快照列表 */ - components: ComponentSnapshot[]; - /** 子实体ID列表 */ - children: number[]; - /** 父实体ID */ - parent?: number; - /** 快照时间戳 */ - timestamp: number; -} - -/** - * 场景快照数据 - */ -export interface SceneSnapshot { - /** 实体快照列表 */ - entities: EntitySnapshot[]; - /** 快照时间戳 */ - timestamp: number; - /** 框架版本 */ - version: string; - /** 快照类型 */ - type: 'full' | 'incremental'; - /** 基础快照ID(增量快照使用) */ - baseSnapshotId?: string; -} \ No newline at end of file diff --git a/packages/network/src/Snapshot/SnapshotExtension.ts b/packages/network/src/Snapshot/SnapshotExtension.ts deleted file mode 100644 index 17e03e2d..00000000 --- a/packages/network/src/Snapshot/SnapshotExtension.ts +++ /dev/null @@ -1,260 +0,0 @@ -import { Component, ComponentType } from '@esengine/ecs-framework'; -import { ISnapshotable, SnapshotConfig } from './ISnapshotable'; - -/** - * 快照扩展接口 - * - * 为Component基类提供快照功能的扩展接口 - */ -export interface ISnapshotExtension { - /** 快照配置 */ - snapshotConfig?: SnapshotConfig; - - /** 序列化方法 */ - serialize?(): any; - - /** 反序列化方法 */ - deserialize?(data: any): void; - - /** 变化检测方法 */ - hasChanged?(baseData: any): boolean; -} - -/** - * 快照装饰器 - * - * 用于标记组件属性为可序列化 - */ -export function Serializable(config?: Partial) { - return function (target: T, propertyKey: keyof T) { - const comp = target as T & { snapshotConfig?: SnapshotConfig; _serializableProperties?: Set }; - - // 确保组件有快照配置 - if (!comp.snapshotConfig) { - comp.snapshotConfig = { - includeInSnapshot: true, - compressionLevel: 0, - syncPriority: 5, - enableIncremental: true - }; - } - - // 标记属性为可序列化 - if (!comp._serializableProperties) { - comp._serializableProperties = new Set(); - } - comp._serializableProperties.add(propertyKey as string); - - // 应用配置 - if (config) { - Object.assign(comp.snapshotConfig, config); - } - }; -} - -/** - * 快照配置装饰器 - * - * 用于配置组件的快照行为 - */ -export function SnapshotConfigDecorator(config: SnapshotConfig) { - return function (target: T) { - target.prototype.snapshotConfig = config; - }; -} - -/** - * 快照扩展工具类 - */ -export class SnapshotExtension { - /** - * 为组件添加快照支持 - * - * @param component - 目标组件 - * @param config - 快照配置 - */ - public static enableSnapshot(component: T, config?: Partial): void { - const defaultConfig: SnapshotConfig = { - includeInSnapshot: true, - compressionLevel: 0, - syncPriority: 5, - enableIncremental: true - }; - - (component as T & { snapshotConfig?: SnapshotConfig }).snapshotConfig = { ...defaultConfig, ...config }; - } - - /** - * 禁用组件的快照功能 - * - * @param component - 目标组件 - */ - public static disableSnapshot(component: T): void { - const comp = component as T & { snapshotConfig?: SnapshotConfig }; - if (comp.snapshotConfig) { - comp.snapshotConfig.includeInSnapshot = false; - } - } - - /** - * 检查组件是否支持快照 - * - * @param component - 目标组件 - * @returns 是否支持快照 - */ - public static isSnapshotable(component: T): component is T & ISnapshotExtension { - const config = (component as T & { snapshotConfig?: SnapshotConfig }).snapshotConfig; - return config?.includeInSnapshot === true; - } - - /** - * 获取组件的可序列化属性 - * - * @param component - 目标组件 - * @returns 可序列化属性列表 - */ - public static getSerializableProperties(component: T): (keyof T)[] { - const comp = component as T & { _serializableProperties?: Set }; - if (comp._serializableProperties) { - return Array.from(comp._serializableProperties) as (keyof T)[]; - } - - // 如果没有标记,返回所有公共属性 - const publicProperties: (keyof T)[] = []; - for (const key in component) { - if (component.hasOwnProperty(key) && - typeof component[key] !== 'function' && - key !== 'id' && - key !== 'entity' && - key !== '_enabled' && - key !== '_updateOrder') { - publicProperties.push(key); - } - } - - return publicProperties; - } - - /** - * 创建组件的默认序列化方法 - * - * @param component - 目标组件 - * @returns 序列化数据 - */ - public static createDefaultSerializer(component: T): () => Partial { - return function(): Partial { - const data = {} as Partial; - const properties = SnapshotExtension.getSerializableProperties(component); - - for (const prop of properties) { - const value = component[prop]; - if (value !== undefined && value !== null) { - data[prop] = value; - } - } - - return data; - }; - } - - /** - * 创建组件的默认反序列化方法 - * - * @param component - 目标组件 - * @returns 反序列化函数 - */ - public static createDefaultDeserializer(component: T): (data: Partial) => void { - return function(data: Partial): void { - const properties = SnapshotExtension.getSerializableProperties(component); - - for (const prop of properties) { - if (data.hasOwnProperty(prop) && data[prop] !== undefined) { - component[prop] = data[prop]!; - } - } - }; - } - - /** - * 创建简单的变化检测方法 - * - * @param component - 目标组件 - * @returns 变化检测函数 - */ - public static createSimpleChangeDetector(component: T): (baseData: Partial) => boolean { - return function(baseData: Partial): boolean { - const properties = SnapshotExtension.getSerializableProperties(component); - - for (const prop of properties) { - const currentValue = component[prop]; - const baseValue = baseData[prop]; - - if (currentValue !== baseValue) { - return true; - } - } - - return false; - }; - } - - /** - * 创建深度变化检测方法 - * - * @param component - 目标组件 - * @returns 变化检测函数 - */ - public static createDeepChangeDetector(component: T): (baseData: Partial) => boolean { - return function(baseData: Partial): boolean { - const properties = SnapshotExtension.getSerializableProperties(component); - - for (const prop of properties) { - const currentValue = component[prop]; - const baseValue = baseData[prop]; - - if (SnapshotExtension.deepCompare(currentValue, baseValue)) { - return true; - } - } - - return false; - }; - } - - /** - * 深度比较两个值 - */ - private static deepCompare(value1: unknown, value2: unknown): boolean { - if (value1 === value2) return false; - - if (typeof value1 !== typeof value2) return true; - - if (value1 === null || value2 === null) return value1 !== value2; - - if (typeof value1 !== 'object' || typeof value2 !== 'object') return value1 !== value2; - - if (Array.isArray(value1) !== Array.isArray(value2)) return true; - - if (Array.isArray(value1) && Array.isArray(value2)) { - if (value1.length !== value2.length) return true; - for (let i = 0; i < value1.length; i++) { - if (this.deepCompare(value1[i], value2[i])) return true; - } - return false; - } - - if (!value1 || !value2) return true; - - const keys1 = Object.keys(value1); - const keys2 = Object.keys(value2); - - if (keys1.length !== keys2.length) return true; - - for (const key of keys1) { - if (!keys2.includes(key)) return true; - if (this.deepCompare((value1 as Record)[key], (value2 as Record)[key])) return true; - } - - return false; - } -} \ No newline at end of file diff --git a/packages/network/src/Snapshot/SnapshotManager.ts b/packages/network/src/Snapshot/SnapshotManager.ts deleted file mode 100644 index 8e10f576..00000000 --- a/packages/network/src/Snapshot/SnapshotManager.ts +++ /dev/null @@ -1,798 +0,0 @@ -import { Entity, Component, ComponentType, createLogger } from '@esengine/ecs-framework'; -import { ISnapshotable, SceneSnapshot, EntitySnapshot, ComponentSnapshot, SnapshotConfig } from './ISnapshotable'; -import { TsrpcSerializer } from '../Serialization/TsrpcSerializer'; -import { SerializedData } from '../Serialization/SerializationTypes'; -import { isTsrpcSerializable } from '../Serialization/TsrpcDecorators'; -import { - NetworkComponentType, - IComponentFactory, - SerializationTarget, - TypeGuards, - INetworkSyncable -} from '../types/NetworkTypes'; - -/** - * 组件类型注册表 - */ -class ComponentTypeRegistry implements IComponentFactory { - private static _instance: ComponentTypeRegistry | null = null; - private _componentTypes: Map = new Map(); - - public static get Instance(): ComponentTypeRegistry { - if (!ComponentTypeRegistry._instance) { - ComponentTypeRegistry._instance = new ComponentTypeRegistry(); - } - return ComponentTypeRegistry._instance; - } - - /** - * 注册组件类型 - */ - public register(name: string, constructor: NetworkComponentType): void { - this._componentTypes.set(name, constructor); - } - - /** - * 获取组件构造函数 - */ - public get(name: string): NetworkComponentType | undefined { - return this._componentTypes.get(name); - } - - /** - * 自动注册组件类型(通过构造函数名) - */ - public autoRegister(constructor: NetworkComponentType): void { - this.register(constructor.name, constructor); - } - - /** - * 获取所有已注册的组件类型 - */ - public getAllTypes(): string[] { - return Array.from(this._componentTypes.keys()); - } - - /** - * 检查组件类型是否已注册(按名称) - */ - public isRegisteredByName(name: string): boolean { - return this._componentTypes.has(name); - } - - /** - * 清除所有注册 - */ - public clear(): void { - this._componentTypes.clear(); - } - - /** - * 创建组件实例 - */ - public create( - componentType: NetworkComponentType, - ...args: unknown[] - ): T { - return new componentType(...args); - } - - /** - * 检查组件类型是否已注册 - */ - public isRegistered( - componentType: NetworkComponentType - ): boolean { - return this._componentTypes.has(componentType.name); - } - - /** - * 获取组件类型名称 - */ - public getTypeName( - componentType: NetworkComponentType - ): string { - return componentType.name; - } -} - -/** - * 快照管理器 - * - * 负责创建和管理ECS系统的快照,支持完整快照和增量快照 - * 使用protobuf序列化 - */ -export class SnapshotManager { - private static readonly logger = createLogger('SnapshotManager'); - - /** 默认快照配置 */ - private static readonly DEFAULT_CONFIG: SnapshotConfig = { - includeInSnapshot: true, - compressionLevel: 0, - syncPriority: 5, - enableIncremental: true - }; - - /** 框架版本 */ - private readonly version: string = '1.0.0'; - - /** 最后快照时间戳 */ - private lastSnapshotTime: number = 0; - - /** 快照缓存 */ - private snapshotCache = new Map(); - - /** 最大缓存数量 */ - private maxCacheSize: number = 10; - - /** TSRPC序列化器 */ - private tsrpcSerializer: TsrpcSerializer; - - /** 组件类型注册表 */ - private componentRegistry: ComponentTypeRegistry; - - /** - * 构造函数 - */ - constructor() { - this.tsrpcSerializer = TsrpcSerializer.getInstance(); - this.componentRegistry = ComponentTypeRegistry.Instance; - } - - - /** - * 创建场景快照 - * - * @param entities - 实体列表 - * @param type - 快照类型 - * @returns 场景快照 - */ - public createSceneSnapshot(entities: Entity[], type: 'full' | 'incremental' = 'full'): SceneSnapshot { - const entitySnapshots: EntitySnapshot[] = []; - - const sortedEntities = entities.sort((a, b) => a.id - b.id); - - for (const entity of sortedEntities) { - if (entity.isDestroyed) continue; - - const entitySnapshot = this.createEntitySnapshot(entity); - if (entitySnapshot) { - entitySnapshots.push(entitySnapshot); - } - } - - const snapshot: SceneSnapshot = { - entities: entitySnapshots, - timestamp: Date.now(), - version: this.version, - type: type - }; - - this.lastSnapshotTime = snapshot.timestamp; - - return snapshot; - } - - /** - * 从快照恢复场景 - * - * @param snapshot - 场景快照 - * @param targetEntities - 目标实体列表(用于增量恢复) - * @param createMissingEntities - 是否创建缺失的实体 - */ - public restoreFromSnapshot(snapshot: SceneSnapshot, targetEntities?: Entity[], createMissingEntities: boolean = false): Entity[] { - if (snapshot.type === 'incremental' && targetEntities) { - return this.restoreIncrementalSnapshot(snapshot, targetEntities); - } else { - return this.restoreFullSnapshot(snapshot, targetEntities, createMissingEntities); - } - } - - /** - * 从快照恢复实体列表 - * - * @param snapshot - 场景快照 - * @param targetEntities - 目标实体列表 - * @param createMissingEntities - 是否创建缺失的实体 - */ - public restoreEntitiesFromSnapshot(snapshot: SceneSnapshot, targetEntities: Entity[], createMissingEntities: boolean = false): Entity[] { - const restoredEntities: Entity[] = []; - const targetEntityMap = new Map(); - - for (const entity of targetEntities) { - targetEntityMap.set(entity.id, entity); - } - - for (const entitySnapshot of snapshot.entities) { - let targetEntity = targetEntityMap.get(entitySnapshot.id); - - if (!targetEntity && createMissingEntities) { - // 创建缺失的实体 - const newEntity = this.createEntityFromSnapshot(entitySnapshot); - if (newEntity) { - restoredEntities.push(newEntity); - } - } else if (targetEntity) { - // 恢复现有实体 - this.restoreEntityFromSnapshot(targetEntity, entitySnapshot); - restoredEntities.push(targetEntity); - } - } - - return restoredEntities; - } - - /** - * 从快照创建实体 - */ - private createEntityFromSnapshot(entitySnapshot: EntitySnapshot): Entity | null { - try { - const entity = new Entity(entitySnapshot.name, entitySnapshot.id); - - // 设置基本属性 - entity.enabled = entitySnapshot.enabled; - entity.active = entitySnapshot.active; - entity.tag = entitySnapshot.tag; - entity.updateOrder = entitySnapshot.updateOrder; - - // 创建组件 - for (const componentSnapshot of entitySnapshot.components) { - this.createComponentFromSnapshot(entity, componentSnapshot); - } - - return entity; - } catch (error) { - SnapshotManager.logger.error(`创建实体失败: ${entitySnapshot.name}`, error); - return null; - } - } - - /** - * 从快照创建组件 - */ - private createComponentFromSnapshot(entity: Entity, componentSnapshot: ComponentSnapshot): void { - try { - // 尝试获取组件构造函数 - const componentType = this.getComponentType(componentSnapshot.type); - if (!componentType) { - SnapshotManager.logger.warn(`未知组件类型: ${componentSnapshot.type}`); - return; - } - - // 创建组件实例 - const component = entity.createComponent(componentType); - - // 恢复组件启用状态 - component.enabled = componentSnapshot.enabled; - - // 恢复组件数据 - const serializedData = componentSnapshot.data as SerializedData; - - if (!isTsrpcSerializable(component)) { - throw new Error(`[SnapshotManager] 组件 ${component.constructor.name} 不支持TSRPC反序列化`); - } - - this.tsrpcSerializer.deserialize(serializedData); - } catch (error) { - SnapshotManager.logger.error(`创建组件失败: ${componentSnapshot.type}`, error); - } - } - - /** - * 获取组件类型 - */ - private getComponentType(typeName: string): NetworkComponentType | null { - const componentType = this.componentRegistry.get(typeName); - if (!componentType) { - SnapshotManager.logger.warn(`组件类型 ${typeName} 未注册,请先调用 registerComponentType() 注册`); - return null; - } - return componentType; - } - - /** - * 创建快速快照(跳过变化检测) - * - * @param entities - 实体列表 - * @returns 场景快照 - */ - public createQuickSnapshot(entities: Entity[]): SceneSnapshot { - return this.createSceneSnapshot(entities, 'full'); - } - - /** - * 创建增量快照 - * - * @param entities - 实体列表 - * @param baseSnapshot - 基础快照 - * @param enableChangeDetection - 是否启用变化检测 - * @returns 增量快照 - */ - public createIncrementalSnapshot(entities: Entity[], baseSnapshot: SceneSnapshot, enableChangeDetection: boolean = true): SceneSnapshot { - const incrementalEntities: EntitySnapshot[] = []; - - const baseEntityMap = new Map(); - for (const entity of baseSnapshot.entities) { - baseEntityMap.set(entity.id, entity); - } - - for (const entity of entities) { - if (entity.isDestroyed) continue; - - const baseEntity = baseEntityMap.get(entity.id); - if (!baseEntity) { - const entitySnapshot = this.createEntitySnapshot(entity); - if (entitySnapshot) { - incrementalEntities.push(entitySnapshot); - } - } else if (enableChangeDetection) { - const changedComponents = this.getChangedComponents(entity, baseEntity); - if (this.hasEntityStructureChanged(entity, baseEntity) || changedComponents.length > 0) { - const incrementalEntitySnapshot = this.createIncrementalEntitySnapshot(entity, baseEntity, changedComponents); - if (incrementalEntitySnapshot) { - incrementalEntities.push(incrementalEntitySnapshot); - } - } - } - } - - return { - entities: incrementalEntities, - timestamp: Date.now(), - version: this.version, - type: 'incremental', - baseSnapshotId: this.generateSnapshotId(baseSnapshot) - }; - } - - /** - * 缓存快照 - * - * @param id - 快照ID - * @param snapshot - 快照数据 - */ - public cacheSnapshot(id: string, snapshot: SceneSnapshot): void { - // 清理过期缓存 - if (this.snapshotCache.size >= this.maxCacheSize) { - const oldestKey = this.snapshotCache.keys().next().value; - if (oldestKey) { - this.snapshotCache.delete(oldestKey); - } - } - - this.snapshotCache.set(id, snapshot); - } - - /** - * 获取缓存的快照 - * - * @param id - 快照ID - * @returns 快照数据或undefined - */ - public getCachedSnapshot(id: string): SceneSnapshot | undefined { - return this.snapshotCache.get(id); - } - - /** - * 清空快照缓存 - */ - public clearCache(): void { - this.snapshotCache.clear(); - } - - /** - * 清空所有缓存 - */ - public clearAllCaches(): void { - this.snapshotCache.clear(); - } - - /** - * 获取缓存统计信息 - */ - public getCacheStats(): { - snapshotCacheSize: number; - tsrpcStats?: { - registeredComponents: number; - tsrpcAvailable: boolean; - }; - } { - const stats: any = { - snapshotCacheSize: this.snapshotCache.size - }; - - if (this.tsrpcSerializer) { - stats.tsrpcStats = this.tsrpcSerializer.getStats(); - } - - return stats; - } - - - /** - * 注册组件类型 - * - * @param constructor - 组件构造函数 - */ - public registerComponentType(constructor: NetworkComponentType): void { - this.componentRegistry.autoRegister(constructor); - SnapshotManager.logger.debug(`已注册组件类型: ${constructor.name}`); - } - - /** - * 批量注册组件类型 - * - * @param constructors - 组件构造函数数组 - */ - public registerComponentTypes(constructors: Array): void { - for (const constructor of constructors) { - this.registerComponentType(constructor as any); - } - } - - /** - * 检查组件类型是否已注册 - * - * @param typeName - 组件类型名称 - */ - public isComponentTypeRegistered(typeName: string): boolean { - return this.componentRegistry.isRegisteredByName(typeName); - } - - /** - * 获取所有已注册的组件类型 - */ - public getRegisteredComponentTypes(): string[] { - return this.componentRegistry.getAllTypes(); - } - - /** - * 创建实体快照 - */ - private createEntitySnapshot(entity: Entity): EntitySnapshot | null { - const componentSnapshots: ComponentSnapshot[] = []; - - for (const component of entity.components) { - const componentSnapshot = this.createComponentSnapshot(component); - if (componentSnapshot) { - componentSnapshots.push(componentSnapshot); - } - } - - return { - id: entity.id, - name: entity.name, - enabled: entity.enabled, - active: entity.active, - tag: entity.tag, - updateOrder: entity.updateOrder, - components: componentSnapshots, - children: entity.children.map(child => child.id), - parent: entity.parent?.id || undefined, - timestamp: Date.now() - }; - } - - /** - * 创建组件快照 - * - * 优先使用TSRPC序列化,fallback到JSON序列化 - */ - private createComponentSnapshot(component: Component): ComponentSnapshot | null { - if (!this.isComponentSnapshotable(component)) { - return null; - } - - let serializedData: SerializedData; - - // 优先使用TSRPC序列化 - if (isTsrpcSerializable(component)) { - try { - const tsrpcResult = this.tsrpcSerializer.serialize(component); - if (tsrpcResult) { - serializedData = tsrpcResult; - } else { - throw new Error('TSRPC序列化返回null'); - } - } catch (error) { - SnapshotManager.logger.warn(`[SnapshotManager] TSRPC序列化失败,fallback到JSON: ${error}`); - serializedData = this.createJsonSerializedData(component); - } - } else { - // fallback到JSON序列化 - serializedData = this.createJsonSerializedData(component); - } - - return { - type: component.constructor.name, - id: component.id, - data: serializedData, - enabled: component.enabled, - config: this.getComponentSnapshotConfig(component) - }; - } - - /** - * 创建JSON序列化数据 - */ - private createJsonSerializedData(component: Component): SerializedData { - // 使用replacer排除循环引用和不需要的属性 - const jsonData = JSON.stringify(component, (key, value) => { - // 排除entity引用以避免循环引用 - if (key === 'entity') { - return undefined; - } - // 排除函数和symbol - if (typeof value === 'function' || typeof value === 'symbol') { - return undefined; - } - return value; - }); - return { - type: 'json', - componentType: component.constructor.name, - data: jsonData, - size: jsonData.length - }; - } - - /** - * 检查组件是否支持快照 - */ - private isComponentSnapshotable(component: Component): boolean { - // 检查是否有快照配置 - const config = this.getComponentSnapshotConfig(component); - return config.includeInSnapshot; - } - - /** - * 获取组件快照配置 - */ - private getComponentSnapshotConfig(component: Component): SnapshotConfig { - // 检查组件是否有自定义配置 - const componentWithConfig = component as Component & { snapshotConfig?: Partial }; - if (componentWithConfig.snapshotConfig) { - return { ...SnapshotManager.DEFAULT_CONFIG, ...componentWithConfig.snapshotConfig }; - } - - return SnapshotManager.DEFAULT_CONFIG; - } - - - /** - * 恢复完整快照 - */ - private restoreFullSnapshot(snapshot: SceneSnapshot, targetEntities?: Entity[], createMissingEntities: boolean = false): Entity[] { - if (targetEntities && createMissingEntities) { - return this.restoreEntitiesFromSnapshot(snapshot, targetEntities, true); - } else if (targetEntities) { - return this.restoreEntitiesFromSnapshot(snapshot, targetEntities, false); - } else { - const restoredEntities: Entity[] = []; - for (const entitySnapshot of snapshot.entities) { - const entity = this.createEntityFromSnapshot(entitySnapshot); - if (entity) { - restoredEntities.push(entity); - } - } - return restoredEntities; - } - } - - /** - * 恢复增量快照 - */ - private restoreIncrementalSnapshot(snapshot: SceneSnapshot, targetEntities: Entity[]): Entity[] { - const restoredEntities: Entity[] = []; - const targetEntityMap = new Map(); - - for (const entity of targetEntities) { - targetEntityMap.set(entity.id, entity); - } - - for (const entitySnapshot of snapshot.entities) { - const targetEntity = targetEntityMap.get(entitySnapshot.id); - if (targetEntity) { - this.restoreEntityFromSnapshot(targetEntity, entitySnapshot); - restoredEntities.push(targetEntity); - } - } - - return restoredEntities; - } - - /** - * 从快照恢复实体 - */ - private restoreEntityFromSnapshot(entity: Entity, entitySnapshot: EntitySnapshot): void { - // 恢复实体基本属性 - entity.enabled = entitySnapshot.enabled; - entity.active = entitySnapshot.active; - entity.tag = entitySnapshot.tag; - entity.updateOrder = entitySnapshot.updateOrder; - - // 恢复组件 - for (const componentSnapshot of entitySnapshot.components) { - this.restoreComponentFromSnapshot(entity, componentSnapshot); - } - } - - /** - * 从快照恢复组件 - * - * 使用TSRPC反序列化 - */ - private restoreComponentFromSnapshot(entity: Entity, componentSnapshot: ComponentSnapshot): void { - // 查找现有组件 - const componentType = this.getComponentType(componentSnapshot.type); - if (!componentType) { - SnapshotManager.logger.warn(`组件类型 ${componentSnapshot.type} 未注册,无法恢复`); - return; - } - - let component = entity.getComponent(componentType); - - if (!component) { - // 组件不存在,需要创建 - SnapshotManager.logger.warn(`组件 ${componentSnapshot.type} 不存在于实体 ${entity.name},无法恢复`); - return; - } - - // 恢复组件启用状态 - component.enabled = componentSnapshot.enabled; - - // 恢复组件数据 - const serializedData = componentSnapshot.data as SerializedData; - - if (serializedData.type === 'tsrpc' && isTsrpcSerializable(component)) { - // 使用TSRPC反序列化 - this.tsrpcSerializer.deserialize(serializedData); - } else if (serializedData.type === 'json') { - // 使用JSON反序列化 - this.deserializeFromJson(component, serializedData); - } else { - SnapshotManager.logger.warn(`[SnapshotManager] 组件 ${component.constructor.name} 序列化类型不匹配或不支持`); - } - } - - /** - * 从JSON数据反序列化组件 - */ - private deserializeFromJson(component: Component, serializedData: SerializedData): void { - try { - const jsonData = JSON.parse(serializedData.data as string); - Object.assign(component, jsonData); - } catch (error) { - SnapshotManager.logger.error(`[SnapshotManager] JSON反序列化失败: ${error}`); - } - } - - /** - * 检查实体结构是否发生变化(组件数量、类型等) - */ - private hasEntityStructureChanged(entity: Entity, baseSnapshot: EntitySnapshot): boolean { - // 检查基本属性变化 - if (entity.enabled !== baseSnapshot.enabled || - entity.active !== baseSnapshot.active || - entity.tag !== baseSnapshot.tag || - entity.updateOrder !== baseSnapshot.updateOrder) { - return true; - } - - // 检查组件数量变化 - if (entity.components.length !== baseSnapshot.components.length) { - return true; - } - - // 检查组件类型变化 - const currentComponentTypes = new Set(entity.components.map(c => c.constructor.name)); - const baseComponentTypes = new Set(baseSnapshot.components.map(c => c.type)); - - if (currentComponentTypes.size !== baseComponentTypes.size) { - return true; - } - - for (const type of currentComponentTypes) { - if (!baseComponentTypes.has(type)) { - return true; - } - } - - return false; - } - - /** - * 获取发生变化的组件列表 - */ - private getChangedComponents(entity: Entity, baseSnapshot: EntitySnapshot): ComponentSnapshot[] { - const changedComponents: ComponentSnapshot[] = []; - - const baseComponentMap = new Map(); - for (const comp of baseSnapshot.components) { - baseComponentMap.set(comp.type, comp); - } - - for (const component of entity.components) { - const baseComponent = baseComponentMap.get(component.constructor.name); - - if (!baseComponent) { - const componentSnapshot = this.createComponentSnapshot(component); - if (componentSnapshot) { - changedComponents.push(componentSnapshot); - } - } else { - if (this.hasComponentDataChanged(component, baseComponent)) { - const componentSnapshot = this.createComponentSnapshot(component); - if (componentSnapshot) { - changedComponents.push(componentSnapshot); - } - } - } - } - - return changedComponents; - } - - /** - * 检查组件数据是否发生变化 - */ - private hasComponentDataChanged(component: Component, baseComponent: ComponentSnapshot): boolean { - if (component.enabled !== baseComponent.enabled) { - return true; - } - - if (this.hasChangeDetectionMethod(component)) { - try { - const componentWithMethod = component as Component & { hasChanged(data: unknown): boolean }; - return componentWithMethod.hasChanged(baseComponent.data); - } catch { - return true; - } - } - - return true; - } - - /** - * 检查组件是否有变化检测方法 - */ - private hasChangeDetectionMethod(component: Component): component is Component & { hasChanged(data: unknown): boolean } { - return typeof (component as any).hasChanged === 'function'; - } - - /** - * 创建增量实体快照(只包含变化的组件) - */ - private createIncrementalEntitySnapshot(entity: Entity, baseSnapshot: EntitySnapshot, changedComponents: ComponentSnapshot[]): EntitySnapshot | null { - // 检查实体基本属性是否变化 - const hasBasicChanges = entity.enabled !== baseSnapshot.enabled || - entity.active !== baseSnapshot.active || - entity.tag !== baseSnapshot.tag || - entity.updateOrder !== baseSnapshot.updateOrder; - - // 如果没有基本变化且没有组件变化,返回null - if (!hasBasicChanges && changedComponents.length === 0) { - return null; - } - - return { - id: entity.id, - name: entity.name, - enabled: entity.enabled, - active: entity.active, - tag: entity.tag, - updateOrder: entity.updateOrder, - components: changedComponents, // 只包含变化的组件 - children: entity.children.map(child => child.id), - parent: entity.parent?.id || undefined, - timestamp: Date.now() - }; - } - - /** - * 生成快照ID - */ - private generateSnapshotId(snapshot: SceneSnapshot): string { - return `${snapshot.timestamp}_${snapshot.entities.length}`; - } -} \ No newline at end of file diff --git a/packages/network/src/Snapshot/index.ts b/packages/network/src/Snapshot/index.ts deleted file mode 100644 index ba239567..00000000 --- a/packages/network/src/Snapshot/index.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 快照系统模块 - * - * 提供ECS系统的快照功能,支持实体和组件的序列化与反序列化 - */ - -// 核心接口 -export * from './ISnapshotable'; - -// 快照管理器 -export { SnapshotManager } from './SnapshotManager'; - -// 快照扩展 -export { - ISnapshotExtension, - Serializable, - SnapshotConfigDecorator, - SnapshotExtension -} from './SnapshotExtension'; \ No newline at end of file diff --git a/packages/network/src/SyncVar/ComponentIdGenerator.ts b/packages/network/src/SyncVar/ComponentIdGenerator.ts deleted file mode 100644 index 96256169..00000000 --- a/packages/network/src/SyncVar/ComponentIdGenerator.ts +++ /dev/null @@ -1,219 +0,0 @@ -import { Entity, createLogger } from '@esengine/ecs-framework'; -import { NetworkEnvironment } from '../Core/NetworkEnvironment'; -import { NetworkIdentity } from '../Core/NetworkIdentity'; - -const logger = createLogger('ComponentIdGenerator'); - -/** - * 组件ID生成器配置 - */ -export interface ComponentIdGeneratorConfig { - useNetworkId: boolean; - useEntityId: boolean; - useTimestamp: boolean; - useSequence: boolean; - customPrefix?: string; -} - -/** - * 组件唯一ID生成器 - * - * 提供多种策略生成组件的唯一标识符 - * 可以集成网络ID系统、实体ID系统等 - */ -export class ComponentIdGenerator { - private static readonly DEFAULT_CONFIG: ComponentIdGeneratorConfig = { - useNetworkId: true, - useEntityId: true, - useTimestamp: true, - useSequence: true - }; - - private _config: ComponentIdGeneratorConfig; - private _sequenceCounter: number = 0; - private _generatedIds: Set = new Set(); - - constructor(config?: Partial) { - this._config = { ...ComponentIdGenerator.DEFAULT_CONFIG, ...config }; - } - - /** - * 为组件生成唯一ID - * - * @param component - 组件实例 - * @returns 唯一ID字符串 - */ - public generateId(component: any): string { - const parts: string[] = []; - - // 添加自定义前缀 - if (this._config.customPrefix) { - parts.push(this._config.customPrefix); - } - - // 添加组件类型名称 - parts.push(component.constructor.name); - - // 尝试使用网络ID - if (this._config.useNetworkId) { - const networkId = this.extractNetworkId(component); - if (networkId) { - parts.push(`net_${networkId}`); - } - } - - // 尝试使用实体ID - if (this._config.useEntityId) { - const entityId = this.extractEntityId(component); - if (entityId !== null) { - parts.push(`ent_${entityId}`); - } - } - - // 添加环境前缀 - const env = NetworkEnvironment.isServer ? 's' : 'c'; - parts.push(env); - - // 添加时间戳 - if (this._config.useTimestamp) { - parts.push(Date.now().toString(36)); - } - - // 添加序列号 - if (this._config.useSequence) { - parts.push((++this._sequenceCounter).toString(36)); - } - - // 生成基础ID - let baseId = parts.join('_'); - - // 确保ID唯一性 - let finalId = baseId; - let counter = 0; - while (this._generatedIds.has(finalId)) { - finalId = `${baseId}_${counter}`; - counter++; - } - - // 记录生成的ID - this._generatedIds.add(finalId); - - logger.debug(`为组件 ${component.constructor.name} 生成ID: ${finalId}`); - return finalId; - } - - /** - * 从组件中提取网络ID - * - * @param component - 组件实例 - * @returns 网络ID或null - */ - private extractNetworkId(component: any): string | null { - try { - // 检查组件是否有网络身份 - if (component.networkIdentity && component.networkIdentity instanceof NetworkIdentity) { - return component.networkIdentity.networkId; - } - - // 检查组件的实体是否有网络身份 - if (component.entity) { - const networkIdentity = component.entity.getComponent(NetworkIdentity); - if (networkIdentity) { - return networkIdentity.networkId; - } - } - - // 检查组件本身是否有networkId属性 - if (component.networkId && typeof component.networkId === 'string') { - return component.networkId; - } - - return null; - } catch (error) { - logger.debug('提取网络ID时出错:', error); - return null; - } - } - - /** - * 从组件中提取实体ID - * - * @param component - 组件实例 - * @returns 实体ID或null - */ - private extractEntityId(component: any): number | null { - try { - // 检查组件是否有实体引用 - if (component.entity && component.entity instanceof Entity) { - return component.entity.id; - } - - // 检查组件本身是否有entityId属性 - if (typeof component.entityId === 'number') { - return component.entityId; - } - - return null; - } catch (error) { - logger.debug('提取实体ID时出错:', error); - return null; - } - } - - /** - * 检查ID是否已经生成过 - * - * @param id - 要检查的ID - * @returns 是否已存在 - */ - public hasGenerated(id: string): boolean { - return this._generatedIds.has(id); - } - - /** - * 清理已生成的ID记录 - * - * @param maxAge - 最大保留时间(毫秒),默认1小时 - */ - public cleanup(maxAge: number = 3600000): void { - // 避免未使用参数警告 - void maxAge; - // 简单实现:清空所有记录 - // 在实际应用中,可以根据时间戳进行更精细的清理 - this._generatedIds.clear(); - this._sequenceCounter = 0; - logger.debug('已清理ID生成器缓存'); - } - - /** - * 获取统计信息 - */ - public getStats(): { - generatedCount: number; - sequenceCounter: number; - config: ComponentIdGeneratorConfig; - } { - return { - generatedCount: this._generatedIds.size, - sequenceCounter: this._sequenceCounter, - config: { ...this._config } - }; - } - - /** - * 重置生成器状态 - */ - public reset(): void { - this._generatedIds.clear(); - this._sequenceCounter = 0; - logger.debug('ID生成器已重置'); - } - - /** - * 更新配置 - */ - public updateConfig(newConfig: Partial): void { - this._config = { ...this._config, ...newConfig }; - logger.debug('ID生成器配置已更新'); - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarAuthority.ts b/packages/network/src/SyncVar/SyncVarAuthority.ts deleted file mode 100644 index 98359718..00000000 --- a/packages/network/src/SyncVar/SyncVarAuthority.ts +++ /dev/null @@ -1,318 +0,0 @@ -import { AUTHORITY_CONFIG, SYNCVAR_CONFIG } from '../constants/NetworkConstants'; -import { NetworkEnvironment } from '../Core/NetworkEnvironment'; -import { NetworkIdentity } from '../Core/NetworkIdentity'; -import { INetworkComponent, INetworkEntity, AuthorityContext as CoreAuthorityContext, AuthorityType as CoreAuthorityType, NetworkEnvironmentType } from '../types/CoreTypes'; - -const logger = { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug -}; - -/** 重新导出核心权限类型 */ -export { AuthorityType } from '../types/CoreTypes'; - -/** 重新导出核心权限上下文 */ -export { AuthorityContext } from '../types/CoreTypes'; - -/** - * 权限规则 - */ -export interface AuthorityRule { - /** 规则名称 */ - name: string; - /** 规则检查函数 */ - check: (component: T, context: CoreAuthorityContext) => boolean; - /** 优先级(数值越大优先级越高) */ - priority: number; - /** 是否启用 */ - enabled: boolean; -} - -/** - * SyncVar权限管理器 - * - * 提供灵活的权限检查机制,支持多种权限策略 - */ -export class SyncVarAuthorityManager { - private static _instance: SyncVarAuthorityManager | null = null; - private _rules: AuthorityRule[] = []; - private _cache: Map = new Map(); - private _cacheTimeout: number = SYNCVAR_CONFIG.DEFAULT_CACHE_TIMEOUT; - - private constructor() { - this.initializeDefaultRules(); - } - - public static get Instance(): SyncVarAuthorityManager { - if (!SyncVarAuthorityManager._instance) { - SyncVarAuthorityManager._instance = new SyncVarAuthorityManager(); - } - return SyncVarAuthorityManager._instance; - } - - /** - * 初始化默认权限规则 - */ - private initializeDefaultRules(): void { - // 服务端权限规则 - this.addRule({ - name: 'server-full-authority', - check: (component, context) => context.environment === 'server', - priority: AUTHORITY_CONFIG.SERVER_AUTHORITY_PRIORITY, - enabled: true - }); - - // 网络身份权限规则 - this.addRule({ - name: 'network-identity-authority', - check: (component, context) => { - if (context.environment !== 'client') return false; - - const networkIdentity = this.extractNetworkIdentity(component); - if (!networkIdentity) return false; - - // 客户端只能控制属于自己的网络对象 - return networkIdentity.hasAuthority && - networkIdentity.ownerId === context.clientId; - }, - priority: AUTHORITY_CONFIG.NETWORK_IDENTITY_PRIORITY, - enabled: true - }); - - // 组件自定义权限规则 - this.addRule({ - name: 'component-custom-authority', - check: (component: any, context) => { - if (typeof component.hasAuthority === 'function') { - return component.hasAuthority(context); - } - if (typeof component.checkAuthority === 'function') { - return component.checkAuthority(context); - } - return false; - }, - priority: AUTHORITY_CONFIG.COMPONENT_CUSTOM_PRIORITY, - enabled: true - }); - - // 实体所有者权限规则 - this.addRule({ - name: 'entity-owner-authority', - check: (component, context) => { - if (context.environment !== 'client') return false; - - const entity = component.entity; - if (!entity) return false; - - // 检查实体的所有者信息 - if ((entity as INetworkEntity).ownerId && (entity as INetworkEntity).ownerId === context.clientId) { - return true; - } - - return false; - }, - priority: AUTHORITY_CONFIG.ENTITY_OWNER_PRIORITY, - enabled: true - }); - - // 默认拒绝规则 - this.addRule({ - name: 'default-deny', - check: () => false, - priority: AUTHORITY_CONFIG.DEFAULT_DENY_PRIORITY, - enabled: true - }); - } - - /** - * 检查组件权限 - * - * @param component - 组件实例 - * @param clientId - 客户端ID(可选) - * @returns 是否有权限 - */ - public hasAuthority(component: T, clientId?: string): boolean { - const context = this.createContext(component, clientId); - const cacheKey = this.generateCacheKey(component, context); - - // 检查缓存 - const cached = this._cache.get(cacheKey); - if (cached && (Date.now() - cached.timestamp) < this._cacheTimeout) { - return cached.result; - } - - // 执行权限检查 - const result = this.checkAuthority(component, context); - - // 缓存结果 - this._cache.set(cacheKey, { - result, - timestamp: Date.now() - }); - - logger.debug(`权限检查结果: ${component.constructor.name} -> ${result}`); - return result; - } - - /** - * 执行权限检查 - */ - private checkAuthority(component: T, context: CoreAuthorityContext): boolean { - const enabledRules = this._rules - .filter(rule => rule.enabled) - .sort((a, b) => b.priority - a.priority); - - for (const rule of enabledRules) { - try { - const result = rule.check(component, context); - if (result) { - logger.debug(`权限规则 "${rule.name}" 通过`); - return true; - } - } catch (error) { - logger.warn(`权限规则 "${rule.name}" 执行失败:`, error); - } - } - - logger.debug('所有权限规则都不匹配,拒绝权限'); - return false; - } - - /** - * 创建权限上下文 - */ - private createContext(component: T, clientId?: string): CoreAuthorityContext { - const networkIdentity = this.extractNetworkIdentity(component); - const entity = component.entity; - - return { - environment: NetworkEnvironment.isServer ? 'server' : 'client' as NetworkEnvironmentType, - networkId: networkIdentity?.networkId, - entityId: entity?.id, - clientId, - level: CoreAuthorityType.ReadWrite, - timestamp: Date.now(), - metadata: {} - }; - } - - /** - * 提取网络身份 - */ - private extractNetworkIdentity(component: T): NetworkIdentity | null { - try { - // 直接检查组件的网络身份 - if ((component as any).networkIdentity instanceof NetworkIdentity) { - return (component as any).networkIdentity; - } - - // 检查组件实体的网络身份 - if (component.entity) { - const networkIdentity = (component.entity as any).getComponent?.(NetworkIdentity); - if (networkIdentity) { - return networkIdentity; - } - } - - return null; - } catch (error) { - logger.debug('提取网络身份时出错:', error); - return null; - } - } - - /** - * 生成缓存键 - */ - private generateCacheKey(component: T, context: CoreAuthorityContext): string { - const parts = [ - component.constructor.name, - context.environment, - context.networkId || 'no-net-id', - context.clientId || 'no-client-id' - ]; - return parts.join('|'); - } - - /** - * 添加权限规则 - */ - public addRule(rule: AuthorityRule): void { - this._rules.push(rule); - this._rules.sort((a, b) => b.priority - a.priority); - this.clearCache(); - logger.debug(`添加权限规则: ${rule.name} (优先级: ${rule.priority})`); - } - - /** - * 移除权限规则 - */ - public removeRule(name: string): boolean { - const index = this._rules.findIndex(rule => rule.name === name); - if (index !== -1) { - this._rules.splice(index, 1); - this.clearCache(); - logger.debug(`移除权限规则: ${name}`); - return true; - } - return false; - } - - /** - * 启用/禁用权限规则 - */ - public setRuleEnabled(name: string, enabled: boolean): boolean { - const rule = this._rules.find(rule => rule.name === name); - if (rule) { - rule.enabled = enabled; - this.clearCache(); - logger.debug(`${enabled ? '启用' : '禁用'}权限规则: ${name}`); - return true; - } - return false; - } - - /** - * 获取所有权限规则 - */ - public getRules(): AuthorityRule[] { - return [...this._rules]; - } - - /** - * 清除权限缓存 - */ - public clearCache(): void { - this._cache.clear(); - logger.debug('权限缓存已清除'); - } - - /** - * 设置缓存超时时间 - */ - public setCacheTimeout(timeout: number): void { - this._cacheTimeout = timeout; - logger.debug(`权限缓存超时时间设为: ${timeout}ms`); - } - - /** - * 获取权限检查统计 - */ - public getStats(): { - rulesCount: number; - cacheSize: number; - cacheTimeout: number; - enabledRules: string[]; - } { - return { - rulesCount: this._rules.length, - cacheSize: this._cache.size, - cacheTimeout: this._cacheTimeout, - enabledRules: this._rules - .filter(rule => rule.enabled) - .map(rule => rule.name) - }; - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarDecorator.ts b/packages/network/src/SyncVar/SyncVarDecorator.ts deleted file mode 100644 index 73131fc5..00000000 --- a/packages/network/src/SyncVar/SyncVarDecorator.ts +++ /dev/null @@ -1,275 +0,0 @@ -import 'reflect-metadata'; -import { createLogger } from '@esengine/ecs-framework'; - -const logger = createLogger('SyncVarDecorator'); - -/** - * SyncVar配置选项 - */ -export interface SyncVarOptions { - /** - * 值变化时的回调函数名 - * - * 回调函数签名: (oldValue: T, newValue: T) => void - */ - hook?: string; - - /** - * 是否只有拥有权限的客户端才能修改 - * - * 默认为false,任何客户端都可以修改 - */ - authorityOnly?: boolean; - - /** - * 自定义序列化函数 - * - * 如果不提供,将使用默认的类型序列化 - */ - serializer?: (value: any) => Uint8Array; - - /** - * 自定义反序列化函数 - */ - deserializer?: (data: Uint8Array) => any; - - /** - * 同步频率限制(毫秒) - * - * 防止过于频繁的网络同步,默认为0(不限制) - */ - throttleMs?: number; -} - -/** - * SyncVar元数据信息 - */ -export interface SyncVarMetadata { - /** - * 属性名称 - */ - propertyKey: string; - - /** - * 字段编号(用于protobuf序列化) - */ - fieldNumber: number; - - /** - * 配置选项 - */ - options: SyncVarOptions; - - /** - * 属性类型 - */ - type: Function; - - /** - * 最后同步时间(用于频率限制) - */ - lastSyncTime: number; -} - -/** - * SyncVar元数据存储 - */ -const SYNCVAR_METADATA_KEY = Symbol('syncvar:metadata'); -const SYNCVAR_FIELD_COUNTER = Symbol('syncvar:field_counter'); - -/** - * 获取类的SyncVar元数据 - * - * @param target - 目标类 - * @returns SyncVar元数据数组 - */ -export function getSyncVarMetadata(target: any): SyncVarMetadata[] { - return Reflect.getMetadata(SYNCVAR_METADATA_KEY, target) || []; -} - -/** - * 设置类的SyncVar元数据 - * - * @param target - 目标类 - * @param metadata - 元数据数组 - */ -export function setSyncVarMetadata(target: any, metadata: SyncVarMetadata[]): void { - Reflect.defineMetadata(SYNCVAR_METADATA_KEY, metadata, target); -} - -/** - * 获取下一个可用的字段编号 - * - * @param target - 目标类 - * @returns 字段编号 - */ -function getNextFieldNumber(target: any): number { - let counter = Reflect.getMetadata(SYNCVAR_FIELD_COUNTER, target) || 1; - const nextNumber = counter; - Reflect.defineMetadata(SYNCVAR_FIELD_COUNTER, counter + 1, target); - return nextNumber; -} - -/** - * 检查属性是否为SyncVar - * - * @param target - 目标对象 - * @param propertyKey - 属性名 - * @returns 是否为SyncVar - */ -export function isSyncVar(target: any, propertyKey: string): boolean { - const metadata = getSyncVarMetadata(target.constructor); - return metadata.some(m => m.propertyKey === propertyKey); -} - -/** - * 获取指定属性的SyncVar元数据 - * - * @param target - 目标对象 - * @param propertyKey - 属性名 - * @returns SyncVar元数据 - */ -export function getSyncVarMetadataForProperty(target: any, propertyKey: string): SyncVarMetadata | undefined { - const metadata = getSyncVarMetadata(target.constructor); - return metadata.find(m => m.propertyKey === propertyKey); -} - -/** - * SyncVar装饰器 - * - * 标记字段为自动同步变量,当值改变时会自动发送给其他客户端 - * - * @param options - 配置选项 - * - * @example - * ```typescript - * class PlayerComponent extends NetworkComponent { - * @SyncVar() - * public health: number = 100; - * - * @SyncVar({ hook: 'onNameChanged' }) - * public playerName: string = 'Player'; - * - * @SyncVar({ authorityOnly: true }) - * public isReady: boolean = false; - * - * onNameChanged(oldName: string, newName: string) { - * const logger = createLogger('PlayerComponent'); - * logger.info(`Name changed: ${oldName} -> ${newName}`); - * } - * } - * ``` - */ -export function SyncVar(options: SyncVarOptions = {}): PropertyDecorator { - return function (target: any, propertyKey: string | symbol) { - if (typeof propertyKey !== 'string') { - throw new Error('SyncVar装饰器只能用于字符串属性名'); - } - - // 获取属性类型 - const type = Reflect.getMetadata('design:type', target, propertyKey); - if (!type) { - logger.warn(`无法获取属性 ${propertyKey} 的类型信息`); - } - - // 获取现有元数据 - const existingMetadata = getSyncVarMetadata(target.constructor); - - // 检查是否已经存在 - const existingIndex = existingMetadata.findIndex(m => m.propertyKey === propertyKey); - if (existingIndex !== -1) { - logger.warn(`属性 ${propertyKey} 已经被标记为SyncVar,将覆盖配置`); - existingMetadata[existingIndex].options = options; - existingMetadata[existingIndex].type = type; - } else { - // 添加新的元数据 - const fieldNumber = getNextFieldNumber(target.constructor); - const metadata: SyncVarMetadata = { - propertyKey, - fieldNumber, - options, - type, - lastSyncTime: 0 - }; - - existingMetadata.push(metadata); - } - - // 保存元数据 - setSyncVarMetadata(target.constructor, existingMetadata); - - logger.debug(`注册同步变量: ${target.constructor.name}.${propertyKey}, 字段编号: ${existingMetadata.find(m => m.propertyKey === propertyKey)?.fieldNumber}`); - }; -} - -/** - * 验证SyncVar配置的有效性 - * - * @param target - 目标类实例 - * @param metadata - SyncVar元数据 - * @returns 验证结果 - */ -export function validateSyncVarMetadata(target: any, metadata: SyncVarMetadata): { - isValid: boolean; - errors: string[]; -} { - const errors: string[] = []; - - // 检查属性是否存在 - if (!(metadata.propertyKey in target)) { - errors.push(`属性 ${metadata.propertyKey} 不存在于类 ${target.constructor.name} 中`); - } - - // 检查hook函数是否存在 - if (metadata.options.hook) { - if (typeof target[metadata.options.hook] !== 'function') { - errors.push(`Hook函数 ${metadata.options.hook} 不存在或不是函数`); - } - } - - // 检查自定义序列化函数 - if (metadata.options.serializer && typeof metadata.options.serializer !== 'function') { - errors.push(`自定义序列化函数必须是function类型`); - } - - if (metadata.options.deserializer && typeof metadata.options.deserializer !== 'function') { - errors.push(`自定义反序列化函数必须是function类型`); - } - - // 检查频率限制 - if (metadata.options.throttleMs !== undefined && - (typeof metadata.options.throttleMs !== 'number' || metadata.options.throttleMs < 0)) { - errors.push(`throttleMs必须是非负数`); - } - - return { - isValid: errors.length === 0, - errors - }; -} - -/** - * 获取类的所有SyncVar统计信息 - * - * @param target - 目标类 - * @returns 统计信息 - */ -export function getSyncVarStats(target: any): { - totalCount: number; - withHooks: number; - authorityOnly: number; - customSerialized: number; - throttled: number; - fieldNumbers: number[]; -} { - const metadata = getSyncVarMetadata(target); - - return { - totalCount: metadata.length, - withHooks: metadata.filter(m => m.options.hook).length, - authorityOnly: metadata.filter(m => m.options.authorityOnly).length, - customSerialized: metadata.filter(m => m.options.serializer || m.options.deserializer).length, - throttled: metadata.filter(m => m.options.throttleMs !== undefined && m.options.throttleMs > 0).length, - fieldNumbers: metadata.map(m => m.fieldNumber).sort((a, b) => a - b) - }; -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarFactory.ts b/packages/network/src/SyncVar/SyncVarFactory.ts deleted file mode 100644 index ede4f1de..00000000 --- a/packages/network/src/SyncVar/SyncVarFactory.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { createSyncVarProxy } from './SyncVarProxy'; -import { getSyncVarMetadata } from './SyncVarDecorator'; -import { INetworkSyncable } from '../types/NetworkTypes'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * SyncVar工厂函数 - * - * 为NetworkComponent创建带有SyncVar代理的实例 - * 这是必需的,因为TypeScript类构造函数不能直接返回代理对象 - */ - -/** - * 创建带SyncVar支持的NetworkComponent实例 - * - * @param ComponentClass - 组件类构造函数 - * @param args - 构造函数参数 - * @returns 带代理的组件实例 - */ -const logger = createLogger('SyncVarFactory'); - -export function createNetworkComponent( - ComponentClass: new (...args: any[]) => T, - ...args: any[] -): T { - // 创建组件实例 - const instance = new ComponentClass(...args); - - // 检查是否有SyncVar字段 - const metadata = getSyncVarMetadata(ComponentClass); - - if (metadata.length === 0) { - // 没有SyncVar,直接返回原实例 - return instance; - } - - // 创建代理包装实例 - const proxy = createSyncVarProxy(instance, { - debugLog: false // 可以根据需要启用调试 - }); - - logger.debug(`为 ${ComponentClass.name} 创建了SyncVar代理,包含 ${metadata.length} 个同步字段`); - - return proxy; -} - -/** - * SyncVar组件装饰器 - * - * 装饰器版本的工厂函数,自动为类添加SyncVar支持 - * 注意:由于TypeScript装饰器的限制,这个方法有一些局限性 - * - * @param options - 配置选项 - */ -export function NetworkComponentWithSyncVar(options: { debugLog?: boolean } = {}) { - return function INetworkSyncable>(constructor: T) { - return class extends constructor { - constructor(...args: any[]) { - super(...args); - - // 检查是否需要创建代理 - const metadata = getSyncVarMetadata(constructor); - if (metadata.length > 0) { - // 返回代理实例 - return createSyncVarProxy(this as INetworkSyncable, { - debugLog: options.debugLog || false - }) as this; - } - - return this; - } - } as T; - }; -} - -/** - * 便捷方法:检查实例是否使用了SyncVar工厂创建 - * - * @param instance - 组件实例 - * @returns 是否使用了SyncVar工厂 - */ -export function isNetworkComponentWithSyncVar(instance: any): boolean { - return instance && (instance._syncVarProxied === true || instance.hasSyncVars?.() === true); -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarManager.ts b/packages/network/src/SyncVar/SyncVarManager.ts deleted file mode 100644 index 66e62864..00000000 --- a/packages/network/src/SyncVar/SyncVarManager.ts +++ /dev/null @@ -1,846 +0,0 @@ -import { - SyncVarMetadata, - getSyncVarMetadata, - validateSyncVarMetadata, - getSyncVarMetadataForProperty -} from './SyncVarDecorator'; -import { NetworkEnvironment } from '../Core/NetworkEnvironment'; -import { SyncVarUpdateMessage, SyncVarFieldUpdate } from '../Messaging/MessageTypes'; -import { - SyncVarValue, - INetworkSyncable, - NetworkComponentType, - TypeGuards -} from '../types/NetworkTypes'; -import { SYNCVAR_CONFIG } from '../constants/NetworkConstants'; -import { INetworkComponent } from '../types/CoreTypes'; -import { ComponentIdGenerator } from './ComponentIdGenerator'; -import { SyncVarAuthorityManager } from './SyncVarAuthority'; - -/** - * SyncVar变化记录 - */ -export interface SyncVarChange { - /** - * 属性名 - */ - propertyKey: string; - - /** - * 字段编号 - */ - fieldNumber: number; - - /** - * 旧值 - */ - oldValue: SyncVarValue; - - /** - * 新值 - */ - newValue: SyncVarValue; - - /** - * 变化时间戳 - */ - timestamp: number; - - /** - * 是否需要网络同步 - */ - needsSync: boolean; -} - -/** - * SyncVar同步数据 - */ -export interface SyncVarSyncData { - /** - * 组件类名 - */ - componentType: string; - - /** - * 网络对象ID(将来实现) - */ - networkId?: string; - - /** - * 字段更新数据 - */ - fieldUpdates: Array<{ - fieldNumber: number; - data: Uint8Array; - }>; - - /** - * 时间戳 - */ - timestamp: number; -} - -/** - * SyncVar管理器 - * - * 负责管理组件的SyncVar变量,检测变化,处理序列化和同步 - */ -export class SyncVarManager { - private static _instance: SyncVarManager | null = null; - private static readonly logger = { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug - }; - - /** - * 组件实例的SyncVar变化监听器 - * Key: 组件实例的唯一ID - * Value: 变化记录数组 - */ - private _componentChanges: Map = new Map(); - - /** - * 组件实例的最后同步时间 - */ - private _lastSyncTimes: Map> = new Map(); - - /** - * 获取SyncVarManager单例 - */ - public static get Instance(): SyncVarManager { - if (!SyncVarManager._instance) { - SyncVarManager._instance = new SyncVarManager(); - } - return SyncVarManager._instance; - } - - private constructor() {} - - /** - * 初始化组件的SyncVar系统 - * - * @param component - 网络组件实例 - * @returns 是否成功初始化 - */ - public initializeComponent(component: T): boolean { - const componentId = this.getComponentId(component); - const metadata = getSyncVarMetadata(component.constructor as NetworkComponentType); - - if (metadata.length === 0) { - // 没有SyncVar,无需初始化 - return false; - } - - // 验证所有SyncVar配置 - const validationErrors: string[] = []; - for (const meta of metadata) { - const validation = validateSyncVarMetadata(component, meta); - if (!validation.isValid) { - validationErrors.push(...validation.errors); - } - } - - if (validationErrors.length > 0) { - SyncVarManager.logger.error(`组件 ${component.constructor.name} 的SyncVar配置错误:`, validationErrors); - return false; - } - - // 初始化变化记录 - this._componentChanges.set(componentId, []); - this._lastSyncTimes.set(componentId, new Map()); - - SyncVarManager.logger.info(`初始化组件 ${component.constructor.name} 的SyncVar系统,共 ${metadata.length} 个同步变量`); - return true; - } - - /** - * 清理组件的SyncVar系统 - * - * @param component - 网络组件实例 - */ - public cleanupComponent(component: T): void { - const componentId = this.getComponentId(component); - this._componentChanges.delete(componentId); - this._lastSyncTimes.delete(componentId); - } - - /** - * 记录SyncVar字段的变化 - * - * @param component - 组件实例 - * @param propertyKey - 属性名 - * @param oldValue - 旧值 - * @param newValue - 新值 - */ - public recordChange( - component: T, - propertyKey: string, - oldValue: SyncVarValue, - newValue: SyncVarValue - ): void { - const componentId = this.getComponentId(component); - const metadata = getSyncVarMetadataForProperty(component, propertyKey); - - if (!metadata) { - SyncVarManager.logger.warn(`属性 ${propertyKey} 不是SyncVar`); - return; - } - - // 检查值是否真的发生了变化 - if (!TypeGuards.isSyncVarValue(oldValue) || !TypeGuards.isSyncVarValue(newValue)) { - SyncVarManager.logger.warn(`无效的SyncVar值类型: ${typeof oldValue}, ${typeof newValue}`); - return; - } - - if (this.isValueEqual(oldValue, newValue)) { - return; - } - - // 检查频率限制 - const now = Date.now(); - const lastSyncTimes = this._lastSyncTimes.get(componentId); - const lastSyncTime = lastSyncTimes?.get(propertyKey) || 0; - - if (metadata.options.throttleMs && metadata.options.throttleMs > 0) { - if (now - lastSyncTime < metadata.options.throttleMs) { - SyncVarManager.logger.debug(`属性 ${propertyKey} 变化过于频繁,跳过同步`); - return; - } - } - - // 检查权限 - if (metadata.options.authorityOnly && !this.hasAuthority(component)) { - SyncVarManager.logger.warn(`属性 ${propertyKey} 需要权限才能修改,但当前没有权限`); - return; - } - - // 记录变化 - const change: SyncVarChange = { - propertyKey, - fieldNumber: metadata.fieldNumber, - oldValue, - newValue, - timestamp: now, - needsSync: this.shouldSync(component, metadata) - }; - - let changes = this._componentChanges.get(componentId); - if (!changes) { - changes = []; - this._componentChanges.set(componentId, changes); - } - - changes.push(change); - - // 更新最后同步时间 - if (lastSyncTimes) { - lastSyncTimes.set(propertyKey, now); - } - - SyncVarManager.logger.debug(`记录变化: ${component.constructor.name}.${propertyKey} = ${newValue} (was ${oldValue})`); - - // 触发hook回调 - this.triggerHook(component, metadata, oldValue, newValue); - } - - /** - * 获取组件的待同步变化 - * - * @param component - 组件实例 - * @returns 待同步的变化数组 - */ - public getPendingChanges(component: T): SyncVarChange[] { - const componentId = this.getComponentId(component); - const changes = this._componentChanges.get(componentId) || []; - return changes.filter(change => change.needsSync); - } - - /** - * 清除组件的变化记录 - * - * @param component - 组件实例 - * @param propertyKeys - 要清除的属性名数组,如果不提供则清除所有 - */ - public clearChanges(component: T, propertyKeys?: string[]): void { - const componentId = this.getComponentId(component); - const changes = this._componentChanges.get(componentId); - - if (!changes) { - return; - } - - if (propertyKeys) { - // 清除指定属性的变化 - const filteredChanges = changes.filter(change => !propertyKeys.includes(change.propertyKey)); - this._componentChanges.set(componentId, filteredChanges); - } else { - // 清除所有变化 - this._componentChanges.set(componentId, []); - } - } - - /** - * 创建同步数据 - * - * @param component - 组件实例 - * @returns 同步数据 - */ - public createSyncData(component: T): SyncVarSyncData | null { - const pendingChanges = this.getPendingChanges(component); - - if (pendingChanges.length === 0) { - return null; - } - - const fieldUpdates: Array<{ fieldNumber: number; data: Uint8Array }> = []; - - for (const change of pendingChanges) { - try { - const serializedData = this.serializeValue(component, change.propertyKey, change.newValue); - fieldUpdates.push({ - fieldNumber: change.fieldNumber, - data: serializedData - }); - } catch (error) { - SyncVarManager.logger.error(`序列化失败 ${change.propertyKey}:`, error); - } - } - - if (fieldUpdates.length === 0) { - return null; - } - - return { - componentType: component.constructor.name, - fieldUpdates, - timestamp: Date.now() - }; - } - - /** - * 应用同步数据 - * - * @param component - 组件实例 - * @param syncData - 同步数据 - */ - public applySyncData(component: T, syncData: SyncVarSyncData): void { - const metadata = getSyncVarMetadata(component.constructor); - const metadataMap = new Map(metadata.map(m => [m.fieldNumber, m])); - - for (const update of syncData.fieldUpdates) { - const meta = metadataMap.get(update.fieldNumber); - if (!meta) { - SyncVarManager.logger.warn(`未找到字段编号 ${update.fieldNumber} 的元数据`); - continue; - } - - try { - const newValue = this.deserializeValue(component, meta.propertyKey, update.data); - const oldValue = component[meta.propertyKey]; - - // 直接设置值,不通过代理(避免循环触发) - this.setValueDirectly(component, meta.propertyKey, newValue); - - // 触发hook回调 - this.triggerHook(component, meta, oldValue, newValue); - - SyncVarManager.logger.debug(`应用同步: ${component.constructor.name}.${meta.propertyKey} = ${newValue}`); - } catch (error) { - SyncVarManager.logger.error(`反序列化失败 ${meta.propertyKey}:`, error); - } - } - } - - /** - * ID生成器实例 - */ - private static _idGenerator: ComponentIdGenerator | null = null; - - /** - * 获取ID生成器实例 - */ - private static getIdGenerator(): ComponentIdGenerator { - if (!SyncVarManager._idGenerator) { - SyncVarManager._idGenerator = new ComponentIdGenerator(); - } - return SyncVarManager._idGenerator; - } - - /** - * 生成组件的唯一ID - * - * @param component - 组件实例 - * @returns 唯一ID - */ - private getComponentId(component: T): string { - if (!component._syncVarId) { - const idGenerator = SyncVarManager.getIdGenerator(); - component._syncVarId = idGenerator.generateId(component); - } - return component._syncVarId; - } - - /** - * 检查两个值是否相等 - * - * @param a - 值A - * @param b - 值B - * @returns 是否相等 - */ - private isValueEqual(a: unknown, b: unknown): boolean { - // 基础类型比较 - if (typeof a !== typeof b) { - return false; - } - - if (a === b) { - return true; - } - - // 对象比较(浅比较) - if (typeof a === 'object' && a !== null && b !== null) { - const keysA = Object.keys(a as Record); - const keysB = Object.keys(b as Record); - - if (keysA.length !== keysB.length) { - return false; - } - - return keysA.every(key => (a as Record)[key] === (b as Record)[key]); - } - - return false; - } - - /** - * 权限管理器实例 - */ - private _authorityManager: SyncVarAuthorityManager = SyncVarAuthorityManager.Instance; - - /** - * 检查组件是否有修改权限 - * - * @param component - 组件实例 - * @param clientId - 客户端ID(可选) - * @returns 是否有权限 - */ - private hasAuthority(component: T, clientId?: string): boolean { - return this._authorityManager.hasAuthority(component as unknown as INetworkComponent, clientId); - } - - /** - * 检查是否应该同步 - * - * @param component - 组件实例 - * @param metadata - SyncVar元数据 - * @returns 是否应该同步 - */ - private shouldSync(component: T, metadata: SyncVarMetadata): boolean { - // 权限检查:权威字段只有在有权限时才同步 - if (metadata.options.authorityOnly && !this.hasAuthority(component)) { - SyncVarManager.logger.debug(`字段 ${metadata.propertyKey} 是权威字段,但当前没有权限,跳过同步`); - return false; - } - - // 环境检查:服务端可以同步所有字段 - if (NetworkEnvironment.isServer) { - return true; - } - - // 客户端:非权威字段可以同步,权威字段需要检查权限 - if (metadata.options.authorityOnly) { - return this.hasAuthority(component); - } - - // 普通字段客户端也可以同步 - return true; - } - - /** - * 触发hook回调 - * - * @param component - 组件实例 - * @param metadata - SyncVar元数据 - * @param oldValue - 旧值 - * @param newValue - 新值 - */ - private triggerHook(component: T, metadata: SyncVarMetadata, oldValue: unknown, newValue: unknown): void { - if (!metadata.options.hook) { - return; - } - - const hookFunction = component[metadata.options.hook]; - if (typeof hookFunction === 'function') { - try { - hookFunction.call(component, oldValue, newValue); - } catch (error) { - SyncVarManager.logger.error(`Hook函数执行失败 ${metadata.options.hook}:`, error); - } - } - } - - /** - * 序列化值 - * - * @param component - 组件实例 - * @param propertyKey - 属性名 - * @param value - 值 - * @returns 序列化数据 - */ - private serializeValue(component: T, propertyKey: string, value: unknown): Uint8Array { - const metadata = getSyncVarMetadataForProperty(component, propertyKey); - - if (metadata?.options.serializer) { - return metadata.options.serializer(value); - } - - return this.serializeValueToBinary(value); - } - - /** - * 反序列化值 - * - * @param component - 组件实例 - * @param propertyKey - 属性名 - * @param data - 序列化数据 - * @returns 反序列化的值 - */ - private deserializeValue(component: T, propertyKey: string, data: Uint8Array): unknown { - const metadata = getSyncVarMetadataForProperty(component, propertyKey); - - if (metadata?.options.deserializer) { - return metadata.options.deserializer(data); - } - - return this.deserializeValueFromBinary(data); - } - - /** - * 将值序列化为二进制数据 - */ - private serializeValueToBinary(value: unknown): Uint8Array { - if (value === null || value === undefined) { - return new Uint8Array([0]); - } - - if (typeof value === 'boolean') { - return new Uint8Array([1, value ? 1 : 0]); - } - - if (typeof value === 'number') { - const buffer = new ArrayBuffer(9); - const view = new DataView(buffer); - view.setUint8(0, 2); - view.setFloat64(1, value, true); - return new Uint8Array(buffer); - } - - if (typeof value === 'string') { - const encoded = new TextEncoder().encode(value); - const buffer = new Uint8Array(5 + encoded.length); - const view = new DataView(buffer.buffer); - view.setUint8(0, 3); - view.setUint32(1, encoded.length, true); - buffer.set(encoded, 5); - return buffer; - } - - const jsonString = JSON.stringify(value); - const encoded = new TextEncoder().encode(jsonString); - const buffer = new Uint8Array(5 + encoded.length); - const view = new DataView(buffer.buffer); - view.setUint8(0, 4); - view.setUint32(1, encoded.length, true); - buffer.set(encoded, 5); - return buffer; - } - - /** - * 从二进制数据反序列化值 - */ - private deserializeValueFromBinary(data: Uint8Array): unknown { - if (data.length === 0) return null; - - const view = new DataView(data.buffer, data.byteOffset); - const type = view.getUint8(0); - - switch (type) { - case 0: return null; - case 1: return view.getUint8(1) === 1; - case 2: return view.getFloat64(1, true); - case 3: { - const length = view.getUint32(1, true); - return new TextDecoder().decode(data.subarray(5, 5 + length)); - } - case 4: { - const length = view.getUint32(1, true); - const jsonString = new TextDecoder().decode(data.subarray(5, 5 + length)); - return JSON.parse(jsonString); - } - default: - throw new Error(`未知的序列化类型: ${type}`); - } - } - - /** - * 直接设置值(绕过代理) - * - * @param component - 组件实例 - * @param propertyKey - 属性名 - * @param value - 值 - */ - private setValueDirectly(component: T, propertyKey: string, value: unknown): void { - // 临时禁用代理监听 - const originalValue = component._syncVarDisabled; - component._syncVarDisabled = true; - - try { - (component as Record)[propertyKey] = value; - } finally { - component._syncVarDisabled = originalValue; - } - } - - /** - * 创建SyncVar更新消息 - * - * @param component - 组件实例 - * @param networkId - 网络对象ID - * @param senderId - 发送者ID - * @param syncSequence - 同步序号 - * @param isFullSync - 是否是完整同步 - * @returns SyncVar更新消息,如果没有待同步的变化则返回null - */ - public createSyncVarUpdateMessage( - component: T, - networkId: string = '', - senderId: string = '', - syncSequence: number = 0, - isFullSync: boolean = false - ): SyncVarUpdateMessage | null { - const pendingChanges = this.getPendingChanges(component); - - if (pendingChanges.length === 0) { - return null; - } - - // 转换变化记录为消息格式 - const fieldUpdates: SyncVarFieldUpdate[] = []; - - for (const change of pendingChanges) { - const metadata = getSyncVarMetadataForProperty(component, change.propertyKey); - if (!metadata) { - continue; - } - - const fieldUpdate: SyncVarFieldUpdate = { - fieldNumber: change.fieldNumber, - propertyKey: change.propertyKey, - newValue: change.newValue as any, - oldValue: change.oldValue as any, - timestamp: change.timestamp, - authorityOnly: metadata.options.authorityOnly - }; - - fieldUpdates.push(fieldUpdate); - } - - if (fieldUpdates.length === 0) { - return null; - } - - const message = new SyncVarUpdateMessage( - networkId, - component.constructor.name, - fieldUpdates, - isFullSync, - senderId, - syncSequence - ); - - SyncVarManager.logger.debug(`创建SyncVar更新消息: ${component.constructor.name}, ${fieldUpdates.length} 个字段`); - return message; - } - - /** - * 应用SyncVar更新消息 - * - * @param component - 组件实例 - * @param message - SyncVar更新消息 - */ - public applySyncVarUpdateMessage(component: T, message: SyncVarUpdateMessage): void { - if (message.componentType !== component.constructor.name) { - SyncVarManager.logger.warn(`组件类型不匹配: 期望 ${component.constructor.name}, 收到 ${message.componentType}`); - return; - } - - const metadata = getSyncVarMetadata(component.constructor); - const metadataMap = new Map(metadata.map(m => [m.fieldNumber, m])); - - for (const fieldUpdate of message.fieldUpdates) { - const meta = metadataMap.get(fieldUpdate.fieldNumber); - if (!meta) { - SyncVarManager.logger.warn(`未找到字段编号 ${fieldUpdate.fieldNumber} 的元数据`); - continue; - } - - // 权限检查:权威字段在客户端通常应该接受来自服务端的更新 - // 只有当客户端试图应用自己产生的权威字段更新时才拒绝 - if (fieldUpdate.authorityOnly && NetworkEnvironment.isClient && !this.hasAuthority(component)) { - // 如果这是来自服务端的更新,则允许应用 - // 这里简单实现:客户端接受所有权威字段的更新 - SyncVarManager.logger.debug(`客户端接受权威字段更新: ${fieldUpdate.propertyKey}`); - } - - try { - const oldValue = component[meta.propertyKey]; - - // 直接设置值,不通过代理(避免循环触发) - this.setValueDirectly(component, meta.propertyKey, fieldUpdate.newValue); - - // 触发hook回调 - this.triggerHook(component, meta, oldValue, fieldUpdate.newValue); - - SyncVarManager.logger.debug(`应用SyncVar消息更新: ${component.constructor.name}.${meta.propertyKey} = ${fieldUpdate.newValue}`); - } catch (error) { - SyncVarManager.logger.error(`应用SyncVar更新失败 ${meta.propertyKey}:`, error); - } - } - - // 清除对应的变化记录(已经同步完成) - this.clearChanges(component, message.fieldUpdates.map(u => u.propertyKey)); - } - - /** - * 批量创建多个组件的SyncVar更新消息 - * - * @param components - 组件实例数组 - * @param networkIds - 对应的网络对象ID数组 - * @param senderId - 发送者ID - * @param syncSequence - 同步序号 - * @returns SyncVar更新消息数组 - */ - public createBatchSyncVarUpdateMessages( - components: T[], - networkIds: string[] = [], - senderId: string = '', - syncSequence: number = 0 - ): SyncVarUpdateMessage[] { - const messages: SyncVarUpdateMessage[] = []; - - for (let i = 0; i < components.length; i++) { - const component = components[i]; - const networkId = networkIds[i] || ''; - - const message = this.createSyncVarUpdateMessage( - component, - networkId, - senderId, - syncSequence + i - ); - - if (message) { - messages.push(message); - } - } - - return messages; - } - - /** - * 过滤需要同步的组件 - * - * @param components - 组件数组 - * @returns 有待同步变化的组件数组 - */ - public filterComponentsWithChanges(components: T[]): T[] { - return components.filter(component => { - const pendingChanges = this.getPendingChanges(component); - return pendingChanges.length > 0; - }); - } - - /** - * 获取组件的变化统计 - * - * @param component - 组件实例 - * @returns 变化统计信息 - */ - public getComponentChangeStats(component: T): { - totalChanges: number; - pendingChanges: number; - lastChangeTime: number; - fieldChangeCounts: Map; - hasAuthorityOnlyChanges: boolean; - } { - const componentId = this.getComponentId(component); - const changes = this._componentChanges.get(componentId) || []; - const pendingChanges = changes.filter(c => c.needsSync); - - const fieldChangeCounts = new Map(); - let lastChangeTime = 0; - let hasAuthorityOnlyChanges = false; - - for (const change of changes) { - const count = fieldChangeCounts.get(change.propertyKey) || 0; - fieldChangeCounts.set(change.propertyKey, count + 1); - lastChangeTime = Math.max(lastChangeTime, change.timestamp); - - if (!hasAuthorityOnlyChanges) { - const metadata = getSyncVarMetadataForProperty(component, change.propertyKey); - if (metadata?.options.authorityOnly) { - hasAuthorityOnlyChanges = true; - } - } - } - - return { - totalChanges: changes.length, - pendingChanges: pendingChanges.length, - lastChangeTime, - fieldChangeCounts, - hasAuthorityOnlyChanges - }; - } - - /** - * 获取管理器统计信息 - * - * @returns 统计信息 - */ - public getStats(): { - totalComponents: number; - totalChanges: number; - pendingChanges: number; - components: Array<{ - id: string; - changes: number; - pending: number; - }>; - } { - let totalChanges = 0; - let pendingChanges = 0; - const components: Array<{ id: string; changes: number; pending: number }> = []; - - for (const [componentId, changes] of this._componentChanges) { - const pendingCount = changes.filter(c => c.needsSync).length; - totalChanges += changes.length; - pendingChanges += pendingCount; - - components.push({ - id: componentId, - changes: changes.length, - pending: pendingCount - }); - } - - return { - totalComponents: this._componentChanges.size, - totalChanges, - pendingChanges, - components - }; - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarMessageHandler.ts b/packages/network/src/SyncVar/SyncVarMessageHandler.ts deleted file mode 100644 index 8791aef5..00000000 --- a/packages/network/src/SyncVar/SyncVarMessageHandler.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { IMessageHandler } from '../Messaging/MessageHandler'; -import { SyncVarUpdateMessage } from '../Messaging/MessageTypes'; -import { NetworkConnection } from '../Core/NetworkConnection'; -import { NetworkIdentityRegistry } from '../Core/NetworkIdentity'; -import { SyncVarManager } from './SyncVarManager'; -import { NetworkEnvironment } from '../Core/NetworkEnvironment'; -import { ComponentRegistry, createLogger } from '@esengine/ecs-framework'; -import { NetworkManager } from '../Core/NetworkManager'; - -/** - * SyncVar更新消息处理器 - * - * 处理接收到的SyncVar更新消息,自动查找目标网络对象并应用更新 - */ -export class SyncVarMessageHandler implements IMessageHandler { - private static readonly logger = createLogger('SyncVarMessageHandler'); - private _processedMessages: Set = new Set(); - private _maxProcessedCache: number = 1000; - - /** - * 处理SyncVar更新消息 - * - * @param message - SyncVar更新消息 - * @param connection - 发送消息的连接(服务端有效) - */ - public async handle(message: SyncVarUpdateMessage, connection?: NetworkConnection): Promise { - try { - // 生成消息唯一标识符用于去重 - const messageKey = this.generateMessageKey(message); - if (this._processedMessages.has(messageKey)) { - SyncVarMessageHandler.logger.debug(` 跳过重复消息: ${messageKey}`); - return; - } - - // 添加到已处理缓存 - this.addToProcessedCache(messageKey); - - // 验证消息基本有效性 - if (!this.validateMessage(message)) { - SyncVarMessageHandler.logger.error(' 消息验证失败'); - return; - } - - // 查找目标网络对象 - const targetIdentity = NetworkIdentityRegistry.Instance.find(message.networkId); - if (!targetIdentity) { - SyncVarMessageHandler.logger.warn(` 未找到网络对象: ${message.networkId}`); - return; - } - - // 权限检查 - if (!this.checkAuthority(message, connection, targetIdentity)) { - SyncVarMessageHandler.logger.warn(` 权限检查失败: ${message.networkId}`); - return; - } - - // 查找目标组件 - const targetComponent = this.findTargetComponent(targetIdentity, message.componentType); - if (!targetComponent) { - SyncVarMessageHandler.logger.warn(` 未找到目标组件: ${message.componentType} on ${message.networkId}`); - return; - } - - // 应用SyncVar更新 - this.applySyncVarUpdates(targetComponent, message); - - // 更新网络对象的同步信息 - targetIdentity.updateSyncTime(); - if (message.syncSequence > targetIdentity.syncSequence) { - targetIdentity.syncSequence = message.syncSequence; - } - - // 如果是服务端接收的消息,需要转发给其他客户端 - if (NetworkEnvironment.isServer && connection) { - await this.forwardToOtherClients(message, connection); - } - - SyncVarMessageHandler.logger.debug(` 成功处理SyncVar更新: ${message.networkId}.${message.componentType}, ${message.fieldUpdates.length}个字段`); - - } catch (error) { - SyncVarMessageHandler.logger.error(' 处理SyncVar更新失败:', error); - } - } - - /** - * 生成消息唯一标识符 - */ - private generateMessageKey(message: SyncVarUpdateMessage): string { - return `${message.networkId}_${message.componentType}_${message.syncSequence}_${message.timestamp}`; - } - - /** - * 添加到已处理消息缓存 - */ - private addToProcessedCache(messageKey: string): void { - this._processedMessages.add(messageKey); - - // 限制缓存大小 - if (this._processedMessages.size > this._maxProcessedCache) { - const toDelete = Array.from(this._processedMessages).slice(0, this._maxProcessedCache / 2); - toDelete.forEach(key => this._processedMessages.delete(key)); - } - } - - /** - * 验证消息基本有效性 - */ - private validateMessage(message: SyncVarUpdateMessage): boolean { - if (!message.networkId || !message.componentType) { - SyncVarMessageHandler.logger.error(' 消息缺少必要字段'); - return false; - } - - if (!message.fieldUpdates || message.fieldUpdates.length === 0) { - SyncVarMessageHandler.logger.error(' 消息没有字段更新'); - return false; - } - - // 检查时间戳合理性(不能是未来的时间,不能太久以前) - const now = Date.now(); - const maxAge = 60000; // 1分钟 - if (message.timestamp > now + 5000 || message.timestamp < now - maxAge) { - SyncVarMessageHandler.logger.warn(` 消息时间戳异常: ${message.timestamp}, 当前: ${now}`); - return false; - } - - return true; - } - - /** - * 检查操作权限 - */ - private checkAuthority( - message: SyncVarUpdateMessage, - connection: NetworkConnection | undefined, - targetIdentity: any - ): boolean { - // 服务端始终有权限处理消息 - if (NetworkEnvironment.isServer) { - // 但需要检查客户端发送的消息是否有权限修改对象 - if (connection) { - // 检查是否是对象拥有者 - if (targetIdentity.ownerId && targetIdentity.ownerId !== connection.connectionId) { - // 非拥有者只能发送非权威字段更新 - const hasAuthorityOnlyUpdates = message.fieldUpdates.some(update => update.authorityOnly); - if (hasAuthorityOnlyUpdates) { - SyncVarMessageHandler.logger.warn(` 非拥有者 ${connection.connectionId} 尝试修改权威字段`); - return false; - } - } - } - return true; - } - - // 客户端接收到的消息通常来自服务端,应该允许 - if (NetworkEnvironment.isClient) { - return true; - } - - return false; - } - - /** - * 查找目标组件 - */ - private findTargetComponent(targetIdentity: any, componentType: string): any { - const entity = targetIdentity.entity; - if (!entity || typeof entity.getComponent !== 'function') { - SyncVarMessageHandler.logger.error(' NetworkIdentity缺少有效的Entity引用'); - return null; - } - - try { - // 获取组件类 - const ComponentClass = this.getComponentClassByName(componentType); - if (!ComponentClass) { - return null; - } - - // 使用Entity的getComponent方法查找组件 - const component = entity.getComponent(ComponentClass); - if (!component) { - SyncVarMessageHandler.logger.warn(` Entity ${entity.id} 上未找到组件: ${componentType}`); - return null; - } - - return component; - } catch (error) { - SyncVarMessageHandler.logger.error(`查找组件失败: ${componentType}`, error); - return null; - } - } - - /** - * 根据组件名称获取组件类 - */ - private getComponentClassByName(componentType: string): any { - const componentClass = ComponentRegistry.getComponentType(componentType); - - if (!componentClass) { - SyncVarMessageHandler.logger.warn(` 未找到组件类型: ${componentType}`); - return null; - } - - return componentClass; - } - - /** - * 应用SyncVar更新到组件 - */ - private applySyncVarUpdates(targetComponent: any, message: SyncVarUpdateMessage): void { - const syncVarManager = SyncVarManager.Instance; - - try { - syncVarManager.applySyncVarUpdateMessage(targetComponent, message); - } catch (error) { - SyncVarMessageHandler.logger.error(' 应用SyncVar更新失败:', error); - throw error; - } - } - - /** - * 转发消息给其他客户端(服务端专用) - */ - private async forwardToOtherClients( - message: SyncVarUpdateMessage, - senderConnection: NetworkConnection - ): Promise { - try { - // 获取NetworkServer实例 - const server = NetworkManager.GetServer(); - - if (!server || !server.isRunning) { - SyncVarMessageHandler.logger.warn(' NetworkServer未运行,无法转发消息'); - return; - } - - // 使用NetworkServer的broadcastSyncVarMessageExcept方法排除发送者 - const successCount = await server.broadcastSyncVarMessageExcept(message, senderConnection.connectionId); - - if (successCount > 0) { - SyncVarMessageHandler.logger.debug(` 成功转发消息给 ${successCount} 个其他客户端 (发送者: ${senderConnection.connectionId})`); - } else { - SyncVarMessageHandler.logger.debug(` 没有其他客户端需要转发消息 (发送者: ${senderConnection.connectionId})`); - } - } catch (error) { - SyncVarMessageHandler.logger.error(`转发消息失败 (发送者: ${senderConnection.connectionId}):`, error); - } - } - - /** - * 获取处理器统计信息 - */ - public getStats(): { - processedMessages: number; - cacheSize: number; - maxCacheSize: number; - } { - return { - processedMessages: this._processedMessages.size, - cacheSize: this._processedMessages.size, - maxCacheSize: this._maxProcessedCache - }; - } - - /** - * 清理已处理消息缓存 - */ - public clearProcessedCache(): void { - this._processedMessages.clear(); - SyncVarMessageHandler.logger.info('已清理消息处理缓存'); - } - - /** - * 设置最大缓存大小 - */ - public setMaxCacheSize(maxSize: number): void { - this._maxProcessedCache = Math.max(100, maxSize); - - // 如果当前缓存超过新的最大值,进行清理 - if (this._processedMessages.size > this._maxProcessedCache) { - const toDelete = Array.from(this._processedMessages).slice(0, this._processedMessages.size - this._maxProcessedCache); - toDelete.forEach(key => this._processedMessages.delete(key)); - } - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarOptimizer.ts b/packages/network/src/SyncVar/SyncVarOptimizer.ts deleted file mode 100644 index 9a7ab0c9..00000000 --- a/packages/network/src/SyncVar/SyncVarOptimizer.ts +++ /dev/null @@ -1,478 +0,0 @@ -import { SyncVarUpdateMessage, SyncVarFieldUpdate } from '../Messaging/MessageTypes'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * SyncVar优化配置 - */ -export interface SyncVarOptimizationConfig { - /** 是否启用消息合并 */ - enableMessageMerging: boolean; - /** 最大合并时间窗口(毫秒) */ - mergeTimeWindow: number; - /** 是否启用Delta压缩 */ - enableDeltaCompression: boolean; - /** 是否启用批量发送 */ - enableBatchSending: boolean; - /** 批量大小限制 */ - batchSizeLimit: number; - /** 是否启用优先级队列 */ - enablePriorityQueue: boolean; - /** 是否启用距离剔除 */ - enableDistanceCulling: boolean; - /** 距离剔除半径 */ - cullingDistance: number; - /** 是否启用频率限制 */ - enableRateLimit: boolean; - /** 每秒最大消息数 */ - maxMessagesPerSecond: number; -} - -/** - * 消息合并器 - */ -export class SyncVarMessageMerger { - private _pendingMessages: Map = new Map(); - private _mergeTimers: Map = new Map(); - private _config: SyncVarOptimizationConfig; - - constructor(config: SyncVarOptimizationConfig) { - this._config = config; - } - - /** - * 添加消息到合并队列 - * - * @param message - SyncVar更新消息 - * @param onMerged - 合并完成回调 - */ - public addMessage( - message: SyncVarUpdateMessage, - onMerged: (mergedMessage: SyncVarUpdateMessage) => void - ): void { - if (!this._config.enableMessageMerging) { - onMerged(message); - return; - } - - const key = `${message.networkId}_${message.componentType}`; - - // 获取或创建消息列表 - let messages = this._pendingMessages.get(key); - if (!messages) { - messages = []; - this._pendingMessages.set(key, messages); - } - - // 添加消息到列表 - messages.push(message); - - // 清除现有计时器 - const existingTimer = this._mergeTimers.get(key); - if (existingTimer) { - clearTimeout(existingTimer); - } - - // 设置新的合并计时器 - const timer = setTimeout(() => { - this.mergeAndSend(key, onMerged); - }, this._config.mergeTimeWindow); - - this._mergeTimers.set(key, timer); - } - - /** - * 合并并发送消息 - */ - private mergeAndSend( - key: string, - onMerged: (mergedMessage: SyncVarUpdateMessage) => void - ): void { - const messages = this._pendingMessages.get(key); - if (!messages || messages.length === 0) { - return; - } - - // 清理 - this._pendingMessages.delete(key); - this._mergeTimers.delete(key); - - if (messages.length === 1) { - // 只有一个消息,直接发送 - onMerged(messages[0]); - return; - } - - // 合并多个消息 - const mergedMessage = this.mergeMessages(messages); - onMerged(mergedMessage); - } - - /** - * 合并多个消息为单个消息 - */ - private mergeMessages(messages: SyncVarUpdateMessage[]): SyncVarUpdateMessage { - if (messages.length === 1) { - return messages[0]; - } - - const firstMessage = messages[0]; - const fieldUpdateMap = new Map(); - - // 合并字段更新(后面的覆盖前面的) - for (const message of messages) { - for (const fieldUpdate of message.fieldUpdates) { - fieldUpdateMap.set(fieldUpdate.fieldNumber, fieldUpdate); - } - } - - // 创建合并后的消息 - const mergedFieldUpdates = Array.from(fieldUpdateMap.values()); - const lastMessage = messages[messages.length - 1]; - - return new SyncVarUpdateMessage( - firstMessage.networkId, - firstMessage.componentType, - mergedFieldUpdates, - false, // 合并的消息总是增量同步 - firstMessage.senderId, - lastMessage.syncSequence // 使用最新的序列号 - ); - } - - /** - * 强制刷新所有待合并的消息 - */ - public flush(onMerged: (mergedMessage: SyncVarUpdateMessage) => void): void { - for (const key of this._pendingMessages.keys()) { - this.mergeAndSend(key, onMerged); - } - } - - /** - * 清理所有待合并的消息 - */ - public clear(): void { - // 清除所有计时器 - for (const timer of this._mergeTimers.values()) { - clearTimeout(timer); - } - - this._pendingMessages.clear(); - this._mergeTimers.clear(); - } -} - -/** - * 频率限制器 - */ -export class SyncVarRateLimiter { - private _messageCount: Map = new Map(); - private _resetTimers: Map = new Map(); - private _config: SyncVarOptimizationConfig; - - constructor(config: SyncVarOptimizationConfig) { - this._config = config; - } - - /** - * 检查是否允许发送消息 - * - * @param networkId - 网络对象ID - * @returns 是否允许发送 - */ - public canSend(networkId: string): boolean { - if (!this._config.enableRateLimit) { - return true; - } - - const currentCount = this._messageCount.get(networkId) || 0; - - if (currentCount >= this._config.maxMessagesPerSecond) { - return false; - } - - // 增加计数 - this._messageCount.set(networkId, currentCount + 1); - - // 如果这是第一个消息,设置重置计时器 - if (currentCount === 0) { - const timer = setTimeout(() => { - this._messageCount.delete(networkId); - this._resetTimers.delete(networkId); - }, 1000); - - this._resetTimers.set(networkId, timer); - } - - return true; - } - - /** - * 重置指定对象的频率限制 - */ - public reset(networkId: string): void { - this._messageCount.delete(networkId); - - const timer = this._resetTimers.get(networkId); - if (timer) { - clearTimeout(timer); - this._resetTimers.delete(networkId); - } - } - - /** - * 清理所有频率限制 - */ - public clear(): void { - for (const timer of this._resetTimers.values()) { - clearTimeout(timer); - } - - this._messageCount.clear(); - this._resetTimers.clear(); - } -} - -/** - * 距离剔除器 - */ -export class SyncVarDistanceCuller { - private _config: SyncVarOptimizationConfig; - private _positionCache: Map = new Map(); - - constructor(config: SyncVarOptimizationConfig) { - this._config = config; - } - - /** - * 更新对象位置 - * - * @param networkId - 网络对象ID - * @param position - 位置坐标 - */ - public updatePosition(networkId: string, position: { x: number; y: number; z?: number }): void { - this._positionCache.set(networkId, { ...position }); - } - - /** - * 检查是否应该向指定观察者发送消息 - * - * @param targetId - 目标对象ID - * @param observerId - 观察者ID - * @returns 是否应该发送 - */ - public shouldSendTo(targetId: string, observerId: string): boolean { - if (!this._config.enableDistanceCulling) { - return true; - } - - const targetPos = this._positionCache.get(targetId); - const observerPos = this._positionCache.get(observerId); - - if (!targetPos || !observerPos) { - // 位置信息不完整,默认发送 - return true; - } - - const distance = this.calculateDistance(targetPos, observerPos); - return distance <= this._config.cullingDistance; - } - - /** - * 获取在指定范围内的观察者列表 - * - * @param targetId - 目标对象ID - * @param observerIds - 观察者ID列表 - * @returns 在范围内的观察者ID列表 - */ - public getObserversInRange(targetId: string, observerIds: string[]): string[] { - if (!this._config.enableDistanceCulling) { - return observerIds; - } - - return observerIds.filter(observerId => this.shouldSendTo(targetId, observerId)); - } - - /** - * 计算两点之间的距离 - */ - private calculateDistance( - pos1: { x: number; y: number; z?: number }, - pos2: { x: number; y: number; z?: number } - ): number { - const dx = pos1.x - pos2.x; - const dy = pos1.y - pos2.y; - const dz = (pos1.z || 0) - (pos2.z || 0); - - return Math.sqrt(dx * dx + dy * dy + dz * dz); - } - - /** - * 清理位置缓存 - */ - public clear(): void { - this._positionCache.clear(); - } - - /** - * 移除指定对象的位置信息 - */ - public remove(networkId: string): void { - this._positionCache.delete(networkId); - } -} - -/** - * SyncVar性能优化器 - */ -export class SyncVarOptimizer { - private static readonly logger = createLogger('SyncVarOptimizer'); - private _config: SyncVarOptimizationConfig; - private _messageMerger: SyncVarMessageMerger; - private _rateLimiter: SyncVarRateLimiter; - private _distanceCuller: SyncVarDistanceCuller; - - // 统计信息 - private _stats = { - messagesProcessed: 0, - messagesBlocked: 0, - messagesMerged: 0, - bytesSaved: 0 - }; - - constructor(config?: Partial) { - // 默认配置 - this._config = { - enableMessageMerging: true, - mergeTimeWindow: 16, // 1帧时间 - enableDeltaCompression: true, - enableBatchSending: true, - batchSizeLimit: 10, - enablePriorityQueue: true, - enableDistanceCulling: false, - cullingDistance: 100, - enableRateLimit: true, - maxMessagesPerSecond: 60, - ...config - }; - - this._messageMerger = new SyncVarMessageMerger(this._config); - this._rateLimiter = new SyncVarRateLimiter(this._config); - this._distanceCuller = new SyncVarDistanceCuller(this._config); - } - - /** - * 处理SyncVar消息优化 - * - * @param message - 原始消息 - * @param targetObservers - 目标观察者列表 - * @param onOptimized - 优化完成回调 - */ - public optimizeMessage( - message: SyncVarUpdateMessage, - targetObservers: string[] = [], - onOptimized: (optimizedMessages: SyncVarUpdateMessage[], observers: string[]) => void - ): void { - this._stats.messagesProcessed++; - - // 频率限制检查 - if (!this._rateLimiter.canSend(message.networkId)) { - this._stats.messagesBlocked++; - SyncVarOptimizer.logger.debug(` 消息被频率限制阻止: ${message.networkId}`); - return; - } - - // 距离剔除 - const validObservers = this._distanceCuller.getObserversInRange(message.networkId, targetObservers); - - if (validObservers.length === 0 && targetObservers.length > 0) { - this._stats.messagesBlocked++; - SyncVarOptimizer.logger.debug(` 消息被距离剔除阻止: ${message.networkId}`); - return; - } - - // 消息合并 - this._messageMerger.addMessage(message, (mergedMessage) => { - if (mergedMessage !== message) { - this._stats.messagesMerged++; - SyncVarOptimizer.logger.debug(` 消息已合并: ${message.networkId}`); - } - - onOptimized([mergedMessage], validObservers); - }); - } - - /** - * 更新对象位置(用于距离剔除) - */ - public updateObjectPosition(networkId: string, position: { x: number; y: number; z?: number }): void { - this._distanceCuller.updatePosition(networkId, position); - } - - /** - * 配置优化器 - */ - public configure(config: Partial): void { - this._config = { ...this._config, ...config }; - SyncVarOptimizer.logger.info(' 配置已更新:', this._config); - } - - /** - * 强制刷新所有待处理的消息 - */ - public flush(onOptimized?: (optimizedMessages: SyncVarUpdateMessage[], observers: string[]) => void): void { - this._messageMerger.flush((mergedMessage) => { - if (onOptimized) { - onOptimized([mergedMessage], []); - } - }); - } - - /** - * 重置指定对象的优化状态 - */ - public resetObject(networkId: string): void { - this._rateLimiter.reset(networkId); - this._distanceCuller.remove(networkId); - } - - /** - * 获取优化统计信息 - */ - public getStats(): typeof SyncVarOptimizer.prototype._stats & { - config: SyncVarOptimizationConfig; - optimizationRatio: number; - } { - const optimizationRatio = this._stats.messagesProcessed > 0 - ? (this._stats.messagesBlocked + this._stats.messagesMerged) / this._stats.messagesProcessed - : 0; - - return { - ...this._stats, - config: { ...this._config }, - optimizationRatio - }; - } - - /** - * 重置统计信息 - */ - public resetStats(): void { - this._stats = { - messagesProcessed: 0, - messagesBlocked: 0, - messagesMerged: 0, - bytesSaved: 0 - }; - } - - /** - * 清理优化器 - */ - public cleanup(): void { - this._messageMerger.clear(); - this._rateLimiter.clear(); - this._distanceCuller.clear(); - this.resetStats(); - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarProxy.ts b/packages/network/src/SyncVar/SyncVarProxy.ts deleted file mode 100644 index 6476485e..00000000 --- a/packages/network/src/SyncVar/SyncVarProxy.ts +++ /dev/null @@ -1,300 +0,0 @@ -import { getSyncVarMetadata, isSyncVar } from './SyncVarDecorator'; -import { SyncVarManager } from './SyncVarManager'; -import { INetworkSyncable, SyncVarValue, TypeGuards } from '../types/NetworkTypes'; -import { createLogger } from '@esengine/ecs-framework'; - -/** - * SyncVar代理配置 - */ -export interface SyncVarProxyOptions { - /** - * 是否启用调试日志 - */ - debugLog?: boolean; - - /** - * 自定义属性过滤器 - */ - propertyFilter?: (propertyKey: string) => boolean; -} - -/** - * 创建SyncVar代理 - * - * 为组件实例创建Proxy,拦截SyncVar字段的读写操作, - * 当字段值发生变化时自动触发同步逻辑 - * - * @param target - 目标组件实例 - * @param options - 代理配置选项 - * @returns 代理包装的组件实例 - */ -export function createSyncVarProxy( - target: T, - options: SyncVarProxyOptions = {} -): T { - const { debugLog = false, propertyFilter } = options; - const syncVarManager = SyncVarManager.Instance; - - // 检查目标是否有SyncVar - const metadata = getSyncVarMetadata(target.constructor); - const logger = createLogger('SyncVarProxy'); - - if (metadata.length === 0) { - if (debugLog) { - logger.debug(`对象 ${target.constructor.name} 没有SyncVar,返回原对象`); - } - return target; - } - - // 初始化SyncVar管理器 - syncVarManager.initializeComponent(target); - - if (debugLog) { - logger.debug(`为 ${target.constructor.name} 创建代理,SyncVar字段:`, - metadata.map(m => m.propertyKey)); - } - - // 存储原始值的副本,用于比较变化 - const originalValues = new Map(); - - // 初始化原始值 - for (const meta of metadata) { - if (meta.propertyKey in target) { - originalValues.set(meta.propertyKey, (target as Record)[meta.propertyKey]); - } - } - - const proxy = new Proxy(target, { - /** - * 拦截属性读取 - */ - get(obj: T, prop: string | symbol): unknown { - // 内部属性和方法直接返回 - if (typeof prop === 'symbol' || prop.startsWith('_') || prop.startsWith('$')) { - return Reflect.get(obj, prop); - } - - const propertyKey = prop as string; - - // 如果有自定义过滤器且不通过,直接返回 - if (propertyFilter && !propertyFilter(propertyKey)) { - return Reflect.get(obj, prop); - } - - const value = Reflect.get(obj, prop); - - if (debugLog && isSyncVar(obj, propertyKey)) { - logger.debug(`GET ${obj.constructor.name}.${propertyKey} = ${value}`); - } - - return value; - }, - - /** - * 拦截属性设置 - */ - set(obj: T, prop: string | symbol, newValue: unknown): boolean { - // 内部属性和方法直接设置 - if (typeof prop === 'symbol' || prop.startsWith('_') || prop.startsWith('$')) { - return Reflect.set(obj, prop, newValue); - } - - const propertyKey = prop as string; - - // 如果有自定义过滤器且不通过,直接设置 - if (propertyFilter && !propertyFilter(propertyKey)) { - return Reflect.set(obj, prop, newValue); - } - - // 检查是否被临时禁用(用于避免循环) - if ((obj as any)._syncVarDisabled) { - return Reflect.set(obj, prop, newValue); - } - - // 检查是否为SyncVar - if (!isSyncVar(obj, propertyKey)) { - return Reflect.set(obj, prop, newValue); - } - - // 获取旧值 - const oldValue = originalValues.get(propertyKey); - - if (debugLog) { - logger.debug(`SET ${obj.constructor.name}.${propertyKey} = ${newValue} (was ${oldValue})`); - } - - // 设置新值 - const result = Reflect.set(obj, prop, newValue); - - if (result) { - // 更新原始值记录 - originalValues.set(propertyKey, newValue); - - // 记录变化到SyncVar管理器 - try { - if (TypeGuards.isSyncVarValue(oldValue) && TypeGuards.isSyncVarValue(newValue)) { - syncVarManager.recordChange(obj, propertyKey, oldValue, newValue); - } - } catch (error) { - logger.error(`记录SyncVar变化失败:`, error); - } - } - - return result; - }, - - /** - * 拦截属性删除 - */ - deleteProperty(obj: T, prop: string | symbol): boolean { - const propertyKey = prop as string; - - if (typeof prop === 'string' && isSyncVar(obj, propertyKey)) { - logger.warn(`尝试删除SyncVar属性 ${propertyKey},这可能会导致同步问题`); - } - - return Reflect.deleteProperty(obj, prop); - }, - - /** - * 拦截属性枚举 - */ - ownKeys(obj: T): ArrayLike { - return Reflect.ownKeys(obj); - }, - - /** - * 拦截属性描述符获取 - */ - getOwnPropertyDescriptor(obj: T, prop: string | symbol): PropertyDescriptor | undefined { - return Reflect.getOwnPropertyDescriptor(obj, prop); - }, - - /** - * 拦截in操作符 - */ - has(obj: T, prop: string | symbol): boolean { - return Reflect.has(obj, prop); - } - }); - - // 标记为已代理 - (proxy as T & { _syncVarProxied: boolean; _syncVarOptions: SyncVarProxyOptions })._syncVarProxied = true; - (proxy as T & { _syncVarProxied: boolean; _syncVarOptions: SyncVarProxyOptions })._syncVarOptions = options; - - if (debugLog) { - logger.debug(`${target.constructor.name} 代理创建完成`); - } - - return proxy; -} - -/** - * 检查对象是否已经被SyncVar代理 - * - * @param obj - 要检查的对象 - * @returns 是否已被代理 - */ -export function isSyncVarProxied(obj: unknown): obj is { _syncVarProxied: boolean } { - return typeof obj === 'object' && obj !== null && '_syncVarProxied' in obj && (obj as any)._syncVarProxied === true; -} - -/** - * 获取代理对象的原始目标 - * - * @param proxy - 代理对象 - * @returns 原始目标对象,如果不是代理则返回原对象 - */ -export function getSyncVarProxyTarget(proxy: T): T { - // 注意:JavaScript的Proxy没有直接方法获取target - // 这里返回proxy本身,因为我们的代理是透明的 - return proxy; -} - -/** - * 销毁SyncVar代理 - * - * 清理代理相关的资源,但注意JavaScript的Proxy无法真正"销毁" - * 这个函数主要是清理管理器中的相关数据 - * - * @param proxy - 代理对象 - */ -export function destroySyncVarProxy(proxy: INetworkSyncable & { _syncVarProxied?: boolean; _syncVarDestroyed?: boolean }): void { - if (!isSyncVarProxied(proxy)) { - return; - } - - // 清理SyncVar管理器中的数据 - const syncVarManager = SyncVarManager.Instance; - syncVarManager.cleanupComponent(proxy); - - // 标记为已销毁(虽然代理仍然存在) - proxy._syncVarProxied = false; - proxy._syncVarDestroyed = true; - - const logger = createLogger('SyncVarProxy'); - logger.debug(`${proxy.constructor?.name || 'Unknown'} 代理已销毁`); -} - -/** - * 临时禁用SyncVar代理监听 - * - * 在回调函数执行期间禁用SyncVar变化监听,避免循环触发 - * - * @param proxy - 代理对象 - * @param callback - 要执行的回调函数 - * @returns 回调函数的返回值 - */ -export function withSyncVarDisabled(proxy: INetworkSyncable & { _syncVarDisabled?: boolean; _syncVarProxied?: boolean }, callback: () => TResult): TResult { - if (!isSyncVarProxied(proxy)) { - return callback(); - } - - const wasDisabled = proxy._syncVarDisabled; - proxy._syncVarDisabled = true; - - try { - return callback(); - } finally { - proxy._syncVarDisabled = wasDisabled; - } -} - -/** - * 批量更新SyncVar字段 - * - * 在批量更新期间暂时禁用同步,最后一次性触发变化检测 - * - * @param proxy - 代理对象 - * @param updates - 要更新的字段和值的映射 - */ -export function batchUpdateSyncVar(proxy: INetworkSyncable & { _syncVarProxied?: boolean; _syncVarDisabled?: boolean }, updates: Record): void { - if (!isSyncVarProxied(proxy)) { - // 如果不是代理对象,直接批量更新 - Object.assign(proxy, updates); - return; - } - - withSyncVarDisabled(proxy, () => { - // 记录旧值 - const oldValues: Record = {}; - for (const key of Object.keys(updates)) { - if (isSyncVar(proxy, key)) { - oldValues[key] = (proxy as unknown as Record)[key]; - } - } - - // 批量更新 - Object.assign(proxy, updates); - - const syncVarManager = SyncVarManager.Instance; - for (const [key, newValue] of Object.entries(updates)) { - if (isSyncVar(proxy, key)) { - const oldValue = oldValues[key]; - if (TypeGuards.isSyncVarValue(oldValue) && TypeGuards.isSyncVarValue(newValue)) { - syncVarManager.recordChange(proxy, key, oldValue, newValue); - } - } - } - }); -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/SyncVarSyncScheduler.ts b/packages/network/src/SyncVar/SyncVarSyncScheduler.ts deleted file mode 100644 index 949d0f8c..00000000 --- a/packages/network/src/SyncVar/SyncVarSyncScheduler.ts +++ /dev/null @@ -1,685 +0,0 @@ -import { SyncVarManager } from './SyncVarManager'; -import { NetworkIdentityRegistry, NetworkIdentity } from '../Core/NetworkIdentity'; -import { SyncVarUpdateMessage } from '../Messaging/MessageTypes'; -import { NetworkEnvironment } from '../Core/NetworkEnvironment'; -import { ComponentRegistry, createLogger } from '@esengine/ecs-framework'; -import { NetworkComponent } from '../NetworkComponent'; - -/** - * SyncVar同步调度配置 - */ -export interface SyncVarSyncConfig { - /** 同步频率(毫秒) */ - syncInterval: number; - /** 最大批处理消息数量 */ - maxBatchSize: number; - /** 最大每帧处理对象数量 */ - maxObjectsPerFrame: number; - /** 是否启用优先级排序 */ - enablePrioritySort: boolean; - /** 最小同步间隔(防止过于频繁) */ - minSyncInterval: number; - /** 是否启用增量同步 */ - enableIncrementalSync: boolean; -} - -/** - * 同步优先级计算器 - */ -export interface ISyncPriorityCalculator { - /** - * 计算组件的同步优先级 - * - * @param component - 网络组件 - * @param identity - 网络身份 - * @returns 优先级值,数字越大优先级越高 - */ - calculatePriority(component: any, identity: NetworkIdentity): number; -} - -/** - * 默认优先级计算器 - */ -export class DefaultSyncPriorityCalculator implements ISyncPriorityCalculator { - public calculatePriority(component: any, identity: NetworkIdentity): number { - let priority = 0; - - // 权威对象优先级更高 - if (identity.hasAuthority) { - priority += 10; - } - - // 距离上次同步时间越长,优先级越高 - const timeSinceLastSync = Date.now() - identity.lastSyncTime; - priority += Math.min(timeSinceLastSync / 1000, 10); // 最多加10分 - - // 变化数量越多,优先级越高 - const syncVarManager = SyncVarManager.Instance; - const changes = syncVarManager.getPendingChanges(component); - priority += changes.length; - - return priority; - } -} - -/** - * SyncVar同步调度器 - * - * 负责定期扫描网络对象的SyncVar变化,创建和分发同步消息 - * 支持批处理、优先级排序和性能优化 - */ -export class SyncVarSyncScheduler { - private static readonly logger = createLogger('SyncVarSyncScheduler'); - private static _instance: SyncVarSyncScheduler | null = null; - - private _config: SyncVarSyncConfig; - private _priorityCalculator: ISyncPriorityCalculator; - private _isRunning: boolean = false; - private _syncTimer: NodeJS.Timeout | null = null; - private _lastSyncTime: number = 0; - private _syncCounter: number = 0; - - // 统计信息 - private _stats = { - totalSyncCycles: 0, - totalObjectsScanned: 0, - totalMessagesSent: 0, - totalChangesProcessed: 0, - averageCycleTime: 0, - lastCycleTime: 0, - errors: 0 - }; - - // 消息发送回调 - private _messageSendCallback: ((message: SyncVarUpdateMessage) => Promise) | null = null; - - /** - * 获取调度器单例 - */ - public static get Instance(): SyncVarSyncScheduler { - if (!SyncVarSyncScheduler._instance) { - SyncVarSyncScheduler._instance = new SyncVarSyncScheduler(); - } - return SyncVarSyncScheduler._instance; - } - - private constructor() { - // 默认配置 - this._config = { - syncInterval: 50, // 20fps - maxBatchSize: 10, - maxObjectsPerFrame: 50, - enablePrioritySort: true, - minSyncInterval: 16, // 最小16ms (60fps) - enableIncrementalSync: true - }; - - this._priorityCalculator = new DefaultSyncPriorityCalculator(); - } - - /** - * 配置调度器 - * - * @param config - 调度器配置 - */ - public configure(config: Partial): void { - this._config = { ...this._config, ...config }; - - // 如果正在运行,重启以应用新配置 - if (this._isRunning) { - this.stop(); - this.start(); - } - - SyncVarSyncScheduler.logger.debug('调度器配置已更新:', this._config); - } - - /** - * 设置优先级计算器 - * - * @param calculator - 优先级计算器 - */ - public setPriorityCalculator(calculator: ISyncPriorityCalculator): void { - this._priorityCalculator = calculator; - SyncVarSyncScheduler.logger.debug('优先级计算器已更新'); - } - - /** - * 设置消息发送回调 - * - * @param callback - 消息发送回调函数 - */ - public setMessageSendCallback(callback: (message: SyncVarUpdateMessage) => Promise): void { - this._messageSendCallback = callback; - SyncVarSyncScheduler.logger.debug('消息发送回调已设置'); - } - - /** - * 启动调度器 - */ - public start(): void { - if (this._isRunning) { - SyncVarSyncScheduler.logger.warn('调度器已经在运行'); - return; - } - - this._isRunning = true; - this._lastSyncTime = Date.now(); - - // 设置定时器 - this._syncTimer = setInterval(() => { - this.performSyncCycle(); - }, this._config.syncInterval); - - SyncVarSyncScheduler.logger.info(`调度器已启动,同步间隔: ${this._config.syncInterval}ms`); - } - - /** - * 停止调度器 - */ - public stop(): void { - if (!this._isRunning) { - return; - } - - this._isRunning = false; - - if (this._syncTimer) { - clearInterval(this._syncTimer); - this._syncTimer = null; - } - - SyncVarSyncScheduler.logger.info('调度器已停止'); - } - - /** - * 执行一次同步周期 - */ - public performSyncCycle(): void { - if (!this._isRunning) { - return; - } - - const cycleStartTime = Date.now(); - - try { - // 检查最小同步间隔 - if (cycleStartTime - this._lastSyncTime < this._config.minSyncInterval) { - return; - } - - this._stats.totalSyncCycles++; - this._lastSyncTime = cycleStartTime; - - // 获取所有激活的网络对象 - const activeObjects = NetworkIdentityRegistry.Instance.getActiveObjects(); - this._stats.totalObjectsScanned += activeObjects.length; - - // 收集需要同步的组件 - const syncCandidates = this.collectSyncCandidates(activeObjects); - - // 优先级排序 - if (this._config.enablePrioritySort) { - syncCandidates.sort((a, b) => b.priority - a.priority); - } - - // 限制每帧处理的对象数量 - const objectsToProcess = syncCandidates.slice(0, this._config.maxObjectsPerFrame); - - // 创建和发送同步消息 - this.processSyncCandidates(objectsToProcess); - - // 更新统计信息 - const cycleTime = Date.now() - cycleStartTime; - this._stats.lastCycleTime = cycleTime; - this._stats.averageCycleTime = (this._stats.averageCycleTime * (this._stats.totalSyncCycles - 1) + cycleTime) / this._stats.totalSyncCycles; - - } catch (error) { - this._stats.errors++; - SyncVarSyncScheduler.logger.error('同步周期执行失败:', error); - } - } - - /** - * 收集同步候选对象 - */ - private collectSyncCandidates(activeObjects: NetworkIdentity[]): Array<{ - identity: NetworkIdentity; - component: any; - priority: number; - changeCount: number; - }> { - const candidates: Array<{ - identity: NetworkIdentity; - component: any; - priority: number; - changeCount: number; - }> = []; - - const syncVarManager = SyncVarManager.Instance; - - for (const identity of activeObjects) { - try { - // 获取对象的所有网络组件 - const components = this.getNetworkComponents(identity); - - for (const component of components) { - // 检查组件是否有SyncVar变化 - const pendingChanges = syncVarManager.getPendingChanges(component); - if (pendingChanges.length === 0) { - continue; - } - - // 权限检查:只有有权限的对象才能发起同步 - if (!this.canComponentSync(component, identity)) { - continue; - } - - // 计算优先级 - const priority = this._priorityCalculator.calculatePriority(component, identity); - - candidates.push({ - identity, - component, - priority, - changeCount: pendingChanges.length - }); - } - } catch (error) { - SyncVarSyncScheduler.logger.error(`处理网络对象失败: ${identity.networkId}`, error); - } - } - - return candidates; - } - - /** - * 获取网络对象的所有网络组件 - */ - private getNetworkComponents(identity: NetworkIdentity): NetworkComponent[] { - const entity = identity.entity; - if (!entity) { - SyncVarSyncScheduler.logger.warn(`NetworkIdentity ${identity.networkId} 缺少Entity引用`); - return []; - } - - const networkComponents: NetworkComponent[] = []; - - try { - // 获取所有已注册的组件类型 - const allRegisteredTypes = ComponentRegistry.getAllRegisteredTypes(); - - for (const [ComponentClass] of allRegisteredTypes) { - // 检查是否为NetworkComponent子类 - if (ComponentClass.prototype instanceof NetworkComponent || ComponentClass === NetworkComponent) { - const component = entity.getComponent(ComponentClass as any); - if (component && component instanceof NetworkComponent) { - networkComponents.push(component); - } - } - } - } catch (error) { - SyncVarSyncScheduler.logger.error(`获取网络组件失败 (${identity.networkId}):`, error); - } - - return networkComponents; - } - - /** - * 检查组件是否可以进行同步 - */ - private canComponentSync(component: any, identity: NetworkIdentity): boolean { - // 服务端对象通常有同步权限 - if (NetworkEnvironment.isServer && identity.hasAuthority) { - return true; - } - - // 客户端只能同步自己拥有的对象 - if (NetworkEnvironment.isClient) { - // 这里需要获取当前客户端ID,暂时简化处理 - return identity.hasAuthority; - } - - return false; - } - - /** - * 处理同步候选对象 - */ - private processSyncCandidates(candidates: Array<{ - identity: NetworkIdentity; - component: any; - priority: number; - changeCount: number; - }>): void { - const syncVarManager = SyncVarManager.Instance; - const messageBatch: SyncVarUpdateMessage[] = []; - - for (const candidate of candidates) { - try { - // 创建SyncVar更新消息 - const message = syncVarManager.createSyncVarUpdateMessage( - candidate.component, - candidate.identity.networkId, - '', // senderId,后续可以从环境获取 - candidate.identity.getNextSyncSequence(), - false // isFullSync - ); - - if (message) { - messageBatch.push(message); - this._stats.totalChangesProcessed += candidate.changeCount; - - // 更新对象的同步时间 - candidate.identity.updateSyncTime(); - - // 清除已同步的变化 - syncVarManager.clearChanges(candidate.component); - } - - // 检查批处理大小限制 - if (messageBatch.length >= this._config.maxBatchSize) { - this.sendMessageBatch(messageBatch); - messageBatch.length = 0; // 清空数组 - } - - } catch (error) { - SyncVarSyncScheduler.logger.error(`处理同步候选对象失败: ${candidate.identity.networkId}`, error); - } - } - - // 发送剩余的消息 - if (messageBatch.length > 0) { - this.sendMessageBatch(messageBatch); - } - } - - /** - * 发送消息批次 - */ - private async sendMessageBatch(messages: SyncVarUpdateMessage[]): Promise { - if (!this._messageSendCallback) { - SyncVarSyncScheduler.logger.warn('没有设置消息发送回调,消息被丢弃'); - return; - } - - for (const message of messages) { - try { - await this._messageSendCallback(message); - this._stats.totalMessagesSent++; - } catch (error) { - SyncVarSyncScheduler.logger.error('发送SyncVar消息失败:', error); - this._stats.errors++; - } - } - } - - /** - * 手动触发同步 - * - * @param networkId - 指定网络对象ID,如果不提供则同步所有对象 - */ - public manualSync(networkId?: string): void { - if (networkId) { - const identity = NetworkIdentityRegistry.Instance.find(networkId); - if (identity) { - this.performSyncCycle(); - } - } else { - this.performSyncCycle(); - } - } - - /** - * 获取调度器统计信息 - */ - public getStats(): typeof SyncVarSyncScheduler.prototype._stats & { - isRunning: boolean; - config: SyncVarSyncConfig; - uptime: number; - } { - return { - ...this._stats, - isRunning: this._isRunning, - config: { ...this._config }, - uptime: this._isRunning ? Date.now() - (this._lastSyncTime - this._config.syncInterval) : 0 - }; - } - - /** - * 重置统计信息 - */ - public resetStats(): void { - this._stats = { - totalSyncCycles: 0, - totalObjectsScanned: 0, - totalMessagesSent: 0, - totalChangesProcessed: 0, - averageCycleTime: 0, - lastCycleTime: 0, - errors: 0 - }; - SyncVarSyncScheduler.logger.debug('统计信息已重置'); - } - - /** - * 获取当前配置 - */ - public getConfig(): SyncVarSyncConfig { - return { ...this._config }; - } - - /** - * 检查调度器是否正在运行 - */ - public get isRunning(): boolean { - return this._isRunning; - } - - /** - * 智能调度配置 - * 根据网络状况动态调整同步策略 - */ - public applyNetworkAdaptiveConfig(adaptiveConfig: { - suggestedSyncInterval: number; - suggestedBatchSize: number; - prioritizeUpdate: boolean; - }): void { - const oldConfig = { ...this._config }; - - // 应用建议的同步间隔,但不超出安全范围 - this._config.syncInterval = Math.max( - Math.min(adaptiveConfig.suggestedSyncInterval, 1000), // 最大1秒 - this._config.minSyncInterval // 最小间隔 - ); - - // 应用建议的批处理大小 - this._config.maxBatchSize = Math.max( - Math.min(adaptiveConfig.suggestedBatchSize, 100), // 最大100 - 1 // 最小1 - ); - - // 根据优先级更新标志调整其他参数 - if (adaptiveConfig.prioritizeUpdate) { - // 高优先级模式:减少每帧处理对象数,确保重要更新及时处理 - this._config.maxObjectsPerFrame = Math.max( - Math.floor(this._config.maxObjectsPerFrame * 0.7), - 10 - ); - this._config.enablePrioritySort = true; - } else { - // 正常模式:恢复标准设置 - this._config.maxObjectsPerFrame = 50; - } - - SyncVarSyncScheduler.logger.info('应用网络自适应配置:', { - oldInterval: oldConfig.syncInterval, - newInterval: this._config.syncInterval, - oldBatchSize: oldConfig.maxBatchSize, - newBatchSize: this._config.maxBatchSize, - prioritizeMode: adaptiveConfig.prioritizeUpdate - }); - - // 重启调度器以应用新配置 - if (this._isRunning) { - this.stop(); - this.start(); - } - } - - /** - * 网络拥塞控制 - * 根据拥塞状态调整同步行为 - */ - public applyCongestionControl(congestionInfo: { - isCongested: boolean; - congestionLevel: 'none' | 'light' | 'moderate' | 'severe'; - }): void { - if (!congestionInfo.isCongested) { - // 无拥塞:可以恢复正常配置 - this.resetToOptimalConfig(); - return; - } - - const emergencyConfig = { ...this._config }; - - switch (congestionInfo.congestionLevel) { - case 'light': - // 轻微拥塞:适度降低频率 - emergencyConfig.syncInterval = Math.max(this._config.syncInterval * 1.2, 80); - emergencyConfig.maxBatchSize = Math.min(this._config.maxBatchSize * 1.5, 15); - break; - - case 'moderate': - // 中度拥塞:显著降低频率,增加批处理 - emergencyConfig.syncInterval = Math.max(this._config.syncInterval * 1.5, 120); - emergencyConfig.maxBatchSize = Math.min(this._config.maxBatchSize * 2, 25); - emergencyConfig.maxObjectsPerFrame = Math.max( - Math.floor(this._config.maxObjectsPerFrame * 0.6), - 15 - ); - break; - - case 'severe': - // 严重拥塞:最保守策略 - emergencyConfig.syncInterval = Math.max(this._config.syncInterval * 2, 200); - emergencyConfig.maxBatchSize = Math.min(this._config.maxBatchSize * 3, 50); - emergencyConfig.maxObjectsPerFrame = Math.max( - Math.floor(this._config.maxObjectsPerFrame * 0.4), - 10 - ); - emergencyConfig.enablePrioritySort = true; // 强制启用优先级排序 - break; - } - - this._config = emergencyConfig; - - SyncVarSyncScheduler.logger.warn(`应用拥塞控制策略 [${congestionInfo.congestionLevel}]:`, { - syncInterval: this._config.syncInterval, - maxBatchSize: this._config.maxBatchSize, - maxObjectsPerFrame: this._config.maxObjectsPerFrame - }); - - // 重启调度器以应用拥塞控制配置 - if (this._isRunning) { - this.stop(); - this.start(); - } - } - - /** - * 重置到优化配置 - * 当网络状况改善时调用 - */ - private resetToOptimalConfig(): void { - const optimalConfig: SyncVarSyncConfig = { - syncInterval: 50, // 20fps - maxBatchSize: 10, - maxObjectsPerFrame: 50, - enablePrioritySort: true, - minSyncInterval: 16, // 最小16ms (60fps) - enableIncrementalSync: true - }; - - const configChanged = ( - this._config.syncInterval !== optimalConfig.syncInterval || - this._config.maxBatchSize !== optimalConfig.maxBatchSize || - this._config.maxObjectsPerFrame !== optimalConfig.maxObjectsPerFrame - ); - - if (configChanged) { - this._config = optimalConfig; - SyncVarSyncScheduler.logger.info('恢复到优化配置'); - - if (this._isRunning) { - this.stop(); - this.start(); - } - } - } - - /** - * 智能批处理优化 - * 基于网络状况和组件变化频率动态调整批处理策略 - */ - public optimizeBatching(): void { - // 分析最近的同步统计 - const recentSuccessRate = this.calculateRecentSuccessRate(); - const avgProcessingTime = this._stats.averageCycleTime; - - // 根据成功率调整批处理大小 - if (recentSuccessRate < 0.8) { - // 成功率低,减少批处理大小 - this._config.maxBatchSize = Math.max( - Math.floor(this._config.maxBatchSize * 0.8), - 2 - ); - SyncVarSyncScheduler.logger.debug(`批处理优化: 降低批大小到 ${this._config.maxBatchSize} (成功率: ${recentSuccessRate.toFixed(2)})`); - } else if (recentSuccessRate > 0.95 && avgProcessingTime < this._config.syncInterval * 0.5) { - // 成功率高且处理时间充足,可以增加批处理大小 - this._config.maxBatchSize = Math.min( - Math.floor(this._config.maxBatchSize * 1.1), - 30 - ); - SyncVarSyncScheduler.logger.debug(`批处理优化: 提升批大小到 ${this._config.maxBatchSize}`); - } - } - - /** - * 计算最近的成功率 - * 这里简化实现,实际可以基于更复杂的统计 - */ - private calculateRecentSuccessRate(): number { - // 简化实现:基于错误率计算成功率 - const totalOperations = this._stats.totalSyncCycles; - if (totalOperations === 0) return 1.0; - - const errorRate = this._stats.errors / totalOperations; - return Math.max(0, 1 - errorRate); - } - - /** - * 动态优先级调整 - * 根据组件活跃度和网络状况调整优先级计算 - */ - public updatePriorityStrategy(networkQuality: number): void { - if (networkQuality >= 80) { - // 高质量网络:可以使用复杂的优先级计算 - this._priorityCalculator = new DefaultSyncPriorityCalculator(); - } else { - // 低质量网络:使用简化的优先级计算,减少CPU开销 - this._priorityCalculator = new SimplifiedPriorityCalculator(); - } - } -} - -/** - * 简化优先级计算器 - * 在网络状况不佳时使用,减少计算开销 - */ -class SimplifiedPriorityCalculator implements ISyncPriorityCalculator { - public calculatePriority(component: any, identity: NetworkIdentity): number { - // 简化的优先级计算:仅基于权威性 - return identity.hasAuthority ? 10 : 5; - } -} \ No newline at end of file diff --git a/packages/network/src/SyncVar/index.ts b/packages/network/src/SyncVar/index.ts deleted file mode 100644 index a45156d8..00000000 --- a/packages/network/src/SyncVar/index.ts +++ /dev/null @@ -1,42 +0,0 @@ -/** - * SyncVar同步变量系统导出 - * - * 提供自动变量同步功能,支持网络状态的实时同步 - */ - -// 装饰器和元数据 -export * from './SyncVarDecorator'; - -// 管理器 -export { SyncVarManager } from './SyncVarManager'; - -// 代理系统 -export * from './SyncVarProxy'; - -// 工厂函数 -export * from './SyncVarFactory'; - -// 消息处理器 -export { SyncVarMessageHandler } from './SyncVarMessageHandler'; - -// 同步调度器 -export { - SyncVarSyncScheduler, - DefaultSyncPriorityCalculator -} from './SyncVarSyncScheduler'; -export type { - SyncVarSyncConfig, - ISyncPriorityCalculator -} from './SyncVarSyncScheduler'; - -// 性能优化器 -export { - SyncVarOptimizer, - SyncVarMessageMerger, - SyncVarRateLimiter, - SyncVarDistanceCuller -} from './SyncVarOptimizer'; -export type { - SyncVarOptimizationConfig -} from './SyncVarOptimizer'; - diff --git a/packages/network/src/TSRPC/TsrpcClient.ts b/packages/network/src/TSRPC/TsrpcClient.ts deleted file mode 100644 index 3b333c9e..00000000 --- a/packages/network/src/TSRPC/TsrpcClient.ts +++ /dev/null @@ -1,383 +0,0 @@ -/** - * TSRPC 客户端 - */ -import { WsClient } from 'tsrpc'; -import { ServiceType, serviceProto } from './protocols/serviceProto'; -import { Component } from '@esengine/ecs-framework'; -import { getTsrpcMetadata } from '../Serialization/TsrpcDecorators'; -import { INetworkSyncable, MessageData } from '../types/NetworkTypes'; -import { ConnectionState, ITypedEventEmitter, NetworkEventHandlers } from '../types/CoreTypes'; -import { TSRPC_CONFIG, NETWORK_CONFIG } from '../constants/NetworkConstants'; - -const logger = { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug -}; - -export class TsrpcNetworkClient { - private _client: WsClient; - private _playerId?: number; - private _roomId?: string; - private _componentUpdateCallbacks = new Map void>(); - private _connectionState: ConnectionState = 'disconnected'; - private _reconnectAttempts: number = 0; - private _maxReconnectAttempts: number = NETWORK_CONFIG.DEFAULT_MAX_RECONNECT_ATTEMPTS; - private _reconnectDelay: number = NETWORK_CONFIG.DEFAULT_RECONNECT_DELAY; - private _lastPingTime: number = 0; - private _rtt: number = 0; - private _eventHandlers = new Map(); - - constructor(serverUrl: string = TSRPC_CONFIG.DEFAULT_SERVER_URL) { - this._client = new WsClient(serviceProto, { - server: serverUrl, - // JSON兼容模式 - json: false, - // 自动重连 - heartbeat: { - interval: TSRPC_CONFIG.DEFAULT_HEARTBEAT.interval, - timeout: TSRPC_CONFIG.DEFAULT_HEARTBEAT.timeout - } - }); - - this.setupMessageHandlers(); - this.setupEvents(); - } - - private setupMessageHandlers() { - // 监听组件更新消息 - this._client.listenMsg('ComponentUpdate', (msg) => { - const { entityId, componentType, componentData, timestamp } = msg; - - logger.debug(`收到组件更新: Entity ${entityId}, Component ${componentType}`); - - const callback = this._componentUpdateCallbacks.get(componentType); - if (callback) { - callback(entityId, componentData); - } - }); - } - - private setupEvents() { - // 连接成功 - this._client.flows.postConnectFlow.push((conn) => { - this._connectionState = 'connected'; - this._reconnectAttempts = 0; - logger.info('已连接到TSRPC服务器'); - this.emit('connected'); - return conn; - }); - - // 连接断开 - this._client.flows.postDisconnectFlow.push((data) => { - this._connectionState = 'disconnected'; - logger.info('与TSRPC服务器断开连接'); - this.emit('disconnected', data?.reason || 'Unknown disconnect reason'); - - // 自动重连 - if (this._reconnectAttempts < this._maxReconnectAttempts) { - this.attemptReconnect(); - } - - return data; - }); - } - - /** - * 连接到服务器 - */ - async connect(): Promise { - this._connectionState = 'connecting'; - this.emit('connecting'); - - const result = await this._client.connect(); - if (result.isSucc) { - this._connectionState = 'connected'; - return true; - } else { - this._connectionState = 'disconnected'; - this.emit('connectError', new Error(result.errMsg || 'Connection failed')); - return false; - } - } - - /** - * 断开连接 - */ - disconnect() { - this._client.disconnect(); - } - - /** - * 加入房间 - */ - async joinRoom(roomId: string, playerName?: string): Promise { - try { - const result = await this._client.callApi('JoinRoom', { - roomId, - playerName - }); - - if (result.isSucc && result.res.success) { - this._playerId = result.res.playerId; - this._roomId = roomId; - logger.info(`成功加入房间 ${roomId}, 玩家ID: ${this._playerId}`); - return true; - } else { - logger.error('加入房间失败:', result.isSucc ? result.res.errorMsg : result.err); - return false; - } - } catch (error) { - logger.error('加入房间异常:', error); - return false; - } - } - - /** - * 同步组件到服务器 - */ - async syncComponent(entityId: number, component: T): Promise { - try { - // 获取组件的TSRPC元数据 - const metadata = getTsrpcMetadata(component.constructor); - if (!metadata) { - logger.error(`组件 ${component.constructor.name} 不支持TSRPC同步`); - return false; - } - - // 提取组件数据 - const componentData: Record = {}; - for (const [fieldName] of metadata.fields) { - componentData[fieldName] = component[fieldName]; - } - - const result = await this._client.callApi('SyncComponent', { - entityId, - componentType: metadata.componentType, - componentData, - timestamp: Date.now() - }); - - if (result.isSucc && result.res.success) { - logger.debug(`组件同步成功: Entity ${entityId}, Component ${metadata.componentType}`); - return true; - } else { - logger.error('组件同步失败:', result.isSucc ? result.res.errorMsg : result.err); - return false; - } - } catch (error) { - logger.error('同步组件异常:', error); - return false; - } - } - - /** - * 注册组件更新回调 - */ - onComponentUpdate(componentType: string, callback: (entityId: number, data: MessageData) => void) { - this._componentUpdateCallbacks.set(componentType, callback); - } - - /** - * 取消组件更新回调 - */ - offComponentUpdate(componentType: string) { - this._componentUpdateCallbacks.delete(componentType); - } - - /** - * 获取客户端状态 - */ - get isConnected(): boolean { - return this._client.isConnected; - } - - /** - * 获取玩家ID - */ - get playerId(): number | undefined { - return this._playerId; - } - - /** - * 获取房间ID - */ - get roomId(): string | undefined { - return this._roomId; - } - - /** - * 获取客户端实例 - */ - get client(): WsClient { - return this._client; - } - - /** - * 尝试重连 - */ - private async attemptReconnect(): Promise { - this._reconnectAttempts++; - const delay = Math.min(this._reconnectDelay * Math.pow(2, this._reconnectAttempts - 1), 30000); - - logger.info(`尝试重连 (${this._reconnectAttempts}/${this._maxReconnectAttempts}),${delay}ms后重试`); - this.emit('reconnecting', this._reconnectAttempts); - - setTimeout(async () => { - try { - const success = await this.connect(); - if (success) { - logger.info('重连成功'); - this.emit('reconnected'); - } else if (this._reconnectAttempts < this._maxReconnectAttempts) { - await this.attemptReconnect(); - } else { - logger.error('重连失败,已达到最大重试次数'); - this.emit('reconnectFailed'); - } - } catch (error) { - logger.error('重连异常:', error); - if (this._reconnectAttempts < this._maxReconnectAttempts) { - await this.attemptReconnect(); - } - } - }, delay); - } - - /** - * Ping服务器测试连接 - */ - public async ping(): Promise { - try { - this._lastPingTime = Date.now(); - const result = await this._client.callApi('Ping', { - timestamp: this._lastPingTime - }); - - if (result.isSucc) { - this._rtt = Date.now() - this._lastPingTime; - return this._rtt; - } else { - throw new Error(result.err?.message || 'Ping失败'); - } - } catch (error) { - logger.error('Ping服务器失败:', error); - throw error; - } - } - - /** - * 获取RTT - */ - public getRtt(): number { - return this._rtt; - } - - /** - * 获取连接状态 - */ - public getConnectionState(): ConnectionState { - return this._connectionState; - } - - /** - * 设置重连配置 - */ - public setReconnectConfig(maxAttempts: number, delay: number): void { - this._maxReconnectAttempts = maxAttempts; - this._reconnectDelay = delay; - } - - /** - * 添加事件监听器 - */ - public on(event: K, handler: NetworkEventHandlers[K]): void { - if (!this._eventHandlers.has(event)) { - this._eventHandlers.set(event, []); - } - this._eventHandlers.get(event)!.push(handler); - } - - /** - * 移除事件监听器 - */ - public off(event: K, handler: NetworkEventHandlers[K]): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - const index = handlers.indexOf(handler); - if (index !== -1) { - handlers.splice(index, 1); - } - } - } - - /** - * 触发事件 - */ - private emit(event: K, ...args: Parameters): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - handlers.forEach(handler => { - try { - (handler as (...args: Parameters) => void)(...args); - } catch (error) { - logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 批量同步组件 - */ - public async syncComponents(updates: Array<{ - entityId: number; - component: T; - }>): Promise<{ success: number; failed: number }> { - let successCount = 0; - let failedCount = 0; - - const promises = updates.map(async ({ entityId, component }) => { - try { - const success = await this.syncComponent(entityId, component); - if (success) { - successCount++; - } else { - failedCount++; - } - } catch (error) { - logger.error(`批量同步组件失败 Entity ${entityId}:`, error); - failedCount++; - } - }); - - await Promise.all(promises); - - logger.debug(`批量同步完成: 成功 ${successCount}, 失败 ${failedCount}`); - return { success: successCount, failed: failedCount }; - } - - /** - * 获取客户端统计信息 - */ - public getStats(): { - connectionState: string; - playerId?: number; - roomId?: string; - rtt: number; - reconnectAttempts: number; - maxReconnectAttempts: number; - componentCallbacks: number; - } { - return { - connectionState: this._connectionState, - playerId: this._playerId, - roomId: this._roomId, - rtt: this._rtt, - reconnectAttempts: this._reconnectAttempts, - maxReconnectAttempts: this._maxReconnectAttempts, - componentCallbacks: this._componentUpdateCallbacks.size - }; - } -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/TsrpcManager.ts b/packages/network/src/TSRPC/TsrpcManager.ts deleted file mode 100644 index 30c2b40c..00000000 --- a/packages/network/src/TSRPC/TsrpcManager.ts +++ /dev/null @@ -1,129 +0,0 @@ -/** - * TSRPC 网络管理器 - */ -import { createLogger } from '@esengine/ecs-framework'; -import { TsrpcNetworkServer } from './TsrpcServer'; -import { TsrpcNetworkClient } from './TsrpcClient'; - -const logger = createLogger('TsrpcManager'); - -export enum NetworkMode { - Server = 'server', - Client = 'client' -} - -export class TsrpcManager { - private static _instance: TsrpcManager | null = null; - private _server: TsrpcNetworkServer | null = null; - private _client: TsrpcNetworkClient | null = null; - private _mode: NetworkMode | null = null; - - private constructor() {} - - public static getInstance(): TsrpcManager { - if (!TsrpcManager._instance) { - TsrpcManager._instance = new TsrpcManager(); - } - return TsrpcManager._instance; - } - - /** - * 初始化为服务器模式 - */ - async initAsServer(port: number = 3000): Promise { - if (this._mode !== null) { - throw new Error('TSRPC管理器已经初始化'); - } - - this._mode = NetworkMode.Server; - this._server = new TsrpcNetworkServer(port); - await this._server.start(); - - logger.info(`TSRPC管理器已初始化为服务器模式,端口: ${port}`); - } - - /** - * 初始化为客户端模式 - */ - async initAsClient(serverUrl: string = 'ws://localhost:3000'): Promise { - if (this._mode !== null) { - throw new Error('TSRPC管理器已经初始化'); - } - - this._mode = NetworkMode.Client; - this._client = new TsrpcNetworkClient(serverUrl); - const connected = await this._client.connect(); - - if (connected) { - logger.info(`TSRPC管理器已初始化为客户端模式,服务器: ${serverUrl}`); - } else { - throw new Error('无法连接到TSRPC服务器'); - } - } - - /** - * 关闭网络连接 - */ - async shutdown(): Promise { - if (this._server) { - await this._server.stop(); - this._server = null; - } - - if (this._client) { - this._client.disconnect(); - this._client = null; - } - - this._mode = null; - logger.info('TSRPC管理器已关闭'); - } - - /** - * 获取服务器实例(仅服务器模式) - */ - get server(): TsrpcNetworkServer { - if (!this._server) { - throw new Error('未初始化为服务器模式或服务器未启动'); - } - return this._server; - } - - /** - * 获取客户端实例(仅客户端模式) - */ - get client(): TsrpcNetworkClient { - if (!this._client) { - throw new Error('未初始化为客户端模式或客户端未连接'); - } - return this._client; - } - - /** - * 获取当前模式 - */ - get mode(): NetworkMode | null { - return this._mode; - } - - /** - * 是否为服务器模式 - */ - get isServer(): boolean { - return this._mode === NetworkMode.Server; - } - - /** - * 是否为客户端模式 - */ - get isClient(): boolean { - return this._mode === NetworkMode.Client; - } - - /** - * 是否已初始化 - */ - get isInitialized(): boolean { - return this._mode !== null; - } -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/TsrpcServer.ts b/packages/network/src/TSRPC/TsrpcServer.ts deleted file mode 100644 index b28347ad..00000000 --- a/packages/network/src/TSRPC/TsrpcServer.ts +++ /dev/null @@ -1,698 +0,0 @@ -/** - * TSRPC 服务器 - */ -import { WsServer } from 'tsrpc'; -import { ServiceType, serviceProto } from './protocols/serviceProto'; -import { createLogger } from '@esengine/ecs-framework'; - -const logger = createLogger('TsrpcServer'); - -export class TsrpcNetworkServer { - private _server: WsServer; - private _connectedClients = new Map(); - private _isRunning: boolean = false; - private _startTime: number = 0; - private _totalConnections: number = 0; - private _maxConcurrentConnections: number = 0; - private _eventHandlers: Map = new Map(); - private _performanceStats = { - totalMessages: 0, - totalBytesTransferred: 0, - averageResponseTime: 0, - peakConnections: 0, - uptime: 0 - }; - - constructor(port: number = 3000) { - this._server = new WsServer(serviceProto, { - port: port, - // JSON兼容模式 - json: false, - // 连接日志 - logConnect: true - }); - - this.setupApi(); - this.setupEvents(); - } - - private setupApi() { - // 实现同步组件API - this._server.implementApi('SyncComponent', async (call) => { - try { - const { entityId, componentType, componentData, timestamp } = call.req; - - logger.debug(`收到组件同步: Entity ${entityId}, Component ${componentType}`); - - // 广播组件更新给其他客户端 - this.broadcastComponentUpdate(entityId, componentType, componentData, timestamp, call.conn.id); - - call.succ({ - success: true, - entityId - }); - } catch (error) { - call.error('同步组件失败', { error: (error as Error).message }); - } - }); - - // 实现加入房间API - this._server.implementApi('JoinRoom', async (call) => { - try { - const { roomId, playerName, password } = call.req; - - // 检查房间是否存在,如果不存在则自动创建 - let room = this._rooms.get(roomId); - if (!room) { - this.createRoom(roomId, { maxPlayers: 10 }); - room = this._rooms.get(roomId)!; - } - - // 检查房间是否已满 - if (room.currentPlayers >= room.maxPlayers) { - call.succ({ - success: false, - errorMsg: '房间已满' - }); - return; - } - - // 检查密码 - if (room.password && room.password !== password) { - call.succ({ - success: false, - errorMsg: '密码错误' - }); - return; - } - - const playerId = Date.now() + Math.floor(Math.random() * 1000); // 更好的ID生成 - - // 添加玩家到房间 - const playerInfo: PlayerInfo = { - playerId, - playerName, - joinedAt: Date.now() - }; - room.players.set(call.conn.id, playerInfo); - room.currentPlayers++; - - // 记录连接 - this._connectedClients.set(call.conn.id, { - playerId, - roomId, - playerName, - connId: call.conn.id - }); - - logger.info(`玩家 ${playerName} (ID: ${playerId}) 加入房间 ${roomId} (${room.currentPlayers}/${room.maxPlayers})`); - - // 通知房间内其他玩家 - this.broadcastToRoom(roomId, 'PlayerJoined', { - playerId, - playerName, - timestamp: Date.now() - }, call.conn.id); - - call.succ({ - success: true, - playerId, - roomInfo: { - roomId, - playerCount: room.currentPlayers, - maxPlayers: room.maxPlayers, - metadata: room.metadata - } - }); - } catch (error) { - call.error('加入房间失败', { error: (error as Error).message }); - } - }); - } - - private setupEvents() { - // 连接建立 - this._server.flows.postConnectFlow.push((conn) => { - logger.info(`客户端连接: ${conn.id}`); - return conn; - }); - - // 连接断开 - this._server.flows.postDisconnectFlow.push((data) => { - const client = this._connectedClients.get(data.conn.id); - if (client) { - logger.info(`客户端断开: ${client.playerName} (${data.conn.id})`); - - // 从房间中移除玩家 - const room = this._rooms.get(client.roomId); - if (room) { - room.players.delete(data.conn.id); - room.currentPlayers--; - - // 通知房间内其他玩家 - this.broadcastToRoom(client.roomId, 'PlayerLeft', { - playerId: client.playerId, - playerName: client.playerName, - timestamp: Date.now() - }); - - logger.info(`玩家 ${client.playerName} 离开房间 ${client.roomId} (${room.currentPlayers}/${room.maxPlayers})`); - - // 如果房间空了,可选择性删除房间 - if (room.currentPlayers === 0) { - logger.info(`房间 ${client.roomId} 已空,保留等待新玩家`); - // 可以选择删除空房间: this._rooms.delete(client.roomId); - } - } - - this._connectedClients.delete(data.conn.id); - } - return data; - }); - } - - /** - * 广播组件更新给其他客户端 - */ - private broadcastComponentUpdate(entityId: number, componentType: string, componentData: any, timestamp: number, excludeConnId?: string) { - const updateMsg = { - entityId, - componentType, - componentData, - timestamp - }; - - // 给所有其他客户端发送更新消息 - const targetConns = this._server.connections.filter(conn => conn.id !== excludeConnId); - this._server.broadcastMsg('ComponentUpdate', updateMsg, targetConns); - } - - /** - * 启动服务器 - */ - async start(): Promise { - await this._server.start(); - logger.info(`TSRPC服务器已启动,端口: ${this._server.options.port}`); - } - - /** - * 停止服务器 - */ - async stop(): Promise { - await this._server.stop(); - logger.info('TSRPC服务器已停止'); - } - - /** - * 获取连接的客户端数量 - */ - get clientCount(): number { - return this._connectedClients.size; - } - - /** - * 获取服务器实例 - */ - get server(): WsServer { - return this._server; - } - - /** - * 高级房间管理功能 - */ - - /** - * 创建房间 - */ - public createRoom(roomId: string, options?: { - maxPlayers?: number; - password?: string; - metadata?: Record; - }): boolean { - if (this._rooms.has(roomId)) { - return false; // 房间已存在 - } - - const room: RoomInfo = { - roomId, - maxPlayers: options?.maxPlayers || 10, - currentPlayers: 0, - players: new Map(), - created: Date.now(), - password: options?.password, - metadata: options?.metadata || {} - }; - - this._rooms.set(roomId, room); - logger.info(`创建房间: ${roomId}, 最大玩家数: ${room.maxPlayers}`); - return true; - } - - /** - * 删除房间 - */ - public removeRoom(roomId: string): boolean { - const room = this._rooms.get(roomId); - if (!room) { - return false; - } - - // 通知房间内的所有玩家 - for (const [connId, playerInfo] of room.players) { - const conn = this._server.connections.find(c => c.id === connId); - if (conn) { - this._server.broadcastMsg('RoomClosed', { roomId, reason: '房间已关闭' }, [conn]); - // 从已连接客户端列表中移除 - this._connectedClients.delete(connId); - } - } - - this._rooms.delete(roomId); - logger.info(`删除房间: ${roomId}`); - return true; - } - - /** - * 获取房间列表 - */ - public getRoomList(includePassword: boolean = false): Array<{ - roomId: string; - currentPlayers: number; - maxPlayers: number; - hasPassword: boolean; - metadata: Record; - }> { - const roomList: Array<{ - roomId: string; - currentPlayers: number; - maxPlayers: number; - hasPassword: boolean; - metadata: Record; - }> = []; - - for (const room of this._rooms.values()) { - roomList.push({ - roomId: room.roomId, - currentPlayers: room.currentPlayers, - maxPlayers: room.maxPlayers, - hasPassword: !!room.password, - metadata: room.metadata - }); - } - - return roomList; - } - - /** - * 获取房间详情 - */ - public getRoomInfo(roomId: string): RoomInfo | null { - return this._rooms.get(roomId) || null; - } - - /** - * 房间内广播消息 - */ - public broadcastToRoom(roomId: string, message: string, data: any, excludeConnId?: string): void { - const room = this._rooms.get(roomId); - if (!room) { - logger.warn(`房间不存在: ${roomId}`); - return; - } - - const targetConns = this._server.connections.filter(conn => { - return room.players.has(conn.id) && conn.id !== excludeConnId; - }); - - if (targetConns.length > 0) { - // 使用对应的房间消息类型 - switch (message) { - case 'PlayerJoined': - this._server.broadcastMsg('PlayerJoined', data, targetConns); - break; - case 'PlayerLeft': - this._server.broadcastMsg('PlayerLeft', data, targetConns); - break; - case 'PlayerKicked': - this._server.broadcastMsg('PlayerKicked', data, targetConns); - break; - default: - // 其他消息使用ComponentUpdate作为通用消息 - this._server.broadcastMsg('ComponentUpdate', { - entityId: 0, - componentType: message, - componentData: data, - timestamp: Date.now() - }, targetConns); - break; - } - } - } - - /** - * 踢出玩家 - */ - public kickPlayer(roomId: string, playerId: number, reason?: string): boolean { - const room = this._rooms.get(roomId); - if (!room) { - return false; - } - - let targetConnId: string | null = null; - for (const [connId, playerInfo] of room.players) { - if (playerInfo.playerId === playerId) { - targetConnId = connId; - break; - } - } - - if (!targetConnId) { - return false; - } - - // 从房间中移除玩家 - room.players.delete(targetConnId); - room.currentPlayers--; - - // 从已连接客户端列表中移除 - this._connectedClients.delete(targetConnId); - - // 通知被踢出的玩家 - const conn = this._server.connections.find(c => c.id === targetConnId); - if (conn) { - this.broadcastToRoom(roomId, 'PlayerKicked', { - playerId, - reason: reason || '被踢出房间' - }); - - // 断开连接 - conn.close(reason); - } - - logger.info(`踢出玩家: ${playerId} 从房间 ${roomId}, 原因: ${reason || '未指定'}`); - return true; - } - - /** - * 设置房间元数据 - */ - public setRoomMetadata(roomId: string, metadata: Record): boolean { - const room = this._rooms.get(roomId); - if (!room) { - return false; - } - - room.metadata = { ...room.metadata, ...metadata }; - logger.debug(`更新房间元数据: ${roomId}`, metadata); - return true; - } - - /** - * 房间状态统计 - */ - public getRoomStats(): { - totalRooms: number; - totalPlayers: number; - averagePlayersPerRoom: number; - roomDetails: Array<{ - roomId: string; - players: number; - maxPlayers: number; - uptime: number; - }>; - } { - let totalPlayers = 0; - const roomDetails: Array<{ - roomId: string; - players: number; - maxPlayers: number; - uptime: number; - }> = []; - - for (const room of this._rooms.values()) { - totalPlayers += room.currentPlayers; - roomDetails.push({ - roomId: room.roomId, - players: room.currentPlayers, - maxPlayers: room.maxPlayers, - uptime: Date.now() - room.created - }); - } - - return { - totalRooms: this._rooms.size, - totalPlayers, - averagePlayersPerRoom: this._rooms.size > 0 ? totalPlayers / this._rooms.size : 0, - roomDetails - }; - } - - // 添加房间映射和房间信息接口 - private _rooms = new Map(); - - /** - * 启动服务器 - */ - public async start(): Promise { - try { - await this._server.start(); - this._isRunning = true; - this._startTime = Date.now(); - logger.info(`TSRPC服务器已启动,端口: ${this._server.options.port}`); - this.emit('started'); - } catch (error) { - logger.error('启动TSRPC服务器失败:', error); - this.emit('startError', error); - throw error; - } - } - - /** - * 停止服务器 - */ - public async stop(): Promise { - try { - if (this._isRunning) { - await this._server.stop(); - this._isRunning = false; - this._startTime = 0; - this._connectedClients.clear(); - this._rooms.clear(); - logger.info('TSRPC服务器已停止'); - this.emit('stopped'); - } - } catch (error) { - logger.error('停止TSRPC服务器失败:', error); - this.emit('stopError', error); - throw error; - } - } - - /** - * 获取服务器统计信息 - */ - public getStats(): { - isRunning: boolean; - uptime: number; - connectedClients: number; - totalConnections: number; - maxConcurrentConnections: number; - rooms: number; - performance: typeof this._performanceStats; - } { - this._performanceStats.uptime = this._isRunning ? Date.now() - this._startTime : 0; - this._performanceStats.peakConnections = Math.max( - this._maxConcurrentConnections, - this._connectedClients.size - ); - - return { - isRunning: this._isRunning, - uptime: this._performanceStats.uptime, - connectedClients: this._connectedClients.size, - totalConnections: this._totalConnections, - maxConcurrentConnections: this._maxConcurrentConnections, - rooms: this._rooms.size, - performance: { ...this._performanceStats } - }; - } - - /** - * 广播消息给所有客户端 - */ - public broadcastToAll(msgName: string, msg: any, excludeConnId?: string): void { - for (const [connId, clientInfo] of this._connectedClients) { - if (excludeConnId && connId === excludeConnId) { - continue; - } - try { - this._server.sendMsg(clientInfo.conn, msgName, msg); - this._performanceStats.totalMessages++; - } catch (error) { - logger.error(`向客户端 ${connId} 广播消息失败:`, error); - } - } - } - - /** - * 向房间内广播消息 - */ - public broadcastToRoom(roomId: string, msgName: string, msg: any, excludeConnId?: string): void { - const room = this._rooms.get(roomId); - if (!room) { - logger.warn(`房间 ${roomId} 不存在`); - return; - } - - for (const [connId] of room.players) { - if (excludeConnId && connId === excludeConnId) { - continue; - } - - const clientInfo = this._connectedClients.get(connId); - if (clientInfo) { - try { - this._server.sendMsg(clientInfo.conn, msgName, msg); - this._performanceStats.totalMessages++; - } catch (error) { - logger.error(`向房间 ${roomId} 客户端 ${connId} 广播消息失败:`, error); - } - } - } - } - - /** - * 添加事件监听器 - */ - public on(event: string, handler: Function): void { - if (!this._eventHandlers.has(event)) { - this._eventHandlers.set(event, []); - } - this._eventHandlers.get(event)!.push(handler); - } - - /** - * 移除事件监听器 - */ - public off(event: string, handler: Function): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - const index = handlers.indexOf(handler); - if (index !== -1) { - handlers.splice(index, 1); - } - } - } - - /** - * 触发事件 - */ - private emit(event: string, ...args: any[]): void { - const handlers = this._eventHandlers.get(event); - if (handlers) { - handlers.forEach(handler => { - try { - handler(...args); - } catch (error) { - logger.error(`事件处理器错误 (${event}):`, error); - } - }); - } - } - - /** - * 获取房间列表 - */ - public getRooms(): Array<{ - roomId: string; - currentPlayers: number; - maxPlayers: number; - created: number; - hasPassword: boolean; - }> { - return Array.from(this._rooms.values()).map(room => ({ - roomId: room.roomId, - currentPlayers: room.currentPlayers, - maxPlayers: room.maxPlayers, - created: room.created, - hasPassword: !!room.password - })); - } - - /** - * 强制踢出玩家 - */ - public kickPlayer(connId: string, reason?: string): boolean { - const clientInfo = this._connectedClients.get(connId); - if (clientInfo) { - try { - // 发送踢出通知 - this._server.sendMsg(clientInfo.conn, 'PlayerKicked', { - reason: reason || '被服务器踢出' - }); - - // 断开连接 - clientInfo.conn.close(); - logger.info(`踢出玩家 ${connId}: ${reason || '未指定原因'}`); - return true; - } catch (error) { - logger.error(`踢出玩家 ${connId} 失败:`, error); - return false; - } - } - return false; - } - - /** - * 获取在线玩家列表 - */ - public getOnlinePlayers(): Array<{ - connId: string; - playerId?: number; - playerName?: string; - joinedAt: number; - roomId?: string; - }> { - const players: Array<{ - connId: string; - playerId?: number; - playerName?: string; - joinedAt: number; - roomId?: string; - }> = []; - - for (const [connId, clientInfo] of this._connectedClients) { - players.push({ - connId, - playerId: clientInfo.playerId, - playerName: clientInfo.playerName, - joinedAt: clientInfo.joinedAt, - roomId: clientInfo.roomId - }); - } - - return players; - } -} - -/** - * 房间信息接口 - */ -interface RoomInfo { - roomId: string; - maxPlayers: number; - currentPlayers: number; - players: Map; // connId -> PlayerInfo - created: number; - password?: string; - metadata: Record; -} - -/** - * 玩家信息接口 - */ -interface PlayerInfo { - playerId: number; - playerName?: string; - joinedAt: number; -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/index.ts b/packages/network/src/TSRPC/index.ts deleted file mode 100644 index da234eda..00000000 --- a/packages/network/src/TSRPC/index.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * TSRPC 网络通信系统导出 - */ - -// 协议定义 -export * from './protocols/serviceProto'; -export * from './protocols/PtlSyncComponent'; -export * from './protocols/PtlJoinRoom'; -export * from './protocols/MsgComponentUpdate'; - -// 客户端和服务器 -export { TsrpcNetworkServer } from './TsrpcServer'; -export { TsrpcNetworkClient } from './TsrpcClient'; -export { TsrpcManager, NetworkMode } from './TsrpcManager'; \ No newline at end of file diff --git a/packages/network/src/TSRPC/protocols/MsgComponentUpdate.ts b/packages/network/src/TSRPC/protocols/MsgComponentUpdate.ts deleted file mode 100644 index cc793e1b..00000000 --- a/packages/network/src/TSRPC/protocols/MsgComponentUpdate.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * 组件更新消息(服务器广播给客户端) - */ -export interface MsgComponentUpdate { - entityId: number; - componentType: string; - componentData: any; - timestamp: number; -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/protocols/PtlJoinRoom.ts b/packages/network/src/TSRPC/protocols/PtlJoinRoom.ts deleted file mode 100644 index 21084351..00000000 --- a/packages/network/src/TSRPC/protocols/PtlJoinRoom.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 加入房间协议 - */ -export interface ReqJoinRoom { - roomId: string; - playerName?: string; - password?: string; -} - -export interface ResJoinRoom { - success: boolean; - playerId?: number; - roomInfo?: { - roomId: string; - playerCount: number; - maxPlayers: number; - metadata?: Record; - }; - errorMsg?: string; -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/protocols/PtlSyncComponent.ts b/packages/network/src/TSRPC/protocols/PtlSyncComponent.ts deleted file mode 100644 index 5378aa50..00000000 --- a/packages/network/src/TSRPC/protocols/PtlSyncComponent.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 组件同步协议定义 - */ - -// 同步组件数据请求 -export interface ReqSyncComponent { - entityId: number; - componentType: string; - componentData: any; - timestamp: number; -} - -// 同步组件数据响应 -export interface ResSyncComponent { - success: boolean; - entityId: number; - errorMsg?: string; -} \ No newline at end of file diff --git a/packages/network/src/TSRPC/protocols/serviceProto.ts b/packages/network/src/TSRPC/protocols/serviceProto.ts deleted file mode 100644 index 11396f46..00000000 --- a/packages/network/src/TSRPC/protocols/serviceProto.ts +++ /dev/null @@ -1,78 +0,0 @@ -/** - * TSRPC 服务协议定义 - */ -import { ServiceProto } from 'tsrpc-proto'; - -// API 协议 -export interface ServiceType { - api: { - 'SyncComponent': { - req: import('./PtlSyncComponent').ReqSyncComponent; - res: import('./PtlSyncComponent').ResSyncComponent; - }; - 'JoinRoom': { - req: import('./PtlJoinRoom').ReqJoinRoom; - res: import('./PtlJoinRoom').ResJoinRoom; - }; - 'Ping': { - req: { timestamp: number }; - res: { timestamp: number; serverTime: number }; - }; - }; - msg: { - 'ComponentUpdate': import('./MsgComponentUpdate').MsgComponentUpdate; - 'RoomClosed': { roomId: string; reason: string }; - 'PlayerJoined': { playerId: number; playerName?: string; timestamp: number }; - 'PlayerLeft': { playerId: number; playerName?: string; timestamp: number }; - 'PlayerKicked': { playerId: number; reason: string }; - }; -} - -export const serviceProto: ServiceProto = { - "version": 1, - "services": [ - // API 服务 - { - "id": 0, - "name": "SyncComponent", - "type": "api" - }, - { - "id": 1, - "name": "JoinRoom", - "type": "api" - }, - { - "id": 2, - "name": "Ping", - "type": "api" - }, - // 消息服务 - { - "id": 3, - "name": "ComponentUpdate", - "type": "msg" - }, - { - "id": 4, - "name": "RoomClosed", - "type": "msg" - }, - { - "id": 5, - "name": "PlayerJoined", - "type": "msg" - }, - { - "id": 6, - "name": "PlayerLeft", - "type": "msg" - }, - { - "id": 7, - "name": "PlayerKicked", - "type": "msg" - } - ], - "types": {} -}; \ No newline at end of file diff --git a/packages/network/src/constants/NetworkConstants.ts b/packages/network/src/constants/NetworkConstants.ts deleted file mode 100644 index 7e7db2f7..00000000 --- a/packages/network/src/constants/NetworkConstants.ts +++ /dev/null @@ -1,211 +0,0 @@ -/** - * 网络库常量定义 - */ - -/** - * 网络配置常量 - */ -export const NETWORK_CONFIG = { - /** 默认连接超时时间 (ms) */ - DEFAULT_CONNECTION_TIMEOUT: 10000, - /** 默认心跳间隔 (ms) */ - DEFAULT_HEARTBEAT_INTERVAL: 5000, - /** 默认心跳超时时间 (ms) */ - DEFAULT_HEARTBEAT_TIMEOUT: 10000, - /** 默认最大重连次数 */ - DEFAULT_MAX_RECONNECT_ATTEMPTS: 5, - /** 默认重连延迟 (ms) */ - DEFAULT_RECONNECT_DELAY: 1000, - /** 默认最大连续丢包数 */ - DEFAULT_MAX_CONSECUTIVE_LOSS: 3, - /** 默认心跳包大小 (bytes) */ - DEFAULT_HEARTBEAT_PACKET_SIZE: 64, - /** 默认RTT历史记录大小 */ - DEFAULT_RTT_HISTORY_SIZE: 50 -} as const; - -/** - * SyncVar配置常量 - */ -export const SYNCVAR_CONFIG = { - /** 默认SyncVar缓存超时 (ms) */ - DEFAULT_CACHE_TIMEOUT: 5000, - /** 默认同步频率限制 (ms) */ - DEFAULT_THROTTLE_MS: 50, - /** 最大字段编号 */ - MAX_FIELD_NUMBER: 65535, - /** 最小字段编号 */ - MIN_FIELD_NUMBER: 1 -} as const; - -/** - * 消息配置常量 - */ -export const MESSAGE_CONFIG = { - /** 默认消息序列号范围 */ - MAX_SEQUENCE_NUMBER: 4294967295, // 2^32 - 1 - /** 消息头部最大大小 (bytes) */ - MAX_HEADER_SIZE: 256, - /** 消息体最大大小 (bytes) */ - MAX_PAYLOAD_SIZE: 1048576, // 1MB - /** 默认消息超时时间 (ms) */ - DEFAULT_MESSAGE_TIMEOUT: 30000, - /** 批处理消息最大数量 */ - MAX_BATCH_SIZE: 100 -} as const; - -/** - * 序列化配置常量 - */ -export const SERIALIZATION_CONFIG = { - /** 默认压缩级别 */ - DEFAULT_COMPRESSION_LEVEL: 6, - /** 启用压缩的最小数据大小 (bytes) */ - MIN_COMPRESSION_SIZE: 1024, - /** 序列化缓冲区初始大小 (bytes) */ - INITIAL_BUFFER_SIZE: 4096, - /** 序列化缓冲区最大大小 (bytes) */ - MAX_BUFFER_SIZE: 16777216 // 16MB -} as const; - -/** - * TSRPC配置常量 - */ -export const TSRPC_CONFIG = { - /** 默认服务器URL */ - DEFAULT_SERVER_URL: 'ws://localhost:3000', - /** 默认超时时间 (ms) */ - DEFAULT_TIMEOUT: 30000, - /** 默认心跳配置 */ - DEFAULT_HEARTBEAT: { - interval: 10000, - timeout: 5000 - }, - /** 默认连接池配置 */ - DEFAULT_POOL_CONFIG: { - minConnections: 1, - maxConnections: 10, - idleTimeout: 300000 // 5分钟 - } -} as const; - -/** - * 权限配置常量 - */ -export const AUTHORITY_CONFIG = { - /** 权限优先级范围 */ - MIN_PRIORITY: 0, - MAX_PRIORITY: 1000, - /** 默认权限规则优先级 */ - DEFAULT_RULE_PRIORITY: 50, - /** 服务端权限优先级 */ - SERVER_AUTHORITY_PRIORITY: 100, - /** 网络身份权限优先级 */ - NETWORK_IDENTITY_PRIORITY: 80, - /** 组件自定义权限优先级 */ - COMPONENT_CUSTOM_PRIORITY: 60, - /** 实体所有者权限优先级 */ - ENTITY_OWNER_PRIORITY: 50, - /** 默认拒绝规则优先级 */ - DEFAULT_DENY_PRIORITY: 0 -} as const; - -/** - * 性能监控配置常量 - */ -export const PERFORMANCE_CONFIG = { - /** 性能统计收集间隔 (ms) */ - STATS_COLLECTION_INTERVAL: 1000, - /** 性能数据保留时间 (ms) */ - STATS_RETENTION_TIME: 300000, // 5分钟 - /** 警告阈值 */ - WARNING_THRESHOLDS: { - /** RTT警告阈值 (ms) */ - RTT: 200, - /** 丢包率警告阈值 */ - PACKET_LOSS: 0.05, // 5% - /** 抖动警告阈值 (ms) */ - JITTER: 50, - /** CPU使用率警告阈值 */ - CPU_USAGE: 0.8, // 80% - /** 内存使用率警告阈值 */ - MEMORY_USAGE: 0.8 // 80% - } -} as const; - -/** - * 错误代码常量 - */ -export const ERROR_CODES = { - // 连接错误 (1000-1099) - CONNECTION_FAILED: 1000, - CONNECTION_TIMEOUT: 1001, - CONNECTION_REFUSED: 1002, - CONNECTION_LOST: 1003, - - // 序列化错误 (1100-1199) - SERIALIZATION_FAILED: 1100, - DESERIALIZATION_FAILED: 1101, - INVALID_DATA_FORMAT: 1102, - - // SyncVar错误 (1200-1299) - SYNCVAR_INIT_FAILED: 1200, - SYNCVAR_METADATA_MISSING: 1201, - SYNCVAR_TYPE_MISMATCH: 1202, - SYNCVAR_AUTHORITY_DENIED: 1203, - - // 消息错误 (1300-1399) - MESSAGE_TIMEOUT: 1300, - MESSAGE_TOO_LARGE: 1301, - MESSAGE_INVALID_TYPE: 1302, - MESSAGE_SEQUENCE_ERROR: 1303, - - // TSRPC错误 (1400-1499) - TSRPC_CALL_FAILED: 1400, - TSRPC_METHOD_NOT_FOUND: 1401, - TSRPC_INVALID_PARAMS: 1402, - TSRPC_SERVER_ERROR: 1403, - - // 权限错误 (1500-1599) - PERMISSION_DENIED: 1500, - INVALID_AUTHORITY: 1501, - AUTHORITY_CHECK_FAILED: 1502 -} as const; - -/** - * 日志级别常量 - */ -export const LOG_LEVELS = { - ERROR: 0, - WARN: 1, - INFO: 2, - DEBUG: 3, - TRACE: 4 -} as const; - -/** - * 环境类型常量 - */ -export const ENVIRONMENTS = { - SERVER: 'server', - CLIENT: 'client', - HYBRID: 'hybrid' -} as const; - -/** - * 网络事件名称常量 - */ -export const NETWORK_EVENTS = { - CONNECTED: 'connected', - DISCONNECTED: 'disconnected', - CONNECTING: 'connecting', - RECONNECTING: 'reconnecting', - RECONNECTED: 'reconnected', - CONNECT_ERROR: 'connectError', - RECONNECT_FAILED: 'reconnectFailed', - MESSAGE: 'message', - ERROR: 'error', - HEARTBEAT: 'heartbeat', - PERFORMANCE_WARNING: 'performanceWarning' -} as const; - diff --git a/packages/network/src/decorators/ClientRpc.ts b/packages/network/src/decorators/ClientRpc.ts new file mode 100644 index 00000000..48c66943 --- /dev/null +++ b/packages/network/src/decorators/ClientRpc.ts @@ -0,0 +1,107 @@ +/** + * ClientRpc 装饰器 + * + * 用于标记可以从服务端调用的客户端方法 + * 只能在服务端调用,会向指定客户端或所有客户端发送RPC消息 + */ + +import 'reflect-metadata'; +import { RpcMetadata } from '../types/NetworkTypes'; + +/** + * ClientRpc 装饰器选项 + */ +export interface ClientRpcOptions { + /** 是否需要权威验证,默认为 true */ + requiresAuthority?: boolean; +} + +/** + * 存储 ClientRpc 元数据的 Symbol + */ +export const CLIENT_RPC_METADATA_KEY = Symbol('client_rpc_metadata'); + +/** + * ClientRpc 装饰器 + * + * @param options 装饰器选项 + * @returns 方法装饰器函数 + * + * @example + * ```typescript + * class PlayerController extends NetworkBehaviour { + * @ClientRpc() + * public showEffect(effectType: string, position: Vector3): void { + * // 这个方法会在客户端执行 + * console.log(`显示特效: ${effectType} at ${position}`); + * } + * + * private triggerEffect(): void { + * if (this.isServer) { + * // 服务端调用,会发送到所有客户端 + * this.showEffect('explosion', new Vector3(0, 0, 0)); + * } + * } + * } + * ``` + */ +export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator { + return function (target: any, 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.constructor); + if (!metadata) { + metadata = []; + Reflect.defineMetadata(CLIENT_RPC_METADATA_KEY, metadata, target.constructor); + } + + // 添加当前方法的元数据 + const rpcMetadata: RpcMetadata = { + methodName: propertyKey, + isClientRpc: true, + requiresAuthority: options.requiresAuthority !== false // 默认为 true + }; + + metadata.push(rpcMetadata); + + // 保存原始方法 + const originalMethod = descriptor.value; + + // 包装方法以添加网络功能 + descriptor.value = function (this: any, ...args: any[]) { + // 如果在服务端调用,发送RPC消息 + if (this.isServer) { + if (rpcMetadata.requiresAuthority && !this.hasAuthority) { + console.warn(`[ClientRpc] 权限不足,无法调用: ${propertyKey}`); + return; + } + + // 发送客户端RPC + this.sendClientRpc?.(propertyKey, args); + } else if (this.isClient) { + // 在客户端直接执行原始方法 + return originalMethod.apply(this, args); + } + }; + + return descriptor; + }; +} + +/** + * 获取类的 ClientRpc 元数据 + */ +export function getClientRpcMetadata(target: any): RpcMetadata[] { + return Reflect.getMetadata(CLIENT_RPC_METADATA_KEY, target) || []; +} + +/** + * 检查方法是否为 ClientRpc + */ +export function isClientRpc(target: any, methodName: string): boolean { + const metadata = getClientRpcMetadata(target); + return metadata.some(m => m.methodName === methodName); +} \ No newline at end of file diff --git a/packages/network/src/decorators/Command.ts b/packages/network/src/decorators/Command.ts new file mode 100644 index 00000000..ef89cda3 --- /dev/null +++ b/packages/network/src/decorators/Command.ts @@ -0,0 +1,108 @@ +/** + * Command 装饰器 + * + * 用于标记可以从客户端调用的服务端方法 + * 只能在客户端调用,会向服务端发送命令消息 + */ + +import 'reflect-metadata'; +import { RpcMetadata } from '../types/NetworkTypes'; + +/** + * Command 装饰器选项 + */ +export interface CommandOptions { + /** 是否需要权威验证,默认为 true */ + requiresAuthority?: boolean; +} + +/** + * 存储 Command 元数据的 Symbol + */ +export const COMMAND_METADATA_KEY = Symbol('command_metadata'); + +/** + * Command 装饰器 + * + * @param options 装饰器选项 + * @returns 方法装饰器函数 + * + * @example + * ```typescript + * class PlayerController extends NetworkBehaviour { + * @Command() + * public movePlayer(direction: Vector3): void { + * // 这个方法会在服务端执行 + * console.log(`玩家移动: ${direction}`); + * // 更新玩家位置逻辑... + * } + * + * private handleInput(): void { + * if (this.isClient && this.hasAuthority) { + * // 客户端调用,会发送到服务端 + * this.movePlayer(new Vector3(1, 0, 0)); + * } + * } + * } + * ``` + */ +export function Command(options: CommandOptions = {}): MethodDecorator { + return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) { + if (typeof propertyKey !== 'string') { + throw new Error('Command can only be applied to string method names'); + } + + // 获取或创建元数据数组 + let metadata: RpcMetadata[] = Reflect.getMetadata(COMMAND_METADATA_KEY, target.constructor); + if (!metadata) { + metadata = []; + Reflect.defineMetadata(COMMAND_METADATA_KEY, metadata, target.constructor); + } + + // 添加当前方法的元数据 + const rpcMetadata: RpcMetadata = { + methodName: propertyKey, + isClientRpc: false, + requiresAuthority: options.requiresAuthority !== false // 默认为 true + }; + + metadata.push(rpcMetadata); + + // 保存原始方法 + const originalMethod = descriptor.value; + + // 包装方法以添加网络功能 + descriptor.value = function (this: any, ...args: any[]) { + // 如果在客户端调用,发送命令消息 + if (this.isClient) { + if (rpcMetadata.requiresAuthority && !this.hasAuthority) { + console.warn(`[Command] 权限不足,无法调用: ${propertyKey}`); + return; + } + + // 发送命令到服务端 + this.sendCommand?.(propertyKey, args); + } else if (this.isServer) { + // 在服务端直接执行原始方法 + return originalMethod.apply(this, args); + } + }; + + return descriptor; + }; +} + +/** + * 获取类的 Command 元数据 + */ +export function getCommandMetadata(target: any): RpcMetadata[] { + return Reflect.getMetadata(COMMAND_METADATA_KEY, target) || []; +} + +/** + * 检查方法是否为 Command + */ +export function isCommand(target: any, methodName: string): boolean { + const metadata = getCommandMetadata(target); + return metadata.some(m => m.methodName === methodName); +} \ No newline at end of file diff --git a/packages/network/src/decorators/SyncVar.ts b/packages/network/src/decorators/SyncVar.ts new file mode 100644 index 00000000..6a6ad97a --- /dev/null +++ b/packages/network/src/decorators/SyncVar.ts @@ -0,0 +1,116 @@ +/** + * SyncVar 装饰器 + * + * 用于标记需要在网络间自动同步的属性 + * 当属性值发生变化时,会自动同步到其他网络端 + */ + +import 'reflect-metadata'; +import { SyncVarMetadata } from '../types/NetworkTypes'; + +/** + * SyncVar 装饰器选项 + */ +export interface SyncVarOptions { + /** 是否仅权威端可修改,默认为 true */ + authorityOnly?: boolean; + /** 变化回调函数名,属性变化时会调用此方法 */ + onChanged?: string; +} + +/** + * 存储 SyncVar 元数据的 Symbol + */ +export const SYNCVAR_METADATA_KEY = Symbol('syncvar_metadata'); + +/** + * SyncVar 装饰器 + * + * @param options 装饰器选项 + * @returns 属性装饰器函数 + * + * @example + * ```typescript + * class PlayerController extends NetworkBehaviour { + * @SyncVar({ onChanged: 'onHealthChanged' }) + * public health: number = 100; + * + * @SyncVar({ authorityOnly: false }) + * public playerName: string = ''; + * + * private onHealthChanged(oldValue: number, newValue: number): void { + * console.log(`Health changed from ${oldValue} to ${newValue}`); + * } + * } + * ``` + */ +export function SyncVar(options: SyncVarOptions = {}): PropertyDecorator { + return function (target: any, propertyKey: string | symbol) { + if (typeof propertyKey !== 'string') { + throw new Error('SyncVar can only be applied to string property keys'); + } + + // 获取或创建元数据数组 + let metadata: SyncVarMetadata[] = Reflect.getMetadata(SYNCVAR_METADATA_KEY, target.constructor); + if (!metadata) { + metadata = []; + Reflect.defineMetadata(SYNCVAR_METADATA_KEY, metadata, target.constructor); + } + + // 添加当前属性的元数据 + const syncVarMetadata: SyncVarMetadata = { + propertyName: propertyKey, + authorityOnly: options.authorityOnly !== false, // 默认为 true + onChanged: options.onChanged + }; + + metadata.push(syncVarMetadata); + + // 创建属性的内部存储 + const internalKey = `_${propertyKey}`; + const changeKey = `_${propertyKey}_changed`; + + // 重新定义属性的 getter 和 setter + Object.defineProperty(target, propertyKey, { + get: function (this: any) { + return this[internalKey]; + }, + set: function (this: any, newValue: any) { + const oldValue = this[internalKey]; + + // 检查值是否真的发生了变化 + if (oldValue === newValue) { + return; + } + + this[internalKey] = newValue; + this[changeKey] = true; + + // 调用变化回调 + if (options.onChanged && typeof this[options.onChanged] === 'function') { + this[options.onChanged](oldValue, newValue); + } + + // 通知同步管理器 + this.notifySyncVarChanged?.(propertyKey, oldValue, newValue); + }, + enumerable: true, + configurable: true + }); + }; +} + +/** + * 获取类的 SyncVar 元数据 + */ +export function getSyncVarMetadata(target: any): SyncVarMetadata[] { + return Reflect.getMetadata(SYNCVAR_METADATA_KEY, target) || []; +} + +/** + * 检查属性是否为 SyncVar + */ +export function isSyncVar(target: any, propertyName: string): boolean { + const metadata = getSyncVarMetadata(target); + return metadata.some(m => m.propertyName === propertyName); +} \ No newline at end of file diff --git a/packages/network/src/index.ts b/packages/network/src/index.ts index 95449984..7b76def3 100644 --- a/packages/network/src/index.ts +++ b/packages/network/src/index.ts @@ -1,29 +1,27 @@ /** - * ECS Framework Network Plugin - 网络插件 + * ECS Network Library * - * 为ECS框架提供完整的网络同步和多人游戏功能 - * 支持客户端-服务端模式 + * 基于 ECS 架构和 TSRPC 的网络同步库 + * 提供简洁易用的网络组件和同步机制 */ -// 核心网络管理 -export * from './Core'; +// 核心组件 +export { NetworkManager } from './NetworkManager'; +export { NetworkIdentity } from './NetworkIdentity'; +export { NetworkBehaviour } from './NetworkBehaviour'; -// 消息系统 -export * from './Messaging'; +// 装饰器 +export { SyncVar } from './decorators/SyncVar'; +export { ClientRpc } from './decorators/ClientRpc'; +export { Command } from './decorators/Command'; -// SyncVar同步变量系统 -export * from './SyncVar'; +// 核心管理器 +export { SyncVarManager } from './core/SyncVarManager'; +export { RpcManager } from './core/RpcManager'; +export { NetworkRegistry } from './core/NetworkRegistry'; -// 网络组件基类 -export { NetworkComponent } from './NetworkComponent'; -export { INetworkSyncable } from './INetworkSyncable'; -export { NetworkRole } from './NetworkRole'; +// 传输层 +export * from './transport'; -// TSRPC网络通信系统 -export * from './TSRPC'; - -// 装饰器序列化系统(用于组件同步) -export * from './Serialization'; - -// 快照系统(帧同步) -export * from './Snapshot'; \ No newline at end of file +// 类型定义 +export * from './types/NetworkTypes'; \ No newline at end of file diff --git a/packages/network/src/transport/TsrpcClient.ts b/packages/network/src/transport/TsrpcClient.ts new file mode 100644 index 00000000..3d1aa24c --- /dev/null +++ b/packages/network/src/transport/TsrpcClient.ts @@ -0,0 +1,431 @@ +/** + * TSRPC 客户端传输层 + * + * 封装TSRPC客户端功能,提供服务端连接和消息收发 + */ + +import { WsClient } from 'tsrpc'; +import { createLogger } from '@esengine/ecs-framework'; +import { serviceProto, ServiceType } from './protocols/serviceProto'; +import { NetworkConfig, NetworkMessage } from '../types/NetworkTypes'; +import { + ReqJoinRoom, ResJoinRoom, + ReqServerStatus, ResServerStatus, + ReqPing, ResPing, + MsgNetworkMessage, + MsgSyncVar, + MsgRpcCall, + MsgNetworkObjectSpawn, + MsgNetworkObjectDespawn, + MsgClientDisconnected, + MsgAuthorityChange +} from './protocols/NetworkProtocols'; + +const logger = createLogger('TsrpcClient'); + +/** + * 连接状态 + */ +type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'; + +/** + * TSRPC客户端包装器 + */ +export class TsrpcClient { + private client: WsClient | null = null; + private config: NetworkConfig; + private connectionState: ConnectionState = 'disconnected'; + private clientId: number = 0; + private roomId: string = ''; + private reconnectAttempts: number = 0; + private maxReconnectAttempts: number = 5; + private reconnectInterval: number = 2000; + private heartbeatInterval: NodeJS.Timeout | null = null; + + /** 统计信息 */ + private stats = { + messagesSent: 0, + messagesReceived: 0, + bytesSent: 0, + bytesReceived: 0, + latency: 0 + }; + + /** 事件处理器 */ + public onConnected?: () => void; + public onDisconnected?: (reason?: string) => void; + public onReconnecting?: () => void; + public onMessage?: (message: NetworkMessage) => void; + public onError?: (error: Error) => void; + + constructor(config: NetworkConfig) { + this.config = config; + } + + /** + * 连接到服务端 + */ + public async connect(): Promise { + if (this.connectionState !== 'disconnected') { + throw new Error('客户端已连接或正在连接中'); + } + + this.connectionState = 'connecting'; + + try { + // 创建WebSocket客户端 + this.client = new WsClient(serviceProto, { + server: `ws://${this.config.host}:${this.config.port}`, + // 自动重连配置 + heartbeat: { + interval: 30000, + timeout: 5000 + } + }); + + this.setupEventHandlers(); + + // 连接到服务端 + const connectResult = await this.client.connect(); + if (!connectResult.isSucc) { + throw new Error(`连接失败: ${connectResult.errMsg}`); + } + + // 加入房间 + const joinResult = await this.client.callApi('network/JoinRoom', { + roomId: this.config.roomId, + clientInfo: { + version: '1.0.0', + platform: typeof window !== 'undefined' ? 'browser' : 'node' + } + }); + + if (!joinResult.isSucc) { + throw new Error(`加入房间失败: ${joinResult.err.message}`); + } + + this.clientId = joinResult.res.clientId; + this.roomId = joinResult.res.roomId; + this.connectionState = 'connected'; + this.reconnectAttempts = 0; + + // 启动心跳 + this.startHeartbeat(); + + logger.info(`连接成功,客户端ID: ${this.clientId}, 房间: ${this.roomId}`); + this.onConnected?.(); + + } catch (error) { + this.connectionState = 'disconnected'; + logger.error('连接服务端失败:', error); + this.onError?.(error as Error); + throw error; + } + } + + /** + * 断开连接 + */ + public async disconnect(): Promise { + if (this.connectionState === 'disconnected') { + return; + } + + this.connectionState = 'disconnected'; + this.stopHeartbeat(); + + if (this.client) { + await this.client.disconnect(); + this.client = null; + } + + logger.info('客户端已断开连接'); + this.onDisconnected?.(); + } + + /** + * 发送消息到服务端 + */ + public async sendMessage(message: NetworkMessage): Promise { + if (!this.isConnected()) { + throw new Error('客户端未连接'); + } + + try { + const tsrpcMessage: MsgNetworkMessage = { + type: message.type as 'syncvar' | 'rpc', + networkId: message.networkId, + data: message.data, + timestamp: message.timestamp + }; + + await this.client!.sendMsg('network/NetworkMessage', tsrpcMessage); + this.stats.messagesSent++; + + logger.debug(`发送消息: ${message.type}, 网络ID: ${message.networkId}`); + } catch (error) { + logger.error('发送消息失败:', error); + throw error; + } + } + + /** + * 发送SyncVar同步消息 + */ + public async sendSyncVar( + networkId: number, + componentType: string, + propertyName: string, + value: any + ): Promise { + if (!this.isConnected()) { + throw new Error('客户端未连接'); + } + + try { + const message: MsgSyncVar = { + networkId, + componentType, + propertyName, + value, + timestamp: Date.now() + }; + + await this.client!.sendMsg('network/SyncVar', message); + this.stats.messagesSent++; + + logger.debug(`发送SyncVar: ${componentType}.${propertyName} = ${value}`); + } catch (error) { + logger.error('发送SyncVar失败:', error); + throw error; + } + } + + /** + * 发送RPC调用消息 + */ + public async sendRpcCall( + networkId: number, + componentType: string, + methodName: string, + args: any[], + isClientRpc: boolean + ): Promise { + if (!this.isConnected()) { + throw new Error('客户端未连接'); + } + + try { + const message: MsgRpcCall = { + networkId, + componentType, + methodName, + args, + isClientRpc, + timestamp: Date.now() + }; + + await this.client!.sendMsg('network/RpcCall', message); + this.stats.messagesSent++; + + logger.debug(`发送RPC: ${componentType}.${methodName}(${isClientRpc ? 'ClientRpc' : 'Command'})`); + } catch (error) { + logger.error('发送RPC失败:', error); + throw error; + } + } + + /** + * 查询服务端状态 + */ + public async getServerStatus(): Promise { + if (!this.isConnected()) { + throw new Error('客户端未连接'); + } + + const result = await this.client!.callApi('network/ServerStatus', {}); + if (!result.isSucc) { + throw new Error(`查询服务端状态失败: ${result.err.message}`); + } + + return result.res; + } + + /** + * 发送心跳 + */ + public async ping(): Promise { + if (!this.isConnected()) { + throw new Error('客户端未连接'); + } + + const startTime = Date.now(); + const result = await this.client!.callApi('network/Ping', { + timestamp: startTime + }); + + if (!result.isSucc) { + throw new Error(`心跳失败: ${result.err.message}`); + } + + const latency = Date.now() - startTime; + this.stats.latency = latency; + return latency; + } + + /** + * 获取连接状态 + */ + public getConnectionState(): ConnectionState { + return this.connectionState; + } + + /** + * 是否已连接 + */ + public isConnected(): boolean { + return this.connectionState === 'connected' && (this.client?.isConnected || false); + } + + /** + * 获取客户端ID + */ + public getClientId(): number { + return this.clientId; + } + + /** + * 获取统计信息 + */ + public getStats() { + return { ...this.stats }; + } + + /** + * 设置事件处理器 + */ + private setupEventHandlers(): void { + if (!this.client) return; + + // 连接断开处理 + this.client.flows.postDisconnectFlow.push((v) => { + if (this.connectionState !== 'disconnected') { + logger.warn('连接意外断开,尝试重连...'); + this.connectionState = 'reconnecting'; + this.onReconnecting?.(); + this.attemptReconnect(); + } + return v; + }); + + // 消息监听 + this.client.listenMsg('network/NetworkMessage', msg => { + this.stats.messagesReceived++; + + const networkMessage: NetworkMessage = { + type: msg.type, + networkId: msg.networkId, + data: msg.data, + timestamp: msg.timestamp + }; + + this.onMessage?.(networkMessage); + }); + + // SyncVar消息监听 + this.client.listenMsg('network/SyncVar', msg => { + this.stats.messagesReceived++; + + const networkMessage: NetworkMessage = { + type: 'syncvar', + networkId: msg.networkId, + data: { + componentType: msg.componentType, + propertyName: msg.propertyName, + value: msg.value + }, + timestamp: msg.timestamp + }; + + this.onMessage?.(networkMessage); + }); + + // RPC消息监听 + this.client.listenMsg('network/RpcCall', msg => { + this.stats.messagesReceived++; + + const networkMessage: NetworkMessage = { + type: 'rpc', + networkId: msg.networkId, + data: { + componentType: msg.componentType, + methodName: msg.methodName, + args: msg.args + }, + timestamp: msg.timestamp + }; + + this.onMessage?.(networkMessage); + }); + + // 客户端断开通知 + this.client.listenMsg('network/ClientDisconnected', msg => { + logger.info(`客户端 ${msg.clientId} 断开连接: ${msg.reason}`); + }); + + // 权威转移通知 + this.client.listenMsg('network/AuthorityChange', msg => { + logger.info(`网络对象 ${msg.networkId} 权威转移给客户端 ${msg.newOwnerId}`); + }); + } + + /** + * 尝试重连 + */ + private async attemptReconnect(): Promise { + if (this.reconnectAttempts >= this.maxReconnectAttempts) { + logger.error('重连次数已达上限,停止重连'); + this.connectionState = 'disconnected'; + this.onDisconnected?.('max_reconnect_attempts_reached'); + return; + } + + this.reconnectAttempts++; + logger.info(`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`); + + try { + await new Promise(resolve => setTimeout(resolve, this.reconnectInterval)); + + // 重新连接 + await this.connect(); + + } catch (error) { + logger.error(`重连失败:`, error); + // 继续尝试重连 + this.attemptReconnect(); + } + } + + /** + * 启动心跳 + */ + private startHeartbeat(): void { + this.heartbeatInterval = setInterval(async () => { + try { + await this.ping(); + } catch (error) { + logger.warn('心跳失败:', error); + } + }, 30000); // 30秒心跳 + } + + /** + * 停止心跳 + */ + private stopHeartbeat(): void { + if (this.heartbeatInterval) { + clearInterval(this.heartbeatInterval); + this.heartbeatInterval = null; + } + } +} \ No newline at end of file diff --git a/packages/network/src/transport/TsrpcServer.ts b/packages/network/src/transport/TsrpcServer.ts new file mode 100644 index 00000000..7f150a10 --- /dev/null +++ b/packages/network/src/transport/TsrpcServer.ts @@ -0,0 +1,364 @@ +/** + * TSRPC 服务端传输层 + * + * 封装TSRPC服务端功能,提供网络消息处理和客户端管理 + */ + +import { WsServer } from 'tsrpc'; +import { createLogger } from '@esengine/ecs-framework'; +import { serviceProto, ServiceType } from './protocols/serviceProto'; +import { NetworkConfig, NetworkMessage } from '../types/NetworkTypes'; +import { + ReqJoinRoom, ResJoinRoom, + ReqServerStatus, ResServerStatus, + ReqPing, ResPing, + MsgNetworkMessage, + MsgSyncVar, + MsgRpcCall, + MsgNetworkObjectSpawn, + MsgNetworkObjectDespawn, + MsgClientDisconnected, + MsgAuthorityChange +} from './protocols/NetworkProtocols'; + +const logger = createLogger('TsrpcServer'); + +/** + * 客户端连接信息 + */ +interface ClientConnection { + /** 客户端ID */ + id: number; + /** 连接对象 */ + connection: any; + /** 连接时间 */ + connectTime: number; + /** 最后活跃时间 */ + lastActivity: number; + /** 客户端信息 */ + clientInfo?: { + version: string; + platform: string; + }; +} + +/** + * TSRPC服务端包装器 + */ +export class TsrpcServer { + private server: WsServer | null = null; + private clients: Map = new Map(); + private nextClientId: number = 1; + private config: NetworkConfig; + private startTime: number = 0; + + /** 统计信息 */ + private stats = { + messagesSent: 0, + messagesReceived: 0, + bytesSent: 0, + bytesReceived: 0 + }; + + /** 事件处理器 */ + public onClientConnected?: (clientId: number) => void; + public onClientDisconnected?: (clientId: number, reason?: string) => void; + public onMessage?: (message: NetworkMessage, fromClientId: number) => void; + + constructor(config: NetworkConfig) { + this.config = config; + } + + /** + * 启动服务端 + */ + public async start(): Promise { + if (this.server) { + throw new Error('服务端已经在运行中'); + } + + // 创建TSRPC WebSocket服务端 + this.server = new WsServer(serviceProto, { + port: this.config.port || 7777 + }); + + this.startTime = Date.now(); + this.setupApiHandlers(); + this.setupMessageHandlers(); + this.setupConnectionHandlers(); + + // 启动服务端 + await this.server.start(); + logger.info(`TSRPC服务端已启动,端口: ${this.config.port}`); + } + + /** + * 停止服务端 + */ + public async stop(): Promise { + if (this.server) { + await this.server.stop(); + this.server = null; + this.clients.clear(); + logger.info('TSRPC服务端已停止'); + } + } + + /** + * 向指定客户端发送消息 + */ + public sendToClient(clientId: number, message: any): void { + const client = this.clients.get(clientId); + if (!client) { + logger.warn(`客户端不存在: ${clientId}`); + return; + } + + try { + // 发送消息给指定连接 + this.server?.broadcastMsg(message.type, message.data || message, [client.connection]); + this.stats.messagesSent++; + logger.debug(`向客户端 ${clientId} 发送消息: ${message.type || 'unknown'}`); + } catch (error) { + logger.error(`向客户端 ${clientId} 发送消息失败:`, error); + } + } + + /** + * 向所有客户端广播消息 + */ + public broadcast(message: any, excludeClientId?: number): void { + if (!this.server) { + logger.warn('服务端未启动,无法广播消息'); + return; + } + + try { + if (excludeClientId) { + // 排除指定客户端 + const targetConnections = Array.from(this.clients.entries()) + .filter(([clientId, client]) => clientId !== excludeClientId) + .map(([clientId, client]) => client.connection); + + this.server.broadcastMsg(message.type, message.data || message, targetConnections); + this.stats.messagesSent += targetConnections.length; + } else { + // 广播给所有客户端 + this.server.broadcastMsg(message.type, message.data || message); + this.stats.messagesSent += this.clients.size; + } + } catch (error) { + logger.error('广播消息失败:', error); + } + + logger.debug(`广播消息给 ${this.clients.size} 个客户端: ${message.type || 'unknown'}`); + } + + /** + * 获取连接的客户端列表 + */ + public getConnectedClients(): number[] { + return Array.from(this.clients.keys()); + } + + /** + * 获取客户端数量 + */ + public getClientCount(): number { + return this.clients.size; + } + + /** + * 获取服务端统计信息 + */ + public getStats() { + return { + ...this.stats, + clientCount: this.clients.size, + uptime: Date.now() - this.startTime + }; + } + + /** + * 设置API处理器 + */ + private setupApiHandlers(): void { + if (!this.server) return; + + // 客户端加入房间 + this.server.implementApi('network/JoinRoom', call => { + const clientId = this.nextClientId++; + const client: ClientConnection = { + id: clientId, + connection: call.conn, + connectTime: Date.now(), + lastActivity: Date.now(), + clientInfo: call.req.clientInfo + }; + + this.clients.set(clientId, client); + logger.info(`客户端 ${clientId} 连接成功`); + + // 通知上层 + this.onClientConnected?.(clientId); + + // 返回响应 + call.succ({ + clientId, + roomId: call.req.roomId || 'default', + serverInfo: { + version: '1.0.0', + syncRate: this.config.syncRate || 20 + } + }); + }); + + // 服务端状态查询 + this.server.implementApi('network/ServerStatus', call => { + const stats = this.getStats(); + call.succ({ + clientCount: stats.clientCount, + networkObjectCount: 0, // 这里需要从NetworkRegistry获取 + uptime: stats.uptime, + networkStats: { + messagesSent: stats.messagesSent, + messagesReceived: stats.messagesReceived, + bytesSent: stats.bytesSent, + bytesReceived: stats.bytesReceived + } + }); + }); + + // 心跳检测 + this.server.implementApi('network/Ping', call => { + call.succ({ + serverTimestamp: Date.now(), + clientTimestamp: call.req.timestamp + }); + }); + } + + /** + * 设置消息处理器 + */ + private setupMessageHandlers(): void { + if (!this.server) return; + + // 网络消息处理 + this.server.listenMsg('network/NetworkMessage', msg => { + const clientId = this.getClientIdByConnection(msg.conn); + if (clientId) { + this.stats.messagesReceived++; + this.updateClientActivity(clientId); + + // 转换为内部消息格式 + const networkMessage: NetworkMessage = { + type: msg.msg.type, + networkId: msg.msg.networkId, + data: msg.msg.data, + timestamp: msg.msg.timestamp + }; + + this.onMessage?.(networkMessage, clientId); + } + }); + + // SyncVar消息处理 + this.server.listenMsg('network/SyncVar', msg => { + const clientId = this.getClientIdByConnection(msg.conn); + if (clientId) { + this.stats.messagesReceived++; + this.updateClientActivity(clientId); + + // 转换并广播给其他客户端 + const syncVarMessage: MsgSyncVar = msg.msg; + this.broadcast({ + type: 'network/SyncVar', + data: syncVarMessage + }, clientId); + } + }); + + // RPC调用消息处理 + this.server.listenMsg('network/RpcCall', msg => { + const clientId = this.getClientIdByConnection(msg.conn); + if (clientId) { + this.stats.messagesReceived++; + this.updateClientActivity(clientId); + + const rpcMessage: MsgRpcCall = msg.msg; + + if (rpcMessage.isClientRpc) { + // 服务端到客户端的RPC,广播给所有客户端 + this.broadcast({ + type: 'network/RpcCall', + data: rpcMessage + }); + } else { + // 客户端到服务端的Command,只在服务端处理 + const networkMessage: NetworkMessage = { + type: 'rpc', + networkId: rpcMessage.networkId, + data: { + componentType: rpcMessage.componentType, + methodName: rpcMessage.methodName, + args: rpcMessage.args + }, + timestamp: rpcMessage.timestamp + }; + + this.onMessage?.(networkMessage, clientId); + } + } + }); + } + + /** + * 设置连接处理器 + */ + private setupConnectionHandlers(): void { + if (!this.server) return; + + // 连接断开处理 + this.server.flows.postDisconnectFlow.push(conn => { + const clientId = this.getClientIdByConnection(conn); + if (clientId) { + this.clients.delete(clientId); + logger.info(`客户端 ${clientId} 断开连接`); + + // 通知其他客户端 + this.broadcast({ + type: 'network/ClientDisconnected', + data: { clientId, reason: 'disconnected' } + }); + + // 通知上层 + this.onClientDisconnected?.(clientId, 'disconnected'); + } + + return conn; + }); + } + + /** + * 根据连接对象获取客户端ID + */ + private getClientIdByConnection(conn: any): number | null { + for (const [clientId, client] of this.clients) { + if (client.connection === conn) { + return clientId; + } + } + return null; + } + + /** + * 更新客户端活跃时间 + */ + private updateClientActivity(clientId: number): void { + const client = this.clients.get(clientId); + if (client) { + client.lastActivity = Date.now(); + } + } +} \ No newline at end of file diff --git a/packages/network/src/transport/TsrpcTransport.ts b/packages/network/src/transport/TsrpcTransport.ts new file mode 100644 index 00000000..c6be15a3 --- /dev/null +++ b/packages/network/src/transport/TsrpcTransport.ts @@ -0,0 +1,338 @@ +/** + * TSRPC 传输管理器 + * + * 统一管理TSRPC服务端和客户端,提供通用的传输接口 + */ + +import { createLogger } from '@esengine/ecs-framework'; +import { TsrpcServer } from './TsrpcServer'; +import { TsrpcClient } from './TsrpcClient'; +import { NetworkConfig, NetworkMessage, NetworkSide } from '../types/NetworkTypes'; + +const logger = createLogger('TsrpcTransport'); + +/** + * 传输事件处理器 + */ +export interface TransportEventHandlers { + /** 连接建立 */ + onConnected?: () => void; + /** 连接断开 */ + onDisconnected?: (reason?: string) => void; + /** 客户端连接(仅服务端) */ + onClientConnected?: (clientId: number) => void; + /** 客户端断开(仅服务端) */ + onClientDisconnected?: (clientId: number, reason?: string) => void; + /** 收到消息 */ + onMessage?: (message: NetworkMessage, fromClientId?: number) => void; + /** 发生错误 */ + onError?: (error: Error) => void; +} + +/** + * TSRPC传输层管理器 + */ +export class TsrpcTransport { + private server: TsrpcServer | null = null; + private client: TsrpcClient | null = null; + private networkSide: NetworkSide = 'client'; + private config: NetworkConfig; + private eventHandlers: TransportEventHandlers = {}; + + constructor(config: NetworkConfig) { + this.config = config; + } + + /** + * 启动服务端 + */ + public async startServer(): Promise { + if (this.server) { + throw new Error('服务端已经在运行中'); + } + + this.networkSide = 'server'; + this.server = new TsrpcServer(this.config); + + // 设置服务端事件处理器 + this.server.onClientConnected = (clientId) => { + logger.info(`客户端 ${clientId} 已连接`); + this.eventHandlers.onClientConnected?.(clientId); + }; + + this.server.onClientDisconnected = (clientId, reason) => { + logger.info(`客户端 ${clientId} 已断开: ${reason}`); + this.eventHandlers.onClientDisconnected?.(clientId, reason); + }; + + this.server.onMessage = (message, fromClientId) => { + this.eventHandlers.onMessage?.(message, fromClientId); + }; + + await this.server.start(); + logger.info('TSRPC服务端已启动'); + this.eventHandlers.onConnected?.(); + } + + /** + * 连接到服务端 + */ + public async connectToServer(): Promise { + if (this.client) { + throw new Error('客户端已经连接或正在连接中'); + } + + this.networkSide = 'client'; + this.client = new TsrpcClient(this.config); + + // 设置客户端事件处理器 + this.client.onConnected = () => { + logger.info('已连接到服务端'); + this.eventHandlers.onConnected?.(); + }; + + this.client.onDisconnected = (reason) => { + logger.info(`已断开连接: ${reason}`); + this.eventHandlers.onDisconnected?.(reason); + }; + + this.client.onMessage = (message) => { + this.eventHandlers.onMessage?.(message); + }; + + this.client.onError = (error) => { + logger.error('客户端错误:', error); + this.eventHandlers.onError?.(error); + }; + + await this.client.connect(); + } + + /** + * 断开连接/停止服务端 + */ + public async disconnect(): Promise { + if (this.server) { + await this.server.stop(); + this.server = null; + logger.info('TSRPC服务端已停止'); + } + + if (this.client) { + await this.client.disconnect(); + this.client = null; + logger.info('TSRPC客户端已断开'); + } + + this.eventHandlers.onDisconnected?.(); + } + + /** + * 发送消息 + */ + public async sendMessage(message: NetworkMessage, targetClientId?: number): Promise { + if (this.networkSide === 'server' && this.server) { + // 服务端模式:发送给指定客户端或广播 + if (targetClientId) { + this.server.sendToClient(targetClientId, { + type: 'network/NetworkMessage', + data: message + }); + } else { + this.server.broadcast({ + type: 'network/NetworkMessage', + data: message + }); + } + } else if (this.networkSide === 'client' && this.client) { + // 客户端模式:发送给服务端 + await this.client.sendMessage(message); + } else { + throw new Error('传输层未初始化或状态错误'); + } + } + + /** + * 发送SyncVar消息 + */ + public async sendSyncVar( + networkId: number, + componentType: string, + propertyName: string, + value: any, + targetClientId?: number + ): Promise { + if (this.networkSide === 'server' && this.server) { + const message = { + type: 'network/SyncVar', + data: { + networkId, + componentType, + propertyName, + value, + timestamp: Date.now() + } + }; + + if (targetClientId) { + this.server.sendToClient(targetClientId, message); + } else { + this.server.broadcast(message); + } + } else if (this.networkSide === 'client' && this.client) { + await this.client.sendSyncVar(networkId, componentType, propertyName, value); + } else { + throw new Error('传输层未初始化或状态错误'); + } + } + + /** + * 发送RPC消息 + */ + public async sendRpcCall( + networkId: number, + componentType: string, + methodName: string, + args: any[], + isClientRpc: boolean, + targetClientId?: number + ): Promise { + if (this.networkSide === 'server' && this.server) { + const message = { + type: 'network/RpcCall', + data: { + networkId, + componentType, + methodName, + args, + isClientRpc, + timestamp: Date.now() + } + }; + + if (targetClientId) { + this.server.sendToClient(targetClientId, message); + } else { + this.server.broadcast(message); + } + } else if (this.networkSide === 'client' && this.client) { + await this.client.sendRpcCall(networkId, componentType, methodName, args, isClientRpc); + } else { + throw new Error('传输层未初始化或状态错误'); + } + } + + /** + * 获取连接状态 + */ + public isConnected(): boolean { + if (this.networkSide === 'server' && this.server) { + return true; // 服务端启动即为连接状态 + } else if (this.networkSide === 'client' && this.client) { + return this.client.isConnected(); + } + return false; + } + + /** + * 获取网络端类型 + */ + public getNetworkSide(): NetworkSide { + return this.networkSide; + } + + /** + * 获取客户端ID(仅客户端模式) + */ + public getClientId(): number { + if (this.networkSide === 'client' && this.client) { + return this.client.getClientId(); + } + return 0; + } + + /** + * 获取连接的客户端列表(仅服务端模式) + */ + public getConnectedClients(): number[] { + if (this.networkSide === 'server' && this.server) { + return this.server.getConnectedClients(); + } + return []; + } + + /** + * 获取客户端数量(仅服务端模式) + */ + public getClientCount(): number { + if (this.networkSide === 'server' && this.server) { + return this.server.getClientCount(); + } + return 0; + } + + /** + * 获取统计信息 + */ + public getStats() { + if (this.networkSide === 'server' && this.server) { + return this.server.getStats(); + } else if (this.networkSide === 'client' && this.client) { + const clientStats = this.client.getStats(); + return { + messagesSent: clientStats.messagesSent, + messagesReceived: clientStats.messagesReceived, + bytesSent: clientStats.bytesSent, + bytesReceived: clientStats.bytesReceived, + clientCount: 0, + uptime: 0 + }; + } + + return { + messagesSent: 0, + messagesReceived: 0, + bytesSent: 0, + bytesReceived: 0, + clientCount: 0, + uptime: 0 + }; + } + + /** + * 查询服务端状态(仅客户端模式) + */ + public async getServerStatus() { + if (this.networkSide === 'client' && this.client) { + return await this.client.getServerStatus(); + } + throw new Error('只能在客户端模式下查询服务端状态'); + } + + /** + * 发送心跳(仅客户端模式) + */ + public async ping(): Promise { + if (this.networkSide === 'client' && this.client) { + return await this.client.ping(); + } + throw new Error('只能在客户端模式下发送心跳'); + } + + /** + * 设置事件处理器 + */ + public setEventHandlers(handlers: TransportEventHandlers): void { + this.eventHandlers = { ...this.eventHandlers, ...handlers }; + } + + /** + * 设置单个事件处理器 + */ + public on( + event: K, + handler: TransportEventHandlers[K] + ): void { + this.eventHandlers[event] = handler; + } +} \ No newline at end of file diff --git a/packages/network/src/transport/index.ts b/packages/network/src/transport/index.ts new file mode 100644 index 00000000..d7dd8f55 --- /dev/null +++ b/packages/network/src/transport/index.ts @@ -0,0 +1,9 @@ +/** + * 传输层模块导出 + */ + +export { TsrpcTransport } from './TsrpcTransport'; +export { TsrpcServer } from './TsrpcServer'; +export { TsrpcClient } from './TsrpcClient'; +export * from './protocols/NetworkProtocols'; +export { serviceProto, ServiceType } from './protocols/serviceProto'; \ No newline at end of file diff --git a/packages/network/src/transport/protocols/NetworkProtocols.ts b/packages/network/src/transport/protocols/NetworkProtocols.ts new file mode 100644 index 00000000..6e7ed4c5 --- /dev/null +++ b/packages/network/src/transport/protocols/NetworkProtocols.ts @@ -0,0 +1,184 @@ +/** + * 网络库 TSRPC 协议定义 + * 定义所有网络消息的类型和结构 + */ + + +/** + * 客户端连接请求 + */ +export interface ReqJoinRoom { + /** 房间ID,可选 */ + roomId?: string; + /** 客户端信息 */ + clientInfo?: { + /** 客户端版本 */ + version: string; + /** 客户端平台 */ + platform: string; + }; +} + +/** + * 客户端连接响应 + */ +export interface ResJoinRoom { + /** 分配的客户端ID */ + clientId: number; + /** 房间ID */ + roomId: string; + /** 服务端信息 */ + serverInfo: { + /** 服务端版本 */ + version: string; + /** 同步频率 */ + syncRate: number; + }; +} + +/** + * 网络消息广播 + */ +export interface MsgNetworkMessage { + /** 消息类型 */ + type: 'syncvar' | 'rpc'; + /** 网络对象ID */ + networkId: number; + /** 消息数据 */ + data: any; + /** 时间戳 */ + timestamp: number; + /** 发送者客户端ID */ + senderId?: number; +} + +/** + * SyncVar 同步消息 + */ +export interface MsgSyncVar { + /** 网络对象ID */ + networkId: number; + /** 组件类型名 */ + componentType: string; + /** 属性名 */ + propertyName: string; + /** 新的属性值 */ + value: any; + /** 时间戳 */ + timestamp: number; +} + +/** + * RPC 调用消息 + */ +export interface MsgRpcCall { + /** 网络对象ID */ + networkId: number; + /** 组件类型名 */ + componentType: string; + /** 方法名 */ + methodName: string; + /** 参数 */ + args: any[]; + /** 是否为客户端RPC */ + isClientRpc: boolean; + /** 时间戳 */ + timestamp: number; +} + +/** + * 网络对象生成通知 + */ +export interface MsgNetworkObjectSpawn { + /** 网络对象ID */ + networkId: number; + /** 实体名称 */ + entityName: string; + /** 所有者客户端ID */ + ownerId: number; + /** 是否拥有权威 */ + hasAuthority: boolean; + /** 初始组件数据 */ + components: Array<{ + /** 组件类型 */ + type: string; + /** 组件数据 */ + data: any; + }>; +} + +/** + * 网络对象销毁通知 + */ +export interface MsgNetworkObjectDespawn { + /** 网络对象ID */ + networkId: number; +} + +/** + * 客户端断开连接通知 + */ +export interface MsgClientDisconnected { + /** 断开连接的客户端ID */ + clientId: number; + /** 断开原因 */ + reason?: string; +} + +/** + * 权威转移通知 + */ +export interface MsgAuthorityChange { + /** 网络对象ID */ + networkId: number; + /** 新的权威所有者ID */ + newOwnerId: number; + /** 是否拥有权威 */ + hasAuthority: boolean; +} + +/** + * 服务端状态查询请求 + */ +export interface ReqServerStatus {} + +/** + * 服务端状态响应 + */ +export interface ResServerStatus { + /** 连接的客户端数量 */ + clientCount: number; + /** 网络对象数量 */ + networkObjectCount: number; + /** 服务器运行时间(毫秒) */ + uptime: number; + /** 网络统计 */ + networkStats: { + /** 发送的消息数 */ + messagesSent: number; + /** 接收的消息数 */ + messagesReceived: number; + /** 发送的字节数 */ + bytesSent: number; + /** 接收的字节数 */ + bytesReceived: number; + }; +} + +/** + * 心跳请求 + */ +export interface ReqPing { + /** 客户端时间戳 */ + timestamp: number; +} + +/** + * 心跳响应 + */ +export interface ResPing { + /** 服务端时间戳 */ + serverTimestamp: number; + /** 客户端时间戳(回传) */ + clientTimestamp: number; +} \ No newline at end of file diff --git a/packages/network/src/transport/protocols/serviceProto.ts b/packages/network/src/transport/protocols/serviceProto.ts new file mode 100644 index 00000000..88f73ff7 --- /dev/null +++ b/packages/network/src/transport/protocols/serviceProto.ts @@ -0,0 +1,108 @@ +/** + * TSRPC 服务协议定义 + * 定义API调用和消息类型 + */ + +import { ServiceProto } from 'tsrpc'; +import { + ReqJoinRoom, ResJoinRoom, + ReqServerStatus, ResServerStatus, + ReqPing, ResPing, + MsgNetworkMessage, + MsgSyncVar, + MsgRpcCall, + MsgNetworkObjectSpawn, + MsgNetworkObjectDespawn, + MsgClientDisconnected, + MsgAuthorityChange +} from './NetworkProtocols'; + +/** + * 网络服务协议 + * 定义所有可用的API和消息类型 + */ +export const serviceProto: ServiceProto = { + "services": [ + { + "id": 0, + "name": "network/JoinRoom", + "type": "api" + }, + { + "id": 1, + "name": "network/ServerStatus", + "type": "api" + }, + { + "id": 2, + "name": "network/Ping", + "type": "api" + }, + { + "id": 3, + "name": "network/NetworkMessage", + "type": "msg" + }, + { + "id": 4, + "name": "network/SyncVar", + "type": "msg" + }, + { + "id": 5, + "name": "network/RpcCall", + "type": "msg" + }, + { + "id": 6, + "name": "network/NetworkObjectSpawn", + "type": "msg" + }, + { + "id": 7, + "name": "network/NetworkObjectDespawn", + "type": "msg" + }, + { + "id": 8, + "name": "network/ClientDisconnected", + "type": "msg" + }, + { + "id": 9, + "name": "network/AuthorityChange", + "type": "msg" + } + ], + "types": {} +}; + +/** + * 服务类型定义 + * 用于类型安全的API调用和消息发送 + */ +export interface ServiceType { + api: { + "network/JoinRoom": { + req: ReqJoinRoom; + res: ResJoinRoom; + }; + "network/ServerStatus": { + req: ReqServerStatus; + res: ResServerStatus; + }; + "network/Ping": { + req: ReqPing; + res: ResPing; + }; + }; + msg: { + "network/NetworkMessage": MsgNetworkMessage; + "network/SyncVar": MsgSyncVar; + "network/RpcCall": MsgRpcCall; + "network/NetworkObjectSpawn": MsgNetworkObjectSpawn; + "network/NetworkObjectDespawn": MsgNetworkObjectDespawn; + "network/ClientDisconnected": MsgClientDisconnected; + "network/AuthorityChange": MsgAuthorityChange; + }; +} \ No newline at end of file diff --git a/packages/network/src/types/CoreTypes.ts b/packages/network/src/types/CoreTypes.ts deleted file mode 100644 index dfae6ac3..00000000 --- a/packages/network/src/types/CoreTypes.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { Component, Entity } from '@esengine/ecs-framework'; - -/** - * 网络环境类型 - */ -export type NetworkEnvironmentType = 'server' | 'client' | 'hybrid'; - -/** - * 连接状态类型 - */ -export type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'; - -/** - * 权限类型 - */ -export enum AuthorityType { - None = 'none', - ReadOnly = 'readonly', - ReadWrite = 'readwrite', - Full = 'full' -} - -/** - * 网络组件基接口 - */ -export interface INetworkComponent extends Component { - networkId?: string; - hasNetworkAuthority?(): boolean; - getNetworkState?(): Uint8Array; - applyNetworkState?(data: Uint8Array): void; - - /** 允许通过字符串键访问属性 */ - [propertyKey: string]: unknown; -} - -/** - * 具有权限检查能力的组件 - */ -export interface IAuthorizedComponent extends INetworkComponent { - hasAuthority(context?: AuthorityContext): boolean; - checkAuthority?(context?: AuthorityContext): boolean; -} - -/** - * 权限上下文 - */ -export interface AuthorityContext { - environment: NetworkEnvironmentType; - networkId?: string; - entityId?: number; - clientId?: string; - level: AuthorityType; - timestamp: number; - metadata?: Record; -} - -/** - * 网络实体接口 - */ -export interface INetworkEntity extends Entity { - ownerId?: string; - hasNetworkAuthority?(): boolean; -} - -/** - * 组件构造器类型 - */ -export type ComponentConstructor = new (...args: unknown[]) => T; - -/** - * 网络组件构造器类型 - */ -export type NetworkComponentConstructor = new (...args: unknown[]) => T; - -/** - * 事件处理器类型映射 - */ -export interface NetworkEventHandlers { - connected: () => void; - disconnected: (reason?: string) => void; - connecting: () => void; - reconnecting: (attempt: number) => void; - reconnected: () => void; - connectError: (error: Error) => void; - reconnectFailed: () => void; - message: (data: Uint8Array) => void; - error: (error: Error) => void; -} - -/** - * 类型安全的事件发射器接口 - */ -export interface ITypedEventEmitter void>> { - on(event: K, handler: TEvents[K]): void; - off(event: K, handler: TEvents[K]): void; - emit(event: K, ...args: Parameters): void; -} - -/** - * 网络统计接口 - */ -export interface NetworkStats { - connectionCount: number; - totalConnections: number; - uptime: number; - bytesTransferred: number; - messagesCount: number; - errors: number; -} - -/** - * 性能指标接口 - */ -export interface PerformanceMetrics { - rtt: number; - latency: number; - jitter: number; - packetLoss: number; - bandwidth: number; - connectionQuality: number; -} - -/** - * 类型守卫工具类 - */ -export class TypeGuards { - static isNetworkComponent(obj: unknown): obj is INetworkComponent { - return obj !== null && - typeof obj === 'object' && - obj instanceof Component; - } - - static isAuthorizedComponent(obj: unknown): obj is IAuthorizedComponent { - return TypeGuards.isNetworkComponent(obj) && - typeof (obj as IAuthorizedComponent).hasAuthority === 'function'; - } - - static isNetworkEntity(obj: unknown): obj is INetworkEntity { - return obj !== null && - typeof obj === 'object' && - obj instanceof Entity; - } - - static isValidNetworkEnvironment(env: string): env is NetworkEnvironmentType { - return ['server', 'client', 'hybrid'].includes(env); - } - - static isValidConnectionState(state: string): state is ConnectionState { - return ['disconnected', 'connecting', 'connected', 'reconnecting'].includes(state); - } -} \ No newline at end of file diff --git a/packages/network/src/types/MessageTypes.ts b/packages/network/src/types/MessageTypes.ts deleted file mode 100644 index 09a73a93..00000000 --- a/packages/network/src/types/MessageTypes.ts +++ /dev/null @@ -1,207 +0,0 @@ -/** - * 消息类型枚举 - */ -export enum MessageType { - // 基础消息类型 (0-99) - HEARTBEAT = 0, - PING = 1, - PONG = 2, - ERROR = 3, - - // 连接管理 (100-199) - CONNECT = 100, - DISCONNECT = 101, - RECONNECT = 102, - AUTH = 103, - - // SyncVar消息 (200-299) - SYNC_VAR_UPDATE = 200, - SYNC_VAR_BATCH = 201, - SYNC_VAR_REQUEST = 202, - - // 快照消息 (300-399) - SNAPSHOT_FULL = 300, - SNAPSHOT_INCREMENTAL = 301, - SNAPSHOT_REQUEST = 302, - - // TSRPC消息 (400-499) - TSRPC_CALL = 400, - TSRPC_RESPONSE = 401, - TSRPC_NOTIFICATION = 402, - - // 自定义消息 (500+) - CUSTOM = 500 -} - -/** - * 消息优先级 - */ -export enum MessagePriority { - CRITICAL = 0, // 关键消息(连接、认证等) - HIGH = 1, // 高优先级(实时游戏数据) - NORMAL = 2, // 普通优先级(常规同步) - LOW = 3, // 低优先级(统计、日志等) - BACKGROUND = 4 // 后台消息(清理、维护等) -} - -/** - * 网络消息基接口 - */ -export interface INetworkMessage { - readonly type: MessageType; - readonly timestamp: number; - readonly priority: MessagePriority; - readonly sequenceNumber?: number; - serialize(): Uint8Array; -} - -/** - * 消息头接口 - */ -export interface MessageHeader { - type: MessageType; - size: number; - timestamp: number; - priority: MessagePriority; - sequenceNumber?: number; - checksum?: number; -} - -/** - * 心跳消息接口 - */ -export interface IHeartbeatMessage extends INetworkMessage { - readonly pingId: string; - readonly payload?: Record; -} - -/** - * SyncVar更新消息接口 - */ -export interface ISyncVarMessage extends INetworkMessage { - readonly networkId: string; - readonly componentType: string; - readonly fieldUpdates: SyncVarFieldUpdate[]; - readonly isFullSync: boolean; - readonly senderId?: string; -} - -/** - * SyncVar字段更新 - */ -export interface SyncVarFieldUpdate { - readonly fieldNumber: number; - readonly propertyKey: string; - readonly newValue: unknown; - readonly oldValue?: unknown; - readonly timestamp: number; - readonly authorityOnly: boolean; -} - -/** - * 错误消息接口 - */ -export interface IErrorMessage extends INetworkMessage { - readonly errorCode: string; - readonly errorMessage: string; - readonly errorData?: Record; - readonly originalMessageType?: MessageType; -} - -/** - * 快照消息接口 - */ -export interface ISnapshotMessage extends INetworkMessage { - readonly snapshotId: string; - readonly snapshotType: 'full' | 'incremental'; - readonly entityData: EntitySnapshot[]; - readonly compressionType?: string; -} - -/** - * 实体快照数据 - */ -export interface EntitySnapshot { - readonly entityId: number; - readonly components: ComponentSnapshot[]; - readonly timestamp: number; -} - -/** - * 组件快照数据 - */ -export interface ComponentSnapshot { - readonly componentType: string; - readonly data: Uint8Array; - readonly version?: string; -} - -/** - * TSRPC消息接口 - */ -export interface ITsrpcMessage extends INetworkMessage { - readonly method: string; - readonly requestId?: string; - readonly params?: Record; - readonly result?: unknown; - readonly error?: TsrpcError; -} - -/** - * TSRPC错误 - */ -export interface TsrpcError { - readonly code: number; - readonly message: string; - readonly data?: unknown; -} - -/** - * 消息工厂接口 - */ -export interface IMessageFactory { - createMessage( - type: MessageType, - data: Record - ): T; - - deserializeMessage(data: Uint8Array): INetworkMessage | null; - - registerMessageType( - type: MessageType, - constructor: new (...args: unknown[]) => T - ): void; -} - -/** - * 消息处理器接口 - */ -export interface IMessageHandler { - readonly messageType: MessageType; - readonly priority: number; - - canHandle(message: INetworkMessage): message is T; - handle(message: T, context?: MessageHandlerContext): Promise | void; -} - -/** - * 消息处理上下文 - */ -export interface MessageHandlerContext { - readonly connectionId?: string; - readonly senderId?: string; - readonly timestamp: number; - readonly metadata?: Record; -} - -/** - * 消息统计接口 - */ -export interface MessageStats { - readonly totalSent: number; - readonly totalReceived: number; - readonly totalDropped: number; - readonly averageSize: number; - readonly messagesByType: Map; - readonly messagesByPriority: Map; -} \ No newline at end of file diff --git a/packages/network/src/types/NetworkTypes.ts b/packages/network/src/types/NetworkTypes.ts index 03aa1ff8..e0fe7ad2 100644 --- a/packages/network/src/types/NetworkTypes.ts +++ b/packages/network/src/types/NetworkTypes.ts @@ -1,306 +1,162 @@ /** - * 网络库类型定义 - * - * 基于核心库的类型系统,为网络功能提供特定的类型约束 + * 网络库核心类型定义 */ -import { ComponentType, IComponent, Component } from '@esengine/ecs-framework'; -import { SerializedData } from '../Serialization/SerializationTypes'; /** - * 网络同步组件接口 - * 扩展核心组件接口,添加网络同步功能 + * 网络连接状态 */ -export interface INetworkSyncable extends IComponent { - /** 内部SyncVar ID */ - _syncVarId?: string; - /** 是否禁用SyncVar监听 */ - _syncVarDisabled?: boolean; - - /** - * 获取网络同步状态 - */ - getNetworkState(): Uint8Array; - - /** - * 应用网络状态 - */ - applyNetworkState(data: Uint8Array): void; - - /** - * 获取脏字段列表 - */ - getDirtyFields(): number[]; - - /** - * 标记为干净状态 - */ - markClean(): void; - - /** - * 标记字段为脏状态 - */ - markFieldDirty(fieldNumber: number): void; - - /** 允许通过字符串键访问属性 */ - [propertyKey: string]: unknown; +export type NetworkConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecting'; + +/** + * 网络端类型 + */ +export type NetworkSide = 'client' | 'server' | 'host'; + +/** + * 网络消息类型 + */ +export interface NetworkMessage { + /** 消息类型 */ + type: string; + /** 网络对象ID */ + networkId: number; + /** 消息数据 */ + data: any; + /** 时间戳 */ + timestamp: number; } /** - * 网络组件构造函数类型 - * 基于核心库的ComponentType,添加网络特性约束 + * 同步变量消息 */ -export type NetworkComponentType = ComponentType; - -/** - * SyncVar值类型约束 - * 定义可以被SyncVar同步的值类型 - */ -export type SyncVarValue = - | string - | number - | boolean - | null - | undefined - | Date - | Uint8Array - | Record - | unknown[]; - - -/** - * SyncVar元数据接口 - * 用于类型安全的SyncVar配置 - */ -export interface ISyncVarMetadata { - /** 属性名 */ - propertyKey: string; - /** 字段编号 */ - fieldNumber: number; - /** 配置选项 */ - options: ISyncVarOptions; +export interface SyncVarMessage extends NetworkMessage { + type: 'syncvar'; + /** 组件类型名 */ + componentType: string; + /** 属性名 */ + propertyName: string; + /** 属性值 */ + value: any; } /** - * SyncVar选项接口 + * RPC消息 */ -export interface ISyncVarOptions { - /** Hook回调函数名 */ - hook?: string; - /** 是否仅权威端可修改 */ - authorityOnly?: boolean; - /** 节流时间(毫秒) */ - throttleMs?: number; - /** 自定义序列化函数 */ - serializer?: (value: T) => Uint8Array; - /** 自定义反序列化函数 */ - deserializer?: (data: Uint8Array) => T; +export interface RpcMessage extends NetworkMessage { + type: 'rpc'; + /** RPC方法名 */ + methodName: string; + /** RPC参数 */ + args: any[]; + /** 是否为客户端RPC */ + isClientRpc: boolean; } /** - * 组件序列化目标类型 - * 约束可以被序列化的组件类型 + * 网络配置选项 */ -export type SerializationTarget = Component & INetworkSyncable & { - readonly constructor: NetworkComponentType; -}; - -/** - * 消息数据约束类型 - * 定义网络消息中可以传输的数据类型 - */ -export type MessageData = - | Record - | Uint8Array - | string - | number - | boolean - | null; - -/** - * 基础网络消息接口 - * 为SyncVar等网络同步功能提供消息接口 - */ -export interface IBasicNetworkMessage { - /** 消息类型 */ - readonly messageType: number; - /** 消息数据 */ - readonly data: TData; - /** 发送者ID */ - senderId?: string; - /** 消息时间戳 */ - timestamp: number; - /** 消息序列号 */ - sequence?: number; - - /** 序列化消息 */ - serialize(): Uint8Array; - /** 反序列化消息 */ - deserialize(data: Uint8Array): void; - /** 获取消息大小 */ - getSize(): number; +export interface NetworkConfig { + /** 服务器端口 */ + port?: number; + /** 服务器地址 */ + host?: string; + /** 房间ID */ + roomId?: string; + /** 最大连接数 */ + maxConnections?: number; + /** 同步频率 (Hz) */ + syncRate?: number; + /** 是否启用压缩 */ + compression?: boolean; } /** - * SyncVar更新数据接口 + * 网络统计信息 */ -export interface ISyncVarFieldUpdate { - /** 字段编号 */ - fieldNumber: number; - /** 属性名 */ - propertyKey: string; - /** 新值 */ - newValue: SyncVarValue; - /** 旧值 */ - oldValue: SyncVarValue; - /** 时间戳 */ - timestamp: number; - /** 是否需要权限 */ - authorityOnly?: boolean; +export interface NetworkStats { + /** 连接数 */ + connectionCount: number; + /** 发送的字节数 */ + bytesSent: number; + /** 接收的字节数 */ + bytesReceived: number; + /** 发送的消息数 */ + messagesSent: number; + /** 接收的消息数 */ + messagesReceived: number; + /** 平均延迟 (ms) */ + averageLatency: number; } /** - * 快照数据接口 + * 网络事件处理器 */ -export interface ISnapshotData { - /** 组件类型名 */ - componentType: string; - /** 序列化数据 */ - data: SerializedData; - /** 组件ID */ - componentId: number; - /** 是否启用 */ - enabled: boolean; +export interface NetworkEventHandlers { + /** 连接建立 */ + onConnected: () => void; + /** 连接断开 */ + onDisconnected: (reason?: string) => void; + /** 客户端加入 */ + onClientConnected: (clientId: number) => void; + /** 客户端离开 */ + onClientDisconnected: (clientId: number, reason?: string) => void; + /** 发生错误 */ + onError: (error: Error) => void; } /** - * 类型安全的组件工厂接口 + * 网络行为接口 + * 所有网络组件都需要实现此接口 */ -export interface IComponentFactory { - /** 创建组件实例 */ - create( - componentType: NetworkComponentType, - ...args: unknown[] - ): T; - - /** 检查组件类型是否已注册 */ - isRegistered( - componentType: NetworkComponentType - ): boolean; - - /** 获取组件类型名称 */ - getTypeName( - componentType: NetworkComponentType - ): string; +export interface INetworkBehaviour { + /** 网络身份组件引用 */ + networkIdentity: any | null; + /** 是否拥有权威 */ + hasAuthority: boolean; + /** 是否为本地玩家 */ + isLocalPlayer: boolean; + /** 是否在服务端 */ + isServer: boolean; + /** 是否在客户端 */ + isClient: boolean; } /** - * 网络性能指标接口 + * 同步变量元数据 */ -export interface INetworkPerformanceMetrics { - /** RTT(往返时间) */ - rtt: number; - /** 带宽利用率 */ - bandwidth: number; - /** 丢包率 */ - packetLoss: number; - /** 抖动 */ - jitter: number; - /** 连接质量评分 */ - quality: number; - /** 最后更新时间 */ - lastUpdate: number; +export interface SyncVarMetadata { + /** 属性名 */ + propertyName: string; + /** 是否仅权威端可修改 */ + authorityOnly: boolean; + /** 变化回调函数名 */ + onChanged?: string; } /** - * 序列化上下文接口 - * 为序列化过程提供上下文信息 + * RPC元数据 */ -export interface ISerializationContext { - /** 目标组件类型 */ - componentType: string; - /** 序列化选项 */ - options?: { - enableValidation?: boolean; - compressionLevel?: number; - }; +export interface RpcMetadata { + /** 方法名 */ + methodName: string; + /** 是否为客户端RPC */ + isClientRpc: boolean; + /** 是否需要权威验证 */ + requiresAuthority: boolean; } -/** - * 类型守卫函数类型定义 - */ -export type TypeGuard = (value: unknown) => value is T; /** - * 常用类型守卫函数 + * 网络连接信息 */ -export const TypeGuards = { - /** 检查是否为SyncVar值 */ - isSyncVarValue: ((value: unknown): value is SyncVarValue => { - return value === null || - value === undefined || - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' || - value instanceof Date || - value instanceof Uint8Array || - (typeof value === 'object' && value !== null); - }) as TypeGuard, - - /** 检查是否为网络消息数据 */ - isMessageData: ((value: unknown): value is MessageData => { - return value === null || - typeof value === 'string' || - typeof value === 'number' || - typeof value === 'boolean' || - value instanceof Uint8Array || - (typeof value === 'object' && value !== null && !(value instanceof Date)); - }) as TypeGuard, - - /** 检查是否为序列化目标 */ - isSerializationTarget: ((value: unknown): value is SerializationTarget => { - return typeof value === 'object' && - value !== null && - 'getNetworkState' in value && - 'applyNetworkState' in value && - typeof (value as { getNetworkState?: unknown }).getNetworkState === 'function'; - }) as TypeGuard -} as const; - -/** - * 网络错误类型枚举 - */ -export enum NetworkErrorType { - CONNECTION_FAILED = 'CONNECTION_FAILED', - SERIALIZATION_FAILED = 'SERIALIZATION_FAILED', - DESERIALIZATION_FAILED = 'DESERIALIZATION_FAILED', - SYNC_VAR_ERROR = 'SYNC_VAR_ERROR', - MESSAGE_TIMEOUT = 'MESSAGE_TIMEOUT', - INVALID_DATA = 'INVALID_DATA', - PERMISSION_DENIED = 'PERMISSION_DENIED' -} - -/** - * 网络错误接口 - */ -export interface INetworkError extends Error { - readonly type: NetworkErrorType; - readonly code?: string | number; - readonly context?: Record; - readonly timestamp: number; -} - -/** - * 创建类型安全的网络错误 - */ -export function createNetworkError( - type: NetworkErrorType, - message: string, - context?: Record -): INetworkError { - const error = new Error(message) as INetworkError; - Object.defineProperty(error, 'type', { value: type, writable: false }); - Object.defineProperty(error, 'context', { value: context, writable: false }); - Object.defineProperty(error, 'timestamp', { value: Date.now(), writable: false }); - return error; +export interface NetworkConnection { + /** 连接ID */ + id: number; + /** 连接状态 */ + state: NetworkConnectionState; + /** 延迟 (ms) */ + latency: number; + /** 最后活跃时间 */ + lastActivity: number; } \ No newline at end of file diff --git a/packages/network/src/types/index.ts b/packages/network/src/types/index.ts deleted file mode 100644 index 9792ed70..00000000 --- a/packages/network/src/types/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -/** - * 网络库类型导出 - */ - -// 核心类型 -export * from './CoreTypes'; -export { - INetworkSyncable, - IBasicNetworkMessage, - NetworkComponentType, - SyncVarValue, - MessageData, - TypeGuards as NetworkTypeGuards -} from './NetworkTypes'; -export { - MessageType, - MessagePriority, - INetworkMessage, - IHeartbeatMessage, - ISyncVarMessage, - IErrorMessage, - ISnapshotMessage, - ITsrpcMessage -} from './MessageTypes'; - -// 常量 -export * from '../constants/NetworkConstants'; - -// 配置 -export * from '../Config/NetworkConfigManager'; - -// 错误处理 -export * from '../Error/NetworkErrorHandler'; \ No newline at end of file diff --git a/packages/network/tests/NetworkComponent.test.ts b/packages/network/tests/NetworkComponent.test.ts deleted file mode 100644 index c5a94f2b..00000000 --- a/packages/network/tests/NetworkComponent.test.ts +++ /dev/null @@ -1,218 +0,0 @@ -import { NetworkRole } from '../src/NetworkRole'; - -// 模拟Component基类 -class Component { - public update(): void { - // 默认空实现 - } -} - -// 模拟INetworkSyncable接口 -interface INetworkSyncable { - getNetworkState(): Uint8Array; - applyNetworkState(data: Uint8Array): void; - getDirtyFields(): number[]; - markClean(): void; - markFieldDirty(fieldNumber: number): void; - isFieldDirty(fieldNumber: number): boolean; -} - -// 简化版NetworkComponent用于测试 -class TestableNetworkComponent extends Component implements INetworkSyncable { - private _dirtyFields: Set = new Set(); - private _fieldTimestamps: Map = new Map(); - - constructor() { - super(); - } - - public getRole(): NetworkRole { - // 模拟环境检测,默认返回客户端 - return NetworkRole.CLIENT; - } - - public isClient(): boolean { - return true; // 在测试中简化为始终是客户端 - } - - public isServer(): boolean { - return false; // 在测试中简化为始终不是服务端 - } - - public onClientUpdate(): void { - // 默认空实现 - } - - public onServerUpdate(): void { - // 默认空实现 - } - - public override update(): void { - if (this.isClient()) { - this.onClientUpdate(); - } else if (this.isServer()) { - this.onServerUpdate(); - } - } - - public getNetworkState(): Uint8Array { - return new Uint8Array([1, 2, 3]); // 模拟数据 - } - - public applyNetworkState(data: Uint8Array): void { - this.markClean(); - } - - public getDirtyFields(): number[] { - return Array.from(this._dirtyFields); - } - - public markClean(): void { - this._dirtyFields.clear(); - } - - public markFieldDirty(fieldNumber: number): void { - this._dirtyFields.add(fieldNumber); - this._fieldTimestamps.set(fieldNumber, Date.now()); - } - - public isFieldDirty(fieldNumber: number): boolean { - return this._dirtyFields.has(fieldNumber); - } - - public getFieldTimestamp(fieldNumber: number): number { - return this._fieldTimestamps.get(fieldNumber) || 0; - } - - public getDirtyFieldsWithTimestamps(): Map { - const result = new Map(); - for (const fieldNumber of this._dirtyFields) { - result.set(fieldNumber, this._fieldTimestamps.get(fieldNumber) || 0); - } - return result; - } -} - -class TestNetworkComponent extends TestableNetworkComponent { - public value: number = 0; - - constructor(value: number = 0) { - super(); - this.value = value; - } - - public override onClientUpdate(): void { - this.value += 1; - this.markFieldDirty(1); - } - - public override onServerUpdate(): void { - this.value += 10; - this.markFieldDirty(1); - } -} - -describe('NetworkComponent', () => { - describe('角色功能', () => { - test('应该正确获取角色信息', () => { - const component = new TestNetworkComponent(); - - expect(component.getRole()).toBe(NetworkRole.CLIENT); - expect(component.isClient()).toBe(true); - expect(component.isServer()).toBe(false); - }); - }); - - describe('更新逻辑', () => { - test('组件应该调用对应的更新方法', () => { - const component = new TestNetworkComponent(5); - - component.update(); - - expect(component.value).toBe(6); // 5 + 1 (客户端更新) - expect(component.getDirtyFields()).toContain(1); - }); - }); - - describe('脏字段管理', () => { - test('应该正确标记和检查脏字段', () => { - const component = new TestNetworkComponent(); - - expect(component.isFieldDirty(1)).toBe(false); - - component.markFieldDirty(1); - - expect(component.isFieldDirty(1)).toBe(true); - expect(component.getDirtyFields()).toContain(1); - }); - - test('应该正确清理脏字段', () => { - const component = new TestNetworkComponent(); - - component.markFieldDirty(1); - component.markFieldDirty(2); - - expect(component.getDirtyFields()).toEqual(expect.arrayContaining([1, 2])); - - component.markClean(); - - expect(component.getDirtyFields()).toEqual([]); - expect(component.isFieldDirty(1)).toBe(false); - expect(component.isFieldDirty(2)).toBe(false); - }); - - test('应该正确记录字段时间戳', () => { - const component = new TestNetworkComponent(); - const beforeTime = Date.now(); - - component.markFieldDirty(1); - - const timestamp = component.getFieldTimestamp(1); - const afterTime = Date.now(); - - expect(timestamp).toBeGreaterThanOrEqual(beforeTime); - expect(timestamp).toBeLessThanOrEqual(afterTime); - }); - - test('应该正确获取脏字段和时间戳', () => { - const component = new TestNetworkComponent(); - - component.markFieldDirty(1); - component.markFieldDirty(3); - - const dirtyFieldsWithTimestamps = component.getDirtyFieldsWithTimestamps(); - - expect(dirtyFieldsWithTimestamps.size).toBe(2); - expect(dirtyFieldsWithTimestamps.has(1)).toBe(true); - expect(dirtyFieldsWithTimestamps.has(3)).toBe(true); - expect(dirtyFieldsWithTimestamps.get(1)).toBeGreaterThan(0); - expect(dirtyFieldsWithTimestamps.get(3)).toBeGreaterThan(0); - }); - }); - - describe('网络状态序列化', () => { - test('应该能获取网络状态', () => { - const component = new TestNetworkComponent(42); - - expect(() => { - const state = component.getNetworkState(); - expect(state).toBeInstanceOf(Uint8Array); - expect(state.length).toBeGreaterThan(0); - }).not.toThrow(); - }); - - test('应该能应用网络状态', () => { - const sourceComponent = new TestNetworkComponent(100); - const targetComponent = new TestNetworkComponent(0); - - const networkState = sourceComponent.getNetworkState(); - - expect(() => { - targetComponent.applyNetworkState(networkState); - }).not.toThrow(); - - // 应用状态后应该清理脏字段 - expect(targetComponent.getDirtyFields()).toEqual([]); - }); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/NetworkCore.test.ts b/packages/network/tests/NetworkCore.test.ts deleted file mode 100644 index 05f47280..00000000 --- a/packages/network/tests/NetworkCore.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { NetworkManager } from '../src/Core/NetworkManager'; -import { MessageHandler } from '../src/Messaging/MessageHandler'; -import { JsonMessage } from '../src/Messaging/NetworkMessage'; - -// 测试消息 -class TestMessage extends JsonMessage<{ text: string }> { - public override readonly messageType: number = 1000; - - constructor(text: string = 'test') { - super({ text }); - } -} - -describe('网络核心功能测试', () => { - let serverPort: number; - - beforeEach(() => { - // 每个测试使用不同端口避免冲突 - serverPort = 8000 + Math.floor(Math.random() * 2000); - }); - - afterEach(async () => { - try { - // 强制重置NetworkManager实例 - const manager = (NetworkManager as any).Instance; - if (manager) { - // 直接重置内部状态 - manager._isServer = false; - manager._isClient = false; - manager._server = null; - manager._client = null; - } - - // 重置单例实例 - (NetworkManager as any)._instance = null; - - // 清理消息处理器 - MessageHandler.Instance.clear(); - - // 短暂等待 - await new Promise(resolve => setTimeout(resolve, 50)); - } catch (error) { - console.warn('清理时发生错误:', error); - } - }, 5000); - - describe('NetworkManager', () => { - test('应该能启动和停止服务端', async () => { - // 启动服务端 - const startResult = await NetworkManager.StartServer(serverPort); - expect(startResult).toBe(true); - expect(NetworkManager.isServer).toBe(true); - expect(NetworkManager.connectionCount).toBe(0); - - // 停止服务端 - await NetworkManager.StopServer(); - expect(NetworkManager.isServer).toBe(false); - }, 10000); - - test('应该能启动和停止客户端', async () => { - // 先启动服务端 - const serverStarted = await NetworkManager.StartServer(serverPort); - expect(serverStarted).toBe(true); - - // 等待服务端完全启动 - await new Promise(resolve => setTimeout(resolve, 200)); - - // 启动客户端 - const connectResult = await NetworkManager.StartClient(`ws://localhost:${serverPort}`); - expect(connectResult).toBe(true); - expect(NetworkManager.isClient).toBe(true); - - // 停止客户端 - await NetworkManager.StopClient(); - expect(NetworkManager.isClient).toBe(false); - }, 10000); - }); - - describe('消息系统', () => { - test('应该能注册和处理消息', async () => { - let receivedMessage: TestMessage | null = null; - - // 注册消息处理器 - MessageHandler.Instance.registerHandler( - 1000, - TestMessage, - { - handle: (message: TestMessage) => { - receivedMessage = message; - } - } - ); - - // 创建测试消息 - const testMessage = new TestMessage('Hello World'); - - // 序列化和反序列化测试 - const serialized = testMessage.serialize(); - expect(serialized.length).toBeGreaterThan(0); - - // 模拟消息处理 - await MessageHandler.Instance.handleRawMessage(serialized); - - // 验证消息被正确处理 - expect(receivedMessage).not.toBeNull(); - expect(receivedMessage!.payload!.text).toBe('Hello World'); - }); - - test('应该能处理多个处理器', async () => { - let handler1Called = false; - let handler2Called = false; - - // 注册多个处理器 - MessageHandler.Instance.registerHandler(1000, TestMessage, { - handle: () => { handler1Called = true; } - }, 0); - - MessageHandler.Instance.registerHandler(1000, TestMessage, { - handle: () => { handler2Called = true; } - }, 1); - - // 发送消息 - const testMessage = new TestMessage('Test'); - await MessageHandler.Instance.handleMessage(testMessage); - - // 验证两个处理器都被调用 - expect(handler1Called).toBe(true); - expect(handler2Called).toBe(true); - }); - }); - - // 暂时跳过端到端通信测试,等其他问题修复后再处理 - describe.skip('端到端通信', () => { - test('客户端和服务端应该能相互通信', async () => { - // 这个测试有复杂的WebSocket连接同步问题,暂时跳过 - }); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/NetworkLibrary.test.ts b/packages/network/tests/NetworkLibrary.test.ts new file mode 100644 index 00000000..076dd641 --- /dev/null +++ b/packages/network/tests/NetworkLibrary.test.ts @@ -0,0 +1,258 @@ +/** + * 网络库基础功能测试 + */ + +import 'reflect-metadata'; +import { NetworkManager } from '../src/NetworkManager'; +import { NetworkIdentity } from '../src/NetworkIdentity'; +import { NetworkBehaviour } from '../src/NetworkBehaviour'; +import { SyncVar } from '../src/decorators/SyncVar'; +import { ClientRpc } from '../src/decorators/ClientRpc'; +import { Command } from '../src/decorators/Command'; +import { NetworkRegistry } from '../src/core/NetworkRegistry'; +import { SyncVarManager } from '../src/core/SyncVarManager'; +import { RpcManager } from '../src/core/RpcManager'; + +// 测试用的玩家组件 +class TestPlayerComponent extends NetworkBehaviour { + @SyncVar({ onChanged: 'onHealthChanged' }) + public health: number = 100; + + @SyncVar() + public playerName: string = 'Player'; + + public lastHealthChangeValue: number = 0; + + @ClientRpc() + public showDamageEffect(damage: number, position: { x: number; y: number }): void { + console.log(`显示伤害特效: ${damage} at (${position.x}, ${position.y})`); + } + + @Command() + public movePlayer(direction: { x: number; y: number }): void { + console.log(`移动玩家: (${direction.x}, ${direction.y})`); + } + + private onHealthChanged(oldValue: number, newValue: number): void { + this.lastHealthChangeValue = newValue; + console.log(`生命值变化: ${oldValue} -> ${newValue}`); + } +} + +// 模拟实体类 +class MockEntity { + private components: any[] = []; + public name: string = 'TestEntity'; + + public addComponent(component: any): void { + this.components.push(component); + component.entity = this; + } + + public getComponent(componentType: any): any { + return this.components.find(c => c instanceof componentType); + } + + public getComponents(): any[] { + return this.components; + } +} + +describe('网络库基础功能测试', () => { + let networkManager: NetworkManager; + let entity: MockEntity; + let networkIdentity: NetworkIdentity; + let playerComponent: TestPlayerComponent; + + beforeEach(() => { + // 重置单例 + (NetworkManager as any)._instance = null; + NetworkRegistry.instance.reset(); + SyncVarManager.instance.clearPendingChanges(); + RpcManager.instance.clearPendingCalls(); + + // 创建网络管理器 + networkManager = new NetworkManager(); + + // 创建测试实体 + entity = new MockEntity(); + networkIdentity = new NetworkIdentity(); + playerComponent = new TestPlayerComponent(); + + entity.addComponent(networkIdentity); + entity.addComponent(playerComponent); + + // 手动调用组件初始化以注册网络行为 + playerComponent.start(); + }); + + afterEach(() => { + networkManager?.destroy(); + }); + + describe('网络身份管理', () => { + test('网络对象注册和查找', () => { + const networkId = networkManager.registerNetworkObject(entity); + + expect(networkId).toBeGreaterThan(0); + expect(networkIdentity.networkId).toBe(networkId); + + const foundIdentity = NetworkRegistry.instance.find(networkId); + expect(foundIdentity).toBe(networkIdentity); + }); + + test('网络权威设置', () => { + networkManager.registerNetworkObject(entity); + + expect(networkIdentity.hasAuthority).toBe(false); + expect(playerComponent.hasAuthority).toBe(false); + + networkIdentity.setAuthority(true, 1); + expect(networkIdentity.hasAuthority).toBe(true); + expect(networkIdentity.ownerId).toBe(1); + expect(playerComponent.hasAuthority).toBe(true); + }); + + test('本地玩家设置', () => { + networkManager.registerNetworkObject(entity); + + expect(networkIdentity.isLocalPlayer).toBe(false); + expect(playerComponent.isLocalPlayer).toBe(false); + + NetworkRegistry.instance.setLocalPlayer(networkIdentity); + expect(networkIdentity.isLocalPlayer).toBe(true); + expect(networkIdentity.hasAuthority).toBe(true); + expect(playerComponent.isLocalPlayer).toBe(true); + }); + }); + + describe('SyncVar 同步变量', () => { + test('SyncVar 属性同步', () => { + networkManager.registerNetworkObject(entity); + networkIdentity.setAuthority(true, 1); + + // 修改同步变量 + playerComponent.health = 80; + playerComponent.playerName = 'TestPlayer'; + + // 检查待同步消息 + const messages = SyncVarManager.instance.getPendingMessages(); + expect(messages.length).toBeGreaterThanOrEqual(2); + + // 验证消息内容 + const healthMessage = messages.find(m => m.propertyName === 'health'); + expect(healthMessage).toBeDefined(); + expect(healthMessage?.value).toBe(80); + + const nameMessage = messages.find(m => m.propertyName === 'playerName'); + expect(nameMessage).toBeDefined(); + expect(nameMessage?.value).toBe('TestPlayer'); + }); + + test('SyncVar 变化回调', () => { + networkManager.registerNetworkObject(entity); + networkIdentity.setAuthority(true, 1); + + expect(playerComponent.lastHealthChangeValue).toBe(0); + + playerComponent.health = 75; + expect(playerComponent.lastHealthChangeValue).toBe(75); + }); + + test('权威验证', () => { + networkManager.registerNetworkObject(entity); + + // 没有权威时不应该能修改 + expect(networkIdentity.hasAuthority).toBe(false); + + const originalHealth = playerComponent.health; + playerComponent.health = 50; + + // 检查是否有待同步消息(没有权威应该没有) + const messages = SyncVarManager.instance.getPendingMessages(); + expect(messages.length).toBe(0); + }); + }); + + describe('RPC 远程过程调用', () => { + test('RPC 方法注册', () => { + networkManager.registerNetworkObject(entity); + + const stats = RpcManager.instance.getStats(); + expect(stats.registeredComponents).toBeGreaterThan(0); + }); + + test('RPC 消息生成', () => { + networkManager.registerNetworkObject(entity); + + // 模拟服务端调用ClientRpc + if (NetworkManager.isServer) { + playerComponent.showDamageEffect(25, { x: 100, y: 200 }); + + const rpcMessages = RpcManager.instance.getPendingRpcMessages(); + const damageMessage = rpcMessages.find(m => m.methodName === 'showDamageEffect'); + expect(damageMessage).toBeDefined(); + expect(damageMessage?.isClientRpc).toBe(true); + } + }); + }); + + describe('网络管理器状态', () => { + test('网络端类型判断', () => { + // 默认应该是客户端 + expect(NetworkManager.isClient).toBe(true); + expect(NetworkManager.isServer).toBe(false); + expect(NetworkManager.isConnected).toBe(false); + }); + + test('连接状态管理', () => { + expect(networkManager.getConnectionState()).toBe('disconnected'); + }); + + test('网络统计信息', () => { + const stats = networkManager.getStats(); + expect(stats).toHaveProperty('connectionCount'); + expect(stats).toHaveProperty('messagesSent'); + expect(stats).toHaveProperty('messagesReceived'); + expect(stats.connectionCount).toBe(0); + }); + }); + + describe('网络注册表管理', () => { + test('多个网络对象管理', () => { + // 创建多个实体 + const entity2 = new MockEntity(); + const networkIdentity2 = new NetworkIdentity(); + entity2.addComponent(networkIdentity2); + const playerComponent2 = new TestPlayerComponent(); + entity2.addComponent(playerComponent2); + playerComponent2.start(); + + const networkId1 = networkManager.registerNetworkObject(entity); + const networkId2 = networkManager.registerNetworkObject(entity2); + + expect(networkId1).not.toBe(networkId2); + expect(NetworkRegistry.instance.getAllNetworkObjects().length).toBe(2); + }); + + test('网络对象注销', () => { + const networkId = networkManager.registerNetworkObject(entity); + + expect(NetworkRegistry.instance.exists(networkId)).toBe(true); + + NetworkRegistry.instance.unregister(networkId); + + expect(NetworkRegistry.instance.exists(networkId)).toBe(false); + expect(NetworkRegistry.instance.find(networkId)).toBeNull(); + }); + + test('按所有者查找对象', () => { + const networkId = networkManager.registerNetworkObject(entity); + networkIdentity.setAuthority(true, 123); + + const ownedObjects = NetworkRegistry.instance.getObjectsByOwner(123); + expect(ownedObjects.length).toBe(1); + expect(ownedObjects[0]).toBe(networkIdentity); + }); + }); +}); \ No newline at end of file diff --git a/packages/network/tests/Serialization/TsrpcSerializer.test.ts b/packages/network/tests/Serialization/TsrpcSerializer.test.ts deleted file mode 100644 index 745f069c..00000000 --- a/packages/network/tests/Serialization/TsrpcSerializer.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Component } from '@esengine/ecs-framework'; -import { TsrpcSerializer, SyncField } from '../../src/Serialization'; -import { TsrpcSerializable } from '../../src/Serialization/TsrpcDecorators'; - -@TsrpcSerializable() -class TestComponent extends Component { - @SyncField() - public health: number = 100; - - @SyncField() - public name: string = 'Test'; - - @SyncField() - public isActive: boolean = true; -} - -describe('TsrpcSerializer', () => { - let serializer: TsrpcSerializer; - let testComponent: TestComponent; - - beforeEach(() => { - serializer = TsrpcSerializer.getInstance(); - testComponent = new TestComponent(); - testComponent.health = 80; - testComponent.name = 'Player'; - testComponent.isActive = false; - }); - - describe('序列化', () => { - it('应该能序列化TSRPC组件', () => { - const result = serializer.serialize(testComponent); - - expect(result).not.toBeNull(); - expect(result?.type).toBe('tsrpc'); - expect(result?.componentType).toBe('TestComponent'); - expect(result?.data).toBeInstanceOf(Uint8Array); - expect(result?.size).toBeGreaterThan(0); - }); - - it('不支持的组件应该返回null', () => { - // 创建一个没有装饰器的组件类 - class UnsupportedComponent extends Component {} - const unsupportedComponent = new UnsupportedComponent(); - const result = serializer.serialize(unsupportedComponent); - - expect(result).toBeNull(); - }); - }); - - describe('反序列化', () => { - it('应该能反序列化TSRPC数据', () => { - // 先序列化 - const serializedData = serializer.serialize(testComponent); - expect(serializedData).not.toBeNull(); - - // 再反序列化 - const deserializedComponent = serializer.deserialize(serializedData!, TestComponent); - - expect(deserializedComponent).not.toBeNull(); - expect(deserializedComponent?.health).toBe(80); - expect(deserializedComponent?.name).toBe('Player'); - expect(deserializedComponent?.isActive).toBe(false); - }); - - it('错误的数据类型应该返回null', () => { - const invalidData = { - type: 'json' as const, - componentType: 'TestComponent', - data: {}, - size: 0 - }; - - const result = serializer.deserialize(invalidData); - expect(result).toBeNull(); - }); - }); - - describe('统计信息', () => { - it('应该正确更新统计信息', () => { - const initialStats = serializer.getStats(); - - // 执行序列化 - serializer.serialize(testComponent); - - const afterSerializeStats = serializer.getStats(); - expect(afterSerializeStats.serializeCount).toBe(initialStats.serializeCount + 1); - - // 执行反序列化 - const serializedData = serializer.serialize(testComponent); - if (serializedData) { - serializer.deserialize(serializedData, TestComponent); - } - - const finalStats = serializer.getStats(); - expect(finalStats.deserializeCount).toBe(initialStats.deserializeCount + 1); - }); - }); - - describe('性能功能', () => { - it('应该正确计算序列化大小', () => { - const initialStats = serializer.getStats(); - - // 执行序列化 - const result = serializer.serialize(testComponent); - - expect(result).not.toBeNull(); - expect(result?.size).toBeGreaterThan(0); - - const finalStats = serializer.getStats(); - expect(finalStats.averageSerializedSize).toBeGreaterThan(0); - }); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/Serialization/index.test.ts b/packages/network/tests/Serialization/index.test.ts deleted file mode 100644 index 6ace3719..00000000 --- a/packages/network/tests/Serialization/index.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * 序列化模块集成测试 - */ - -// 导入所有测试 -import './TsrpcSerializer.test'; - -// 这个文件确保所有序列化相关的测试都被包含在测试套件中 -describe('序列化模块集成测试', () => { - it('应该包含所有序列化测试', () => { - // 这个测试确保模块正确加载 - expect(true).toBe(true); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/SyncVar.test.ts b/packages/network/tests/SyncVar.test.ts deleted file mode 100644 index 09b987c6..00000000 --- a/packages/network/tests/SyncVar.test.ts +++ /dev/null @@ -1,261 +0,0 @@ -import { SyncVar, getSyncVarMetadata, SyncVarManager } from '../src/SyncVar'; -import { createNetworkComponent } from '../src/SyncVar/SyncVarFactory'; -import { createSyncVarProxy } from '../src/SyncVar/SyncVarProxy'; -import { NetworkComponent } from '../src/NetworkComponent'; - -// 测试用的组件类 -class TestPlayerComponent extends NetworkComponent { - private _hasAuthority: boolean = false; - - public hasAuthority(): boolean { - return this._hasAuthority; - } - - public setAuthority(hasAuthority: boolean): void { - this._hasAuthority = hasAuthority; - } - @SyncVar() - public health: number = 100; - - @SyncVar({ hook: 'onNameChanged' }) - public playerName: string = 'Player'; - - @SyncVar({ authorityOnly: true }) - public isReady: boolean = false; - - @SyncVar() - public position = { x: 0, y: 0 }; - - // Hook回调函数 - public onNameChangedCallCount = 0; - public lastNameChange: { oldName: string; newName: string } | null = null; - - onNameChanged(oldName: string, newName: string) { - this.onNameChangedCallCount++; - this.lastNameChange = { oldName, newName }; - console.log(`Name changed: ${oldName} -> ${newName}`); - } -} - -class TestComponentWithoutSyncVar extends NetworkComponent { - public normalField: number = 42; - - public hasAuthority(): boolean { return true; } -} - -describe('SyncVar系统测试', () => { - beforeEach(() => { - // 清理SyncVar管理器 - const manager = SyncVarManager.Instance; - manager['_componentChanges'].clear(); - manager['_lastSyncTimes'].clear(); - }); - - describe('装饰器和元数据', () => { - test('应该正确收集SyncVar元数据', () => { - const metadata = getSyncVarMetadata(TestPlayerComponent); - - expect(metadata.length).toBe(4); - - const healthMeta = metadata.find(m => m.propertyKey === 'health'); - expect(healthMeta).toBeDefined(); - expect(healthMeta!.fieldNumber).toBe(1); - expect(healthMeta!.options.hook).toBeUndefined(); - - const nameMeta = metadata.find(m => m.propertyKey === 'playerName'); - expect(nameMeta).toBeDefined(); - expect(nameMeta!.options.hook).toBe('onNameChanged'); - - const readyMeta = metadata.find(m => m.propertyKey === 'isReady'); - expect(readyMeta).toBeDefined(); - expect(readyMeta!.options.authorityOnly).toBe(true); - }); - - test('没有SyncVar的组件应该返回空元数据', () => { - const metadata = getSyncVarMetadata(TestComponentWithoutSyncVar); - expect(metadata.length).toBe(0); - }); - }); - - describe('代理和变化检测', () => { - test('代理应该能检测到字段变化', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 修改SyncVar字段 - proxy.health = 80; - - const changes = proxy.getSyncVarChanges(); - expect(changes.length).toBe(1); - expect(changes[0].propertyKey).toBe('health'); - expect(changes[0].oldValue).toBe(100); - expect(changes[0].newValue).toBe(80); - }); - - test('非SyncVar字段不应该被记录', () => { - class TestMixedComponent extends NetworkComponent { - @SyncVar() - public syncField: number = 1; - - public normalField: number = 2; - - public hasAuthority(): boolean { return true; } - } - - const instance = new TestMixedComponent(); - const proxy = createSyncVarProxy(instance); - - // 修改SyncVar字段 - proxy.syncField = 10; - // 修改普通字段 - proxy.normalField = 20; - - const changes = proxy.getSyncVarChanges(); - expect(changes.length).toBe(1); - expect(changes[0].propertyKey).toBe('syncField'); - }); - - test('Hook回调应该被触发', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 修改带hook的字段 - proxy.playerName = 'NewPlayer'; - - expect(proxy.onNameChangedCallCount).toBe(1); - expect(proxy.lastNameChange).toEqual({ - oldName: 'Player', - newName: 'NewPlayer' - }); - }); - - test('相同值不应该触发变化记录', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 设置相同的值 - proxy.health = 100; // 原始值就是100 - - const changes = proxy.getSyncVarChanges(); - expect(changes.length).toBe(0); - }); - }); - - describe('同步数据创建和应用', () => { - test('应该能创建同步数据', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 修改多个字段 - proxy.health = 75; - proxy.playerName = 'Hero'; - - const syncData = proxy.createSyncVarData(); - expect(syncData).not.toBeNull(); - expect(syncData.componentType).toBe('TestPlayerComponent'); - expect(syncData.fieldUpdates.length).toBe(2); - }); - - test('没有变化时不应该创建同步数据', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - const syncData = proxy.createSyncVarData(); - expect(syncData).toBeNull(); - }); - - test('应该能应用同步数据', () => { - const sourceInstance = new TestPlayerComponent(); - const sourceProxy = createSyncVarProxy(sourceInstance); - - const targetInstance = new TestPlayerComponent(); - const targetProxy = createSyncVarProxy(targetInstance); - - // 修改源实例 - sourceProxy.health = 60; - sourceProxy.playerName = 'Warrior'; - - // 创建同步数据 - const syncData = sourceProxy.createSyncVarData(); - expect(syncData).not.toBeNull(); - - // 应用到目标实例 - targetProxy.applySyncVarData(syncData); - - // 验证目标实例的值已更新 - expect(targetProxy.health).toBe(60); - expect(targetProxy.playerName).toBe('Warrior'); - - // 验证hook被触发 - expect(targetProxy.onNameChangedCallCount).toBe(1); - }); - }); - - describe('对象类型同步', () => { - test('应该能同步对象类型', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 修改对象字段 - proxy.position = { x: 100, y: 200 }; - - const changes = proxy.getSyncVarChanges(); - expect(changes.length).toBe(1); - expect(changes[0].propertyKey).toBe('position'); - expect(changes[0].newValue).toEqual({ x: 100, y: 200 }); - }); - - test('对象浅比较应该正确工作', () => { - const instance = new TestPlayerComponent(); - const proxy = createSyncVarProxy(instance); - - // 设置相同的对象值 - proxy.position = { x: 0, y: 0 }; // 原始值 - - const changes = proxy.getSyncVarChanges(); - expect(changes.length).toBe(0); // 应该没有变化 - }); - }); - - describe('工厂函数', () => { - test('createNetworkComponent应该为有SyncVar的组件创建代理', () => { - const component = createNetworkComponent(TestPlayerComponent); - - expect(component.hasSyncVars()).toBe(true); - - // 测试代理功能 - component.health = 90; - const changes = component.getSyncVarChanges(); - expect(changes.length).toBe(1); - }); - - test('createNetworkComponent应该为没有SyncVar的组件返回原实例', () => { - const component = createNetworkComponent(TestComponentWithoutSyncVar); - - expect(component.hasSyncVars()).toBe(false); - - // 修改普通字段不应该有变化记录 - component.normalField = 999; - const changes = component.getSyncVarChanges(); - expect(changes.length).toBe(0); - }); - }); - - describe('管理器统计', () => { - test('应该能获取管理器统计信息', () => { - const component1 = createNetworkComponent(TestPlayerComponent); - const component2 = createNetworkComponent(TestPlayerComponent); - - component1.health = 80; - component2.health = 70; - component2.playerName = 'Test'; - - const manager = SyncVarManager.Instance; - const stats = manager.getStats(); - - expect(stats.totalComponents).toBe(2); - expect(stats.totalChanges).toBe(3); // 1 + 2 = 3 - expect(stats.pendingChanges).toBe(3); - }); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/SyncVarE2E.test.ts b/packages/network/tests/SyncVarE2E.test.ts deleted file mode 100644 index a6873309..00000000 --- a/packages/network/tests/SyncVarE2E.test.ts +++ /dev/null @@ -1,400 +0,0 @@ -import { NetworkIdentity, NetworkIdentityRegistry } from '../src/Core/NetworkIdentity'; -import { SyncVar, SyncVarManager } from '../src/SyncVar'; -import { createSyncVarProxy } from '../src/SyncVar/SyncVarProxy'; -import { SyncVarSyncScheduler } from '../src/SyncVar/SyncVarSyncScheduler'; -import { SyncVarOptimizer } from '../src/SyncVar/SyncVarOptimizer'; -import { SyncVarUpdateMessage } from '../src/Messaging/MessageTypes'; -import { NetworkComponent } from '../src/NetworkComponent'; -import { NetworkEnvironment, NetworkEnvironmentState } from '../src/Core/NetworkEnvironment'; - -// 测试用网络组件 -class TestGameObject extends NetworkComponent { - @SyncVar() - public health: number = 100; - - @SyncVar({ hook: 'onPositionChanged' }) - public position: { x: number; y: number } = { x: 0, y: 0 }; - - @SyncVar({ authorityOnly: true }) - public serverFlag: boolean = false; - - @SyncVar() - public playerName: string = 'TestPlayer'; - - public positionChangeCount: number = 0; - - onPositionChanged(oldPos: any, newPos: any) { - this.positionChangeCount++; - console.log(`Position changed from ${JSON.stringify(oldPos)} to ${JSON.stringify(newPos)}`); - } -} - -describe('SyncVar端到端测试', () => { - let gameObject1: TestGameObject; - let gameObject2: TestGameObject; - let identity1: NetworkIdentity; - let identity2: NetworkIdentity; - let syncVarManager: SyncVarManager; - let syncScheduler: SyncVarSyncScheduler; - let optimizer: SyncVarOptimizer; - - // 消息交换模拟 - let messageExchange: Map = new Map(); - - beforeEach(async () => { - // 重置环境 - const env = NetworkEnvironment['Instance']; - env['_state'] = NetworkEnvironmentState.None; - env['_serverStartTime'] = 0; - env['_clientConnectTime'] = 0; - NetworkEnvironment.SetServerMode(); - - // 清理组件 - syncVarManager = SyncVarManager.Instance; - syncVarManager['_componentChanges'].clear(); - syncVarManager['_lastSyncTimes'].clear(); - - syncScheduler = SyncVarSyncScheduler.Instance; - optimizer = new SyncVarOptimizer(); - messageExchange.clear(); - - // 创建测试对象 - gameObject1 = createSyncVarProxy(new TestGameObject()) as TestGameObject; - gameObject2 = createSyncVarProxy(new TestGameObject()) as TestGameObject; - - // 创建网络身份 - identity1 = new NetworkIdentity('player1', true); - identity2 = new NetworkIdentity('player2', false); - - // 初始化SyncVar系统 - syncVarManager.initializeComponent(gameObject1); - syncVarManager.initializeComponent(gameObject2); - - // 模拟消息发送回调 - syncScheduler.setMessageSendCallback(async (message: SyncVarUpdateMessage) => { - // 将消息添加到交换队列 - const messages = messageExchange.get(message.networkId) || []; - messages.push(message); - messageExchange.set(message.networkId, messages); - - console.log(`[E2E] 模拟发送消息: ${message.networkId} -> ${message.fieldUpdates.length} 字段更新`); - }); - }); - - afterEach(async () => { - // 清理 - identity1.cleanup(); - identity2.cleanup(); - NetworkIdentityRegistry.Instance.clear(); - syncScheduler.stop(); - optimizer.cleanup(); - // NetworkManager.Stop(); - }); - - test('基本SyncVar同步流程', async () => { - // 修改gameObject1的属性 - gameObject1.health = 80; - gameObject1.playerName = 'Hero'; - gameObject1.position = { x: 10, y: 20 }; - - // 检查是否有待同步的变化 - const changes = syncVarManager.getPendingChanges(gameObject1); - expect(changes.length).toBe(3); - - // 创建同步消息 - const message = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId, - 'server', - 1 - ); - - expect(message).not.toBeNull(); - expect(message!.fieldUpdates.length).toBe(3); - expect(message!.networkId).toBe('player1'); - - // 验证字段更新内容 - const healthUpdate = message!.fieldUpdates.find(u => u.propertyKey === 'health'); - expect(healthUpdate).toBeDefined(); - expect(healthUpdate!.newValue).toBe(80); - expect(healthUpdate!.oldValue).toBe(100); - }); - - test('消息序列化和反序列化', async () => { - // 修改属性 - gameObject1.health = 75; - gameObject1.position = { x: 5, y: 15 }; - - // 创建消息 - const originalMessage = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId - ); - - expect(originalMessage).not.toBeNull(); - - // 序列化 - const serialized = originalMessage!.serialize(); - expect(serialized.length).toBeGreaterThan(0); - - // 反序列化 - const deserializedMessage = new SyncVarUpdateMessage(); - deserializedMessage.deserialize(serialized); - - // 验证反序列化结果 - expect(deserializedMessage.networkId).toBe(originalMessage!.networkId); - expect(deserializedMessage.componentType).toBe(originalMessage!.componentType); - expect(deserializedMessage.fieldUpdates.length).toBe(originalMessage!.fieldUpdates.length); - - // 验证字段内容 - for (let i = 0; i < originalMessage!.fieldUpdates.length; i++) { - const original = originalMessage!.fieldUpdates[i]; - const deserialized = deserializedMessage.fieldUpdates[i]; - - expect(deserialized.fieldNumber).toBe(original.fieldNumber); - expect(deserialized.propertyKey).toBe(original.propertyKey); - expect(deserialized.newValue).toEqual(original.newValue); - } - }); - - test('SyncVar消息应用', async () => { - // 在gameObject1上创建变化 - gameObject1.health = 60; - gameObject1.playerName = 'Warrior'; - - // 创建消息 - const message = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId - ); - - expect(message).not.toBeNull(); - - // 清除gameObject1的变化记录 - syncVarManager.clearChanges(gameObject1); - - // 应用到gameObject2 - syncVarManager.applySyncVarUpdateMessage(gameObject2, message!); - - // 验证gameObject2的状态 - expect(gameObject2.health).toBe(60); - expect(gameObject2.playerName).toBe('Warrior'); - - // 验证Hook被触发 - expect(gameObject2.positionChangeCount).toBe(0); // position没有改变 - }); - - test('Hook回调触发', async () => { - // 修改position触发hook - gameObject1.position = { x: 100, y: 200 }; - - // 创建并应用消息 - const message = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId - ); - - expect(message).not.toBeNull(); - - // 应用到gameObject2 - syncVarManager.applySyncVarUpdateMessage(gameObject2, message!); - - // 验证Hook被触发 - expect(gameObject2.positionChangeCount).toBe(1); - expect(gameObject2.position).toEqual({ x: 100, y: 200 }); - }); - - test('权威字段保护', async () => { - // 切换到客户端环境 - const env = NetworkEnvironment['Instance']; - env['_state'] = NetworkEnvironmentState.None; - NetworkEnvironment.SetClientMode(); - - // 客户端尝试修改权威字段 - gameObject1.serverFlag = true; // 这应该被阻止 - - // 检查是否有待同步变化 - const changes = syncVarManager.getPendingChanges(gameObject1); - expect(changes.length).toBe(0); // 应该没有变化被记录 - - // 尝试创建消息 - const message = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId - ); - - expect(message).toBeNull(); // 应该没有消息 - }); - - test('消息优化器功能', async () => { - // 配置优化器 - optimizer.configure({ - enableMessageMerging: true, - mergeTimeWindow: 50, - enableRateLimit: true, - maxMessagesPerSecond: 10 - }); - - // 快速连续修改属性 - gameObject1.health = 90; - gameObject1.health = 80; - gameObject1.health = 70; - - const messages: SyncVarUpdateMessage[] = []; - - // 创建多个消息 - for (let i = 0; i < 3; i++) { - const msg = syncVarManager.createSyncVarUpdateMessage( - gameObject1, - identity1.networkId, - 'server', - i + 1 - ); - if (msg) { - messages.push(msg); - } - } - - expect(messages.length).toBeGreaterThan(0); - - // 测试优化器处理 - let optimizedCount = 0; - - for (const message of messages) { - optimizer.optimizeMessage(message, ['observer1'], (optimizedMessages, observers) => { - optimizedCount++; - expect(optimizedMessages.length).toBeGreaterThan(0); - expect(observers.length).toBeGreaterThan(0); - }); - } - - // 等待合并完成 - await new Promise(resolve => setTimeout(resolve, 100)); - - // 强制刷新优化器 - optimizer.flush(() => { - optimizedCount++; - }); - - expect(optimizedCount).toBeGreaterThan(0); - - // 检查统计信息 - const stats = optimizer.getStats(); - expect(stats.messagesProcessed).toBeGreaterThan(0); - }); - - test('网络对象身份管理', async () => { - const registry = NetworkIdentityRegistry.Instance; - - // 验证对象已注册 - const foundIdentity1 = registry.find(identity1.networkId); - const foundIdentity2 = registry.find(identity2.networkId); - - expect(foundIdentity1).toBeDefined(); - expect(foundIdentity2).toBeDefined(); - expect(foundIdentity1!.networkId).toBe('player1'); - expect(foundIdentity2!.networkId).toBe('player2'); - - // 测试权威对象查询 - const authorityObjects = registry.getAuthorityObjects(); - expect(authorityObjects.length).toBe(1); - expect(authorityObjects[0].networkId).toBe('player1'); - - // 测试激活状态 - identity1.activate(); - identity2.activate(); - - const activeObjects = registry.getActiveObjects(); - expect(activeObjects.length).toBe(2); - - // 测试统计信息 - const stats = registry.getStats(); - expect(stats.totalObjects).toBe(2); - expect(stats.activeObjects).toBe(2); - expect(stats.authorityObjects).toBe(1); - }); - - test('同步调度器集成测试', async () => { - // 配置调度器 - syncScheduler.configure({ - syncInterval: 50, - maxBatchSize: 5, - enablePrioritySort: true - }); - - // 激活网络对象 - identity1.activate(); - identity2.activate(); - - // 修改多个对象的属性 - gameObject1.health = 85; - gameObject1.playerName = 'Hero1'; - - gameObject2.health = 75; - gameObject2.playerName = 'Hero2'; - - // 启动调度器 - syncScheduler.start(); - - // 等待调度器处理 - await new Promise(resolve => setTimeout(resolve, 200)); - - // 检查消息交换 - const messages1 = messageExchange.get('player1') || []; - const messages2 = messageExchange.get('player2') || []; - - console.log(`Player1 messages: ${messages1.length}, Player2 messages: ${messages2.length}`); - - // 停止调度器 - syncScheduler.stop(); - - // 检查统计信息 - const schedulerStats = syncScheduler.getStats(); - expect(schedulerStats.totalSyncCycles).toBeGreaterThan(0); - }); - - test('完整的客户端-服务端模拟', async () => { - // 服务端环境设置 - NetworkEnvironment.SetServerMode(); - const serverObject = createSyncVarProxy(new TestGameObject()) as TestGameObject; - const serverIdentity = new NetworkIdentity('server_obj', true); - serverIdentity.activate(); - - syncVarManager.initializeComponent(serverObject); - - // 客户端环境设置 - const env = NetworkEnvironment['Instance']; - env['_state'] = NetworkEnvironmentState.None; - NetworkEnvironment.SetClientMode(); - - const clientObject = createSyncVarProxy(new TestGameObject()) as TestGameObject; - syncVarManager.initializeComponent(clientObject); - - // 服务端修改数据 - NetworkEnvironment.SetServerMode(); - serverObject.health = 50; - serverObject.playerName = 'ServerPlayer'; - serverObject.position = { x: 30, y: 40 }; - - // 创建服务端消息 - const serverMessage = syncVarManager.createSyncVarUpdateMessage( - serverObject, - serverIdentity.networkId, - 'server' - ); - - expect(serverMessage).not.toBeNull(); - - // 切换到客户端接收消息 - NetworkEnvironment.SetClientMode(); - syncVarManager.applySyncVarUpdateMessage(clientObject, serverMessage!); - - // 验证客户端状态 - expect(clientObject.health).toBe(50); - expect(clientObject.playerName).toBe('ServerPlayer'); - expect(clientObject.position).toEqual({ x: 30, y: 40 }); - expect(clientObject.positionChangeCount).toBe(1); - - console.log('[E2E] 客户端-服务端同步测试完成'); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/SyncVarE2ESimple.test.ts b/packages/network/tests/SyncVarE2ESimple.test.ts deleted file mode 100644 index 84565999..00000000 --- a/packages/network/tests/SyncVarE2ESimple.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import 'reflect-metadata'; -import { SyncVar } from '../src/SyncVar'; -import { createSyncVarProxy } from '../src/SyncVar/SyncVarProxy'; -import { NetworkComponent } from '../src/NetworkComponent'; - -// 简化的测试用网络组件 -class SimpleTestComponent extends NetworkComponent { - @SyncVar() - public health: number = 100; - - @SyncVar() - public name: string = 'TestPlayer'; -} - -describe('SyncVar端到端简单测试', () => { - test('基本的SyncVar代理创建', () => { - const component = new SimpleTestComponent(); - const proxiedComponent = createSyncVarProxy(component) as SimpleTestComponent; - - expect(proxiedComponent).toBeDefined(); - expect(proxiedComponent.health).toBe(100); - expect(proxiedComponent.name).toBe('TestPlayer'); - - // 修改值应该能正常工作 - proxiedComponent.health = 80; - expect(proxiedComponent.health).toBe(80); - }); - - test('SyncVar变化记录', () => { - const component = createSyncVarProxy(new SimpleTestComponent()) as SimpleTestComponent; - - // 修改值 - component.health = 75; - component.name = 'Hero'; - - // 检查是否有变化记录 - const changes = component.getSyncVarChanges(); - expect(changes.length).toBeGreaterThan(0); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/SyncVarMessage.test.ts b/packages/network/tests/SyncVarMessage.test.ts deleted file mode 100644 index 5b10adbd..00000000 --- a/packages/network/tests/SyncVarMessage.test.ts +++ /dev/null @@ -1,430 +0,0 @@ -import { SyncVarUpdateMessage, SyncVarFieldUpdate, MessageType } from '../src/Messaging/MessageTypes'; -import { SyncVar, getSyncVarMetadata, SyncVarManager } from '../src/SyncVar'; -import { createSyncVarProxy } from '../src/SyncVar/SyncVarProxy'; -import { NetworkEnvironment, NetworkEnvironmentState } from '../src/Core/NetworkEnvironment'; -import { NetworkComponent } from '../src/NetworkComponent'; - -// 测试用的组件类 -class TestPlayerComponent extends NetworkComponent { - private _hasAuthority: boolean = false; - - public hasAuthority(): boolean { - return this._hasAuthority; - } - - public setAuthority(hasAuthority: boolean): void { - this._hasAuthority = hasAuthority; - } - @SyncVar() - public health: number = 100; - - @SyncVar({ hook: 'onNameChanged' }) - public playerName: string = 'Player'; - - @SyncVar({ authorityOnly: true }) - public isReady: boolean = false; - - @SyncVar() - public position = { x: 0, y: 0 }; - - // Hook回调函数 - public onNameChangedCallCount = 0; - public lastNameChange: { oldName: string; newName: string } | null = null; - - onNameChanged(oldName: string, newName: string) { - this.onNameChangedCallCount++; - this.lastNameChange = { oldName, newName }; - console.log(`Name changed: ${oldName} -> ${newName}`); - } -} - -describe('SyncVar消息系统测试', () => { - let syncVarManager: SyncVarManager; - - beforeEach(() => { - // 重置网络环境 - 先清除所有状态再设置服务端 - const env = NetworkEnvironment['Instance']; - env['_state'] = NetworkEnvironmentState.None; - env['_serverStartTime'] = 0; - env['_clientConnectTime'] = 0; - NetworkEnvironment.SetServerMode(); - - // 获取SyncVar管理器实例 - syncVarManager = SyncVarManager.Instance; - - // 清理管理器状态 - syncVarManager['_componentChanges'].clear(); - syncVarManager['_lastSyncTimes'].clear(); - }); - - describe('SyncVarUpdateMessage基础功能', () => { - test('应该能正确创建SyncVarUpdateMessage', () => { - const fieldUpdates: SyncVarFieldUpdate[] = [ - { - fieldNumber: 1, - propertyKey: 'health', - newValue: 80, - oldValue: 100, - timestamp: Date.now(), - authorityOnly: false - } - ]; - - const message = new SyncVarUpdateMessage( - 'player_001', - 'TestPlayerComponent', - fieldUpdates, - false, - 'server_001', - 123 - ); - - expect(message.messageType).toBe(MessageType.SYNC_VAR_UPDATE); - expect(message.networkId).toBe('player_001'); - expect(message.componentType).toBe('TestPlayerComponent'); - expect(message.fieldUpdates.length).toBe(1); - expect(message.isFullSync).toBe(false); - expect(message.senderId).toBe('server_001'); - expect(message.syncSequence).toBe(123); - }); - - test('应该能添加和移除字段更新', () => { - const message = new SyncVarUpdateMessage(); - - expect(message.hasUpdates()).toBe(false); - expect(message.getUpdateCount()).toBe(0); - - const fieldUpdate: SyncVarFieldUpdate = { - fieldNumber: 1, - propertyKey: 'health', - newValue: 80, - timestamp: Date.now() - }; - - message.addFieldUpdate(fieldUpdate); - expect(message.hasUpdates()).toBe(true); - expect(message.getUpdateCount()).toBe(1); - - const retrieved = message.getFieldUpdate(1); - expect(retrieved).toBeDefined(); - expect(retrieved?.propertyKey).toBe('health'); - - const removed = message.removeFieldUpdate(1); - expect(removed).toBe(true); - expect(message.hasUpdates()).toBe(false); - }); - - test('应该能序列化和反序列化消息', () => { - const fieldUpdates: SyncVarFieldUpdate[] = [ - { - fieldNumber: 1, - propertyKey: 'health', - newValue: 75, - oldValue: 100, - timestamp: Date.now() - }, - { - fieldNumber: 2, - propertyKey: 'playerName', - newValue: 'Hero', - oldValue: 'Player', - timestamp: Date.now() - } - ]; - - const originalMessage = new SyncVarUpdateMessage( - 'player_001', - 'TestPlayerComponent', - fieldUpdates, - true, - 'server_001', - 456 - ); - - // 序列化 - const serializedData = originalMessage.serialize(); - expect(serializedData.length).toBeGreaterThan(0); - - // 反序列化 - const deserializedMessage = new SyncVarUpdateMessage(); - deserializedMessage.deserialize(serializedData); - - // 验证反序列化结果 - expect(deserializedMessage.networkId).toBe(originalMessage.networkId); - expect(deserializedMessage.componentType).toBe(originalMessage.componentType); - expect(deserializedMessage.fieldUpdates.length).toBe(originalMessage.fieldUpdates.length); - expect(deserializedMessage.isFullSync).toBe(originalMessage.isFullSync); - expect(deserializedMessage.senderId).toBe(originalMessage.senderId); - expect(deserializedMessage.syncSequence).toBe(originalMessage.syncSequence); - }); - - test('应该能获取消息统计信息', () => { - const message = new SyncVarUpdateMessage(); - - // 空消息统计 - let stats = message.getStats(); - expect(stats.updateCount).toBe(0); - expect(stats.hasAuthorityOnlyFields).toBe(false); - expect(stats.oldestUpdateTime).toBe(0); - expect(stats.newestUpdateTime).toBe(0); - - // 添加字段更新 - const now = Date.now(); - message.addFieldUpdate({ - fieldNumber: 1, - propertyKey: 'health', - newValue: 80, - timestamp: now - 1000 - }); - - message.addFieldUpdate({ - fieldNumber: 2, - propertyKey: 'isReady', - newValue: true, - timestamp: now, - authorityOnly: true - }); - - stats = message.getStats(); - expect(stats.updateCount).toBe(2); - expect(stats.hasAuthorityOnlyFields).toBe(true); - expect(stats.oldestUpdateTime).toBe(now - 1000); - expect(stats.newestUpdateTime).toBe(now); - }); - }); - - describe('SyncVarManager消息集成', () => { - test('应该能从组件变化创建SyncVarUpdateMessage', () => { - const component = new TestPlayerComponent(); - const proxy = createSyncVarProxy(component); - - // 初始化组件 - syncVarManager.initializeComponent(proxy); - - // 修改字段 - proxy.health = 75; - proxy.playerName = 'Hero'; - - // 创建消息 - const message = syncVarManager.createSyncVarUpdateMessage( - proxy, - 'player_001', - 'server_001', - 100 - ); - - expect(message).not.toBeNull(); - expect(message!.networkId).toBe('player_001'); - expect(message!.componentType).toBe('TestPlayerComponent'); - expect(message!.senderId).toBe('server_001'); - expect(message!.syncSequence).toBe(100); - expect(message!.fieldUpdates.length).toBe(2); - - // 验证字段更新内容 - const healthUpdate = message!.fieldUpdates.find(u => u.propertyKey === 'health'); - expect(healthUpdate).toBeDefined(); - expect(healthUpdate!.newValue).toBe(75); - expect(healthUpdate!.oldValue).toBe(100); - - const nameUpdate = message!.fieldUpdates.find(u => u.propertyKey === 'playerName'); - expect(nameUpdate).toBeDefined(); - expect(nameUpdate!.newValue).toBe('Hero'); - expect(nameUpdate!.oldValue).toBe('Player'); - }); - - test('没有变化时应该返回null', () => { - const component = new TestPlayerComponent(); - const proxy = createSyncVarProxy(component); - - syncVarManager.initializeComponent(proxy); - - // 没有修改任何字段 - const message = syncVarManager.createSyncVarUpdateMessage(proxy); - - expect(message).toBeNull(); - }); - - test('应该能应用SyncVarUpdateMessage到组件', () => { - const sourceComponent = new TestPlayerComponent(); - const sourceProxy = createSyncVarProxy(sourceComponent); - - const targetComponent = new TestPlayerComponent(); - const targetProxy = createSyncVarProxy(targetComponent); - - // 初始化组件 - syncVarManager.initializeComponent(sourceProxy); - syncVarManager.initializeComponent(targetProxy); - - // 修改源组件 - sourceProxy.health = 60; - sourceProxy.playerName = 'Warrior'; - - // 创建消息 - const message = syncVarManager.createSyncVarUpdateMessage( - sourceProxy, - 'player_001' - ); - - expect(message).not.toBeNull(); - - // 应用到目标组件 - syncVarManager.applySyncVarUpdateMessage(targetProxy, message!); - - // 验证目标组件状态 - expect(targetProxy.health).toBe(60); - expect(targetProxy.playerName).toBe('Warrior'); - - // 验证hook被触发 - expect(targetProxy.onNameChangedCallCount).toBe(1); - expect(targetProxy.lastNameChange).toEqual({ - oldName: 'Player', - newName: 'Warrior' - }); - }); - - test('应该能批量创建多个组件的消息', () => { - const component1 = createSyncVarProxy(new TestPlayerComponent()); - const component2 = createSyncVarProxy(new TestPlayerComponent()); - - syncVarManager.initializeComponent(component1); - syncVarManager.initializeComponent(component2); - - // 修改组件 - component1.health = 80; - component2.playerName = 'Hero2'; - - // 批量创建消息 - const messages = syncVarManager.createBatchSyncVarUpdateMessages( - [component1, component2], - ['player_001', 'player_002'], - 'server_001', - 200 - ); - - expect(messages.length).toBe(2); - - expect(messages[0].networkId).toBe('player_001'); - expect(messages[0].syncSequence).toBe(200); - - expect(messages[1].networkId).toBe('player_002'); - expect(messages[1].syncSequence).toBe(201); - }); - - test('应该能过滤有变化的组件', () => { - const component1 = createSyncVarProxy(new TestPlayerComponent()); - const component2 = createSyncVarProxy(new TestPlayerComponent()); - const component3 = createSyncVarProxy(new TestPlayerComponent()); - - syncVarManager.initializeComponent(component1); - syncVarManager.initializeComponent(component2); - syncVarManager.initializeComponent(component3); - - // 只修改component1和component3 - component1.health = 80; - component3.playerName = 'Hero3'; - // component2没有修改 - - const componentsWithChanges = syncVarManager.filterComponentsWithChanges([ - component1, component2, component3 - ]); - - expect(componentsWithChanges.length).toBe(2); - expect(componentsWithChanges).toContain(component1); - expect(componentsWithChanges).toContain(component3); - expect(componentsWithChanges).not.toContain(component2); - }); - - test('应该能获取组件变化统计', () => { - const component = createSyncVarProxy(new TestPlayerComponent()); - syncVarManager.initializeComponent(component); - - // 修改多个字段 - component.health = 80; - component.health = 70; // 再次修改同一字段 - component.playerName = 'Hero'; - - const stats = syncVarManager.getComponentChangeStats(component); - - expect(stats.totalChanges).toBe(3); - expect(stats.pendingChanges).toBe(3); - expect(stats.lastChangeTime).toBeGreaterThan(0); - expect(stats.fieldChangeCounts.get('health')).toBe(2); - expect(stats.fieldChangeCounts.get('playerName')).toBe(1); - expect(stats.hasAuthorityOnlyChanges).toBe(false); - }); - }); - - describe('权限和环境检查', () => { - test('权威字段应该被正确处理', () => { - // 重置环境为纯客户端模式 - const env = NetworkEnvironment['Instance']; - env['_state'] = NetworkEnvironmentState.None; - env['_serverStartTime'] = 0; - env['_clientConnectTime'] = 0; - NetworkEnvironment.SetClientMode(); // 切换到纯客户端模式 - - const component = createSyncVarProxy(new TestPlayerComponent()); - // 明确设置没有权限 - component.setAuthority(false); - - syncVarManager.initializeComponent(component); - - console.log('当前环境:', NetworkEnvironment.isServer ? 'server' : 'client'); - console.log('isServer:', NetworkEnvironment.isServer); - console.log('isClient:', NetworkEnvironment.isClient); - console.log('组件权限:', component.hasAuthority()); - - // 修改权威字段(客户端没有权限) - component.isReady = true; - - // 检查待同步变化 - const pendingChanges = syncVarManager.getPendingChanges(component); - console.log('待同步变化:', pendingChanges); - - const message = syncVarManager.createSyncVarUpdateMessage(component); - console.log('创建的消息:', message); - - // 在客户端模式下,权威字段不应该被同步 - expect(message).toBeNull(); - }); - - test('客户端应该能接受来自服务端的权威字段更新', () => { - NetworkEnvironment.SetClientMode(); // 客户端模式 - - const component = createSyncVarProxy(new TestPlayerComponent()); - syncVarManager.initializeComponent(component); - - const fieldUpdates: SyncVarFieldUpdate[] = [ - { - fieldNumber: 3, // isReady字段 - propertyKey: 'isReady', - newValue: true, - oldValue: false, - timestamp: Date.now(), - authorityOnly: true - } - ]; - - const message = new SyncVarUpdateMessage( - 'player_001', - 'TestPlayerComponent', - fieldUpdates - ); - - // 记录初始值 - const initialValue = component.isReady; - expect(initialValue).toBe(false); - - // 应用消息(客户端应该接受来自服务端的权威字段更新) - syncVarManager.applySyncVarUpdateMessage(component, message); - - // 值应该改变 - expect(component.isReady).toBe(true); - }); - }); - - afterEach(() => { - // 清理 - NetworkEnvironment.SetServerMode(); // 重置为服务器模式 - }); -}); \ No newline at end of file diff --git a/packages/network/tests/SyncVarSimple.test.ts b/packages/network/tests/SyncVarSimple.test.ts deleted file mode 100644 index 6f2a1f89..00000000 --- a/packages/network/tests/SyncVarSimple.test.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * SyncVar简单测试 - */ - -import 'reflect-metadata'; - -describe('SyncVar简单测试', () => { - test('基础功能测试', () => { - expect(true).toBe(true); - }); -}); \ No newline at end of file diff --git a/packages/network/tests/TsrpcTransport.test.ts b/packages/network/tests/TsrpcTransport.test.ts new file mode 100644 index 00000000..30a941c2 --- /dev/null +++ b/packages/network/tests/TsrpcTransport.test.ts @@ -0,0 +1,145 @@ +/** + * TSRPC传输层测试 + */ + +import 'reflect-metadata'; +import { TsrpcTransport } from '../src/transport/TsrpcTransport'; +import { NetworkConfig } from '../src/types/NetworkTypes'; + +// 简化测试,只验证基本功能 +describe('TSRPC传输层测试', () => { + let serverTransport: TsrpcTransport; + let clientTransport: TsrpcTransport; + + const serverConfig: NetworkConfig = { + port: 18888, // 使用不同端口避免冲突 + host: 'localhost', + syncRate: 20 + }; + + const clientConfig: NetworkConfig = { + port: 18888, + host: 'localhost' + }; + + beforeEach(() => { + serverTransport = new TsrpcTransport(serverConfig); + clientTransport = new TsrpcTransport(clientConfig); + }); + + afterEach(async () => { + if (serverTransport) { + await serverTransport.disconnect(); + } + if (clientTransport) { + await clientTransport.disconnect(); + } + }); + + describe('传输层创建', () => { + test('创建服务端传输层', () => { + expect(serverTransport).toBeDefined(); + expect(serverTransport.getNetworkSide()).toBe('client'); // 默认为客户端 + expect(serverTransport.isConnected()).toBe(false); + }); + + test('创建客户端传输层', () => { + expect(clientTransport).toBeDefined(); + expect(clientTransport.getNetworkSide()).toBe('client'); + expect(clientTransport.isConnected()).toBe(false); + }); + }); + + describe('事件处理器设置', () => { + test('设置事件处理器', () => { + let connectedCalled = false; + let disconnectedCalled = false; + + serverTransport.setEventHandlers({ + onConnected: () => { + connectedCalled = true; + }, + onDisconnected: () => { + disconnectedCalled = true; + } + }); + + // 验证事件处理器被正确设置 + expect(connectedCalled).toBe(false); + expect(disconnectedCalled).toBe(false); + }); + + test('单独设置事件处理器', () => { + let errorCalled = false; + + serverTransport.on('onError', (error) => { + errorCalled = true; + }); + + expect(errorCalled).toBe(false); + }); + }); + + describe('基本功能验证', () => { + test('获取统计信息', () => { + const stats = serverTransport.getStats(); + + expect(stats).toHaveProperty('messagesSent'); + expect(stats).toHaveProperty('messagesReceived'); + expect(stats).toHaveProperty('bytesSent'); + expect(stats).toHaveProperty('bytesReceived'); + expect(stats).toHaveProperty('clientCount'); + expect(stats).toHaveProperty('uptime'); + + // 初始值应该为0 + expect(stats.messagesSent).toBe(0); + expect(stats.messagesReceived).toBe(0); + expect(stats.clientCount).toBe(0); + }); + + test('客户端模式方法调用异常处理', async () => { + // 客户端模式下调用服务端方法应该抛出错误 + await expect(serverTransport.getServerStatus()).rejects.toThrow('只能在客户端模式下查询服务端状态'); + await expect(serverTransport.ping()).rejects.toThrow('只能在客户端模式下发送心跳'); + }); + + test('未初始化时发送消息异常处理', async () => { + const testMessage = { + type: 'test', + networkId: 1, + data: { test: 'data' }, + timestamp: Date.now() + }; + + // 未连接时发送消息应该抛出错误 + await expect(serverTransport.sendMessage(testMessage)).rejects.toThrow('传输层未初始化或状态错误'); + await expect(serverTransport.sendSyncVar(1, 'TestComponent', 'testProp', 'testValue')).rejects.toThrow('传输层未初始化或状态错误'); + await expect(serverTransport.sendRpcCall(1, 'TestComponent', 'testMethod', [], true)).rejects.toThrow('传输层未初始化或状态错误'); + }); + }); + + describe('网络配置', () => { + test('获取正确的网络端类型', async () => { + // 测试服务端模式 + const config: NetworkConfig = { + port: 18889, + host: 'localhost' + }; + + const transport = new TsrpcTransport(config); + expect(transport.getNetworkSide()).toBe('client'); // 创建时默认为客户端 + + await transport.disconnect(); + }); + + test('获取客户端ID和连接信息', () => { + expect(serverTransport.getClientId()).toBe(0); // 未连接时为0 + expect(serverTransport.getConnectedClients()).toEqual([]); // 客户端模式返回空数组 + expect(serverTransport.getClientCount()).toBe(0); // 客户端模式返回0 + }); + }); + + // 注意:由于在测试环境中启动真实的网络服务可能很复杂, + // 这里主要测试API的正确性和错误处理, + // 真正的端到端网络测试需要在集成测试中进行 +}); \ No newline at end of file diff --git a/packages/network/tests/setup.ts b/packages/network/tests/setup.ts index a6f70895..602ba657 100644 --- a/packages/network/tests/setup.ts +++ b/packages/network/tests/setup.ts @@ -1,72 +1,28 @@ /** - * Jest 测试全局设置文件 - * - * 此文件在每个测试文件执行前运行,用于设置全局测试环境 + * Jest测试设置文件 */ -// 设置测试超时时间(毫秒) +import 'reflect-metadata'; + +// 全局Jest配置 +expect.extend({}); + +// 设置测试环境变量 +process.env.NODE_ENV = 'test'; + +// 全局错误处理 +process.on('unhandledRejection', (reason, promise) => { + console.error('未处理的Promise拒绝:', reason); +}); + +// 设置全局测试超时 jest.setTimeout(10000); -// 模拟控制台方法以减少测试输出噪音 -const originalConsoleLog = console.log; -const originalConsoleWarn = console.warn; -const originalConsoleError = console.error; - -// 在测试环境中可以选择性地静默某些日志 -beforeAll(() => { - // 可以在这里设置全局的模拟或配置 -}); - -afterAll(() => { - // 清理全局资源 -}); - -// 每个测试前的清理 -beforeEach(() => { - // 清理定时器 - jest.clearAllTimers(); -}); - +// 清理函数 afterEach(() => { - // 恢复所有模拟 - jest.restoreAllMocks(); -}); - -// 导出测试工具函数 -export const TestUtils = { - /** - * 创建测试用的延迟 - * @param ms 延迟毫秒数 - */ - delay: (ms: number): Promise => { - return new Promise(resolve => setTimeout(resolve, ms)); - }, - - /** - * 等待条件满足 - * @param condition 条件函数 - * @param timeout 超时时间(毫秒) - * @param interval 检查间隔(毫秒) - */ - waitFor: async ( - condition: () => boolean, - timeout: number = 5000, - interval: number = 10 - ): Promise => { - const start = Date.now(); - while (!condition() && Date.now() - start < timeout) { - await TestUtils.delay(interval); - } - if (!condition()) { - throw new Error(`等待条件超时 (${timeout}ms)`); - } - }, - - /** - * 模拟时间前进 - * @param ms 前进的毫秒数 - */ - advanceTime: (ms: number): void => { - jest.advanceTimersByTime(ms); - } -}; \ No newline at end of file + // 清理所有定时器 + jest.clearAllTimers(); + + // 清理所有模拟 + jest.clearAllMocks(); +}); \ No newline at end of file