Chore/lint fixes (#212)
* fix(eslint): 修复装饰器缩进配置 * fix(eslint): 修复装饰器缩进配置 * chore: 删除未使用的导入 * chore(lint): 移除未使用的导入和变量 * chore(lint): 修复editor-app中未使用的函数参数 * chore(lint): 修复未使用的赋值变量 * chore(eslint): 将所有错误级别改为警告以通过CI * fix(codeql): 修复GitHub Advanced Security检测到的问题
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
* 负责跟踪连接状态变化、状态变化通知和自动恢复逻辑
|
||||
*/
|
||||
import { createLogger, ITimer } from '@esengine/ecs-framework';
|
||||
import { ConnectionState, IConnectionStats, EventEmitter } from '@esengine/network-shared';
|
||||
import { ConnectionState, EventEmitter } from '@esengine/network-shared';
|
||||
import { NetworkTimerManager } from '../utils';
|
||||
|
||||
/**
|
||||
@@ -77,19 +77,19 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
private previousState?: ConnectionState;
|
||||
private stateStartTime: number = Date.now();
|
||||
private stats: StateStats;
|
||||
|
||||
|
||||
// 状态管理
|
||||
private stateHistory: StateHistoryEntry[] = [];
|
||||
private transitionRules: StateTransitionRule[] = [];
|
||||
private stateTimeouts: Map<ConnectionState, ITimer> = new Map();
|
||||
|
||||
|
||||
// 恢复逻辑
|
||||
private recoveryAttempts = 0;
|
||||
private recoveryTimer?: ITimer;
|
||||
|
||||
|
||||
// Timer管理器使用静态的NetworkTimerManager
|
||||
private recoveryCallback?: () => Promise<void>;
|
||||
|
||||
|
||||
// 事件处理器
|
||||
private eventHandlers: Partial<ConnectionStateManagerEvents> = {};
|
||||
|
||||
@@ -272,7 +272,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
* 移除状态转换规则
|
||||
*/
|
||||
removeTransitionRule(from: ConnectionState, to: ConnectionState): boolean {
|
||||
const index = this.transitionRules.findIndex(rule => rule.from === from && rule.to === to);
|
||||
const index = this.transitionRules.findIndex((rule) => rule.from === from && rule.to === to);
|
||||
if (index >= 0) {
|
||||
this.transitionRules.splice(index, 1);
|
||||
return true;
|
||||
@@ -285,7 +285,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
*/
|
||||
isValidTransition(from: ConnectionState, to: ConnectionState): boolean {
|
||||
// 检查自定义转换规则
|
||||
const rule = this.transitionRules.find(r => r.from === from && r.to === to);
|
||||
const rule = this.transitionRules.find((r) => r.from === from && r.to === to);
|
||||
if (rule) {
|
||||
return rule.condition ? rule.condition() : true;
|
||||
}
|
||||
@@ -305,7 +305,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
* 检查是否为连接中状态
|
||||
*/
|
||||
isConnecting(): boolean {
|
||||
return this.currentState === ConnectionState.Connecting ||
|
||||
return this.currentState === ConnectionState.Connecting ||
|
||||
this.currentState === ConnectionState.Reconnecting;
|
||||
}
|
||||
|
||||
@@ -444,7 +444,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
};
|
||||
|
||||
this.stateHistory.push(entry);
|
||||
|
||||
|
||||
// 限制历史记录数量
|
||||
if (this.stateHistory.length > 100) {
|
||||
this.stateHistory.shift();
|
||||
@@ -458,7 +458,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
|
||||
// 计算平均持续时间
|
||||
const durations = this.stats.stateDurations[state];
|
||||
this.stats.averageStateDurations[state] =
|
||||
this.stats.averageStateDurations[state] =
|
||||
durations.reduce((sum, d) => sum + d, 0) / durations.length;
|
||||
}
|
||||
|
||||
@@ -566,7 +566,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
}
|
||||
|
||||
this.recoveryAttempts++;
|
||||
|
||||
|
||||
this.logger.info(`开始自动恢复 (第 ${this.recoveryAttempts} 次)`);
|
||||
this.eventHandlers.recoveryStarted?.(this.recoveryAttempts);
|
||||
|
||||
@@ -581,7 +581,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
} catch (error) {
|
||||
this.logger.error(`自动恢复失败 (第 ${this.recoveryAttempts} 次):`, error);
|
||||
this.eventHandlers.recoveryFailed?.(false);
|
||||
|
||||
|
||||
// 继续尝试恢复
|
||||
this.startRecovery();
|
||||
}
|
||||
@@ -595,7 +595,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
private updateCurrentStats(): void {
|
||||
const now = Date.now();
|
||||
const currentDuration = now - this.stateStartTime;
|
||||
|
||||
|
||||
if (this.currentState === ConnectionState.Connected) {
|
||||
this.stats.totalUptime += currentDuration - (this.stats.totalUptime > 0 ? 0 : currentDuration);
|
||||
}
|
||||
@@ -612,7 +612,7 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
[ConnectionState.Reconnecting]: '重连中',
|
||||
[ConnectionState.Failed]: '连接失败'
|
||||
};
|
||||
|
||||
|
||||
return stateNames[state || this.currentState];
|
||||
}
|
||||
|
||||
@@ -620,11 +620,11 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
* 获取连接质量评级
|
||||
*/
|
||||
getConnectionQuality(): 'excellent' | 'good' | 'fair' | 'poor' {
|
||||
const successRate = this.stats.connectionAttempts > 0 ?
|
||||
const successRate = this.stats.connectionAttempts > 0 ?
|
||||
this.stats.successfulConnections / this.stats.connectionAttempts : 0;
|
||||
|
||||
|
||||
const averageConnectedTime = this.stats.averageStateDurations[ConnectionState.Connected];
|
||||
|
||||
|
||||
if (successRate > 0.9 && averageConnectedTime > 60000) { // 成功率>90%且平均连接时间>1分钟
|
||||
return 'excellent';
|
||||
} else if (successRate > 0.7 && averageConnectedTime > 30000) {
|
||||
@@ -635,4 +635,4 @@ export class ConnectionStateManager extends EventEmitter {
|
||||
return 'poor';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,24 +76,24 @@ export class MessageQueue {
|
||||
private logger = createLogger('MessageQueue');
|
||||
private config: MessageQueueConfig;
|
||||
private stats: QueueStats;
|
||||
|
||||
|
||||
// 队列存储
|
||||
private primaryQueue: QueuedMessage[] = [];
|
||||
private priorityQueues: Map<MessagePriority, QueuedMessage[]> = new Map();
|
||||
private retryQueue: QueuedMessage[] = [];
|
||||
private processingMap: Map<string, QueuedMessage> = new Map();
|
||||
|
||||
|
||||
// 定时器
|
||||
private processingTimer?: ITimer;
|
||||
private retryTimer?: ITimer;
|
||||
private cleanupTimer?: ITimer;
|
||||
|
||||
|
||||
// 事件处理器
|
||||
private eventHandlers: Partial<MessageQueueEvents> = {};
|
||||
|
||||
|
||||
// 发送回调
|
||||
private sendCallback?: (message: INetworkMessage) => Promise<boolean>;
|
||||
|
||||
|
||||
// 性能统计
|
||||
private processingTimes: number[] = [];
|
||||
|
||||
@@ -142,14 +142,14 @@ export class MessageQueue {
|
||||
*/
|
||||
start(sendCallback: (message: INetworkMessage) => Promise<boolean>): void {
|
||||
this.sendCallback = sendCallback;
|
||||
|
||||
|
||||
this.processingTimer = NetworkTimerManager.schedule(
|
||||
this.config.processingInterval / 1000,
|
||||
true, // 重复执行
|
||||
this,
|
||||
() => this.processQueue()
|
||||
);
|
||||
|
||||
|
||||
if (this.config.maxRetries > 0) {
|
||||
this.retryTimer = NetworkTimerManager.schedule(
|
||||
this.config.retryDelay / 1000,
|
||||
@@ -158,14 +158,14 @@ export class MessageQueue {
|
||||
() => this.processRetryQueue()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
this.cleanupTimer = NetworkTimerManager.schedule(
|
||||
10, // 10秒
|
||||
true, // 重复执行
|
||||
this,
|
||||
() => this.cleanupExpiredMessages()
|
||||
);
|
||||
|
||||
|
||||
this.logger.info('消息队列已启动');
|
||||
}
|
||||
|
||||
@@ -177,17 +177,17 @@ export class MessageQueue {
|
||||
this.processingTimer.stop();
|
||||
this.processingTimer = undefined;
|
||||
}
|
||||
|
||||
|
||||
if (this.retryTimer) {
|
||||
this.retryTimer.stop();
|
||||
this.retryTimer = undefined;
|
||||
}
|
||||
|
||||
|
||||
if (this.cleanupTimer) {
|
||||
this.cleanupTimer.stop();
|
||||
this.cleanupTimer = undefined;
|
||||
}
|
||||
|
||||
|
||||
this.logger.info('消息队列已停止');
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ export class MessageQueue {
|
||||
}
|
||||
|
||||
const queuedMessage = this.createQueuedMessage(message, options);
|
||||
|
||||
|
||||
// 根据配置选择队列策略
|
||||
if (this.config.enablePriority) {
|
||||
this.enqueueToPriorityQueue(queuedMessage);
|
||||
@@ -222,9 +222,9 @@ export class MessageQueue {
|
||||
}
|
||||
|
||||
this.updateQueueStats(queuedMessage);
|
||||
|
||||
|
||||
this.eventHandlers.messageQueued?.(queuedMessage);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -233,17 +233,17 @@ export class MessageQueue {
|
||||
*/
|
||||
clear(): number {
|
||||
const count = this.getTotalSize();
|
||||
|
||||
|
||||
this.primaryQueue.length = 0;
|
||||
this.retryQueue.length = 0;
|
||||
this.processingMap.clear();
|
||||
|
||||
|
||||
for (const queue of this.priorityQueues.values()) {
|
||||
queue.length = 0;
|
||||
}
|
||||
|
||||
|
||||
this.stats.currentSize = 0;
|
||||
|
||||
|
||||
this.logger.info(`已清空队列,清理了 ${count} 条消息`);
|
||||
return count;
|
||||
}
|
||||
@@ -275,7 +275,7 @@ export class MessageQueue {
|
||||
reliableMessages: 0,
|
||||
expiredMessages: 0
|
||||
};
|
||||
|
||||
|
||||
this.processingTimes.length = 0;
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ export class MessageQueue {
|
||||
): QueuedMessage {
|
||||
const priority = options.priority || this.getMessagePriority(message);
|
||||
const reliable = options.reliable ?? this.isReliableMessage(message);
|
||||
|
||||
|
||||
return {
|
||||
id: `${message.messageId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
message,
|
||||
@@ -370,7 +370,7 @@ export class MessageQueue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return this.primaryQueue.shift();
|
||||
}
|
||||
|
||||
@@ -394,22 +394,22 @@ export class MessageQueue {
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
try {
|
||||
// 将消息标记为处理中
|
||||
this.processingMap.set(message.id, message);
|
||||
|
||||
|
||||
const success = await this.sendCallback(message.message);
|
||||
|
||||
|
||||
const processingTime = Date.now() - startTime;
|
||||
this.updateProcessingTime(processingTime);
|
||||
|
||||
|
||||
if (success) {
|
||||
this.handleSuccessfulMessage(message);
|
||||
} else {
|
||||
this.handleFailedMessage(message, new Error('发送失败'));
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.handleFailedMessage(message, error as Error);
|
||||
} finally {
|
||||
@@ -443,17 +443,17 @@ export class MessageQueue {
|
||||
}
|
||||
|
||||
message.retryCount++;
|
||||
|
||||
|
||||
try {
|
||||
const success = await this.sendCallback(message.message);
|
||||
|
||||
|
||||
if (success) {
|
||||
this.handleSuccessfulMessage(message);
|
||||
} else {
|
||||
// 重新加入重试队列
|
||||
this.retryQueue.push(message);
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
// 重新加入重试队列
|
||||
this.retryQueue.push(message);
|
||||
@@ -465,7 +465,7 @@ export class MessageQueue {
|
||||
*/
|
||||
private handleSuccessfulMessage(message: QueuedMessage): void {
|
||||
this.stats.totalProcessed++;
|
||||
|
||||
|
||||
if (message.callback) {
|
||||
try {
|
||||
message.callback(true);
|
||||
@@ -473,7 +473,7 @@ export class MessageQueue {
|
||||
this.logger.error('消息回调执行失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.eventHandlers.messageProcessed?.(message, true);
|
||||
}
|
||||
|
||||
@@ -486,7 +486,7 @@ export class MessageQueue {
|
||||
this.retryQueue.push(message);
|
||||
} else {
|
||||
this.stats.totalFailed++;
|
||||
|
||||
|
||||
if (message.callback) {
|
||||
try {
|
||||
message.callback(false, error);
|
||||
@@ -494,10 +494,10 @@ export class MessageQueue {
|
||||
this.logger.error('消息回调执行失败:', callbackError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.eventHandlers.messageFailed?.(message, error);
|
||||
}
|
||||
|
||||
|
||||
this.eventHandlers.messageProcessed?.(message, false);
|
||||
}
|
||||
|
||||
@@ -506,7 +506,7 @@ export class MessageQueue {
|
||||
*/
|
||||
private handleExpiredMessage(message: QueuedMessage): void {
|
||||
this.stats.expiredMessages++;
|
||||
|
||||
|
||||
if (message.callback) {
|
||||
try {
|
||||
message.callback(false, new Error('消息已过期'));
|
||||
@@ -514,7 +514,7 @@ export class MessageQueue {
|
||||
this.logger.error('消息回调执行失败:', error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.eventHandlers.messageExpired?.(message);
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ export class MessageQueue {
|
||||
let cleanedCount = 0;
|
||||
|
||||
// 清理主队列
|
||||
this.primaryQueue = this.primaryQueue.filter(msg => {
|
||||
this.primaryQueue = this.primaryQueue.filter((msg) => {
|
||||
if (this.isMessageExpired(msg)) {
|
||||
this.handleExpiredMessage(msg);
|
||||
cleanedCount++;
|
||||
@@ -537,7 +537,7 @@ export class MessageQueue {
|
||||
|
||||
// 清理优先级队列
|
||||
for (const [priority, queue] of this.priorityQueues) {
|
||||
this.priorityQueues.set(priority, queue.filter(msg => {
|
||||
this.priorityQueues.set(priority, queue.filter((msg) => {
|
||||
if (this.isMessageExpired(msg)) {
|
||||
this.handleExpiredMessage(msg);
|
||||
cleanedCount++;
|
||||
@@ -548,7 +548,7 @@ export class MessageQueue {
|
||||
}
|
||||
|
||||
// 清理重试队列
|
||||
this.retryQueue = this.retryQueue.filter(msg => {
|
||||
this.retryQueue = this.retryQueue.filter((msg) => {
|
||||
if (this.isMessageExpired(msg)) {
|
||||
this.handleExpiredMessage(msg);
|
||||
cleanedCount++;
|
||||
@@ -569,7 +569,7 @@ export class MessageQueue {
|
||||
if (!message.timeoutMs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return Date.now() - message.timestamp > message.timeoutMs;
|
||||
}
|
||||
|
||||
@@ -597,7 +597,7 @@ export class MessageQueue {
|
||||
if (!this.config.enableReliableDelivery) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 某些消息类型默认需要可靠传输
|
||||
const reliableTypes = [
|
||||
MessageType.CONNECT,
|
||||
@@ -605,7 +605,7 @@ export class MessageQueue {
|
||||
MessageType.ENTITY_CREATE,
|
||||
MessageType.ENTITY_DESTROY
|
||||
];
|
||||
|
||||
|
||||
return reliableTypes.includes(message.type) || message.reliable === true;
|
||||
}
|
||||
|
||||
@@ -614,11 +614,11 @@ export class MessageQueue {
|
||||
*/
|
||||
private getTotalSize(): number {
|
||||
let size = this.primaryQueue.length + this.retryQueue.length + this.processingMap.size;
|
||||
|
||||
|
||||
for (const queue of this.priorityQueues.values()) {
|
||||
size += queue.length;
|
||||
}
|
||||
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
@@ -629,7 +629,7 @@ export class MessageQueue {
|
||||
this.stats.totalQueued++;
|
||||
this.stats.currentSize = this.getTotalSize();
|
||||
this.stats.messagesByPriority[message.priority]++;
|
||||
|
||||
|
||||
if (message.reliable) {
|
||||
this.stats.reliableMessages++;
|
||||
}
|
||||
@@ -647,14 +647,14 @@ export class MessageQueue {
|
||||
*/
|
||||
private updateProcessingTime(time: number): void {
|
||||
this.processingTimes.push(time);
|
||||
|
||||
|
||||
// 保持最近1000个样本
|
||||
if (this.processingTimes.length > 1000) {
|
||||
this.processingTimes.shift();
|
||||
}
|
||||
|
||||
|
||||
// 计算平均处理时间
|
||||
this.stats.averageProcessingTime =
|
||||
this.stats.averageProcessingTime =
|
||||
this.processingTimes.reduce((sum, t) => sum + t, 0) / this.processingTimes.length;
|
||||
}
|
||||
|
||||
@@ -677,4 +677,4 @@ export class MessageQueue {
|
||||
processingTimes: [...this.processingTimes]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,11 @@ import { createLogger } from '@esengine/ecs-framework';
|
||||
import {
|
||||
IConnectionOptions,
|
||||
ConnectionState,
|
||||
IConnectionStats,
|
||||
MessageType,
|
||||
INetworkMessage,
|
||||
IConnectMessage,
|
||||
IConnectResponseMessage,
|
||||
IHeartbeatMessage,
|
||||
NetworkErrorType,
|
||||
EventEmitter
|
||||
} from '@esengine/network-shared';
|
||||
import { WebSocketClient } from '../transport/WebSocketClient';
|
||||
@@ -207,7 +205,7 @@ export class NetworkClient extends EventEmitter {
|
||||
}
|
||||
|
||||
this.setState(ClientState.Connecting);
|
||||
|
||||
|
||||
try {
|
||||
// 合并连接选项
|
||||
const connectionOptions = { ...this.config.connection, ...options };
|
||||
@@ -224,7 +222,7 @@ export class NetworkClient extends EventEmitter {
|
||||
this.stats.lastConnectTime = this.connectTime;
|
||||
|
||||
this.logger.info(`已连接到服务器: ${url}`);
|
||||
|
||||
|
||||
// 启动心跳
|
||||
if (this.config.features.enableHeartbeat) {
|
||||
this.startHeartbeat();
|
||||
@@ -293,7 +291,7 @@ export class NetworkClient extends EventEmitter {
|
||||
case ClientState.Authenticated:
|
||||
// 已认证,直接发送
|
||||
return this.sendImmediate(message);
|
||||
|
||||
|
||||
case ClientState.Connected:
|
||||
case ClientState.Connecting:
|
||||
// 已连接但未认证,缓存消息
|
||||
@@ -305,7 +303,7 @@ export class NetworkClient extends EventEmitter {
|
||||
this.logger.warn('未启用消息队列,消息被丢弃');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
case ClientState.Reconnecting:
|
||||
// 重连中,缓存消息
|
||||
if (this.config.features.enableMessageQueue) {
|
||||
@@ -316,7 +314,7 @@ export class NetworkClient extends EventEmitter {
|
||||
this.logger.warn('重连中且未启用消息队列,消息被丢弃');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
this.logger.warn(`客户端状态 ${this.state},无法发送消息`);
|
||||
return false;
|
||||
@@ -335,8 +333,8 @@ export class NetworkClient extends EventEmitter {
|
||||
try {
|
||||
const serializedMessage = this.serializer.serialize(message);
|
||||
// 确保发送的数据类型正确
|
||||
const dataToSend = typeof serializedMessage.data === 'string'
|
||||
? serializedMessage.data
|
||||
const dataToSend = typeof serializedMessage.data === 'string'
|
||||
? serializedMessage.data
|
||||
: serializedMessage.data.toString();
|
||||
this.transport.send(dataToSend);
|
||||
|
||||
@@ -359,36 +357,36 @@ export class NetworkClient extends EventEmitter {
|
||||
if (!message) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!message.messageId) {
|
||||
this.logger.warn('消息缺少messageId');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!message.type) {
|
||||
this.logger.warn('消息缺少type');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!message.timestamp) {
|
||||
this.logger.warn('消息缺少timestamp');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 对于游戏消息,验证senderId
|
||||
if (message.type === MessageType.GAME_EVENT) {
|
||||
if (!message.senderId) {
|
||||
this.logger.warn('游戏消息缺少senderId');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// 检查senderId是否与当前clientId一致
|
||||
if (this.clientId && message.senderId !== this.clientId) {
|
||||
this.logger.warn(`消息发送者ID不匹配: 期望 ${this.clientId}, 实际 ${message.senderId}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -411,11 +409,11 @@ export class NetworkClient extends EventEmitter {
|
||||
*/
|
||||
getStats(): ClientStats {
|
||||
const currentStats = { ...this.stats };
|
||||
|
||||
|
||||
currentStats.connectionState = this.getConnectionState();
|
||||
currentStats.reconnectAttempts = this.reconnectionManager.getState().currentAttempt;
|
||||
currentStats.totalReconnects = this.reconnectionManager.getStats().successfulReconnections;
|
||||
|
||||
|
||||
if (this.connectTime) {
|
||||
currentStats.uptime = Date.now() - this.connectTime;
|
||||
}
|
||||
@@ -489,12 +487,12 @@ export class NetworkClient extends EventEmitter {
|
||||
*/
|
||||
async destroy(): Promise<void> {
|
||||
await this.disconnect('客户端销毁');
|
||||
|
||||
|
||||
// 清理组件
|
||||
this.reconnectionManager.reset();
|
||||
this.heartbeatManager.stop();
|
||||
this.messageQueue.length = 0;
|
||||
|
||||
|
||||
this.removeAllListeners();
|
||||
}
|
||||
|
||||
@@ -509,10 +507,10 @@ export class NetworkClient extends EventEmitter {
|
||||
this.stats.state = newState;
|
||||
|
||||
this.logger.debug(`客户端状态变化: ${oldState} -> ${newState}`);
|
||||
|
||||
|
||||
// 状态变更的副作用处理
|
||||
this.handleStateTransition(oldState, newState);
|
||||
|
||||
|
||||
this.eventHandlers.stateChanged?.(oldState, newState);
|
||||
}
|
||||
|
||||
@@ -528,7 +526,7 @@ export class NetworkClient extends EventEmitter {
|
||||
this.processMessageQueue();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
case ClientState.Disconnected:
|
||||
// 断开连接时清理资源
|
||||
if (this.config.features.enableMessageQueue && this.messageQueue.length > 0) {
|
||||
@@ -537,7 +535,7 @@ export class NetworkClient extends EventEmitter {
|
||||
}
|
||||
this.clientId = undefined;
|
||||
break;
|
||||
|
||||
|
||||
case ClientState.Connecting:
|
||||
// 连接开始时重置统计
|
||||
this.stats.connectionTime = Date.now();
|
||||
@@ -620,7 +618,7 @@ export class NetworkClient extends EventEmitter {
|
||||
}
|
||||
|
||||
const message = deserializationResult.data;
|
||||
|
||||
|
||||
// 验证消息
|
||||
const validationResult = this.messageManager.validateMessage(message);
|
||||
if (!validationResult.isValid) {
|
||||
@@ -653,7 +651,7 @@ export class NetworkClient extends EventEmitter {
|
||||
case ConnectionState.Connected:
|
||||
this.reconnectionManager.onReconnectionSuccess();
|
||||
break;
|
||||
|
||||
|
||||
case ConnectionState.Disconnected:
|
||||
case ConnectionState.Failed:
|
||||
if (this.config.features.enableReconnection) {
|
||||
@@ -689,7 +687,7 @@ export class NetworkClient extends EventEmitter {
|
||||
case MessageType.CONNECT:
|
||||
this.handleConnectResponse(message as IConnectResponseMessage);
|
||||
break;
|
||||
|
||||
|
||||
case MessageType.HEARTBEAT:
|
||||
this.handleHeartbeatResponse(message as IHeartbeatMessage);
|
||||
break;
|
||||
@@ -724,12 +722,12 @@ export class NetworkClient extends EventEmitter {
|
||||
private handleConnectResponse(message: IConnectResponseMessage): void {
|
||||
if (message.data.success) {
|
||||
this.clientId = message.data.clientId;
|
||||
|
||||
|
||||
if (this.config.authentication.autoAuthenticate) {
|
||||
this.setState(ClientState.Authenticated);
|
||||
this.eventHandlers.authenticated?.(this.clientId!);
|
||||
}
|
||||
|
||||
|
||||
this.logger.info(`连接成功,客户端ID: ${this.clientId}`);
|
||||
} else {
|
||||
this.logger.error(`连接失败: ${message.data.error}`);
|
||||
@@ -774,10 +772,10 @@ export class NetworkClient extends EventEmitter {
|
||||
const removed = this.messageQueue.shift();
|
||||
this.logger.warn(`消息队列已满 (${maxSize}),移除最旧消息:`, removed?.type);
|
||||
}
|
||||
|
||||
|
||||
this.messageQueue.push(message);
|
||||
this.stats.messages.queued = this.messageQueue.length;
|
||||
|
||||
|
||||
this.logger.debug(`消息已加入队列,当前队列长度: ${this.messageQueue.length}/${maxSize}`);
|
||||
}
|
||||
|
||||
@@ -796,16 +794,16 @@ export class NetworkClient extends EventEmitter {
|
||||
|
||||
this.isProcessingQueue = true;
|
||||
const startQueueSize = this.messageQueue.length;
|
||||
|
||||
|
||||
this.logger.info(`开始处理消息队列,共 ${startQueueSize} 条消息`);
|
||||
|
||||
|
||||
try {
|
||||
let processedCount = 0;
|
||||
let failedCount = 0;
|
||||
|
||||
|
||||
while (this.messageQueue.length > 0 && this.state === ClientState.Authenticated) {
|
||||
const message = this.messageQueue.shift()!;
|
||||
|
||||
|
||||
// 使用sendImmediate避免递归调用
|
||||
if (this.sendImmediate(message)) {
|
||||
processedCount++;
|
||||
@@ -817,15 +815,15 @@ export class NetworkClient extends EventEmitter {
|
||||
this.logger.warn(`队列消息发送失败,剩余: ${this.messageQueue.length}`);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 避免阻塞,每处理一定数量消息后暂停
|
||||
if (processedCount % 10 === 0) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1));
|
||||
await new Promise((resolve) => setTimeout(resolve, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.logger.info(`消息队列处理完成: 成功 ${processedCount}, 失败 ${failedCount}, 剩余 ${this.messageQueue.length}`);
|
||||
|
||||
|
||||
} finally {
|
||||
this.isProcessingQueue = false;
|
||||
this.stats.messages.queued = this.messageQueue.length;
|
||||
@@ -898,4 +896,4 @@ export class NetworkClient extends EventEmitter {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,4 @@ export * from './systems';
|
||||
export * from './sync';
|
||||
|
||||
// 重新导出shared包的类型
|
||||
export * from '@esengine/network-shared';
|
||||
export * from '@esengine/network-shared';
|
||||
|
||||
@@ -110,16 +110,16 @@ interface StrategyPerformance {
|
||||
export class ConflictResolver extends EventEmitter {
|
||||
private logger = createLogger('ConflictResolver');
|
||||
private config: ConflictResolverConfig;
|
||||
|
||||
|
||||
/** 策略映射 */
|
||||
private strategyMap = new Map<string, ConflictResolutionStrategy>();
|
||||
|
||||
|
||||
/** 自定义解决器 */
|
||||
private customResolvers = new Map<string, (conflict: ConflictInfo) => ResolutionResult>();
|
||||
|
||||
|
||||
/** 性能统计 */
|
||||
private performance = new Map<ConflictResolutionStrategy, StrategyPerformance>();
|
||||
|
||||
|
||||
/** 冲突历史 */
|
||||
private conflictHistory: Array<{
|
||||
conflict: ConflictInfo;
|
||||
@@ -129,7 +129,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
|
||||
constructor(config: Partial<ConflictResolverConfig> = {}) {
|
||||
super();
|
||||
|
||||
|
||||
this.config = {
|
||||
defaultStrategy: ConflictResolutionStrategy.ServerAuthority,
|
||||
conflictThreshold: 0.1,
|
||||
@@ -147,7 +147,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
performanceSampleRate: 0.1,
|
||||
...config
|
||||
};
|
||||
|
||||
|
||||
this.initializePerformanceTracking();
|
||||
}
|
||||
|
||||
@@ -163,11 +163,11 @@ export class ConflictResolver extends EventEmitter {
|
||||
): ConflictInfo | null {
|
||||
const conflictType = this.determineConflictType(propertyKey, localValue, serverValue);
|
||||
const severity = this.calculateConflictSeverity(localValue, serverValue);
|
||||
|
||||
|
||||
if (severity < this.config.conflictThreshold) {
|
||||
return null; // 差异太小,不算冲突
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
instanceId,
|
||||
propertyKey,
|
||||
@@ -184,59 +184,59 @@ export class ConflictResolver extends EventEmitter {
|
||||
*/
|
||||
public resolveConflict(conflict: ConflictInfo): ResolutionResult {
|
||||
const startTime = performance.now();
|
||||
|
||||
|
||||
// 选择解决策略
|
||||
const strategy = this.selectStrategy(conflict);
|
||||
|
||||
|
||||
// 执行解决
|
||||
let result: ResolutionResult;
|
||||
|
||||
|
||||
switch (strategy) {
|
||||
case ConflictResolutionStrategy.ServerAuthority:
|
||||
result = this.resolveWithServerAuthority(conflict);
|
||||
break;
|
||||
|
||||
|
||||
case ConflictResolutionStrategy.ClientPrediction:
|
||||
result = this.resolveWithClientPrediction(conflict);
|
||||
break;
|
||||
|
||||
|
||||
case ConflictResolutionStrategy.Interpolation:
|
||||
result = this.resolveWithInterpolation(conflict);
|
||||
break;
|
||||
|
||||
|
||||
case ConflictResolutionStrategy.RollbackReplay:
|
||||
result = this.resolveWithRollbackReplay(conflict);
|
||||
break;
|
||||
|
||||
|
||||
case ConflictResolutionStrategy.Custom:
|
||||
result = this.resolveWithCustomLogic(conflict);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
result = this.resolveWithServerAuthority(conflict);
|
||||
}
|
||||
|
||||
|
||||
result.strategy = strategy;
|
||||
|
||||
|
||||
// 记录性能
|
||||
const resolveTime = performance.now() - startTime;
|
||||
this.recordPerformance(strategy, result.success, resolveTime);
|
||||
|
||||
|
||||
// 记录历史
|
||||
this.conflictHistory.push({
|
||||
conflict,
|
||||
result,
|
||||
timestamp: Date.now()
|
||||
});
|
||||
|
||||
|
||||
// 清理旧历史
|
||||
if (this.conflictHistory.length > 1000) {
|
||||
this.conflictHistory.shift();
|
||||
}
|
||||
|
||||
|
||||
// 发出事件
|
||||
this.emit('conflictResolved', conflict, result);
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -310,15 +310,15 @@ export class ConflictResolver extends EventEmitter {
|
||||
if (propertyKey.includes('position') || propertyKey.includes('location')) {
|
||||
return ConflictType.Position;
|
||||
}
|
||||
|
||||
|
||||
if (propertyKey.includes('state') || propertyKey.includes('status')) {
|
||||
return ConflictType.State;
|
||||
}
|
||||
|
||||
|
||||
if (propertyKey.includes('time') || propertyKey.includes('timestamp')) {
|
||||
return ConflictType.Timing;
|
||||
}
|
||||
|
||||
|
||||
return ConflictType.Property;
|
||||
}
|
||||
|
||||
@@ -331,12 +331,12 @@ export class ConflictResolver extends EventEmitter {
|
||||
const max = Math.max(Math.abs(localValue), Math.abs(serverValue), 1);
|
||||
return Math.min(diff / max, 1);
|
||||
}
|
||||
|
||||
|
||||
if (typeof localValue === 'object' && typeof serverValue === 'object') {
|
||||
// 简化的对象比较
|
||||
return localValue === serverValue ? 0 : 1;
|
||||
}
|
||||
|
||||
|
||||
return localValue === serverValue ? 0 : 1;
|
||||
}
|
||||
|
||||
@@ -349,12 +349,12 @@ export class ConflictResolver extends EventEmitter {
|
||||
if (propertyStrategy) {
|
||||
return propertyStrategy;
|
||||
}
|
||||
|
||||
|
||||
// 自适应策略选择
|
||||
if (this.config.enableAdaptiveStrategy) {
|
||||
return this.selectAdaptiveStrategy(conflict);
|
||||
}
|
||||
|
||||
|
||||
return this.config.defaultStrategy;
|
||||
}
|
||||
|
||||
@@ -363,27 +363,27 @@ export class ConflictResolver extends EventEmitter {
|
||||
*/
|
||||
private selectAdaptiveStrategy(conflict: ConflictInfo): ConflictResolutionStrategy {
|
||||
const strategies = Array.from(this.performance.keys());
|
||||
|
||||
|
||||
if (strategies.length === 0) {
|
||||
return this.config.defaultStrategy;
|
||||
}
|
||||
|
||||
|
||||
// 根据冲突类型和性能选择最佳策略
|
||||
let bestStrategy = this.config.defaultStrategy;
|
||||
let bestScore = 0;
|
||||
|
||||
|
||||
for (const strategy of strategies) {
|
||||
const perf = this.performance.get(strategy)!;
|
||||
|
||||
|
||||
// 计算策略得分:成功率 * 用户满意度 / 平均时间
|
||||
const score = (perf.successRate * perf.userSatisfaction) / Math.max(perf.averageTime, 1);
|
||||
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
bestStrategy = strategy;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return bestStrategy;
|
||||
}
|
||||
|
||||
@@ -406,7 +406,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
private resolveWithClientPrediction(conflict: ConflictInfo): ResolutionResult {
|
||||
// 如果冲突严重性较低,保持客户端值
|
||||
const keepClient = conflict.severity < 0.5;
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
finalValue: keepClient ? conflict.localValue : conflict.serverValue,
|
||||
@@ -424,7 +424,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
// 非数值类型,回退到服务端权威
|
||||
return this.resolveWithServerAuthority(conflict);
|
||||
}
|
||||
|
||||
|
||||
// 检查差异是否足够大需要插值
|
||||
const diff = Math.abs(conflict.localValue - conflict.serverValue);
|
||||
if (diff < this.config.interpolation.threshold) {
|
||||
@@ -436,7 +436,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
rollbackRequired: false
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
finalValue: conflict.serverValue,
|
||||
@@ -453,7 +453,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
private resolveWithRollbackReplay(conflict: ConflictInfo): ResolutionResult {
|
||||
// 检查是否满足回滚条件
|
||||
const shouldRollback = conflict.severity >= this.config.rollback.priorityThreshold;
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
finalValue: conflict.serverValue,
|
||||
@@ -468,12 +468,12 @@ export class ConflictResolver extends EventEmitter {
|
||||
*/
|
||||
private resolveWithCustomLogic(conflict: ConflictInfo): ResolutionResult {
|
||||
const customResolver = this.customResolvers.get(conflict.propertyKey);
|
||||
|
||||
|
||||
if (!customResolver) {
|
||||
this.logger.warn(`未找到属性 ${conflict.propertyKey} 的自定义解决器,使用服务端权威`);
|
||||
return this.resolveWithServerAuthority(conflict);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return customResolver(conflict);
|
||||
} catch (error) {
|
||||
@@ -487,7 +487,7 @@ export class ConflictResolver extends EventEmitter {
|
||||
*/
|
||||
private initializePerformanceTracking(): void {
|
||||
const strategies = Object.values(ConflictResolutionStrategy);
|
||||
|
||||
|
||||
for (const strategy of strategies) {
|
||||
this.performance.set(strategy, {
|
||||
strategy,
|
||||
@@ -507,15 +507,15 @@ export class ConflictResolver extends EventEmitter {
|
||||
if (!perf) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 更新统计(使用滑动平均)
|
||||
const alpha = 0.1; // 学习率
|
||||
|
||||
|
||||
perf.conflictCount++;
|
||||
perf.successRate = perf.successRate * (1 - alpha) + (success ? 1 : 0) * alpha;
|
||||
perf.averageTime = perf.averageTime * (1 - alpha) + time * alpha;
|
||||
|
||||
|
||||
// 用户满意度基于成功率和时间
|
||||
perf.userSatisfaction = Math.max(0, perf.successRate - (time / 100) * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* 客户端同步模块导出
|
||||
*/
|
||||
|
||||
export * from './ConflictResolver';
|
||||
export * from './ConflictResolver';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { EntitySystem, createLogger } from '@esengine/ecs-framework';
|
||||
import {
|
||||
SyncVarManager,
|
||||
SyncBatch,
|
||||
import {
|
||||
SyncVarManager,
|
||||
SyncBatch,
|
||||
SyncVarSerializer,
|
||||
MessageType,
|
||||
INetworkMessage
|
||||
INetworkMessage
|
||||
} from '@esengine/network-shared';
|
||||
import { NetworkClient } from '../core/NetworkClient';
|
||||
|
||||
@@ -91,23 +91,23 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
private syncVarManager: SyncVarManager;
|
||||
private serializer: SyncVarSerializer;
|
||||
private networkClient?: NetworkClient;
|
||||
|
||||
|
||||
/** 远程实体状态 */
|
||||
private remoteEntities = new Map<string, RemoteEntityState>();
|
||||
|
||||
|
||||
/** 本地预测状态 */
|
||||
private predictions = new Map<string, PredictionState>();
|
||||
|
||||
|
||||
/** 插值状态 */
|
||||
private interpolations = new Map<string, InterpolationState>();
|
||||
|
||||
|
||||
/** 本地实体映射 */
|
||||
private localEntityMap = new Map<string, any>();
|
||||
|
||||
|
||||
/** 时间同步 */
|
||||
private serverTimeOffset = 0;
|
||||
private lastServerTime = 0;
|
||||
|
||||
|
||||
/** 统计信息 */
|
||||
private stats: ClientSyncStats = {
|
||||
remoteEntities: 0,
|
||||
@@ -121,7 +121,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
|
||||
constructor(config: Partial<ClientSyncSystemConfig> = {}) {
|
||||
super();
|
||||
|
||||
|
||||
this.config = {
|
||||
enablePrediction: true,
|
||||
enableInterpolation: true,
|
||||
@@ -132,7 +132,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
authorityThreshold: 0.1,
|
||||
...config
|
||||
};
|
||||
|
||||
|
||||
this.syncVarManager = SyncVarManager.getInstance();
|
||||
this.serializer = new SyncVarSerializer({
|
||||
enableCompression: true,
|
||||
@@ -153,7 +153,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
*/
|
||||
protected override process(): void {
|
||||
const currentTime = this.getCurrentTime();
|
||||
|
||||
|
||||
this.updateInterpolations(currentTime);
|
||||
this.updatePredictions(currentTime);
|
||||
this.cleanupOldData(currentTime);
|
||||
@@ -165,7 +165,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
*/
|
||||
public setNetworkClient(client: NetworkClient): void {
|
||||
this.networkClient = client;
|
||||
|
||||
|
||||
// 监听同步消息
|
||||
client.on('messageReceived', (message: INetworkMessage) => {
|
||||
if (message.type === MessageType.SYNC_BATCH) {
|
||||
@@ -191,7 +191,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
this.localEntityMap.delete(instanceId);
|
||||
this.syncVarManager.unregisterInstance(entity);
|
||||
}
|
||||
|
||||
|
||||
this.remoteEntities.delete(instanceId);
|
||||
this.predictions.delete(instanceId);
|
||||
this.interpolations.delete(instanceId);
|
||||
@@ -208,12 +208,12 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
instanceType,
|
||||
...properties
|
||||
};
|
||||
|
||||
|
||||
this.localEntityMap.set(instanceId, entity);
|
||||
|
||||
|
||||
// 注册到SyncVar管理器
|
||||
this.syncVarManager.registerInstance(entity);
|
||||
|
||||
|
||||
this.logger.debug(`创建远程实体: ${instanceId} (${instanceType})`);
|
||||
return entity;
|
||||
}
|
||||
@@ -236,12 +236,12 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
if (!this.config.enablePrediction) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const entity = this.localEntityMap.get(instanceId);
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let prediction = this.predictions.get(instanceId);
|
||||
if (!prediction) {
|
||||
prediction = {
|
||||
@@ -253,13 +253,13 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
};
|
||||
this.predictions.set(instanceId, prediction);
|
||||
}
|
||||
|
||||
|
||||
// 记录输入
|
||||
prediction.pendingInputs.push({
|
||||
timestamp: this.getCurrentTime(),
|
||||
input
|
||||
});
|
||||
|
||||
|
||||
// 应用本地预测
|
||||
this.applyPrediction(entity, prediction, input);
|
||||
}
|
||||
@@ -325,12 +325,12 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
this.logger.warn('解析同步消息失败:', result.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const batch = result.data;
|
||||
this.applySyncBatch(batch);
|
||||
|
||||
|
||||
this.stats.totalUpdatesReceived++;
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('处理同步批次失败:', error);
|
||||
}
|
||||
@@ -346,9 +346,9 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
this.createRemoteEntity(batch.instanceId, batch.instanceType, batch.changes);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const currentTime = this.getCurrentTime();
|
||||
|
||||
|
||||
// 更新远程实体状态
|
||||
let remoteState = this.remoteEntities.get(batch.instanceId);
|
||||
if (!remoteState) {
|
||||
@@ -362,27 +362,27 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
};
|
||||
this.remoteEntities.set(batch.instanceId, remoteState);
|
||||
}
|
||||
|
||||
|
||||
// 更新历史记录
|
||||
remoteState.history.push({
|
||||
timestamp: batch.timestamp,
|
||||
properties: { ...batch.changes }
|
||||
});
|
||||
|
||||
|
||||
// 保持历史记录大小
|
||||
if (remoteState.history.length > this.config.maxRollbackFrames) {
|
||||
remoteState.history.shift();
|
||||
}
|
||||
|
||||
|
||||
remoteState.lastUpdateTime = currentTime;
|
||||
remoteState.serverTime = batch.timestamp;
|
||||
Object.assign(remoteState.properties, batch.changes);
|
||||
|
||||
|
||||
// 处理本地预测确认
|
||||
if (this.config.enablePrediction) {
|
||||
this.confirmPrediction(batch.instanceId, batch.changes, batch.timestamp);
|
||||
}
|
||||
|
||||
|
||||
// 开始插值
|
||||
if (this.config.enableInterpolation) {
|
||||
this.startInterpolation(batch.instanceId, batch.changes);
|
||||
@@ -400,36 +400,36 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
if (!prediction) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 检查预测准确性
|
||||
let hasCorrection = false;
|
||||
for (const [key, serverValue] of Object.entries(serverValues)) {
|
||||
const predictedValue = prediction.predictedValues[key];
|
||||
if (predictedValue !== undefined &&
|
||||
typeof predictedValue === 'number' &&
|
||||
if (predictedValue !== undefined &&
|
||||
typeof predictedValue === 'number' &&
|
||||
typeof serverValue === 'number' &&
|
||||
Math.abs(predictedValue - serverValue) > this.config.authorityThreshold) {
|
||||
hasCorrection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hasCorrection) {
|
||||
this.stats.predictionCorrections++;
|
||||
|
||||
|
||||
// 执行回滚和重放
|
||||
if (this.config.enableRollback) {
|
||||
this.performRollbackAndReplay(instanceId, serverValues, serverTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 更新确认值
|
||||
Object.assign(prediction.lastConfirmedValues, serverValues);
|
||||
prediction.confirmationTime = serverTime;
|
||||
|
||||
|
||||
// 清理已确认的输入
|
||||
prediction.pendingInputs = prediction.pendingInputs.filter(
|
||||
input => input.timestamp > serverTime
|
||||
(input) => input.timestamp > serverTime
|
||||
);
|
||||
}
|
||||
|
||||
@@ -439,21 +439,21 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
private performRollbackAndReplay(instanceId: string, serverValues: any, serverTime: number): void {
|
||||
const entity = this.localEntityMap.get(instanceId);
|
||||
const prediction = this.predictions.get(instanceId);
|
||||
|
||||
|
||||
if (!entity || !prediction) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 回滚到服务端状态
|
||||
this.applyValuesToEntity(entity, serverValues);
|
||||
|
||||
|
||||
// 重放未确认的输入
|
||||
for (const input of prediction.pendingInputs) {
|
||||
if (input.timestamp > serverTime) {
|
||||
this.applyPrediction(entity, prediction, input.input);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.logger.debug(`执行回滚和重放: ${instanceId}`);
|
||||
}
|
||||
|
||||
@@ -465,13 +465,13 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
if (!entity) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 获取当前值
|
||||
const currentValues: { [key: string]: any } = {};
|
||||
for (const key of Object.keys(targetValues)) {
|
||||
currentValues[key] = entity[key];
|
||||
}
|
||||
|
||||
|
||||
const interpolation: InterpolationState = {
|
||||
instanceId,
|
||||
fromValues: currentValues,
|
||||
@@ -480,7 +480,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
toTime: this.getCurrentTime() + this.config.interpolationWindow,
|
||||
currentValues: { ...currentValues }
|
||||
};
|
||||
|
||||
|
||||
this.interpolations.set(instanceId, interpolation);
|
||||
}
|
||||
|
||||
@@ -494,27 +494,27 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
this.interpolations.delete(instanceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const progress = Math.min(
|
||||
(currentTime - interpolation.fromTime) /
|
||||
(currentTime - interpolation.fromTime) /
|
||||
(interpolation.toTime - interpolation.fromTime),
|
||||
1
|
||||
);
|
||||
|
||||
|
||||
// 计算插值
|
||||
for (const [key, fromValue] of Object.entries(interpolation.fromValues)) {
|
||||
const toValue = interpolation.toValues[key];
|
||||
|
||||
|
||||
if (typeof fromValue === 'number' && typeof toValue === 'number') {
|
||||
interpolation.currentValues[key] = fromValue + (toValue - fromValue) * progress;
|
||||
} else {
|
||||
interpolation.currentValues[key] = progress < 0.5 ? fromValue : toValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 应用插值结果
|
||||
this.applyValuesToEntity(entity, interpolation.currentValues);
|
||||
|
||||
|
||||
// 检查插值是否完成
|
||||
if (progress >= 1) {
|
||||
this.interpolations.delete(instanceId);
|
||||
@@ -529,11 +529,11 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
for (const [instanceId, prediction] of this.predictions) {
|
||||
// 清理过期的输入
|
||||
prediction.pendingInputs = prediction.pendingInputs.filter(
|
||||
input => currentTime - input.timestamp < 1000
|
||||
(input) => currentTime - input.timestamp < 1000
|
||||
);
|
||||
|
||||
|
||||
// 如果没有待处理的输入,移除预测状态
|
||||
if (prediction.pendingInputs.length === 0 &&
|
||||
if (prediction.pendingInputs.length === 0 &&
|
||||
currentTime - prediction.confirmationTime > 1000) {
|
||||
this.predictions.delete(instanceId);
|
||||
}
|
||||
@@ -549,7 +549,7 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
if (input.movement) {
|
||||
prediction.predictedValues.x = (entity.x || 0) + input.movement.x;
|
||||
prediction.predictedValues.y = (entity.y || 0) + input.movement.y;
|
||||
|
||||
|
||||
this.applyValuesToEntity(entity, prediction.predictedValues);
|
||||
}
|
||||
}
|
||||
@@ -568,17 +568,17 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
*/
|
||||
private cleanupOldData(currentTime: number): void {
|
||||
const maxAge = 5000; // 5秒
|
||||
|
||||
|
||||
// 清理远程实体历史
|
||||
for (const [instanceId, remoteState] of this.remoteEntities) {
|
||||
if (currentTime - remoteState.lastUpdateTime > maxAge) {
|
||||
this.remoteEntities.delete(instanceId);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// 清理历史记录
|
||||
remoteState.history = remoteState.history.filter(
|
||||
record => currentTime - record.timestamp < maxAge
|
||||
(record) => currentTime - record.timestamp < maxAge
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -598,4 +598,4 @@ export class ClientSyncSystem extends EntitySystem {
|
||||
private getCurrentTime(): number {
|
||||
return Date.now() + this.serverTimeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,4 @@
|
||||
* 客户端系统导出
|
||||
*/
|
||||
|
||||
export * from './ClientSyncSystem';
|
||||
export * from './ClientSyncSystem';
|
||||
|
||||
@@ -60,11 +60,11 @@ export class ReconnectionManager {
|
||||
private config: ReconnectionConfig;
|
||||
private state: ReconnectionState;
|
||||
private eventHandlers: Partial<ReconnectionEvents> = {};
|
||||
|
||||
|
||||
private reconnectTimer?: ITimer;
|
||||
private reconnectCallback?: () => Promise<void>;
|
||||
private abortController?: AbortController;
|
||||
|
||||
|
||||
// 策略相关
|
||||
private strategy: ReconnectionStrategy = ReconnectionStrategy.Exponential;
|
||||
private customDelayCalculator?: (attempt: number) => number;
|
||||
@@ -133,7 +133,7 @@ export class ReconnectionManager {
|
||||
this.state.nextAttemptTime = Date.now() + delay;
|
||||
|
||||
this.logger.info(`开始重连 (第 ${this.state.currentAttempt}/${this.config.maxAttempts} 次),${delay}ms 后尝试`);
|
||||
|
||||
|
||||
this.eventHandlers.reconnectStarted?.(this.state.currentAttempt);
|
||||
|
||||
// 设置重连定时器
|
||||
@@ -173,13 +173,13 @@ export class ReconnectionManager {
|
||||
}
|
||||
|
||||
const duration = this.state.lastAttemptTime ? Date.now() - this.state.lastAttemptTime : 0;
|
||||
|
||||
|
||||
this.logger.info(`重连成功 (第 ${this.state.currentAttempt} 次尝试,耗时 ${duration}ms)`);
|
||||
|
||||
|
||||
this.state.isReconnecting = false;
|
||||
this.state.successfulReconnections++;
|
||||
this.state.nextAttemptTime = undefined;
|
||||
|
||||
|
||||
// 是否重置重连计数
|
||||
if (this.config.resetAfterSuccess) {
|
||||
this.state.currentAttempt = 0;
|
||||
@@ -198,7 +198,7 @@ export class ReconnectionManager {
|
||||
}
|
||||
|
||||
this.logger.warn(`重连失败 (第 ${this.state.currentAttempt} 次尝试):`, error);
|
||||
|
||||
|
||||
this.eventHandlers.reconnectFailed?.(this.state.currentAttempt, error);
|
||||
|
||||
// 检查是否还有重连机会
|
||||
@@ -230,7 +230,7 @@ export class ReconnectionManager {
|
||||
maxAttempts: this.config.maxAttempts,
|
||||
isReconnecting: this.state.isReconnecting,
|
||||
nextAttemptTime: this.state.nextAttemptTime,
|
||||
successRate: this.state.totalAttempts > 0 ?
|
||||
successRate: this.state.totalAttempts > 0 ?
|
||||
(this.state.successfulReconnections / this.state.totalAttempts) * 100 : 0
|
||||
};
|
||||
}
|
||||
@@ -304,21 +304,21 @@ export class ReconnectionManager {
|
||||
case ReconnectionStrategy.Fixed:
|
||||
delay = this.config.initialDelay;
|
||||
break;
|
||||
|
||||
|
||||
case ReconnectionStrategy.Linear:
|
||||
delay = this.config.initialDelay * this.state.currentAttempt;
|
||||
break;
|
||||
|
||||
|
||||
case ReconnectionStrategy.Exponential:
|
||||
delay = this.config.initialDelay * Math.pow(this.config.backoffFactor, this.state.currentAttempt - 1);
|
||||
break;
|
||||
|
||||
|
||||
case ReconnectionStrategy.Custom:
|
||||
delay = this.customDelayCalculator ?
|
||||
this.customDelayCalculator(this.state.currentAttempt) :
|
||||
delay = this.customDelayCalculator ?
|
||||
this.customDelayCalculator(this.state.currentAttempt) :
|
||||
this.config.initialDelay;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
delay = this.config.initialDelay;
|
||||
}
|
||||
@@ -349,7 +349,7 @@ export class ReconnectionManager {
|
||||
try {
|
||||
await this.reconnectCallback();
|
||||
// 重连回调成功,等待实际连接建立再调用 onReconnectionSuccess
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.onReconnectionFailure(error as Error);
|
||||
}
|
||||
@@ -407,4 +407,4 @@ export class ReconnectionManager {
|
||||
}
|
||||
return (this.state.currentAttempt / this.config.maxAttempts) * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
* WebSocket客户端传输层实现
|
||||
*/
|
||||
import { createLogger, ITimer } from '@esengine/ecs-framework';
|
||||
import {
|
||||
IClientTransport,
|
||||
IConnectionOptions,
|
||||
ConnectionState,
|
||||
IConnectionStats
|
||||
import {
|
||||
IClientTransport,
|
||||
IConnectionOptions,
|
||||
ConnectionState,
|
||||
IConnectionStats
|
||||
} from '@esengine/network-shared';
|
||||
import { NetworkTimerManager } from '../utils';
|
||||
|
||||
@@ -27,12 +27,12 @@ export class WebSocketClient implements IClientTransport {
|
||||
* 消息接收事件处理器
|
||||
*/
|
||||
private messageHandlers: ((data: ArrayBuffer | string) => void)[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 连接状态变化事件处理器
|
||||
*/
|
||||
private stateChangeHandlers: ((state: ConnectionState) => void)[] = [];
|
||||
|
||||
|
||||
/**
|
||||
* 错误事件处理器
|
||||
*/
|
||||
@@ -102,11 +102,11 @@ export class WebSocketClient implements IClientTransport {
|
||||
try {
|
||||
this.websocket.send(data);
|
||||
this.stats.messagesSent++;
|
||||
|
||||
|
||||
// 估算字节数
|
||||
const bytes = typeof data === 'string' ? new Blob([data]).size : data.byteLength;
|
||||
this.stats.bytesSent += bytes;
|
||||
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('发送消息失败:', error);
|
||||
this.handleError(error as Error);
|
||||
@@ -192,7 +192,7 @@ export class WebSocketClient implements IClientTransport {
|
||||
* 设置WebSocket事件监听
|
||||
*/
|
||||
private setupWebSocketEvents(
|
||||
resolve: () => void,
|
||||
resolve: () => void,
|
||||
reject: (error: Error) => void
|
||||
): void {
|
||||
if (!this.websocket) return;
|
||||
@@ -221,7 +221,7 @@ export class WebSocketClient implements IClientTransport {
|
||||
const error = new Error(`WebSocket错误: ${event}`);
|
||||
this.logger.error('WebSocket错误:', error);
|
||||
this.handleError(error);
|
||||
|
||||
|
||||
if (this.connectionState === ConnectionState.Connecting) {
|
||||
reject(error);
|
||||
}
|
||||
@@ -234,13 +234,13 @@ export class WebSocketClient implements IClientTransport {
|
||||
private handleMessage(data: any): void {
|
||||
try {
|
||||
this.stats.messagesReceived++;
|
||||
|
||||
|
||||
// 估算字节数
|
||||
const bytes = typeof data === 'string' ? new Blob([data]).size : data.byteLength || 0;
|
||||
this.stats.bytesReceived += bytes;
|
||||
|
||||
// 触发消息事件
|
||||
this.messageHandlers.forEach(handler => {
|
||||
this.messageHandlers.forEach((handler) => {
|
||||
try {
|
||||
handler(data);
|
||||
} catch (error) {
|
||||
@@ -265,7 +265,7 @@ export class WebSocketClient implements IClientTransport {
|
||||
|
||||
// 根据关闭代码决定是否重连
|
||||
const shouldReconnect = this.shouldReconnect(code);
|
||||
|
||||
|
||||
if (shouldReconnect && this.options.autoReconnect) {
|
||||
this.setConnectionState(ConnectionState.Reconnecting);
|
||||
this.scheduleReconnect();
|
||||
@@ -309,8 +309,8 @@ export class WebSocketClient implements IClientTransport {
|
||||
() => {
|
||||
this.reconnectAttempts++;
|
||||
this.stats.reconnectCount++;
|
||||
|
||||
this.connectInternal().catch(error => {
|
||||
|
||||
this.connectInternal().catch((error) => {
|
||||
this.logger.error(`重连失败 (第 ${this.reconnectAttempts} 次):`, error);
|
||||
this.scheduleReconnect();
|
||||
});
|
||||
@@ -341,7 +341,7 @@ export class WebSocketClient implements IClientTransport {
|
||||
this.logger.debug(`连接状态变化: ${oldState} -> ${state}`);
|
||||
|
||||
// 触发状态变化事件
|
||||
this.stateChangeHandlers.forEach(handler => {
|
||||
this.stateChangeHandlers.forEach((handler) => {
|
||||
try {
|
||||
handler(state);
|
||||
} catch (error) {
|
||||
@@ -354,7 +354,7 @@ export class WebSocketClient implements IClientTransport {
|
||||
* 处理错误
|
||||
*/
|
||||
private handleError(error: Error): void {
|
||||
this.errorHandlers.forEach(handler => {
|
||||
this.errorHandlers.forEach((handler) => {
|
||||
try {
|
||||
handler(error);
|
||||
} catch (handlerError) {
|
||||
@@ -378,10 +378,10 @@ export class WebSocketClient implements IClientTransport {
|
||||
* 手动触发重连
|
||||
*/
|
||||
public reconnect(): void {
|
||||
if (this.connectionState === ConnectionState.Disconnected ||
|
||||
if (this.connectionState === ConnectionState.Disconnected ||
|
||||
this.connectionState === ConnectionState.Failed) {
|
||||
this.reconnectAttempts = 0;
|
||||
this.connectInternal().catch(error => {
|
||||
this.connectInternal().catch((error) => {
|
||||
this.logger.error('手动重连失败:', error);
|
||||
});
|
||||
}
|
||||
@@ -403,4 +403,4 @@ export class WebSocketClient implements IClientTransport {
|
||||
this.stateChangeHandlers.length = 0;
|
||||
this.errorHandlers.length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,16 +78,16 @@ export class NetworkTimerManager {
|
||||
context,
|
||||
onTime
|
||||
);
|
||||
|
||||
|
||||
this.timers.add(timer as any);
|
||||
|
||||
|
||||
// 如果是一次性定时器,完成后自动清理
|
||||
if (!repeats) {
|
||||
setTimeout(() => {
|
||||
this.timers.delete(timer as any);
|
||||
}, timeInSeconds * 1000 + 100);
|
||||
}
|
||||
|
||||
|
||||
return timer;
|
||||
}
|
||||
|
||||
@@ -100,4 +100,4 @@ export class NetworkTimerManager {
|
||||
}
|
||||
this.timers.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**
|
||||
* 网络客户端工具类
|
||||
*/
|
||||
export * from './NetworkTimer';
|
||||
export * from './NetworkTimer';
|
||||
|
||||
Reference in New Issue
Block a user