更新network库及core库优化
This commit is contained in:
@@ -1,185 +0,0 @@
|
||||
/**
|
||||
* 网络注册表
|
||||
*
|
||||
* 管理所有网络对象的注册和查找
|
||||
*/
|
||||
|
||||
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<number, NetworkIdentity> = 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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,223 +0,0 @@
|
||||
/**
|
||||
* 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<string> = 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
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
/**
|
||||
* 同步变量管理器
|
||||
*
|
||||
* 负责管理 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<string, NetworkBehaviour[]> = 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)
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/**
|
||||
* 网络组件基类
|
||||
*
|
||||
* 所有需要网络同步功能的组件都应继承此类
|
||||
* 提供 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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
/**
|
||||
* 网络身份组件
|
||||
*
|
||||
* 标识网络对象的唯一身份,管理网络组件和权威性
|
||||
* 所有需要网络同步的实体都必须拥有此组件
|
||||
*/
|
||||
|
||||
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<T extends INetworkBehaviour>(type: new (...args: any[]) => T): T | null {
|
||||
return this.networkBehaviours.find(b => b instanceof type) as T || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有指定类型的网络组件
|
||||
*/
|
||||
public getNetworkBehaviours<T extends INetworkBehaviour>(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;
|
||||
}
|
||||
}
|
||||
@@ -1,449 +0,0 @@
|
||||
/**
|
||||
* 网络管理器
|
||||
*
|
||||
* 网络库的核心管理类,负责:
|
||||
* - 客户端/服务端连接管理
|
||||
* - 网络消息路由和处理
|
||||
* - 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -1,116 +0,0 @@
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* ECS Network Library
|
||||
*
|
||||
* 基于 ECS 架构和 TSRPC 的网络同步库
|
||||
* 提供简洁易用的网络组件和同步机制
|
||||
*/
|
||||
|
||||
// 核心组件
|
||||
export { NetworkManager } from './NetworkManager';
|
||||
export { NetworkIdentity } from './NetworkIdentity';
|
||||
export { NetworkBehaviour } from './NetworkBehaviour';
|
||||
|
||||
// 装饰器
|
||||
export { SyncVar } from './decorators/SyncVar';
|
||||
export { ClientRpc } from './decorators/ClientRpc';
|
||||
export { Command } from './decorators/Command';
|
||||
|
||||
// 核心管理器
|
||||
export { SyncVarManager } from './Core/SyncVarManager';
|
||||
export { RpcManager } from './Core/RpcManager';
|
||||
export { NetworkRegistry } from './Core/NetworkRegistry';
|
||||
|
||||
// 传输层
|
||||
export * from './transport';
|
||||
|
||||
// 类型定义
|
||||
export * from './Types/NetworkTypes';
|
||||
@@ -1,431 +0,0 @@
|
||||
/**
|
||||
* 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<ServiceType> | 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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<ResServerStatus> {
|
||||
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<number> {
|
||||
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<void> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
/**
|
||||
* 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<ServiceType> | null = null;
|
||||
private clients: Map<number, ClientConnection> = 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<void> {
|
||||
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<void> {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
/**
|
||||
* 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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<number> {
|
||||
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<K extends keyof TransportEventHandlers>(
|
||||
event: K,
|
||||
handler: TransportEventHandlers[K]
|
||||
): void {
|
||||
this.eventHandlers[event] = handler;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* 传输层模块导出
|
||||
*/
|
||||
|
||||
export { TsrpcTransport } from './TsrpcTransport';
|
||||
export { TsrpcServer } from './TsrpcServer';
|
||||
export { TsrpcClient } from './TsrpcClient';
|
||||
export * from './protocols/NetworkProtocols';
|
||||
export { serviceProto, ServiceType } from './protocols/serviceProto';
|
||||
@@ -1,184 +0,0 @@
|
||||
/**
|
||||
* 网络库 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;
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/**
|
||||
* 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<ServiceType> = {
|
||||
"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;
|
||||
};
|
||||
}
|
||||
@@ -1,162 +0,0 @@
|
||||
/**
|
||||
* 网络库核心类型定义
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 网络连接状态
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步变量消息
|
||||
*/
|
||||
export interface SyncVarMessage extends NetworkMessage {
|
||||
type: 'syncvar';
|
||||
/** 组件类型名 */
|
||||
componentType: string;
|
||||
/** 属性名 */
|
||||
propertyName: string;
|
||||
/** 属性值 */
|
||||
value: any;
|
||||
}
|
||||
|
||||
/**
|
||||
* RPC消息
|
||||
*/
|
||||
export interface RpcMessage extends NetworkMessage {
|
||||
type: 'rpc';
|
||||
/** RPC方法名 */
|
||||
methodName: string;
|
||||
/** RPC参数 */
|
||||
args: any[];
|
||||
/** 是否为客户端RPC */
|
||||
isClientRpc: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络配置选项
|
||||
*/
|
||||
export interface NetworkConfig {
|
||||
/** 服务器端口 */
|
||||
port?: number;
|
||||
/** 服务器地址 */
|
||||
host?: string;
|
||||
/** 房间ID */
|
||||
roomId?: string;
|
||||
/** 最大连接数 */
|
||||
maxConnections?: number;
|
||||
/** 同步频率 (Hz) */
|
||||
syncRate?: number;
|
||||
/** 是否启用压缩 */
|
||||
compression?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络统计信息
|
||||
*/
|
||||
export interface NetworkStats {
|
||||
/** 连接数 */
|
||||
connectionCount: number;
|
||||
/** 发送的字节数 */
|
||||
bytesSent: number;
|
||||
/** 接收的字节数 */
|
||||
bytesReceived: number;
|
||||
/** 发送的消息数 */
|
||||
messagesSent: number;
|
||||
/** 接收的消息数 */
|
||||
messagesReceived: number;
|
||||
/** 平均延迟 (ms) */
|
||||
averageLatency: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络事件处理器
|
||||
*/
|
||||
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 INetworkBehaviour {
|
||||
/** 网络身份组件引用 */
|
||||
networkIdentity: any | null;
|
||||
/** 是否拥有权威 */
|
||||
hasAuthority: boolean;
|
||||
/** 是否为本地玩家 */
|
||||
isLocalPlayer: boolean;
|
||||
/** 是否在服务端 */
|
||||
isServer: boolean;
|
||||
/** 是否在客户端 */
|
||||
isClient: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步变量元数据
|
||||
*/
|
||||
export interface SyncVarMetadata {
|
||||
/** 属性名 */
|
||||
propertyName: string;
|
||||
/** 是否仅权威端可修改 */
|
||||
authorityOnly: boolean;
|
||||
/** 变化回调函数名 */
|
||||
onChanged?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* RPC元数据
|
||||
*/
|
||||
export interface RpcMetadata {
|
||||
/** 方法名 */
|
||||
methodName: string;
|
||||
/** 是否为客户端RPC */
|
||||
isClientRpc: boolean;
|
||||
/** 是否需要权威验证 */
|
||||
requiresAuthority: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 网络连接信息
|
||||
*/
|
||||
export interface NetworkConnection {
|
||||
/** 连接ID */
|
||||
id: number;
|
||||
/** 连接状态 */
|
||||
state: NetworkConnectionState;
|
||||
/** 延迟 (ms) */
|
||||
latency: number;
|
||||
/** 最后活跃时间 */
|
||||
lastActivity: number;
|
||||
}
|
||||
Reference in New Issue
Block a user