EG/plugins/user/network_sync/clock/clock_sync.py
2025-12-12 16:16:15 +08:00

621 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
时钟同步模块
负责客户端与服务器之间的时间同步
"""
import time
from typing import Dict, Any, List, Optional
import threading
class ClockSync:
"""
时钟同步器
负责客户端与服务器之间的时间同步
"""
def __init__(self, plugin):
"""
初始化时钟同步器
Args:
plugin: 网络同步插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 时钟同步配置
self.clock_config = {
'sync_interval': 5.0, # 同步间隔(秒)
'samples_per_sync': 10, # 每次同步的样本数
'max_offset_samples': 100, # 最大偏移样本数
'enable_interpolation': True, # 启用时间插值
'interpolation_rate': 60.0, # 插值更新频率
'max_drift_correction': 0.1, # 最大漂移校正(秒/秒)
'min_sync_quality': 0.8, # 最小同步质量
'timeout': 10.0 # 同步超时时间
}
# 时钟状态
self.clock_state = {
'is_synchronized': False,
'time_offset': 0.0, # 本地时间与服务器时间的偏移
'time_drift': 0.0, # 时间漂移率
'last_sync_time': 0.0, # 最后同步时间
'sync_quality': 0.0, # 同步质量
'round_trip_time': 0.0, # 往返时间
'sync_samples': [] # 同步样本
}
# 插值状态
self.interpolation_state = {
'interpolated_time': 0.0,
'target_time': 0.0,
'interpolation_start': 0.0,
'interpolation_duration': 0.0
}
# 时钟统计
self.clock_stats = {
'sync_attempts': 0,
'successful_syncs': 0,
'failed_syncs': 0,
'average_rtt': 0.0,
'min_rtt': float('inf'),
'max_rtt': 0.0,
'sync_errors': 0
}
# 同步请求队列
self.sync_requests = []
self.sync_lock = threading.Lock()
# 回调函数
self.clock_callbacks = {
'sync_started': [],
'sync_completed': [],
'sync_failed': [],
'time_updated': [],
'sync_quality_changed': []
}
# 时间戳记录
self.last_sync_attempt = 0.0
self.last_time_update = 0.0
self.last_interpolation_update = 0.0
print("✓ 时钟同步器已创建")
def initialize(self) -> bool:
"""
初始化时钟同步器
Returns:
是否初始化成功
"""
try:
self.initialized = True
print("✓ 时钟同步器初始化完成")
return True
except Exception as e:
print(f"✗ 时钟同步器初始化失败: {e}")
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用时钟同步器
Returns:
是否启用成功
"""
try:
if not self.initialized:
print("✗ 时钟同步器未初始化")
return False
self.enabled = True
print("✓ 时钟同步器已启用")
return True
except Exception as e:
print(f"✗ 时钟同步器启用失败: {e}")
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用时钟同步器"""
try:
self.enabled = False
print("✓ 时钟同步器已禁用")
except Exception as e:
print(f"✗ 时钟同步器禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理时钟同步器资源"""
try:
self.disable()
self.sync_requests.clear()
self.clock_callbacks.clear()
self.initialized = False
print("✓ 时钟同步器资源已清理")
except Exception as e:
print(f"✗ 时钟同步器资源清理失败: {e}")
import traceback
traceback.print_exc()
def update(self, dt: float):
"""
更新时钟同步器状态
Args:
dt: 时间增量
"""
try:
if not self.enabled:
return
current_time = time.time()
# 定期发起时钟同步
if current_time - self.last_sync_attempt >= self.clock_config['sync_interval']:
self._request_time_sync()
self.last_sync_attempt = current_time
# 更新时间插值
if self.clock_config['enable_interpolation']:
self._update_time_interpolation(current_time)
self.last_time_update = current_time
except Exception as e:
print(f"✗ 时钟同步器更新失败: {e}")
import traceback
traceback.print_exc()
def _request_time_sync(self):
"""请求时间同步"""
try:
if not self.plugin.network_manager or not self.plugin.protocol_handler:
return
# 增加同步尝试计数
self.clock_stats['sync_attempts'] += 1
# 触发同步开始回调
self._trigger_clock_callback('sync_started', {
'timestamp': time.time()
})
# 发送时间同步请求
sync_data = {
'client_time': time.time(),
'request_id': f"sync_{int(time.time() * 1000000)}"
}
success = self.plugin.protocol_handler.send_message('SYNC_REQUEST', sync_data)
if success:
# 记录同步请求
with self.sync_lock:
self.sync_requests.append({
'request_id': sync_data['request_id'],
'send_time': time.time(),
'completed': False
})
else:
self.clock_stats['failed_syncs'] += 1
self._trigger_clock_callback('sync_failed', {
'reason': 'send_failed',
'timestamp': time.time()
})
except Exception as e:
print(f"✗ 时间同步请求失败: {e}")
self.clock_stats['sync_errors'] += 1
def handle_sync_request(self, data: Dict[str, Any], client_id: str = None):
"""
处理时间同步请求
Args:
data: 同步请求数据
client_id: 客户端ID
"""
try:
# 构造同步响应
response_data = {
'client_time': data.get('client_time', 0),
'server_time': time.time(),
'request_id': data.get('request_id', ''),
'client_id': client_id
}
# 发送同步响应
if self.plugin.protocol_handler:
self.plugin.protocol_handler.send_message('SYNC_RESPONSE', response_data, client_id)
except Exception as e:
print(f"✗ 时间同步请求处理失败: {e}")
def handle_sync_response(self, data: Dict[str, Any]):
"""
处理时间同步响应
Args:
data: 同步响应数据
"""
try:
client_send_time = data.get('client_time', 0)
server_time = data.get('server_time', 0)
request_id = data.get('request_id', '')
receive_time = time.time()
# 计算往返时间
rtt = receive_time - client_send_time
# 计算时间偏移(假设网络延迟对称)
time_offset = server_time - (client_send_time + rtt / 2)
# 更新统计信息
self.clock_stats['successful_syncs'] += 1
self.clock_stats['average_rtt'] = (
(self.clock_stats['average_rtt'] * (self.clock_stats['successful_syncs'] - 1) + rtt) /
self.clock_stats['successful_syncs']
)
self.clock_stats['min_rtt'] = min(self.clock_stats['min_rtt'], rtt)
self.clock_stats['max_rtt'] = max(self.clock_stats['max_rtt'], rtt)
# 添加同步样本
sample = {
'offset': time_offset,
'rtt': rtt,
'timestamp': receive_time
}
with self.sync_lock:
self.clock_state['sync_samples'].append(sample)
# 保持样本数量在限制范围内
if len(self.clock_state['sync_samples']) > self.clock_config['max_offset_samples']:
self.clock_state['sync_samples'].pop(0)
# 更新时钟状态
self._update_clock_state()
# 标记请求完成
with self.sync_lock:
for request in self.sync_requests:
if request['request_id'] == request_id:
request['completed'] = True
break
# 触发同步完成回调
self._trigger_clock_callback('sync_completed', {
'offset': time_offset,
'rtt': rtt,
'quality': self.clock_state['sync_quality'],
'timestamp': receive_time
})
self.clock_state['last_sync_time'] = receive_time
except Exception as e:
print(f"✗ 时间同步响应处理失败: {e}")
self.clock_stats['sync_errors'] += 1
self._trigger_clock_callback('sync_failed', {
'reason': 'process_failed',
'error': str(e),
'timestamp': time.time()
})
def _update_clock_state(self):
"""更新时钟状态"""
try:
with self.sync_lock:
if not self.clock_state['sync_samples']:
return
# 计算平均时间偏移
offsets = [sample['offset'] for sample in self.clock_state['sync_samples']]
avg_offset = sum(offsets) / len(offsets)
# 计算时间偏移的标准差(用于质量评估)
if len(offsets) > 1:
variance = sum((offset - avg_offset) ** 2 for offset in offsets) / (len(offsets) - 1)
std_dev = variance ** 0.5
else:
std_dev = 0.0
# 计算同步质量基于标准差和RTT
if std_dev < 0.001: # 小于1毫秒的标准差
quality = 1.0
else:
# 质量与标准差成反比
quality = max(0.0, 1.0 - (std_dev * 1000)) # 转换为毫秒
# 计算平均RTT
rtts = [sample['rtt'] for sample in self.clock_state['sync_samples']]
avg_rtt = sum(rtts) / len(rtts)
# 更新时钟状态
self.clock_state['time_offset'] = avg_offset
self.clock_state['round_trip_time'] = avg_rtt
self.clock_state['sync_quality'] = quality
self.clock_state['is_synchronized'] = quality >= self.clock_config['min_sync_quality']
# 触发同步质量变化回调
self._trigger_clock_callback('sync_quality_changed', {
'quality': quality,
'offset': avg_offset,
'rtt': avg_rtt
})
except Exception as e:
print(f"✗ 时钟状态更新失败: {e}")
def _update_time_interpolation(self, current_time: float):
"""
更新时间插值
Args:
current_time: 当前时间
"""
try:
if not self.clock_config['enable_interpolation']:
return
# 计算插值时间
server_time = self.get_server_time()
if self.interpolation_state['interpolated_time'] == 0.0:
# 初始化插值
self.interpolation_state['interpolated_time'] = server_time
self.interpolation_state['target_time'] = server_time
else:
# 更新目标时间
self.interpolation_state['target_time'] = server_time
# 执行插值
if self.interpolation_state['interpolated_time'] != self.interpolation_state['target_time']:
interpolation_speed = self.clock_config['interpolation_rate']
time_diff = self.interpolation_state['target_time'] - self.interpolation_state['interpolated_time']
max_step = interpolation_speed * (1.0 / 60.0) # 假设60FPS
if abs(time_diff) > max_step:
# 逐步接近目标时间
step = max_step if time_diff > 0 else -max_step
self.interpolation_state['interpolated_time'] += step
else:
# 直接设置为目标时间
self.interpolation_state['interpolated_time'] = self.interpolation_state['target_time']
# 触发时间更新回调
self._trigger_clock_callback('time_updated', {
'server_time': server_time,
'interpolated_time': self.interpolation_state['interpolated_time'],
'local_time': current_time
})
self.last_interpolation_update = current_time
except Exception as e:
print(f"✗ 时间插值更新失败: {e}")
def get_server_time(self) -> float:
"""
获取服务器时间
Returns:
服务器时间戳
"""
try:
local_time = time.time()
return local_time + self.clock_state['time_offset']
except Exception as e:
print(f"✗ 服务器时间获取失败: {e}")
return time.time()
def get_interpolated_time(self) -> float:
"""
获取插值时间
Returns:
插值时间戳
"""
try:
if self.clock_config['enable_interpolation'] and self.interpolation_state['interpolated_time'] != 0.0:
return self.interpolation_state['interpolated_time']
else:
return self.get_server_time()
except Exception as e:
print(f"✗ 插值时间获取失败: {e}")
return time.time()
def get_time_offset(self) -> float:
"""
获取时间偏移
Returns:
时间偏移值
"""
return self.clock_state['time_offset']
def get_sync_quality(self) -> float:
"""
获取同步质量
Returns:
同步质量值 (0.0-1.0)
"""
return self.clock_state['sync_quality']
def is_synchronized(self) -> bool:
"""
检查是否已同步
Returns:
是否已同步
"""
return self.clock_state['is_synchronized']
def get_round_trip_time(self) -> float:
"""
获取往返时间
Returns:
往返时间(秒)
"""
return self.clock_state['round_trip_time']
def get_clock_stats(self) -> Dict[str, Any]:
"""
获取时钟统计信息
Returns:
时钟统计字典
"""
return self.clock_stats.copy()
def reset_clock_stats(self):
"""重置时钟统计信息"""
try:
self.clock_stats = {
'sync_attempts': 0,
'successful_syncs': 0,
'failed_syncs': 0,
'average_rtt': 0.0,
'min_rtt': float('inf'),
'max_rtt': 0.0,
'sync_errors': 0
}
print("✓ 时钟统计信息已重置")
except Exception as e:
print(f"✗ 时钟统计信息重置失败: {e}")
def force_sync(self) -> bool:
"""
强制进行时钟同步
Returns:
是否成功发起同步
"""
try:
self._request_time_sync()
self.last_sync_attempt = time.time()
print("✓ 强制时钟同步已发起")
return True
except Exception as e:
print(f"✗ 强制时钟同步失败: {e}")
return False
def reset_sync(self):
"""重置时钟同步状态"""
try:
with self.sync_lock:
self.clock_state = {
'is_synchronized': False,
'time_offset': 0.0,
'time_drift': 0.0,
'last_sync_time': 0.0,
'sync_quality': 0.0,
'round_trip_time': 0.0,
'sync_samples': []
}
self.interpolation_state = {
'interpolated_time': 0.0,
'target_time': 0.0,
'interpolation_start': 0.0,
'interpolation_duration': 0.0
}
self.sync_requests.clear()
print("✓ 时钟同步状态已重置")
except Exception as e:
print(f"✗ 时钟同步状态重置失败: {e}")
def set_clock_config(self, config: Dict[str, Any]) -> bool:
"""
设置时钟配置
Args:
config: 时钟配置字典
Returns:
是否设置成功
"""
try:
self.clock_config.update(config)
print(f"✓ 时钟配置已更新: {self.clock_config}")
return True
except Exception as e:
print(f"✗ 时钟配置设置失败: {e}")
return False
def get_clock_config(self) -> Dict[str, Any]:
"""
获取时钟配置
Returns:
时钟配置字典
"""
return self.clock_config.copy()
def _trigger_clock_callback(self, callback_type: str, data: Dict[str, Any]):
"""
触发时钟回调
Args:
callback_type: 回调类型
data: 回调数据
"""
try:
if callback_type in self.clock_callbacks:
for callback in self.clock_callbacks[callback_type]:
try:
callback(data)
except Exception as e:
print(f"✗ 时钟回调执行失败: {e}")
except Exception as e:
print(f"✗ 时钟回调触发失败: {e}")
def register_clock_callback(self, callback_type: str, callback: callable):
"""
注册时钟回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.clock_callbacks:
self.clock_callbacks[callback_type].append(callback)
print(f"✓ 时钟回调已注册: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 时钟回调注册失败: {e}")
def unregister_clock_callback(self, callback_type: str, callback: callable):
"""
注销时钟回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.clock_callbacks:
if callback in self.clock_callbacks[callback_type]:
self.clock_callbacks[callback_type].remove(callback)
print(f"✓ 时钟回调已注销: {callback_type}")
except Exception as e:
print(f"✗ 时钟回调注销失败: {e}")