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:
YHH
2025-11-02 23:50:41 +08:00
committed by GitHub
parent 50a01d9dd3
commit ddc7a7750e
122 changed files with 11453 additions and 11761 deletions

View File

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

View File

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

View File

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

View File

@@ -19,4 +19,4 @@ export * from './systems';
export * from './sync';
// 重新导出shared包的类型
export * from '@esengine/network-shared';
export * from '@esengine/network-shared';

View File

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

View File

@@ -2,4 +2,4 @@
* 客户端同步模块导出
*/
export * from './ConflictResolver';
export * from './ConflictResolver';

View File

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

View File

@@ -2,4 +2,4 @@
* 客户端系统导出
*/
export * from './ClientSyncSystem';
export * from './ClientSyncSystem';

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
/**
* 网络客户端工具类
*/
export * from './NetworkTimer';
export * from './NetworkTimer';