621 lines
21 KiB
Python
621 lines
21 KiB
Python
"""
|
||
时钟同步模块
|
||
负责客户端与服务器之间的时间同步
|
||
"""
|
||
|
||
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}") |