重构network库(mvp版本)搭建基础设施和核心接口
定义ITransport/ISerializer/INetworkMessage接口 NetworkIdentity组件 基础事件定义
This commit is contained in:
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
* 客户端网络行为基类
|
||||
*
|
||||
* 类似Unity Mirror的NetworkBehaviour,提供网络功能
|
||||
*/
|
||||
|
||||
import { Component, Entity } from '@esengine/ecs-framework';
|
||||
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
|
||||
import { NetworkClient } from './NetworkClient';
|
||||
import { NetworkIdentity } from './NetworkIdentity';
|
||||
|
||||
/**
|
||||
* 客户端网络行为基类
|
||||
*/
|
||||
export abstract class ClientNetworkBehaviour extends Component {
|
||||
/** 网络标识组件 */
|
||||
protected networkIdentity: NetworkIdentity | null = null;
|
||||
/** 网络客户端实例 */
|
||||
protected networkClient: NetworkClient | null = null;
|
||||
|
||||
/**
|
||||
* 组件初始化
|
||||
*/
|
||||
initialize(): void {
|
||||
|
||||
// 获取网络标识组件
|
||||
this.networkIdentity = this.entity.getComponent(NetworkIdentity);
|
||||
if (!this.networkIdentity) {
|
||||
throw new Error('NetworkBehaviour requires NetworkIdentity component');
|
||||
}
|
||||
|
||||
// 从全局获取网络客户端实例
|
||||
this.networkClient = this.getNetworkClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络客户端实例
|
||||
*/
|
||||
protected getNetworkClient(): NetworkClient | null {
|
||||
// 这里需要实现从全局管理器获取客户端实例的逻辑
|
||||
// 暂时返回null,在实际使用时需要通过单例模式或依赖注入获取
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为本地玩家
|
||||
*/
|
||||
get isLocalPlayer(): boolean {
|
||||
return this.networkIdentity?.isLocalPlayer ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为服务器权威
|
||||
*/
|
||||
get hasAuthority(): boolean {
|
||||
return this.networkIdentity?.hasAuthority ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络ID
|
||||
*/
|
||||
get networkId(): string {
|
||||
return this.networkIdentity?.networkId ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已连接
|
||||
*/
|
||||
get isConnected(): boolean {
|
||||
return this.networkClient?.isInRoom() ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送RPC到服务器
|
||||
*/
|
||||
protected async sendServerRpc(methodName: string, ...args: NetworkValue[]): Promise<NetworkValue> {
|
||||
if (!this.networkClient || !this.networkIdentity) {
|
||||
throw new Error('Network client or identity not available');
|
||||
}
|
||||
|
||||
return this.networkClient.sendRpc(this.networkIdentity.networkId, methodName, args, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送不可靠RPC到服务器
|
||||
*/
|
||||
protected async sendServerRpcUnreliable(methodName: string, ...args: NetworkValue[]): Promise<void> {
|
||||
if (!this.networkClient || !this.networkIdentity) {
|
||||
throw new Error('Network client or identity not available');
|
||||
}
|
||||
|
||||
await this.networkClient.sendRpc(this.networkIdentity.networkId, methodName, args, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新SyncVar
|
||||
*/
|
||||
protected async updateSyncVar(fieldName: string, value: NetworkValue): Promise<void> {
|
||||
if (!this.networkClient || !this.networkIdentity) {
|
||||
throw new Error('Network client or identity not available');
|
||||
}
|
||||
|
||||
await this.networkClient.updateSyncVar(this.networkIdentity.networkId, fieldName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 当收到RPC调用时
|
||||
*/
|
||||
onRpcReceived(methodName: string, args: NetworkValue[]): void {
|
||||
// 尝试调用对应的方法
|
||||
const method = (this as any)[methodName];
|
||||
if (typeof method === 'function') {
|
||||
try {
|
||||
method.apply(this, args);
|
||||
} catch (error) {
|
||||
console.error(`Error calling RPC method ${methodName}:`, error);
|
||||
}
|
||||
} else {
|
||||
console.warn(`RPC method ${methodName} not found on ${this.constructor.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 当SyncVar更新时
|
||||
*/
|
||||
onSyncVarChanged(fieldName: string, oldValue: NetworkValue, newValue: NetworkValue): void {
|
||||
// 子类可以重写此方法来处理SyncVar变化
|
||||
}
|
||||
|
||||
/**
|
||||
* 当获得权威时
|
||||
*/
|
||||
onStartAuthority(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 当失去权威时
|
||||
*/
|
||||
onStopAuthority(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 当成为本地玩家时
|
||||
*/
|
||||
onStartLocalPlayer(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 当不再是本地玩家时
|
||||
*/
|
||||
onStopLocalPlayer(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络启动时调用
|
||||
*/
|
||||
onNetworkStart(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络停止时调用
|
||||
*/
|
||||
onNetworkStop(): void {
|
||||
// 子类可以重写此方法
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件销毁
|
||||
*/
|
||||
onDestroy(): void {
|
||||
this.networkIdentity = null;
|
||||
this.networkClient = null;
|
||||
}
|
||||
}
|
||||
@@ -1,638 +0,0 @@
|
||||
/**
|
||||
* 网络客户端主类
|
||||
*
|
||||
* 管理连接、认证、房间加入等功能
|
||||
*/
|
||||
|
||||
import { Scene, EntityManager, Emitter, ITimer, Core } from '@esengine/ecs-framework';
|
||||
import {
|
||||
NetworkIdentity as SharedNetworkIdentity,
|
||||
NetworkValue,
|
||||
RpcMessage,
|
||||
SyncVarMessage
|
||||
} from '@esengine/ecs-framework-network-shared';
|
||||
import {
|
||||
ClientTransport,
|
||||
WebSocketClientTransport,
|
||||
HttpClientTransport,
|
||||
ConnectionState,
|
||||
ClientMessage,
|
||||
ClientTransportConfig,
|
||||
WebSocketClientConfig,
|
||||
HttpClientConfig
|
||||
} from '../transport';
|
||||
|
||||
/**
|
||||
* 网络客户端配置
|
||||
*/
|
||||
export interface NetworkClientConfig {
|
||||
/** 传输类型 */
|
||||
transport: 'websocket' | 'http';
|
||||
/** 传输配置 */
|
||||
transportConfig: WebSocketClientConfig | HttpClientConfig;
|
||||
/** 是否启用预测 */
|
||||
enablePrediction?: boolean;
|
||||
/** 预测缓冲区大小 */
|
||||
predictionBuffer?: number;
|
||||
/** 是否启用插值 */
|
||||
enableInterpolation?: boolean;
|
||||
/** 插值延迟(毫秒) */
|
||||
interpolationDelay?: number;
|
||||
/** 网络对象同步间隔(毫秒) */
|
||||
syncInterval?: number;
|
||||
/** 是否启用调试 */
|
||||
debug?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
export interface UserInfo {
|
||||
/** 用户ID */
|
||||
userId: string;
|
||||
/** 用户名 */
|
||||
username: string;
|
||||
/** 用户数据 */
|
||||
data?: NetworkValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 房间信息
|
||||
*/
|
||||
export interface RoomInfo {
|
||||
/** 房间ID */
|
||||
roomId: string;
|
||||
/** 房间名称 */
|
||||
name: string;
|
||||
/** 当前人数 */
|
||||
playerCount: number;
|
||||
/** 最大人数 */
|
||||
maxPlayers: number;
|
||||
/** 房间元数据 */
|
||||
metadata?: NetworkValue;
|
||||
/** 是否私有房间 */
|
||||
isPrivate?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 认证消息
|
||||
*/
|
||||
export interface AuthMessage {
|
||||
action: string;
|
||||
username: string;
|
||||
password?: string;
|
||||
userData?: NetworkValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 房间消息
|
||||
*/
|
||||
export interface RoomMessage {
|
||||
action: string;
|
||||
roomId?: string;
|
||||
name?: string;
|
||||
maxPlayers?: number;
|
||||
metadata?: NetworkValue;
|
||||
isPrivate?: boolean;
|
||||
password?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络客户端事件
|
||||
*/
|
||||
export interface NetworkClientEvents {
|
||||
/** 连接建立 */
|
||||
'connected': () => void;
|
||||
/** 连接断开 */
|
||||
'disconnected': (reason: string) => void;
|
||||
/** 认证成功 */
|
||||
'authenticated': (userInfo: UserInfo) => void;
|
||||
/** 加入房间成功 */
|
||||
'joined-room': (roomInfo: RoomInfo) => void;
|
||||
/** 离开房间 */
|
||||
'left-room': (roomId: string) => void;
|
||||
/** 房间列表更新 */
|
||||
'room-list-updated': (rooms: RoomInfo[]) => void;
|
||||
/** 玩家加入房间 */
|
||||
'player-joined': (userId: string, userInfo: UserInfo) => void;
|
||||
/** 玩家离开房间 */
|
||||
'player-left': (userId: string) => void;
|
||||
/** 网络对象创建 */
|
||||
'network-object-created': (networkId: string, data: NetworkValue) => void;
|
||||
/** 网络对象销毁 */
|
||||
'network-object-destroyed': (networkId: string) => void;
|
||||
/** SyncVar 更新 */
|
||||
'syncvar-updated': (networkId: string, fieldName: string, value: NetworkValue) => void;
|
||||
/** RPC 调用 */
|
||||
'rpc-received': (networkId: string, methodName: string, args: NetworkValue[]) => void;
|
||||
/** 错误发生 */
|
||||
'error': (error: Error) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络客户端主类
|
||||
*/
|
||||
export class NetworkClient {
|
||||
private transport: ClientTransport;
|
||||
private config: NetworkClientConfig;
|
||||
private currentUser: UserInfo | null = null;
|
||||
private currentRoom: RoomInfo | null = null;
|
||||
private availableRooms: Map<string, RoomInfo> = new Map();
|
||||
private networkObjects: Map<string, SharedNetworkIdentity> = new Map();
|
||||
private pendingRpcs: Map<string, { resolve: Function; reject: Function; timeout: ITimer<any> }> = new Map();
|
||||
private scene: Scene | null = null;
|
||||
private eventEmitter: Emitter<keyof NetworkClientEvents, any>;
|
||||
|
||||
constructor(config: NetworkClientConfig) {
|
||||
this.eventEmitter = new Emitter();
|
||||
|
||||
this.config = {
|
||||
enablePrediction: true,
|
||||
predictionBuffer: 64,
|
||||
enableInterpolation: true,
|
||||
interpolationDelay: 100,
|
||||
syncInterval: 50,
|
||||
debug: false,
|
||||
...config
|
||||
};
|
||||
|
||||
this.transport = this.createTransport();
|
||||
this.setupTransportEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建传输层
|
||||
*/
|
||||
private createTransport(): ClientTransport {
|
||||
switch (this.config.transport) {
|
||||
case 'websocket':
|
||||
return new WebSocketClientTransport(this.config.transportConfig as WebSocketClientConfig);
|
||||
case 'http':
|
||||
return new HttpClientTransport(this.config.transportConfig as HttpClientConfig);
|
||||
default:
|
||||
throw new Error(`Unsupported transport type: ${this.config.transport}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置传输层事件监听
|
||||
*/
|
||||
private setupTransportEvents(): void {
|
||||
this.transport.on('connected', () => {
|
||||
this.eventEmitter.emit('connected');
|
||||
});
|
||||
|
||||
this.transport.on('disconnected', (reason) => {
|
||||
this.handleDisconnected(reason);
|
||||
});
|
||||
|
||||
this.transport.on('message', (message) => {
|
||||
this.handleMessage(message);
|
||||
});
|
||||
|
||||
this.transport.on('error', (error) => {
|
||||
this.eventEmitter.emit('error', error);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接到服务器
|
||||
*/
|
||||
async connect(): Promise<void> {
|
||||
return this.transport.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* 断开连接
|
||||
*/
|
||||
async disconnect(): Promise<void> {
|
||||
await this.transport.disconnect();
|
||||
this.cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户认证
|
||||
*/
|
||||
async authenticate(username: string, password?: string, userData?: NetworkValue): Promise<UserInfo> {
|
||||
if (!this.transport.isConnected()) {
|
||||
throw new Error('Not connected to server');
|
||||
}
|
||||
|
||||
const authMessage: AuthMessage = {
|
||||
action: 'login',
|
||||
username,
|
||||
password,
|
||||
userData
|
||||
};
|
||||
|
||||
const response = await this.sendRequestWithResponse('system', authMessage as any);
|
||||
|
||||
if (response.success && response.userInfo) {
|
||||
this.currentUser = response.userInfo as UserInfo;
|
||||
this.eventEmitter.emit('authenticated', this.currentUser);
|
||||
return this.currentUser;
|
||||
} else {
|
||||
throw new Error(response.error || 'Authentication failed');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取房间列表
|
||||
*/
|
||||
async getRoomList(): Promise<RoomInfo[]> {
|
||||
if (!this.isAuthenticated()) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
const roomMessage: RoomMessage = {
|
||||
action: 'list-rooms'
|
||||
};
|
||||
|
||||
const response = await this.sendRequestWithResponse('system', roomMessage as any);
|
||||
|
||||
if (response.success && response.rooms) {
|
||||
this.availableRooms.clear();
|
||||
response.rooms.forEach((room: RoomInfo) => {
|
||||
this.availableRooms.set(room.roomId, room);
|
||||
});
|
||||
|
||||
this.eventEmitter.emit('room-list-updated', response.rooms);
|
||||
return response.rooms;
|
||||
} else {
|
||||
throw new Error(response.error || 'Failed to get room list');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建房间
|
||||
*/
|
||||
async createRoom(name: string, maxPlayers: number = 8, metadata?: NetworkValue, isPrivate = false): Promise<RoomInfo> {
|
||||
if (!this.isAuthenticated()) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
const roomMessage: RoomMessage = {
|
||||
action: 'create-room',
|
||||
name,
|
||||
maxPlayers,
|
||||
metadata,
|
||||
isPrivate
|
||||
};
|
||||
|
||||
const response = await this.sendRequestWithResponse('system', roomMessage as any);
|
||||
|
||||
if (response.success && response.room) {
|
||||
this.currentRoom = response.room as RoomInfo;
|
||||
this.eventEmitter.emit('joined-room', this.currentRoom);
|
||||
return this.currentRoom;
|
||||
} else {
|
||||
throw new Error(response.error || 'Failed to create room');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入房间
|
||||
*/
|
||||
async joinRoom(roomId: string, password?: string): Promise<RoomInfo> {
|
||||
if (!this.isAuthenticated()) {
|
||||
throw new Error('Not authenticated');
|
||||
}
|
||||
|
||||
const roomMessage: RoomMessage = {
|
||||
action: 'join-room',
|
||||
roomId,
|
||||
password
|
||||
};
|
||||
|
||||
const response = await this.sendRequestWithResponse('system', roomMessage as any);
|
||||
|
||||
if (response.success && response.room) {
|
||||
this.currentRoom = response.room as RoomInfo;
|
||||
this.eventEmitter.emit('joined-room', this.currentRoom);
|
||||
return this.currentRoom;
|
||||
} else {
|
||||
throw new Error(response.error || 'Failed to join room');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 离开房间
|
||||
*/
|
||||
async leaveRoom(): Promise<void> {
|
||||
if (!this.currentRoom) {
|
||||
return;
|
||||
}
|
||||
|
||||
const roomMessage: RoomMessage = {
|
||||
action: 'leave-room',
|
||||
roomId: this.currentRoom.roomId
|
||||
};
|
||||
|
||||
try {
|
||||
await this.sendRequestWithResponse('system', roomMessage as any);
|
||||
} finally {
|
||||
const roomId = this.currentRoom.roomId;
|
||||
this.currentRoom = null;
|
||||
this.networkObjects.clear();
|
||||
this.eventEmitter.emit('left-room', roomId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送RPC调用
|
||||
*/
|
||||
async sendRpc(networkId: string, methodName: string, args: NetworkValue[] = [], reliable = true): Promise<NetworkValue> {
|
||||
if (!this.isInRoom()) {
|
||||
throw new Error('Not in a room');
|
||||
}
|
||||
|
||||
const rpcMessage: any = {
|
||||
networkId,
|
||||
methodName,
|
||||
args,
|
||||
isServer: false,
|
||||
messageId: this.generateMessageId()
|
||||
};
|
||||
|
||||
if (reliable) {
|
||||
return this.sendRequestWithResponse('rpc', rpcMessage);
|
||||
} else {
|
||||
await this.transport.sendMessage({
|
||||
type: 'rpc',
|
||||
data: rpcMessage as NetworkValue,
|
||||
reliable: false
|
||||
});
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新SyncVar
|
||||
*/
|
||||
async updateSyncVar(networkId: string, fieldName: string, value: NetworkValue): Promise<void> {
|
||||
if (!this.isInRoom()) {
|
||||
throw new Error('Not in a room');
|
||||
}
|
||||
|
||||
const syncMessage: any = {
|
||||
networkId,
|
||||
propertyName: fieldName,
|
||||
value,
|
||||
isServer: false
|
||||
};
|
||||
|
||||
await this.transport.sendMessage({
|
||||
type: 'syncvar',
|
||||
data: syncMessage as NetworkValue,
|
||||
reliable: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ECS场景
|
||||
*/
|
||||
setScene(scene: Scene): void {
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
getCurrentUser(): UserInfo | null {
|
||||
return this.currentUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前房间信息
|
||||
*/
|
||||
getCurrentRoom(): RoomInfo | null {
|
||||
return this.currentRoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连接状态
|
||||
*/
|
||||
getConnectionState(): ConnectionState {
|
||||
return this.transport.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已认证
|
||||
*/
|
||||
isAuthenticated(): boolean {
|
||||
return this.currentUser !== null && this.transport.isConnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否在房间中
|
||||
*/
|
||||
isInRoom(): boolean {
|
||||
return this.isAuthenticated() && this.currentRoom !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络对象
|
||||
*/
|
||||
getNetworkObject(networkId: string): SharedNetworkIdentity | null {
|
||||
return this.networkObjects.get(networkId) || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有网络对象
|
||||
*/
|
||||
getAllNetworkObjects(): SharedNetworkIdentity[] {
|
||||
return Array.from(this.networkObjects.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理断开连接
|
||||
*/
|
||||
private handleDisconnected(reason: string): void {
|
||||
this.cleanup();
|
||||
this.eventEmitter.emit('disconnected', reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理接收到的消息
|
||||
*/
|
||||
private handleMessage(message: ClientMessage): void {
|
||||
try {
|
||||
switch (message.type) {
|
||||
case 'system':
|
||||
this.handleSystemMessage(message);
|
||||
break;
|
||||
case 'rpc':
|
||||
this.handleRpcMessage(message);
|
||||
break;
|
||||
case 'syncvar':
|
||||
this.handleSyncVarMessage(message);
|
||||
break;
|
||||
case 'custom':
|
||||
this.handleCustomMessage(message);
|
||||
break;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error handling message:', error);
|
||||
this.eventEmitter.emit('error', error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理系统消息
|
||||
*/
|
||||
private handleSystemMessage(message: ClientMessage): void {
|
||||
const data = message.data as any;
|
||||
|
||||
// 处理响应消息
|
||||
if (message.messageId && this.pendingRpcs.has(message.messageId)) {
|
||||
const pending = this.pendingRpcs.get(message.messageId)!;
|
||||
pending.timeout.stop();
|
||||
this.pendingRpcs.delete(message.messageId);
|
||||
|
||||
if (data.success) {
|
||||
pending.resolve(data);
|
||||
} else {
|
||||
pending.reject(new Error(data.error || 'Request failed'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 处理广播消息
|
||||
switch (data.action) {
|
||||
case 'player-joined':
|
||||
this.eventEmitter.emit('player-joined', data.userId, data.userInfo);
|
||||
break;
|
||||
case 'player-left':
|
||||
this.eventEmitter.emit('player-left', data.userId);
|
||||
break;
|
||||
case 'network-object-created':
|
||||
this.handleNetworkObjectCreated(data);
|
||||
break;
|
||||
case 'network-object-destroyed':
|
||||
this.handleNetworkObjectDestroyed(data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理RPC消息
|
||||
*/
|
||||
private handleRpcMessage(message: ClientMessage): void {
|
||||
const rpcData = message.data as any;
|
||||
this.eventEmitter.emit('rpc-received', rpcData.networkId, rpcData.methodName, rpcData.args || []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理SyncVar消息
|
||||
*/
|
||||
private handleSyncVarMessage(message: ClientMessage): void {
|
||||
const syncData = message.data as any;
|
||||
this.eventEmitter.emit('syncvar-updated', syncData.networkId, syncData.propertyName, syncData.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自定义消息
|
||||
*/
|
||||
private handleCustomMessage(message: ClientMessage): void {
|
||||
// 可扩展的自定义消息处理
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理网络对象创建
|
||||
*/
|
||||
private handleNetworkObjectCreated(data: any): void {
|
||||
const networkObject = new SharedNetworkIdentity();
|
||||
this.networkObjects.set(data.networkId, networkObject);
|
||||
this.eventEmitter.emit('network-object-created', data.networkId, data.data || {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理网络对象销毁
|
||||
*/
|
||||
private handleNetworkObjectDestroyed(data: any): void {
|
||||
this.networkObjects.delete(data.networkId);
|
||||
this.eventEmitter.emit('network-object-destroyed', data.networkId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送请求并等待响应
|
||||
*/
|
||||
private sendRequestWithResponse(type: ClientMessage['type'], data: NetworkValue, timeout = 30000): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const messageId = this.generateMessageId();
|
||||
|
||||
const timeoutTimer = Core.schedule(timeout / 1000, false, this, () => {
|
||||
this.pendingRpcs.delete(messageId);
|
||||
reject(new Error('Request timeout'));
|
||||
});
|
||||
|
||||
this.pendingRpcs.set(messageId, {
|
||||
resolve,
|
||||
reject,
|
||||
timeout: timeoutTimer
|
||||
});
|
||||
|
||||
this.transport.sendMessage({
|
||||
type,
|
||||
data,
|
||||
messageId,
|
||||
reliable: true
|
||||
}).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成消息ID
|
||||
*/
|
||||
private generateMessageId(): string {
|
||||
return Date.now().toString(36) + Math.random().toString(36).substr(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理资源
|
||||
*/
|
||||
private cleanup(): void {
|
||||
this.currentUser = null;
|
||||
this.currentRoom = null;
|
||||
this.availableRooms.clear();
|
||||
this.networkObjects.clear();
|
||||
|
||||
// 取消所有待处理的RPC
|
||||
this.pendingRpcs.forEach(pending => {
|
||||
pending.timeout.stop();
|
||||
pending.reject(new Error('Connection closed'));
|
||||
});
|
||||
this.pendingRpcs.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁客户端
|
||||
*/
|
||||
destroy(): void {
|
||||
this.disconnect();
|
||||
this.transport.destroy();
|
||||
// 清理事件监听器,由于Emitter没有clear方法,我们重新创建一个
|
||||
this.eventEmitter = new Emitter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的事件监听
|
||||
*/
|
||||
on<K extends keyof NetworkClientEvents>(event: K, listener: NetworkClientEvents[K]): void {
|
||||
this.eventEmitter.addObserver(event, listener, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除事件监听
|
||||
*/
|
||||
off<K extends keyof NetworkClientEvents>(event: K, listener: NetworkClientEvents[K]): void {
|
||||
this.eventEmitter.removeObserver(event, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* 类型安全的事件触发
|
||||
*/
|
||||
emit<K extends keyof NetworkClientEvents>(event: K, ...args: Parameters<NetworkClientEvents[K]>): void {
|
||||
this.eventEmitter.emit(event, ...args);
|
||||
}
|
||||
}
|
||||
@@ -1,378 +0,0 @@
|
||||
/**
|
||||
* 客户端网络标识组件
|
||||
*
|
||||
* 标识网络对象并管理其状态
|
||||
*/
|
||||
|
||||
import { Component, Entity } from '@esengine/ecs-framework';
|
||||
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
|
||||
import { ClientNetworkBehaviour } from './ClientNetworkBehaviour';
|
||||
|
||||
/**
|
||||
* 网络权威类型
|
||||
*/
|
||||
export enum NetworkAuthority {
|
||||
/** 服务器权威 */
|
||||
SERVER = 'server',
|
||||
/** 客户端权威 */
|
||||
CLIENT = 'client',
|
||||
/** 所有者权威 */
|
||||
OWNER = 'owner'
|
||||
}
|
||||
|
||||
/**
|
||||
* SyncVar信息
|
||||
*/
|
||||
export interface SyncVarInfo {
|
||||
/** 字段名 */
|
||||
fieldName: string;
|
||||
/** 当前值 */
|
||||
currentValue: NetworkValue;
|
||||
/** 上一个值 */
|
||||
previousValue: NetworkValue;
|
||||
/** 最后更新时间 */
|
||||
lastUpdateTime: number;
|
||||
/** 是否已变更 */
|
||||
isDirty: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 网络标识组件
|
||||
*/
|
||||
export class NetworkIdentity extends Component {
|
||||
/** 网络ID */
|
||||
private _networkId: string = '';
|
||||
/** 所有者用户ID */
|
||||
private _ownerId: string = '';
|
||||
/** 是否为本地玩家 */
|
||||
private _isLocalPlayer: boolean = false;
|
||||
/** 权威类型 */
|
||||
private _authority: NetworkAuthority = NetworkAuthority.SERVER;
|
||||
/** 是否有权威 */
|
||||
private _hasAuthority: boolean = false;
|
||||
/** 网络行为组件列表 */
|
||||
private networkBehaviours: ClientNetworkBehaviour[] = [];
|
||||
/** SyncVar信息映射 */
|
||||
private syncVars: Map<string, SyncVarInfo> = new Map();
|
||||
/** 预测状态 */
|
||||
private predictionEnabled: boolean = false;
|
||||
/** 插值状态 */
|
||||
private interpolationEnabled: boolean = true;
|
||||
|
||||
/**
|
||||
* 网络ID
|
||||
*/
|
||||
get networkId(): string {
|
||||
return this._networkId;
|
||||
}
|
||||
|
||||
set networkId(value: string) {
|
||||
this._networkId = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 所有者用户ID
|
||||
*/
|
||||
get ownerId(): string {
|
||||
return this._ownerId;
|
||||
}
|
||||
|
||||
set ownerId(value: string) {
|
||||
this._ownerId = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否为本地玩家
|
||||
*/
|
||||
get isLocalPlayer(): boolean {
|
||||
return this._isLocalPlayer;
|
||||
}
|
||||
|
||||
set isLocalPlayer(value: boolean) {
|
||||
if (this._isLocalPlayer !== value) {
|
||||
this._isLocalPlayer = value;
|
||||
this.notifyLocalPlayerChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 权威类型
|
||||
*/
|
||||
get authority(): NetworkAuthority {
|
||||
return this._authority;
|
||||
}
|
||||
|
||||
set authority(value: NetworkAuthority) {
|
||||
if (this._authority !== value) {
|
||||
this._authority = value;
|
||||
this.updateAuthorityStatus();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有权威
|
||||
*/
|
||||
get hasAuthority(): boolean {
|
||||
return this._hasAuthority;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用预测
|
||||
*/
|
||||
get isPredictionEnabled(): boolean {
|
||||
return this.predictionEnabled;
|
||||
}
|
||||
|
||||
set isPredictionEnabled(value: boolean) {
|
||||
this.predictionEnabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否启用插值
|
||||
*/
|
||||
get isInterpolationEnabled(): boolean {
|
||||
return this.interpolationEnabled;
|
||||
}
|
||||
|
||||
set isInterpolationEnabled(value: boolean) {
|
||||
this.interpolationEnabled = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件初始化
|
||||
*/
|
||||
initialize(): void {
|
||||
this.collectNetworkBehaviours();
|
||||
this.notifyNetworkStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集网络行为组件
|
||||
*/
|
||||
private collectNetworkBehaviours(): void {
|
||||
// 暂时留空,等待实际集成时实现
|
||||
this.networkBehaviours = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新权威状态
|
||||
*/
|
||||
private updateAuthorityStatus(): void {
|
||||
const oldHasAuthority = this._hasAuthority;
|
||||
|
||||
// 根据权威类型计算是否有权威
|
||||
switch (this._authority) {
|
||||
case NetworkAuthority.SERVER:
|
||||
this._hasAuthority = false; // 客户端永远没有服务器权威
|
||||
break;
|
||||
case NetworkAuthority.CLIENT:
|
||||
this._hasAuthority = true; // 客户端权威
|
||||
break;
|
||||
case NetworkAuthority.OWNER:
|
||||
this._hasAuthority = this._isLocalPlayer; // 本地玩家才有权威
|
||||
break;
|
||||
}
|
||||
|
||||
// 通知权威变化
|
||||
if (oldHasAuthority !== this._hasAuthority) {
|
||||
this.notifyAuthorityChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知权威变化
|
||||
*/
|
||||
private notifyAuthorityChanged(): void {
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
if (this._hasAuthority) {
|
||||
behaviour.onStartAuthority();
|
||||
} else {
|
||||
behaviour.onStopAuthority();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知本地玩家状态变化
|
||||
*/
|
||||
private notifyLocalPlayerChanged(): void {
|
||||
this.updateAuthorityStatus(); // 本地玩家状态影响权威
|
||||
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
if (this._isLocalPlayer) {
|
||||
behaviour.onStartLocalPlayer();
|
||||
} else {
|
||||
behaviour.onStopLocalPlayer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知网络启动
|
||||
*/
|
||||
private notifyNetworkStart(): void {
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
behaviour.onNetworkStart();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 通知网络停止
|
||||
*/
|
||||
private notifyNetworkStop(): void {
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
behaviour.onNetworkStop();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理RPC调用
|
||||
*/
|
||||
handleRpcCall(methodName: string, args: NetworkValue[]): void {
|
||||
// 将RPC调用分发给所有网络行为组件
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
behaviour.onRpcReceived(methodName, args);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册SyncVar
|
||||
*/
|
||||
registerSyncVar(fieldName: string, initialValue: NetworkValue): void {
|
||||
this.syncVars.set(fieldName, {
|
||||
fieldName,
|
||||
currentValue: initialValue,
|
||||
previousValue: initialValue,
|
||||
lastUpdateTime: Date.now(),
|
||||
isDirty: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新SyncVar
|
||||
*/
|
||||
updateSyncVar(fieldName: string, newValue: NetworkValue): void {
|
||||
const syncVar = this.syncVars.get(fieldName);
|
||||
if (!syncVar) {
|
||||
console.warn(`SyncVar ${fieldName} not registered on ${this._networkId}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const oldValue = syncVar.currentValue;
|
||||
syncVar.previousValue = oldValue;
|
||||
syncVar.currentValue = newValue;
|
||||
syncVar.lastUpdateTime = Date.now();
|
||||
syncVar.isDirty = true;
|
||||
|
||||
// 通知所有网络行为组件
|
||||
this.networkBehaviours.forEach(behaviour => {
|
||||
behaviour.onSyncVarChanged(fieldName, oldValue, newValue);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取SyncVar值
|
||||
*/
|
||||
getSyncVar(fieldName: string): NetworkValue | undefined {
|
||||
return this.syncVars.get(fieldName)?.currentValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有SyncVar
|
||||
*/
|
||||
getAllSyncVars(): Map<string, SyncVarInfo> {
|
||||
return new Map(this.syncVars);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取脏SyncVar
|
||||
*/
|
||||
getDirtySyncVars(): SyncVarInfo[] {
|
||||
return Array.from(this.syncVars.values()).filter(syncVar => syncVar.isDirty);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除脏标记
|
||||
*/
|
||||
clearDirtyFlags(): void {
|
||||
this.syncVars.forEach(syncVar => {
|
||||
syncVar.isDirty = false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 序列化网络状态
|
||||
*/
|
||||
serializeState(): NetworkValue {
|
||||
const state: any = {
|
||||
networkId: this._networkId,
|
||||
ownerId: this._ownerId,
|
||||
isLocalPlayer: this._isLocalPlayer,
|
||||
authority: this._authority,
|
||||
syncVars: {}
|
||||
};
|
||||
|
||||
// 序列化SyncVar
|
||||
this.syncVars.forEach((syncVar, fieldName) => {
|
||||
state.syncVars[fieldName] = syncVar.currentValue;
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 反序列化网络状态
|
||||
*/
|
||||
deserializeState(state: any): void {
|
||||
if (state.networkId) this._networkId = state.networkId;
|
||||
if (state.ownerId) this._ownerId = state.ownerId;
|
||||
if (typeof state.isLocalPlayer === 'boolean') this.isLocalPlayer = state.isLocalPlayer;
|
||||
if (state.authority) this.authority = state.authority;
|
||||
|
||||
// 反序列化SyncVar
|
||||
if (state.syncVars) {
|
||||
Object.entries(state.syncVars).forEach(([fieldName, value]) => {
|
||||
if (this.syncVars.has(fieldName)) {
|
||||
this.updateSyncVar(fieldName, value as NetworkValue);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置预测状态
|
||||
*/
|
||||
setPredictionState(enabled: boolean): void {
|
||||
this.predictionEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置插值状态
|
||||
*/
|
||||
setInterpolationState(enabled: boolean): void {
|
||||
this.interpolationEnabled = enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以发送RPC
|
||||
*/
|
||||
canSendRpc(): boolean {
|
||||
return this._hasAuthority || this._isLocalPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否可以更新SyncVar
|
||||
*/
|
||||
canUpdateSyncVar(): boolean {
|
||||
return this._hasAuthority;
|
||||
}
|
||||
|
||||
/**
|
||||
* 组件销毁
|
||||
*/
|
||||
onDestroy(): void {
|
||||
this.notifyNetworkStop();
|
||||
this.networkBehaviours = [];
|
||||
this.syncVars.clear();
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 核心模块导出
|
||||
*/
|
||||
|
||||
export * from './NetworkClient';
|
||||
export * from './ClientNetworkBehaviour';
|
||||
export * from './NetworkIdentity';
|
||||
Reference in New Issue
Block a user