重构network库(mvp版本)搭建基础设施和核心接口

定义ITransport/ISerializer/INetworkMessage接口
NetworkIdentity组件
基础事件定义
This commit is contained in:
YHH
2025-08-13 13:07:40 +08:00
parent 25136349ff
commit 62f250b43c
97 changed files with 1877 additions and 16607 deletions

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -1,7 +0,0 @@
/**
* 核心模块导出
*/
export * from './NetworkClient';
export * from './ClientNetworkBehaviour';
export * from './NetworkIdentity';

View File

@@ -1,108 +0,0 @@
/**
* ClientRpc装饰器 - 客户端版本
*
* 用于标记可以从服务器调用的客户端方法
*/
import 'reflect-metadata';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
/**
* ClientRpc配置选项
*/
export interface ClientRpcOptions {
/** 是否可靠传输 */
reliable?: boolean;
/** 超时时间(毫秒) */
timeout?: number;
/** 是否仅发送给所有者 */
ownerOnly?: boolean;
/** 是否包含发送者 */
includeSender?: boolean;
/** 权限要求 */
requireAuthority?: boolean;
}
/**
* ClientRpc元数据键
*/
export const CLIENT_RPC_METADATA_KEY = Symbol('client_rpc');
/**
* ClientRpc元数据
*/
export interface ClientRpcMetadata {
/** 方法名 */
methodName: string;
/** 配置选项 */
options: ClientRpcOptions;
/** 原始方法 */
originalMethod: Function;
}
/**
* ClientRpc装饰器
*/
export function ClientRpc(options: ClientRpcOptions = {}): MethodDecorator {
return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
const methodName = propertyKey as string;
const originalMethod = descriptor.value;
// 获取已有的ClientRpc元数据
const existingMetadata: ClientRpcMetadata[] = Reflect.getMetadata(CLIENT_RPC_METADATA_KEY, target.constructor) || [];
// 添加新的ClientRpc元数据
existingMetadata.push({
methodName,
options: {
reliable: true,
timeout: 30000,
ownerOnly: false,
includeSender: false,
requireAuthority: false,
...options
},
originalMethod
});
// 设置元数据
Reflect.defineMetadata(CLIENT_RPC_METADATA_KEY, existingMetadata, target.constructor);
// 包装原方法客户端接收RPC调用时执行
descriptor.value = function (this: any, ...args: NetworkValue[]) {
try {
// 直接调用原方法客户端接收RPC调用
return originalMethod.apply(this, args);
} catch (error) {
console.error(`Error executing ClientRpc ${methodName}:`, error);
throw error;
}
};
return descriptor;
};
}
/**
* 获取类的所有ClientRpc元数据
*/
export function getClientRpcMetadata(target: any): ClientRpcMetadata[] {
return Reflect.getMetadata(CLIENT_RPC_METADATA_KEY, target) || [];
}
/**
* 检查方法是否为ClientRpc
*/
export function isClientRpc(target: any, methodName: string): boolean {
const metadata = getClientRpcMetadata(target);
return metadata.some(meta => meta.methodName === methodName);
}
/**
* 获取特定方法的ClientRpc选项
*/
export function getClientRpcOptions(target: any, methodName: string): ClientRpcOptions | null {
const metadata = getClientRpcMetadata(target);
const rpc = metadata.find(meta => meta.methodName === methodName);
return rpc ? rpc.options : null;
}

View File

@@ -1,138 +0,0 @@
/**
* ServerRpc装饰器 - 客户端版本
*
* 用于标记可以向服务器发送的RPC方法
*/
import 'reflect-metadata';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
import { ClientNetworkBehaviour } from '../core/ClientNetworkBehaviour';
/**
* ServerRpc配置选项
*/
export interface ServerRpcOptions {
/** 是否可靠传输 */
reliable?: boolean;
/** 超时时间(毫秒) */
timeout?: number;
/** 是否需要权威 */
requireAuthority?: boolean;
/** 是否需要是本地玩家 */
requireLocalPlayer?: boolean;
}
/**
* ServerRpc元数据键
*/
export const SERVER_RPC_METADATA_KEY = Symbol('server_rpc');
/**
* ServerRpc元数据
*/
export interface ServerRpcMetadata {
/** 方法名 */
methodName: string;
/** 配置选项 */
options: ServerRpcOptions;
/** 原始方法 */
originalMethod: Function;
}
/**
* ServerRpc装饰器
*/
export function ServerRpc(options: ServerRpcOptions = {}): MethodDecorator {
return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
const methodName = propertyKey as string;
const originalMethod = descriptor.value;
// 获取已有的ServerRpc元数据
const existingMetadata: ServerRpcMetadata[] = Reflect.getMetadata(SERVER_RPC_METADATA_KEY, target.constructor) || [];
// 添加新的ServerRpc元数据
existingMetadata.push({
methodName,
options: {
reliable: true,
timeout: 30000,
requireAuthority: false,
requireLocalPlayer: false,
...options
},
originalMethod
});
// 设置元数据
Reflect.defineMetadata(SERVER_RPC_METADATA_KEY, existingMetadata, target.constructor);
// 替换方法实现为发送RPC调用
descriptor.value = async function (this: ClientNetworkBehaviour, ...args: NetworkValue[]) {
try {
// 获取NetworkIdentity
const networkIdentity = this.entity?.getComponent('NetworkIdentity' as any);
if (!networkIdentity) {
throw new Error('NetworkIdentity component not found');
}
// 检查权限要求
if (options.requireAuthority && !(networkIdentity as any).hasAuthority) {
throw new Error(`ServerRpc ${methodName} requires authority`);
}
if (options.requireLocalPlayer && !(networkIdentity as any).isLocalPlayer) {
throw new Error(`ServerRpc ${methodName} requires local player`);
}
// 发送RPC到服务器
if (options.reliable) {
const result = await this.sendServerRpc(methodName, ...args);
return result;
} else {
await this.sendServerRpcUnreliable(methodName, ...args);
return null;
}
} catch (error) {
console.error(`Error sending ServerRpc ${methodName}:`, error);
throw error;
}
};
// 保存原方法到特殊属性,用于本地预测或调试
(descriptor.value as any).__originalMethod = originalMethod;
return descriptor;
};
}
/**
* 获取类的所有ServerRpc元数据
*/
export function getServerRpcMetadata(target: any): ServerRpcMetadata[] {
return Reflect.getMetadata(SERVER_RPC_METADATA_KEY, target) || [];
}
/**
* 检查方法是否为ServerRpc
*/
export function isServerRpc(target: any, methodName: string): boolean {
const metadata = getServerRpcMetadata(target);
return metadata.some(meta => meta.methodName === methodName);
}
/**
* 获取特定方法的ServerRpc选项
*/
export function getServerRpcOptions(target: any, methodName: string): ServerRpcOptions | null {
const metadata = getServerRpcMetadata(target);
const rpc = metadata.find(meta => meta.methodName === methodName);
return rpc ? rpc.options : null;
}
/**
* 获取方法的原始实现(未被装饰器修改的版本)
*/
export function getOriginalMethod(method: Function): Function | null {
return (method as any).__originalMethod || null;
}

View File

@@ -1,146 +0,0 @@
/**
* SyncVar装饰器 - 客户端版本
*
* 用于标记需要同步的变量
*/
import 'reflect-metadata';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
import { ClientNetworkBehaviour } from '../core/ClientNetworkBehaviour';
/**
* SyncVar配置选项
*/
export interface SyncVarOptions {
/** 是否可从客户端修改 */
clientCanModify?: boolean;
/** 同步间隔(毫秒)0表示立即同步 */
syncInterval?: number;
/** 是否仅同步给所有者 */
ownerOnly?: boolean;
/** 自定义序列化器 */
serializer?: (value: any) => NetworkValue;
/** 自定义反序列化器 */
deserializer?: (value: NetworkValue) => any;
}
/**
* SyncVar元数据键
*/
export const SYNCVAR_METADATA_KEY = Symbol('syncvar');
/**
* SyncVar元数据
*/
export interface SyncVarMetadata {
/** 属性名 */
propertyKey: string;
/** 配置选项 */
options: SyncVarOptions;
}
/**
* SyncVar装饰器
*/
export function SyncVar(options: SyncVarOptions = {}): PropertyDecorator {
return function (target: any, propertyKey: string | symbol) {
const key = propertyKey as string;
// 获取已有的SyncVar元数据
const existingMetadata: SyncVarMetadata[] = Reflect.getMetadata(SYNCVAR_METADATA_KEY, target.constructor) || [];
// 添加新的SyncVar元数据
existingMetadata.push({
propertyKey: key,
options: {
clientCanModify: false,
syncInterval: 0,
ownerOnly: false,
...options
}
});
// 设置元数据
Reflect.defineMetadata(SYNCVAR_METADATA_KEY, existingMetadata, target.constructor);
// 存储原始属性名(用于内部存储)
const privateKey = `_syncvar_${key}`;
// 创建属性访问器
Object.defineProperty(target, key, {
get: function (this: ClientNetworkBehaviour) {
// 从NetworkIdentity获取SyncVar值
const networkIdentity = this.entity?.getComponent('NetworkIdentity' as any);
if (networkIdentity) {
const syncVarValue = (networkIdentity as any).getSyncVar(key);
if (syncVarValue !== undefined) {
return options.deserializer ? options.deserializer(syncVarValue) : syncVarValue;
}
}
// 如果网络值不存在,返回本地存储的值
return (this as any)[privateKey];
},
set: function (this: ClientNetworkBehaviour, value: any) {
const oldValue = (this as any)[privateKey];
const newValue = options.serializer ? options.serializer(value) : value;
// 存储到本地
(this as any)[privateKey] = value;
// 获取NetworkIdentity
const networkIdentity = this.entity?.getComponent('NetworkIdentity' as any);
if (!networkIdentity) {
return;
}
// 检查是否可以修改
if (!options.clientCanModify && !(networkIdentity as any).hasAuthority) {
console.warn(`Cannot modify SyncVar ${key} without authority`);
return;
}
// 注册SyncVar如果尚未注册
(networkIdentity as any).registerSyncVar(key, newValue);
// 更新NetworkIdentity中的值
(networkIdentity as any).updateSyncVar(key, newValue);
// 如果有权威且值发生变化,发送到服务器
if ((networkIdentity as any).hasAuthority && oldValue !== value) {
this.updateSyncVar(key, newValue).catch(error => {
console.error(`Failed to sync variable ${key}:`, error);
});
}
},
enumerable: true,
configurable: true
});
};
}
/**
* 获取类的所有SyncVar元数据
*/
export function getSyncVarMetadata(target: any): SyncVarMetadata[] {
return Reflect.getMetadata(SYNCVAR_METADATA_KEY, target) || [];
}
/**
* 检查属性是否为SyncVar
*/
export function isSyncVar(target: any, propertyKey: string): boolean {
const metadata = getSyncVarMetadata(target);
return metadata.some(meta => meta.propertyKey === propertyKey);
}
/**
* 获取特定属性的SyncVar选项
*/
export function getSyncVarOptions(target: any, propertyKey: string): SyncVarOptions | null {
const metadata = getSyncVarMetadata(target);
const syncVar = metadata.find(meta => meta.propertyKey === propertyKey);
return syncVar ? syncVar.options : null;
}

View File

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

View File

@@ -1,23 +1,24 @@
/**
* ECS Framework 网络库 - 客户端
*
* 提供网络客户端功能,包括连接管理、预测、插值等
* @esengine/network-client
* ECS Framework网络层 - 客户端实现
*/
// 核心模块
export * from './core';
// 核心客户端 (待实现)
// export * from './core/NetworkClient';
// export * from './core/ServerConnection';
// 传输层
export * from './transport';
// 传输层 (待实现)
// export * from './transport/WebSocketClient';
// export * from './transport/HttpClient';
// 装饰器
export * from './decorators';
// 系统层 (待实现)
// export * from './systems/ClientSyncSystem';
// export * from './systems/ClientRpcSystem';
// export * from './systems/InterpolationSystem';
// 系统
export * from './systems';
// 平台适配器 (待实现)
// export * from './adapters/BrowserAdapter';
// export * from './adapters/CocosAdapter';
// 接口
export * from './interfaces';
// 版本信息
export const VERSION = '1.0.11';
// 重新导出shared包的类型
export * from '@esengine/network-shared';

View File

@@ -1,34 +0,0 @@
/**
* 网络系统相关接口
*/
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
/**
* 可预测组件接口
*
* 实现此接口的组件可以参与客户端预测系统
*/
export interface IPredictable {
/**
* 预测更新
*
* @param inputs 输入数据
* @param timestamp 时间戳
*/
predictUpdate(inputs: NetworkValue, timestamp: number): void;
}
/**
* 可插值组件接口
*
* 实现此接口的组件可以参与插值系统
*/
export interface IInterpolatable {
/**
* 应用插值状态
*
* @param state 插值后的状态数据
*/
applyInterpolatedState(state: NetworkValue): void;
}

View File

@@ -1,5 +0,0 @@
/**
* 接口导出
*/
export * from './NetworkInterfaces';

View File

@@ -1,520 +0,0 @@
/**
* 客户端插值系统
*
* 实现网络对象的平滑插值
*/
import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
import { NetworkIdentity } from '../core/NetworkIdentity';
import { IInterpolatable } from '../interfaces/NetworkInterfaces';
/**
* 插值状态快照
*/
export interface InterpolationSnapshot {
/** 时间戳 */
timestamp: number;
/** 网络ID */
networkId: string;
/** 状态数据 */
state: NetworkValue;
}
/**
* 插值目标
*/
export interface InterpolationTarget {
/** 网络ID */
networkId: string;
/** 起始状态 */
fromState: NetworkValue;
/** 目标状态 */
toState: NetworkValue;
/** 起始时间 */
fromTime: number;
/** 结束时间 */
toTime: number;
/** 当前插值进度 (0-1) */
progress: number;
}
/**
* 插值配置
*/
export interface InterpolationConfig {
/** 插值延迟(毫秒) */
delay: number;
/** 最大插值时间(毫秒) */
maxTime: number;
/** 插值缓冲区大小 */
bufferSize: number;
/** 外推是否启用 */
enableExtrapolation: boolean;
/** 最大外推时间(毫秒) */
maxExtrapolationTime: number;
}
/**
* 插值算法类型
*/
export enum InterpolationType {
/** 线性插值 */
LINEAR = 'linear',
/** 平滑插值 */
SMOOTHSTEP = 'smoothstep',
/** 三次贝塞尔插值 */
CUBIC = 'cubic'
}
/**
* 客户端插值系统
*/
export class InterpolationSystem extends EntitySystem {
/** 插值状态缓冲区 */
private stateBuffer: Map<string, InterpolationSnapshot[]> = new Map();
/** 当前插值目标 */
private interpolationTargets: Map<string, InterpolationTarget> = new Map();
/** 插值配置 */
private config: InterpolationConfig;
/** 当前时间 */
private currentTime: number = 0;
constructor(config?: Partial<InterpolationConfig>) {
// 使用Matcher查询具有NetworkIdentity的实体
super(Matcher.all(NetworkIdentity));
this.config = {
delay: 100,
maxTime: 500,
bufferSize: 32,
enableExtrapolation: false,
maxExtrapolationTime: 50,
...config
};
this.currentTime = Date.now();
}
/**
* 系统初始化
*/
override initialize(): void {
super.initialize();
this.currentTime = Date.now();
}
/**
* 系统更新
*/
override update(): void {
this.currentTime = Date.now();
this.cleanupOldStates();
// 调用父类update会自动调用process方法处理匹配的实体
super.update();
}
/**
* 处理匹配的实体
*/
protected override process(entities: Entity[]): void {
const interpolationTime = this.currentTime - this.config.delay;
for (const entity of entities) {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (networkIdentity && networkIdentity.isInterpolationEnabled) {
const networkId = networkIdentity.networkId;
const target = this.interpolationTargets.get(networkId);
if (target) {
// 计算插值进度
const duration = target.toTime - target.fromTime;
if (duration > 0) {
const elapsed = interpolationTime - target.fromTime;
target.progress = Math.max(0, Math.min(1, elapsed / duration));
// 执行插值
const interpolatedState = this.interpolateStates(
target.fromState,
target.toState,
target.progress,
InterpolationType.LINEAR
);
// 应用插值状态
this.applyInterpolatedState(entity, interpolatedState);
// 检查是否需要外推
if (target.progress >= 1 && this.config.enableExtrapolation) {
this.performExtrapolation(entity, target, interpolationTime);
}
}
}
}
}
}
/**
* 添加网络状态快照
*/
addStateSnapshot(networkId: string, state: NetworkValue, timestamp: number): void {
// 获取或创建缓冲区
if (!this.stateBuffer.has(networkId)) {
this.stateBuffer.set(networkId, []);
}
const buffer = this.stateBuffer.get(networkId)!;
const snapshot: InterpolationSnapshot = {
timestamp,
networkId,
state
};
// 插入到正确的位置(按时间戳排序)
const insertIndex = this.findInsertIndex(buffer, timestamp);
buffer.splice(insertIndex, 0, snapshot);
// 保持缓冲区大小
if (buffer.length > this.config.bufferSize) {
buffer.shift();
}
// 更新插值目标
this.updateInterpolationTarget(networkId);
}
/**
* 更新插值目标
*/
private updateInterpolationTarget(networkId: string): void {
const buffer = this.stateBuffer.get(networkId);
if (!buffer || buffer.length < 2) {
return;
}
const interpolationTime = this.currentTime - this.config.delay;
// 查找插值区间
const { from, to } = this.findInterpolationRange(buffer, interpolationTime);
if (!from || !to) {
return;
}
// 更新或创建插值目标
this.interpolationTargets.set(networkId, {
networkId,
fromState: from.state,
toState: to.state,
fromTime: from.timestamp,
toTime: to.timestamp,
progress: 0
});
}
/**
* 查找插值区间
*/
private findInterpolationRange(buffer: InterpolationSnapshot[], time: number): {
from: InterpolationSnapshot | null;
to: InterpolationSnapshot | null;
} {
let from: InterpolationSnapshot | null = null;
let to: InterpolationSnapshot | null = null;
for (let i = 0; i < buffer.length - 1; i++) {
const current = buffer[i];
const next = buffer[i + 1];
if (time >= current.timestamp && time <= next.timestamp) {
from = current;
to = next;
break;
}
}
// 如果没有找到区间,使用最近的两个状态
if (!from && !to && buffer.length >= 2) {
if (time < buffer[0].timestamp) {
// 时间过早,使用前两个状态
from = buffer[0];
to = buffer[1];
} else if (time > buffer[buffer.length - 1].timestamp) {
// 时间过晚,使用后两个状态
from = buffer[buffer.length - 2];
to = buffer[buffer.length - 1];
}
}
return { from, to };
}
/**
* 状态插值
*/
private interpolateStates(
fromState: NetworkValue,
toState: NetworkValue,
progress: number,
type: InterpolationType
): NetworkValue {
// 调整插值进度曲线
const adjustedProgress = this.adjustProgress(progress, type);
try {
return this.interpolateValue(fromState, toState, adjustedProgress);
} catch (error) {
console.error('Error interpolating states:', error);
return toState; // 出错时返回目标状态
}
}
/**
* 递归插值值
*/
private interpolateValue(from: NetworkValue, to: NetworkValue, progress: number): NetworkValue {
// 如果类型不同,直接返回目标值
if (typeof from !== typeof to) {
return to;
}
// 数字插值
if (typeof from === 'number' && typeof to === 'number') {
return from + (to - from) * progress;
}
// 字符串插值(直接切换)
if (typeof from === 'string' && typeof to === 'string') {
return progress < 0.5 ? from : to;
}
// 布尔插值(直接切换)
if (typeof from === 'boolean' && typeof to === 'boolean') {
return progress < 0.5 ? from : to;
}
// 数组插值
if (Array.isArray(from) && Array.isArray(to)) {
const result: NetworkValue[] = [];
const maxLength = Math.max(from.length, to.length);
for (let i = 0; i < maxLength; i++) {
const fromValue = i < from.length ? from[i] : to[i];
const toValue = i < to.length ? to[i] : from[i];
result[i] = this.interpolateValue(fromValue, toValue, progress);
}
return result;
}
// 对象插值
if (from && to && typeof from === 'object' && typeof to === 'object') {
const result: any = {};
const allKeys = new Set([...Object.keys(from), ...Object.keys(to)]);
for (const key of allKeys) {
const fromValue = (from as any)[key];
const toValue = (to as any)[key];
if (fromValue !== undefined && toValue !== undefined) {
result[key] = this.interpolateValue(fromValue, toValue, progress);
} else {
result[key] = toValue !== undefined ? toValue : fromValue;
}
}
return result;
}
// 其他类型直接返回目标值
return to;
}
/**
* 调整插值进度曲线
*/
private adjustProgress(progress: number, type: InterpolationType): number {
switch (type) {
case InterpolationType.LINEAR:
return progress;
case InterpolationType.SMOOTHSTEP:
return progress * progress * (3 - 2 * progress);
case InterpolationType.CUBIC:
return progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
default:
return progress;
}
}
/**
* 应用插值状态到实体
*/
private applyInterpolatedState(entity: Entity, state: NetworkValue): void {
// 获取所有可插值的组件
const components: any[] = [];
for (const component of components) {
if (this.isInterpolatable(component)) {
try {
(component as IInterpolatable).applyInterpolatedState(state);
} catch (error) {
console.error('Error applying interpolated state:', error);
}
}
}
// 更新NetworkIdentity中的状态
const networkIdentity = entity.getComponent(NetworkIdentity);
if (networkIdentity && typeof networkIdentity.deserializeState === 'function') {
try {
networkIdentity.deserializeState(state);
} catch (error) {
console.error('Error deserializing interpolated state:', error);
}
}
}
/**
* 检查组件是否实现了IInterpolatable接口
*/
private isInterpolatable(component: any): component is IInterpolatable {
return component && typeof component.applyInterpolatedState === 'function';
}
/**
* 执行外推
*/
private performExtrapolation(entity: Entity, target: InterpolationTarget, currentTime: number): void {
if (!this.config.enableExtrapolation) {
return;
}
const extrapolationTime = currentTime - target.toTime;
if (extrapolationTime > this.config.maxExtrapolationTime) {
return;
}
// 计算外推状态
const extrapolationProgress = extrapolationTime / (target.toTime - target.fromTime);
const extrapolatedState = this.extrapolateState(
target.fromState,
target.toState,
1 + extrapolationProgress
);
// 应用外推状态
this.applyInterpolatedState(entity, extrapolatedState);
}
/**
* 状态外推
*/
private extrapolateState(fromState: NetworkValue, toState: NetworkValue, progress: number): NetworkValue {
// 简单的线性外推
return this.interpolateValue(fromState, toState, progress);
}
/**
* 查找插入位置
*/
private findInsertIndex(buffer: InterpolationSnapshot[], timestamp: number): number {
let left = 0;
let right = buffer.length;
while (left < right) {
const mid = Math.floor((left + right) / 2);
if (buffer[mid].timestamp < timestamp) {
left = mid + 1;
} else {
right = mid;
}
}
return left;
}
/**
* 清理过期状态
*/
private cleanupOldStates(): void {
const cutoffTime = this.currentTime - this.config.maxTime;
this.stateBuffer.forEach((buffer, networkId) => {
// 移除过期的状态
const validStates = buffer.filter(snapshot => snapshot.timestamp > cutoffTime);
if (validStates.length !== buffer.length) {
this.stateBuffer.set(networkId, validStates);
}
// 如果缓冲区为空,移除它
if (validStates.length === 0) {
this.stateBuffer.delete(networkId);
this.interpolationTargets.delete(networkId);
}
});
}
/**
* 根据网络ID查找实体
*/
private findEntityByNetworkId(networkId: string): Entity | null {
// 使用系统的entities属性来查找
for (const entity of this.entities) {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (networkIdentity && networkIdentity.networkId === networkId) {
return entity;
}
}
return null;
}
/**
* 设置插值配置
*/
setInterpolationConfig(config: Partial<InterpolationConfig>): void {
this.config = { ...this.config, ...config };
}
/**
* 获取插值统计信息
*/
getInterpolationStats(): { [networkId: string]: { bufferSize: number; progress: number } } {
const stats: { [networkId: string]: { bufferSize: number; progress: number } } = {};
this.stateBuffer.forEach((buffer, networkId) => {
const target = this.interpolationTargets.get(networkId);
stats[networkId] = {
bufferSize: buffer.length,
progress: target ? target.progress : 0
};
});
return stats;
}
/**
* 清空所有插值数据
*/
clearInterpolationData(): void {
this.stateBuffer.clear();
this.interpolationTargets.clear();
}
/**
* 系统销毁
*/
onDestroy(): void {
this.clearInterpolationData();
}
}

View File

@@ -1,362 +0,0 @@
/**
* 客户端预测系统
*
* 实现客户端预测和服务器和解
*/
import { EntitySystem, Entity, Matcher, Time } from '@esengine/ecs-framework';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
import { NetworkIdentity } from '../core/NetworkIdentity';
import { IPredictable } from '../interfaces/NetworkInterfaces';
/**
* 预测状态快照
*/
export interface PredictionSnapshot {
/** 时间戳 */
timestamp: number;
/** 网络ID */
networkId: string;
/** 状态数据 */
state: NetworkValue;
/** 输入数据 */
inputs?: NetworkValue;
}
/**
* 预测输入
*/
export interface PredictionInput {
/** 时间戳 */
timestamp: number;
/** 输入数据 */
data: NetworkValue;
}
/**
* 客户端预测系统
*/
export class PredictionSystem extends EntitySystem {
/** 预测状态缓冲区 */
private predictionBuffer: Map<string, PredictionSnapshot[]> = new Map();
/** 输入缓冲区 */
private inputBuffer: PredictionInput[] = [];
/** 最大缓冲区大小 */
private maxBufferSize: number = 64;
/** 预测时间窗口(毫秒) */
private predictionWindow: number = 500;
/** 当前预测时间戳 */
private currentPredictionTime: number = 0;
constructor(maxBufferSize = 64, predictionWindow = 500) {
// 使用Matcher查询具有NetworkIdentity的实体
super(Matcher.all(NetworkIdentity));
this.maxBufferSize = maxBufferSize;
this.predictionWindow = predictionWindow;
this.currentPredictionTime = Date.now();
}
/**
* 系统初始化
*/
override initialize(): void {
super.initialize();
this.currentPredictionTime = Date.now();
}
/**
* 系统更新
*/
override update(): void {
this.currentPredictionTime = Date.now();
this.cleanupOldSnapshots();
// 调用父类update会自动调用process方法处理匹配的实体
super.update();
}
/**
* 处理匹配的实体
*/
protected override process(entities: Entity[]): void {
for (const entity of entities) {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (networkIdentity &&
networkIdentity.isPredictionEnabled &&
networkIdentity.isLocalPlayer) {
// 保存当前状态快照
this.saveSnapshot(entity);
// 应用当前输入进行预测
const currentInputs = this.getCurrentInputs();
if (currentInputs) {
this.applyInputs(entity, currentInputs, this.currentPredictionTime);
}
}
}
}
/**
* 添加预测输入
*/
addInput(input: PredictionInput): void {
this.inputBuffer.push(input);
// 保持输入缓冲区大小
if (this.inputBuffer.length > this.maxBufferSize) {
this.inputBuffer.shift();
}
// 按时间戳排序
this.inputBuffer.sort((a, b) => a.timestamp - b.timestamp);
}
/**
* 保存预测状态快照
*/
saveSnapshot(entity: Entity): void {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (!networkIdentity || !networkIdentity.isPredictionEnabled) {
return;
}
const networkId = networkIdentity.networkId;
const snapshot: PredictionSnapshot = {
timestamp: this.currentPredictionTime,
networkId,
state: networkIdentity.serializeState(),
inputs: this.getCurrentInputs() || undefined
};
// 获取或创建缓冲区
if (!this.predictionBuffer.has(networkId)) {
this.predictionBuffer.set(networkId, []);
}
const buffer = this.predictionBuffer.get(networkId)!;
buffer.push(snapshot);
// 保持缓冲区大小
if (buffer.length > this.maxBufferSize) {
buffer.shift();
}
}
/**
* 从服务器接收权威状态进行和解
*/
reconcileWithServer(networkId: string, serverState: NetworkValue, serverTimestamp: number): void {
const buffer = this.predictionBuffer.get(networkId);
if (!buffer || buffer.length === 0) {
return;
}
// 查找对应时间戳的预测状态
const predictionSnapshot = this.findSnapshot(buffer, serverTimestamp);
if (!predictionSnapshot) {
return;
}
// 比较预测状态和服务器状态
if (this.statesMatch(predictionSnapshot.state, serverState)) {
// 预测正确,移除已确认的快照
this.removeSnapshotsBeforeTimestamp(buffer, serverTimestamp);
return;
}
// 预测错误,需要进行和解
this.performReconciliation(networkId, serverState, serverTimestamp);
}
/**
* 执行预测和解
*/
private performReconciliation(networkId: string, serverState: NetworkValue, serverTimestamp: number): void {
const entity = this.findEntityByNetworkId(networkId);
if (!entity) {
return;
}
const networkIdentity = entity.getComponent(NetworkIdentity);
if (!networkIdentity) {
return;
}
// 回滚到服务器状态
if (typeof networkIdentity.deserializeState === 'function') {
networkIdentity.deserializeState(serverState);
}
// 重新应用服务器时间戳之后的输入
const buffer = this.predictionBuffer.get(networkId)!;
const snapshotsToReplay = buffer.filter(snapshot => snapshot.timestamp > serverTimestamp);
for (const snapshot of snapshotsToReplay) {
if (snapshot.inputs) {
this.applyInputs(entity, snapshot.inputs, snapshot.timestamp);
}
}
// 清理已和解的快照
this.removeSnapshotsBeforeTimestamp(buffer, serverTimestamp);
}
/**
* 应用输入进行预测计算
*/
private applyInputs(entity: Entity, inputs: NetworkValue, timestamp: number): void {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (!networkIdentity) return;
// 获取实体的所有组件并检查是否实现了IPredictable接口
const components: any[] = [];
for (const component of components) {
if (this.isPredictable(component)) {
try {
(component as IPredictable).predictUpdate(inputs, timestamp);
} catch (error) {
console.error('Error applying prediction:', error);
}
}
}
}
/**
* 检查组件是否实现了IPredictable接口
*/
private isPredictable(component: any): component is IPredictable {
return component && typeof component.predictUpdate === 'function';
}
/**
* 获取当前输入
*/
private getCurrentInputs(): NetworkValue | null {
if (this.inputBuffer.length === 0) {
return null;
}
// 获取最新的输入
return this.inputBuffer[this.inputBuffer.length - 1].data;
}
/**
* 查找指定时间戳的快照
*/
private findSnapshot(buffer: PredictionSnapshot[], timestamp: number): PredictionSnapshot | null {
// 查找最接近的快照
let closest: PredictionSnapshot | null = null;
let minDiff = Number.MAX_SAFE_INTEGER;
for (const snapshot of buffer) {
const diff = Math.abs(snapshot.timestamp - timestamp);
if (diff < minDiff) {
minDiff = diff;
closest = snapshot;
}
}
return closest;
}
/**
* 比较两个状态是否匹配
*/
private statesMatch(predictedState: NetworkValue, serverState: NetworkValue): boolean {
try {
// 简单的JSON比较实际应用中可能需要更精确的比较
return JSON.stringify(predictedState) === JSON.stringify(serverState);
} catch (error) {
return false;
}
}
/**
* 移除指定时间戳之前的快照
*/
private removeSnapshotsBeforeTimestamp(buffer: PredictionSnapshot[], timestamp: number): void {
for (let i = buffer.length - 1; i >= 0; i--) {
if (buffer[i].timestamp < timestamp) {
buffer.splice(0, i + 1);
break;
}
}
}
/**
* 清理过期的快照
*/
private cleanupOldSnapshots(): void {
const cutoffTime = this.currentPredictionTime - this.predictionWindow;
this.predictionBuffer.forEach((buffer, networkId) => {
this.removeSnapshotsBeforeTimestamp(buffer, cutoffTime);
// 如果缓冲区为空,移除它
if (buffer.length === 0) {
this.predictionBuffer.delete(networkId);
}
});
// 清理过期的输入
this.inputBuffer = this.inputBuffer.filter(input =>
input.timestamp > cutoffTime
);
}
/**
* 根据网络ID查找实体
*/
private findEntityByNetworkId(networkId: string): Entity | null {
// 使用系统的entities属性来查找
for (const entity of this.entities) {
const networkIdentity = entity.getComponent(NetworkIdentity);
if (networkIdentity && networkIdentity.networkId === networkId) {
return entity;
}
}
return null;
}
/**
* 设置预测配置
*/
setPredictionConfig(maxBufferSize: number, predictionWindow: number): void {
this.maxBufferSize = maxBufferSize;
this.predictionWindow = predictionWindow;
}
/**
* 获取预测统计信息
*/
getPredictionStats(): { [networkId: string]: number } {
const stats: { [networkId: string]: number } = {};
this.predictionBuffer.forEach((buffer, networkId) => {
stats[networkId] = buffer.length;
});
return stats;
}
/**
* 清空所有预测数据
*/
clearPredictionData(): void {
this.predictionBuffer.clear();
this.inputBuffer = [];
}
/**
* 系统销毁
*/
onDestroy(): void {
this.clearPredictionData();
}
}

View File

@@ -1,6 +0,0 @@
/**
* 系统导出
*/
export * from './PredictionSystem';
export * from './InterpolationSystem';

View File

@@ -1,445 +0,0 @@
/**
* 客户端传输层抽象接口
*/
import { Emitter, ITimer, Core } from '@esengine/ecs-framework';
import { NetworkValue } from '@esengine/ecs-framework-network-shared';
/**
* 客户端传输配置
*/
export interface ClientTransportConfig {
/** 服务器地址 */
host: string;
/** 服务器端口 */
port: number;
/** 是否使用安全连接 */
secure?: boolean;
/** 连接超时时间(毫秒) */
connectionTimeout?: number;
/** 重连间隔(毫秒) */
reconnectInterval?: number;
/** 最大重连次数 */
maxReconnectAttempts?: number;
/** 心跳间隔(毫秒) */
heartbeatInterval?: number;
/** 消息队列最大大小 */
maxQueueSize?: number;
}
/**
* 连接状态
*/
export enum ConnectionState {
/** 断开连接 */
DISCONNECTED = 'disconnected',
/** 连接中 */
CONNECTING = 'connecting',
/** 已连接 */
CONNECTED = 'connected',
/** 认证中 */
AUTHENTICATING = 'authenticating',
/** 已认证 */
AUTHENTICATED = 'authenticated',
/** 重连中 */
RECONNECTING = 'reconnecting',
/** 连接错误 */
ERROR = 'error'
}
/**
* 客户端消息
*/
export interface ClientMessage {
/** 消息类型 */
type: 'rpc' | 'syncvar' | 'system' | 'custom';
/** 消息数据 */
data: NetworkValue;
/** 消息ID用于响应匹配 */
messageId?: string;
/** 是否可靠传输 */
reliable?: boolean;
/** 时间戳 */
timestamp?: number;
}
/**
* 连接统计信息
*/
export interface ConnectionStats {
/** 连接时间 */
connectedAt: Date | null;
/** 连接持续时间(毫秒) */
connectionDuration: number;
/** 发送消息数 */
messagesSent: number;
/** 接收消息数 */
messagesReceived: number;
/** 发送字节数 */
bytesSent: number;
/** 接收字节数 */
bytesReceived: number;
/** 重连次数 */
reconnectCount: number;
/** 丢失消息数 */
messagesLost: number;
/** 平均延迟(毫秒) */
averageLatency: number;
}
/**
* 客户端传输事件
*/
export interface ClientTransportEvents {
/** 连接建立 */
'connected': () => void;
/** 连接断开 */
'disconnected': (reason: string) => void;
/** 连接状态变化 */
'state-changed': (oldState: ConnectionState, newState: ConnectionState) => void;
/** 收到消息 */
'message': (message: ClientMessage) => void;
/** 连接错误 */
'error': (error: Error) => void;
/** 重连开始 */
'reconnecting': (attempt: number, maxAttempts: number) => void;
/** 重连成功 */
'reconnected': () => void;
/** 重连失败 */
'reconnect-failed': () => void;
/** 延迟更新 */
'latency-updated': (latency: number) => void;
}
/**
* 客户端传输层抽象类
*/
export abstract class ClientTransport {
protected config: ClientTransportConfig;
protected state: ConnectionState = ConnectionState.DISCONNECTED;
protected stats: ConnectionStats;
protected messageQueue: ClientMessage[] = [];
protected reconnectAttempts = 0;
protected reconnectTimer: ITimer<any> | null = null;
protected heartbeatTimer: ITimer<any> | null = null;
private latencyMeasurements: number[] = [];
private eventEmitter: Emitter<keyof ClientTransportEvents, any>;
constructor(config: ClientTransportConfig) {
this.eventEmitter = new Emitter<keyof ClientTransportEvents, any>();
this.config = {
secure: false,
connectionTimeout: 10000, // 10秒
reconnectInterval: 3000, // 3秒
maxReconnectAttempts: 10,
heartbeatInterval: 30000, // 30秒
maxQueueSize: 1000,
...config
};
this.stats = {
connectedAt: null,
connectionDuration: 0,
messagesSent: 0,
messagesReceived: 0,
bytesSent: 0,
bytesReceived: 0,
reconnectCount: 0,
messagesLost: 0,
averageLatency: 0
};
}
/**
* 连接到服务器
*/
abstract connect(): Promise<void>;
/**
* 断开连接
*/
abstract disconnect(): Promise<void>;
/**
* 发送消息
*/
abstract sendMessage(message: ClientMessage): Promise<boolean>;
/**
* 获取当前连接状态
*/
getState(): ConnectionState {
return this.state;
}
/**
* 检查是否已连接
*/
isConnected(): boolean {
return this.state === ConnectionState.CONNECTED ||
this.state === ConnectionState.AUTHENTICATED;
}
/**
* 获取连接统计信息
*/
getStats(): ConnectionStats {
if (this.stats.connectedAt) {
this.stats.connectionDuration = Date.now() - this.stats.connectedAt.getTime();
}
return { ...this.stats };
}
/**
* 获取配置
*/
getConfig(): Readonly<ClientTransportConfig> {
return this.config;
}
/**
* 设置状态
*/
protected setState(newState: ConnectionState): void {
if (this.state !== newState) {
const oldState = this.state;
this.state = newState;
this.eventEmitter.emit('state-changed', oldState, newState);
// 特殊状态处理
if (newState === ConnectionState.CONNECTED) {
this.stats.connectedAt = new Date();
this.reconnectAttempts = 0;
this.startHeartbeat();
this.processMessageQueue();
this.eventEmitter.emit('connected');
if (oldState === ConnectionState.RECONNECTING) {
this.eventEmitter.emit('reconnected');
}
} else if (newState === ConnectionState.DISCONNECTED) {
this.stats.connectedAt = null;
this.stopHeartbeat();
}
}
}
/**
* 处理接收到的消息
*/
protected handleMessage(message: ClientMessage): void {
this.stats.messagesReceived++;
if (message.data) {
try {
const messageSize = JSON.stringify(message.data).length;
this.stats.bytesReceived += messageSize;
} catch (error) {
// 忽略序列化错误
}
}
// 处理系统消息
if (message.type === 'system') {
this.handleSystemMessage(message);
return;
}
this.eventEmitter.emit('message', message);
}
/**
* 处理系统消息
*/
protected handleSystemMessage(message: ClientMessage): void {
const data = message.data as any;
switch (data.action) {
case 'ping':
// 响应ping
this.sendMessage({
type: 'system',
data: { action: 'pong', timestamp: data.timestamp }
});
break;
case 'pong':
// 计算延迟
if (data.timestamp) {
const latency = Date.now() - data.timestamp;
this.updateLatency(latency);
}
break;
}
}
/**
* 处理连接错误
*/
protected handleError(error: Error): void {
console.error('Transport error:', error.message);
this.eventEmitter.emit('error', error);
if (this.isConnected()) {
this.setState(ConnectionState.ERROR);
this.startReconnect();
}
}
/**
* 开始重连
*/
protected startReconnect(): void {
if (this.reconnectAttempts >= this.config.maxReconnectAttempts!) {
this.eventEmitter.emit('reconnect-failed');
return;
}
this.setState(ConnectionState.RECONNECTING);
this.reconnectAttempts++;
this.stats.reconnectCount++;
this.eventEmitter.emit('reconnecting', this.reconnectAttempts, this.config.maxReconnectAttempts!);
this.reconnectTimer = Core.schedule(this.config.reconnectInterval! / 1000, false, this, async () => {
try {
await this.connect();
} catch (error) {
this.startReconnect(); // 继续重连
}
});
}
/**
* 停止重连
*/
protected stopReconnect(): void {
if (this.reconnectTimer) {
this.reconnectTimer.stop();
this.reconnectTimer = null;
}
}
/**
* 将消息加入队列
*/
protected queueMessage(message: ClientMessage): boolean {
if (this.messageQueue.length >= this.config.maxQueueSize!) {
this.stats.messagesLost++;
return false;
}
this.messageQueue.push(message);
return true;
}
/**
* 处理消息队列
*/
protected async processMessageQueue(): Promise<void> {
while (this.messageQueue.length > 0 && this.isConnected()) {
const message = this.messageQueue.shift()!;
await this.sendMessage(message);
}
}
/**
* 开始心跳
*/
protected startHeartbeat(): void {
if (this.config.heartbeatInterval && this.config.heartbeatInterval > 0) {
this.heartbeatTimer = Core.schedule(this.config.heartbeatInterval / 1000, true, this, () => {
this.sendHeartbeat();
});
}
}
/**
* 停止心跳
*/
protected stopHeartbeat(): void {
if (this.heartbeatTimer) {
this.heartbeatTimer.stop();
this.heartbeatTimer = null;
}
}
/**
* 发送心跳
*/
protected sendHeartbeat(): void {
this.sendMessage({
type: 'system',
data: { action: 'ping', timestamp: Date.now() }
}).catch(() => {
// 心跳发送失败,可能连接有问题
});
}
/**
* 更新延迟统计
*/
protected updateLatency(latency: number): void {
this.latencyMeasurements.push(latency);
// 只保留最近的10个测量值
if (this.latencyMeasurements.length > 10) {
this.latencyMeasurements.shift();
}
// 计算平均延迟
const sum = this.latencyMeasurements.reduce((a, b) => a + b, 0);
this.stats.averageLatency = sum / this.latencyMeasurements.length;
this.eventEmitter.emit('latency-updated', latency);
}
/**
* 更新发送统计
*/
protected updateSendStats(message: ClientMessage): void {
this.stats.messagesSent++;
if (message.data) {
try {
const messageSize = JSON.stringify(message.data).length;
this.stats.bytesSent += messageSize;
} catch (error) {
// 忽略序列化错误
}
}
}
/**
* 销毁传输层
*/
destroy(): void {
this.stopReconnect();
this.stopHeartbeat();
this.messageQueue = [];
}
/**
* 类型安全的事件监听
*/
on<K extends keyof ClientTransportEvents>(event: K, listener: ClientTransportEvents[K]): this {
this.eventEmitter.addObserver(event, listener, this);
return this;
}
/**
* 移除事件监听
*/
off<K extends keyof ClientTransportEvents>(event: K, listener: ClientTransportEvents[K]): this {
this.eventEmitter.removeObserver(event, listener);
return this;
}
/**
* 类型安全的事件触发
*/
emit<K extends keyof ClientTransportEvents>(event: K, ...args: Parameters<ClientTransportEvents[K]>): void {
this.eventEmitter.emit(event, ...args);
}
}

View File

@@ -1,427 +0,0 @@
/**
* HTTP 客户端传输实现
*
* 支持 REST API 和长轮询
*/
import { Core, ITimer } from '@esengine/ecs-framework';
import {
ClientTransport,
ClientTransportConfig,
ConnectionState,
ClientMessage
} from './ClientTransport';
/**
* HTTP 客户端配置
*/
export interface HttpClientConfig extends ClientTransportConfig {
/** API 路径前缀 */
apiPrefix?: string;
/** 请求超时时间(毫秒) */
requestTimeout?: number;
/** 长轮询超时时间(毫秒) */
longPollTimeout?: number;
/** 是否启用长轮询 */
enableLongPolling?: boolean;
/** 额外的请求头 */
headers?: Record<string, string>;
/** 认证令牌 */
authToken?: string;
}
/**
* HTTP 响应接口
*/
interface HttpResponse {
success: boolean;
data?: any;
error?: string;
messages?: ClientMessage[];
}
/**
* HTTP 客户端传输
*/
export class HttpClientTransport extends ClientTransport {
private connectionId: string | null = null;
private longPollController: AbortController | null = null;
private longPollRunning = false;
private connectPromise: Promise<void> | null = null;
private requestTimers: Set<ITimer<any>> = new Set();
protected override config: HttpClientConfig;
constructor(config: HttpClientConfig) {
super(config);
this.config = {
apiPrefix: '/api',
requestTimeout: 30000, // 30秒
longPollTimeout: 25000, // 25秒
enableLongPolling: true,
headers: {
'Content-Type': 'application/json'
},
...config
};
}
/**
* 连接到服务器
*/
async connect(): Promise<void> {
if (this.state === ConnectionState.CONNECTING ||
this.state === ConnectionState.CONNECTED) {
return this.connectPromise || Promise.resolve();
}
this.setState(ConnectionState.CONNECTING);
this.stopReconnect();
this.connectPromise = this.performConnect();
return this.connectPromise;
}
/**
* 执行连接
*/
private async performConnect(): Promise<void> {
try {
// 发送连接请求
const response = await this.makeRequest('/connect', 'POST', {});
if (response.success && response.data.connectionId) {
this.connectionId = response.data.connectionId;
this.setState(ConnectionState.CONNECTED);
// 启动长轮询
if (this.config.enableLongPolling) {
this.startLongPolling();
}
} else {
throw new Error(response.error || 'Connection failed');
}
} catch (error) {
this.setState(ConnectionState.ERROR);
throw error;
}
}
/**
* 断开连接
*/
async disconnect(): Promise<void> {
this.stopReconnect();
this.stopLongPolling();
if (this.connectionId) {
try {
await this.makeRequest('/disconnect', 'POST', {
connectionId: this.connectionId
});
} catch (error) {
// 忽略断开连接时的错误
}
this.connectionId = null;
}
this.setState(ConnectionState.DISCONNECTED);
this.connectPromise = null;
}
/**
* 发送消息
*/
async sendMessage(message: ClientMessage): Promise<boolean> {
if (!this.connectionId) {
// 如果未连接,将消息加入队列
if (this.state === ConnectionState.CONNECTING ||
this.state === ConnectionState.RECONNECTING) {
return this.queueMessage(message);
}
return false;
}
try {
const response = await this.makeRequest('/send', 'POST', {
connectionId: this.connectionId,
message: {
...message,
timestamp: message.timestamp || Date.now()
}
});
if (response.success) {
this.updateSendStats(message);
return true;
} else {
console.error('Send message failed:', response.error);
return false;
}
} catch (error) {
this.handleError(error as Error);
return false;
}
}
/**
* 启动长轮询
*/
private startLongPolling(): void {
if (this.longPollRunning || !this.connectionId) {
return;
}
this.longPollRunning = true;
this.performLongPoll();
}
/**
* 停止长轮询
*/
private stopLongPolling(): void {
this.longPollRunning = false;
if (this.longPollController) {
this.longPollController.abort();
this.longPollController = null;
}
}
/**
* 执行长轮询
*/
private async performLongPoll(): Promise<void> {
while (this.longPollRunning && this.connectionId) {
try {
this.longPollController = new AbortController();
const response = await this.makeRequest('/poll', 'GET', {
connectionId: this.connectionId
}, {
signal: this.longPollController.signal,
timeout: this.config.longPollTimeout
});
if (response.success && response.messages && response.messages.length > 0) {
// 处理接收到的消息
for (const message of response.messages) {
this.handleMessage(message);
}
}
// 如果服务器指示断开连接
if (response.data && response.data.disconnected) {
this.handleServerDisconnect();
break;
}
} catch (error) {
if ((error as any).name === 'AbortError') {
// 被主动取消,正常情况
break;
}
console.warn('Long polling error:', (error as Error).message);
// 如果是网络错误,尝试重连
if (this.isNetworkError(error as Error)) {
this.handleError(error as Error);
break;
}
// 短暂等待后重试
await this.delay(1000);
}
this.longPollController = null;
}
}
/**
* 处理服务器主动断开连接
*/
private handleServerDisconnect(): void {
this.connectionId = null;
this.stopLongPolling();
this.emit('disconnected', 'Server disconnect');
if (this.reconnectAttempts < this.config.maxReconnectAttempts!) {
this.startReconnect();
} else {
this.setState(ConnectionState.DISCONNECTED);
}
}
/**
* 发送 HTTP 请求
*/
private async makeRequest(
path: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' = 'GET',
data?: any,
options: {
signal?: AbortSignal;
timeout?: number;
} = {}
): Promise<HttpResponse> {
const url = this.buildUrl(path);
const headers = this.buildHeaders();
const requestOptions: RequestInit = {
method,
headers,
signal: options.signal
};
// 添加请求体
if (method !== 'GET' && data) {
requestOptions.body = JSON.stringify(data);
} else if (method === 'GET' && data) {
// GET 请求将数据作为查询参数
const params = new URLSearchParams();
Object.entries(data).forEach(([key, value]) => {
params.append(key, String(value));
});
const separator = url.includes('?') ? '&' : '?';
return this.fetchWithTimeout(`${url}${separator}${params}`, requestOptions, options.timeout);
}
return this.fetchWithTimeout(url, requestOptions, options.timeout);
}
/**
* 带超时的 fetch 请求
*/
private async fetchWithTimeout(
url: string,
options: RequestInit,
timeout?: number
): Promise<HttpResponse> {
const actualTimeout = timeout || this.config.requestTimeout!;
const controller = new AbortController();
let timeoutTimer: ITimer<any> | null = null;
// 创建超时定时器
timeoutTimer = Core.schedule(actualTimeout / 1000, false, this, () => {
controller.abort();
if (timeoutTimer) {
this.requestTimers.delete(timeoutTimer);
}
});
this.requestTimers.add(timeoutTimer);
try {
const response = await fetch(url, {
...options,
signal: options.signal || controller.signal
});
// 清理定时器
if (timeoutTimer) {
timeoutTimer.stop();
this.requestTimers.delete(timeoutTimer);
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
return result as HttpResponse;
} catch (error) {
// 清理定时器
if (timeoutTimer) {
timeoutTimer.stop();
this.requestTimers.delete(timeoutTimer);
}
throw error;
}
}
/**
* 构建请求URL
*/
private buildUrl(path: string): string {
const protocol = this.config.secure ? 'https' : 'http';
const basePath = this.config.apiPrefix || '';
const cleanPath = path.startsWith('/') ? path : `/${path}`;
return `${protocol}://${this.config.host}:${this.config.port}${basePath}${cleanPath}`;
}
/**
* 构建请求头
*/
private buildHeaders(): Record<string, string> {
const headers = { ...this.config.headers };
if (this.config.authToken) {
headers['Authorization'] = `Bearer ${this.config.authToken}`;
}
return headers;
}
/**
* 检查是否为网络错误
*/
private isNetworkError(error: Error): boolean {
return error.message.includes('fetch') ||
error.message.includes('network') ||
error.message.includes('timeout') ||
error.name === 'TypeError';
}
/**
* 延迟函数
*/
private delay(ms: number): Promise<void> {
return new Promise(resolve => {
const timer = Core.schedule(ms / 1000, false, this, () => {
this.requestTimers.delete(timer);
resolve();
});
this.requestTimers.add(timer);
});
}
/**
* 设置认证令牌
*/
setAuthToken(token: string): void {
this.config.authToken = token;
}
/**
* 获取连接ID
*/
getConnectionId(): string | null {
return this.connectionId;
}
/**
* 检查是否支持 Fetch API
*/
static isSupported(): boolean {
return typeof fetch !== 'undefined';
}
/**
* 销毁传输层
*/
override destroy(): void {
// 清理所有请求定时器
this.requestTimers.forEach(timer => timer.stop());
this.requestTimers.clear();
this.disconnect();
super.destroy();
}
}

View File

@@ -1,282 +0,0 @@
/**
* WebSocket 客户端传输实现
*/
import { Core, ITimer } from '@esengine/ecs-framework';
import {
ClientTransport,
ClientTransportConfig,
ConnectionState,
ClientMessage
} from './ClientTransport';
/**
* WebSocket 客户端配置
*/
export interface WebSocketClientConfig extends ClientTransportConfig {
/** WebSocket 路径 */
path?: string;
/** 协议列表 */
protocols?: string | string[];
/** 额外的请求头 */
headers?: Record<string, string>;
/** 是否启用二进制消息 */
binaryType?: 'blob' | 'arraybuffer';
/** WebSocket 扩展 */
extensions?: any;
}
/**
* WebSocket 客户端传输
*/
export class WebSocketClientTransport extends ClientTransport {
private websocket: WebSocket | null = null;
private connectionPromise: Promise<void> | null = null;
private connectionTimeoutTimer: ITimer<any> | null = null;
protected override config: WebSocketClientConfig;
constructor(config: WebSocketClientConfig) {
super(config);
this.config = {
path: '/ws',
protocols: [],
headers: {},
binaryType: 'arraybuffer',
...config
};
}
/**
* 连接到服务器
*/
async connect(): Promise<void> {
if (this.state === ConnectionState.CONNECTING ||
this.state === ConnectionState.CONNECTED) {
return this.connectionPromise || Promise.resolve();
}
this.setState(ConnectionState.CONNECTING);
this.stopReconnect(); // 停止任何正在进行的重连
this.connectionPromise = new Promise((resolve, reject) => {
try {
// 构建WebSocket URL
const protocol = this.config.secure ? 'wss' : 'ws';
const url = `${protocol}://${this.config.host}:${this.config.port}${this.config.path}`;
// 创建WebSocket连接
this.websocket = new WebSocket(url, this.config.protocols);
if (this.config.binaryType) {
this.websocket.binaryType = this.config.binaryType;
}
// 设置连接超时
this.connectionTimeoutTimer = Core.schedule(this.config.connectionTimeout! / 1000, false, this, () => {
if (this.websocket && this.websocket.readyState === WebSocket.CONNECTING) {
this.websocket.close();
reject(new Error('Connection timeout'));
}
});
// WebSocket 事件处理
this.websocket.onopen = () => {
if (this.connectionTimeoutTimer) {
this.connectionTimeoutTimer.stop();
this.connectionTimeoutTimer = null;
}
this.setState(ConnectionState.CONNECTED);
resolve();
};
this.websocket.onclose = (event) => {
if (this.connectionTimeoutTimer) {
this.connectionTimeoutTimer.stop();
this.connectionTimeoutTimer = null;
}
this.handleClose(event.code, event.reason);
if (this.state === ConnectionState.CONNECTING) {
reject(new Error(`Connection failed: ${event.reason || 'Unknown error'}`));
}
};
this.websocket.onerror = (event) => {
if (this.connectionTimeoutTimer) {
this.connectionTimeoutTimer.stop();
this.connectionTimeoutTimer = null;
}
const error = new Error('WebSocket error');
this.handleError(error);
if (this.state === ConnectionState.CONNECTING) {
reject(error);
}
};
this.websocket.onmessage = (event) => {
this.handleWebSocketMessage(event);
};
} catch (error) {
this.setState(ConnectionState.ERROR);
reject(error);
}
});
return this.connectionPromise;
}
/**
* 断开连接
*/
async disconnect(): Promise<void> {
this.stopReconnect();
if (this.websocket) {
// 设置状态为断开连接,避免触发重连
this.setState(ConnectionState.DISCONNECTED);
if (this.websocket.readyState === WebSocket.OPEN ||
this.websocket.readyState === WebSocket.CONNECTING) {
this.websocket.close(1000, 'Client disconnect');
}
this.websocket = null;
}
this.connectionPromise = null;
}
/**
* 发送消息
*/
async sendMessage(message: ClientMessage): Promise<boolean> {
if (!this.websocket || this.websocket.readyState !== WebSocket.OPEN) {
// 如果未连接,将消息加入队列
if (this.state === ConnectionState.CONNECTING ||
this.state === ConnectionState.RECONNECTING) {
return this.queueMessage(message);
}
return false;
}
try {
// 序列化消息
const serialized = JSON.stringify({
...message,
timestamp: message.timestamp || Date.now()
});
// 发送消息
this.websocket.send(serialized);
this.updateSendStats(message);
return true;
} catch (error) {
this.handleError(error as Error);
return false;
}
}
/**
* 处理 WebSocket 消息
*/
private handleWebSocketMessage(event: MessageEvent): void {
try {
let data: string;
if (event.data instanceof ArrayBuffer) {
// 处理二进制数据
data = new TextDecoder().decode(event.data);
} else if (event.data instanceof Blob) {
// Blob 需要异步处理
event.data.text().then(text => {
this.processMessage(text);
});
return;
} else {
// 字符串数据
data = event.data;
}
this.processMessage(data);
} catch (error) {
console.error('Error processing WebSocket message:', error);
}
}
/**
* 处理消息内容
*/
private processMessage(data: string): void {
try {
const message: ClientMessage = JSON.parse(data);
this.handleMessage(message);
} catch (error) {
console.error('Error parsing message:', error);
}
}
/**
* 处理连接关闭
*/
private handleClose(code: number, reason: string): void {
this.websocket = null;
this.connectionPromise = null;
const wasConnected = this.isConnected();
// 根据关闭代码决定是否重连
if (code === 1000) {
// 正常关闭,不重连
this.setState(ConnectionState.DISCONNECTED);
this.emit('disconnected', reason || 'Normal closure');
} else if (wasConnected && this.reconnectAttempts < this.config.maxReconnectAttempts!) {
// 异常关闭,尝试重连
this.emit('disconnected', reason || `Abnormal closure (${code})`);
this.startReconnect();
} else {
// 达到最大重连次数或其他情况
this.setState(ConnectionState.DISCONNECTED);
this.emit('disconnected', reason || `Connection lost (${code})`);
}
}
/**
* 获取 WebSocket 就绪状态
*/
getReadyState(): number {
return this.websocket?.readyState ?? WebSocket.CLOSED;
}
/**
* 获取 WebSocket 实例
*/
getWebSocket(): WebSocket | null {
return this.websocket;
}
/**
* 检查是否支持 WebSocket
*/
static isSupported(): boolean {
return typeof WebSocket !== 'undefined';
}
/**
* 销毁传输层
*/
override destroy(): void {
if (this.connectionTimeoutTimer) {
this.connectionTimeoutTimer.stop();
this.connectionTimeoutTimer = null;
}
this.disconnect();
super.destroy();
}
}

View File

@@ -1,7 +0,0 @@
/**
* 传输层导出
*/
export * from './ClientTransport';
export * from './WebSocketClientTransport';
export * from './HttpClientTransport';