449 lines
11 KiB
TypeScript
449 lines
11 KiB
TypeScript
|
|
/**
|
|||
|
|
* 网络管理器
|
|||
|
|
*
|
|||
|
|
* 网络库的核心管理类,负责:
|
|||
|
|
* - 客户端/服务端连接管理
|
|||
|
|
* - 网络消息路由和处理
|
|||
|
|
* - 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<NetworkEventHandlers> = {};
|
|||
|
|
|
|||
|
|
/** 同步定时器 */
|
|||
|
|
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<NetworkConfig>): Promise<void> {
|
|||
|
|
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<void> {
|
|||
|
|
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<void> {
|
|||
|
|
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<K extends keyof NetworkEventHandlers>(
|
|||
|
|
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<void> {
|
|||
|
|
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<void> {
|
|||
|
|
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}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|