553 lines
20 KiB
Python
553 lines
20 KiB
Python
"""
|
||
高级骨骼动画系统 - 网络同步模块
|
||
提供动画状态网络同步、多人协作等功能
|
||
"""
|
||
|
||
import json
|
||
import hashlib
|
||
from typing import Dict, List, Optional, Any, Callable
|
||
from dataclasses import dataclass, field
|
||
from enum import Enum
|
||
|
||
from panda3d.core import *
|
||
from direct.distributed.ClockDelta import globalClockDelta
|
||
from direct.showbase.DirectObject import DirectObject
|
||
|
||
# 定义常量
|
||
NETWORK_SETTINGS = {
|
||
'enable_sync': True,
|
||
'sync_interval': 0.1, # 同步间隔(秒)
|
||
'interpolation_mode': 'linear', # 插值模式
|
||
'extrapolation_limit': 0.5, # 外推限制(秒)
|
||
'compression_enabled': True,
|
||
'delta_compression': True
|
||
}
|
||
|
||
# 枚举定义
|
||
class NetworkMode(Enum):
|
||
"""网络模式枚举"""
|
||
DISABLED = 0
|
||
SERVER = 1
|
||
CLIENT = 2
|
||
PEER_TO_PEER = 3
|
||
|
||
class SyncMode(Enum):
|
||
"""同步模式枚举"""
|
||
STATE_SYNC = 0 # 状态同步
|
||
INPUT_SYNC = 1 # 输入同步
|
||
INTERPOLATION = 2 # 插值同步
|
||
|
||
@dataclass
|
||
class NetworkActorState:
|
||
"""网络Actor状态数据类"""
|
||
actor_id: str
|
||
position: Tuple[float, float, float]
|
||
rotation: Tuple[float, float, float]
|
||
scale: Tuple[float, float, float]
|
||
active_animations: Dict[str, Dict[str, Any]]
|
||
timestamp: float
|
||
network_id: str = ""
|
||
checksum: str = ""
|
||
|
||
@dataclass
|
||
class NetworkSyncConfig:
|
||
"""网络同步配置数据类"""
|
||
mode: NetworkMode = NetworkMode.DISABLED
|
||
sync_mode: SyncMode = SyncMode.STATE_SYNC
|
||
sync_interval: float = 0.1
|
||
interpolation_mode: str = 'linear'
|
||
enable_compression: bool = True
|
||
max_buffer_size: int = 100
|
||
update_rate: int = 20 # 每秒更新次数
|
||
|
||
class NetworkSyncBuffer:
|
||
"""网络同步缓冲区"""
|
||
|
||
def __init__(self, max_size: int = 100):
|
||
self.max_size = max_size
|
||
self.buffer: List[NetworkActorState] = []
|
||
self.last_processed_time = 0.0
|
||
|
||
def add_state(self, state: NetworkActorState) -> None:
|
||
"""添加状态到缓冲区"""
|
||
self.buffer.append(state)
|
||
# 保持缓冲区大小
|
||
if len(self.buffer) > self.max_size:
|
||
self.buffer.pop(0)
|
||
|
||
def get_interpolated_state(self, target_time: float) -> Optional[NetworkActorState]:
|
||
"""
|
||
获取插值状态
|
||
:param target_time: 目标时间
|
||
:return: 插值状态
|
||
"""
|
||
if len(self.buffer) < 2:
|
||
return self.buffer[-1] if self.buffer else None
|
||
|
||
# 找到时间范围内的两个状态
|
||
prev_state = None
|
||
next_state = None
|
||
|
||
for state in self.buffer:
|
||
if state.timestamp <= target_time:
|
||
prev_state = state
|
||
else:
|
||
next_state = state
|
||
break
|
||
|
||
if not prev_state or not next_state:
|
||
return self.buffer[-1] if self.buffer else None
|
||
|
||
# 计算插值因子
|
||
time_range = next_state.timestamp - prev_state.timestamp
|
||
if time_range <= 0:
|
||
return prev_state
|
||
|
||
t = (target_time - prev_state.timestamp) / time_range
|
||
|
||
# 执行线性插值
|
||
interpolated_pos = tuple(
|
||
prev_state.position[i] + t * (next_state.position[i] - prev_state.position[i])
|
||
for i in range(3)
|
||
)
|
||
|
||
interpolated_rot = tuple(
|
||
prev_state.rotation[i] + t * (next_state.rotation[i] - prev_state.rotation[i])
|
||
for i in range(3)
|
||
)
|
||
|
||
interpolated_scale = tuple(
|
||
prev_state.scale[i] + t * (next_state.scale[i] - prev_state.scale[i])
|
||
for i in range(3)
|
||
)
|
||
|
||
# 创建插值状态
|
||
interpolated_state = NetworkActorState(
|
||
actor_id=prev_state.actor_id,
|
||
position=interpolated_pos,
|
||
rotation=interpolated_rot,
|
||
scale=interpolated_scale,
|
||
active_animations=prev_state.active_animations, # 简化处理
|
||
timestamp=target_time,
|
||
network_id=prev_state.network_id
|
||
)
|
||
|
||
return interpolated_state
|
||
|
||
def clear(self) -> None:
|
||
"""清空缓冲区"""
|
||
self.buffer.clear()
|
||
|
||
class AnimationNetworkSync(DirectObject):
|
||
"""
|
||
动画网络同步系统
|
||
支持多人环境中动画状态的同步
|
||
"""
|
||
|
||
def __init__(self, world, core_system):
|
||
"""
|
||
初始化网络同步系统
|
||
:param world: 世界对象
|
||
:param core_system: 核心动画系统
|
||
"""
|
||
super().__init__()
|
||
self.world = world
|
||
self.core_system = core_system
|
||
self.config = NetworkSyncConfig()
|
||
self.network_states: Dict[str, NetworkActorState] = {}
|
||
self.state_buffers: Dict[str, NetworkSyncBuffer] = {}
|
||
self.sync_callbacks: Dict[str, List[Callable]] = {}
|
||
self.is_connected = False
|
||
self.last_sync_time = 0.0
|
||
self.sync_task = None
|
||
self.network_id_counter = 0
|
||
|
||
print("动画网络同步系统初始化完成")
|
||
|
||
def set_network_mode(self, mode: NetworkMode) -> None:
|
||
"""
|
||
设置网络模式
|
||
:param mode: 网络模式
|
||
"""
|
||
self.config.mode = mode
|
||
if mode != NetworkMode.DISABLED:
|
||
self.is_connected = True
|
||
self.start_sync_task()
|
||
else:
|
||
self.is_connected = False
|
||
self.stop_sync_task()
|
||
print(f"网络模式设置为: {mode.name}")
|
||
|
||
def set_sync_mode(self, mode: SyncMode) -> None:
|
||
"""
|
||
设置同步模式
|
||
:param mode: 同步模式
|
||
"""
|
||
self.config.sync_mode = mode
|
||
print(f"同步模式设置为: {mode.name}")
|
||
|
||
def configure_sync(self, sync_interval: float = 0.1, interpolation_mode: str = 'linear',
|
||
enable_compression: bool = True) -> None:
|
||
"""
|
||
配置同步参数
|
||
:param sync_interval: 同步间隔
|
||
:param interpolation_mode: 插值模式
|
||
:param enable_compression: 是否启用压缩
|
||
"""
|
||
self.config.sync_interval = sync_interval
|
||
self.config.interpolation_mode = interpolation_mode
|
||
self.config.enable_compression = enable_compression
|
||
print(f"同步配置更新: 间隔={sync_interval}s, 插值={interpolation_mode}, 压缩={enable_compression}")
|
||
|
||
def register_actor_for_sync(self, actor_id: str, network_id: Optional[str] = None) -> str:
|
||
"""
|
||
注册Actor以进行网络同步
|
||
:param actor_id: Actor ID
|
||
:param network_id: 网络ID,如果为None则自动生成
|
||
:return: 网络ID
|
||
"""
|
||
if network_id is None:
|
||
network_id = f"net_{self.network_id_counter}"
|
||
self.network_id_counter += 1
|
||
|
||
# 初始化状态缓冲区
|
||
self.state_buffers[actor_id] = NetworkSyncBuffer(self.config.max_buffer_size)
|
||
|
||
print(f"Actor {actor_id} 已注册网络同步 (网络ID: {network_id})")
|
||
return network_id
|
||
|
||
def unregister_actor_from_sync(self, actor_id: str) -> None:
|
||
"""
|
||
取消Actor的网络同步注册
|
||
:param actor_id: Actor ID
|
||
"""
|
||
if actor_id in self.state_buffers:
|
||
self.state_buffers[actor_id].clear()
|
||
del self.state_buffers[actor_id]
|
||
|
||
if actor_id in self.network_states:
|
||
del self.network_states[actor_id]
|
||
|
||
print(f"Actor {actor_id} 已取消网络同步注册")
|
||
|
||
def _capture_actor_state(self, actor_id: str) -> Optional[NetworkActorState]:
|
||
"""
|
||
捕获Actor当前状态
|
||
:param actor_id: Actor ID
|
||
:return: 状态数据
|
||
"""
|
||
actor_info = self.core_system.actors.get(actor_id)
|
||
if not actor_info:
|
||
return None
|
||
|
||
actor = actor_info.actor
|
||
|
||
# 获取基本变换
|
||
position = actor.getPos().toTuple()
|
||
rotation = actor.getHpr().toTuple()
|
||
scale = actor.getScale().toTuple()
|
||
|
||
# 获取活动动画状态
|
||
active_animations = {}
|
||
for anim_name in actor_info.active_animations:
|
||
if anim_name in actor_info.animations:
|
||
anim_info = actor_info.animations[anim_name]
|
||
control = actor.getAnimControl(anim_name) if anim_name in actor.getAnimNames() else None
|
||
|
||
active_animations[anim_name] = {
|
||
'frame': anim_info.current_frame,
|
||
'play_rate': anim_info.play_rate,
|
||
'loop': anim_info.loop,
|
||
'state': anim_info.state.name,
|
||
'blend_weight': anim_info.blend_weight
|
||
}
|
||
|
||
# 创建状态对象
|
||
state = NetworkActorState(
|
||
actor_id=actor_id,
|
||
position=position,
|
||
rotation=rotation,
|
||
scale=scale,
|
||
active_animations=active_animations,
|
||
timestamp=globalClock.getRealTime()
|
||
)
|
||
|
||
# 计算校验和
|
||
state.checksum = self._calculate_checksum(state)
|
||
|
||
return state
|
||
|
||
def _calculate_checksum(self, state: NetworkActorState) -> str:
|
||
"""
|
||
计算状态校验和
|
||
:param state: 状态数据
|
||
:return: 校验和
|
||
"""
|
||
# 创建用于校验的数据
|
||
checksum_data = {
|
||
'position': state.position,
|
||
'rotation': state.rotation,
|
||
'scale': state.scale,
|
||
'active_animations': state.active_animations,
|
||
'timestamp': state.timestamp
|
||
}
|
||
|
||
# 生成JSON字符串并计算MD5
|
||
data_str = json.dumps(checksum_data, sort_keys=True)
|
||
return hashlib.md5(data_str.encode()).hexdigest()
|
||
|
||
def _validate_checksum(self, state: NetworkActorState) -> bool:
|
||
"""
|
||
验证状态校验和
|
||
:param state: 状态数据
|
||
:return: 是否有效
|
||
"""
|
||
expected_checksum = state.checksum
|
||
state.checksum = "" # 临时清空校验和
|
||
actual_checksum = self._calculate_checksum(state)
|
||
state.checksum = expected_checksum # 恢复校验和
|
||
return expected_checksum == actual_checksum
|
||
|
||
def send_actor_state(self, actor_id: str) -> None:
|
||
"""
|
||
发送Actor状态(服务器模式)
|
||
:param actor_id: Actor ID
|
||
"""
|
||
if not self.is_connected or self.config.mode != NetworkMode.SERVER:
|
||
return
|
||
|
||
# 捕获当前状态
|
||
state = self._capture_actor_state(actor_id)
|
||
if not state:
|
||
return
|
||
|
||
# 发送状态(在实际实现中,这里会通过网络发送)
|
||
self._broadcast_actor_state(state)
|
||
|
||
def _broadcast_actor_state(self, state: NetworkActorState) -> None:
|
||
"""
|
||
广播Actor状态
|
||
:param state: 状态数据
|
||
"""
|
||
# 在实际实现中,这里会通过网络发送数据
|
||
# 为简化,我们直接更新本地网络状态
|
||
self.network_states[state.actor_id] = state
|
||
|
||
# 添加到缓冲区用于插值
|
||
if state.actor_id in self.state_buffers:
|
||
self.state_buffers[state.actor_id].add_state(state)
|
||
|
||
print(f"广播Actor {state.actor_id} 状态更新 (时间: {state.timestamp:.3f})")
|
||
|
||
def receive_actor_state(self, state_data: Dict[str, Any]) -> None:
|
||
"""
|
||
接收Actor状态(客户端模式)
|
||
:param state_data: 状态数据
|
||
"""
|
||
if not self.is_connected or self.config.mode != NetworkMode.CLIENT:
|
||
return
|
||
|
||
# 反序列化状态
|
||
state = NetworkActorState(
|
||
actor_id=state_data['actor_id'],
|
||
position=tuple(state_data['position']),
|
||
rotation=tuple(state_data['rotation']),
|
||
scale=tuple(state_data['scale']),
|
||
active_animations=state_data['active_animations'],
|
||
timestamp=state_data['timestamp'],
|
||
network_id=state_data.get('network_id', ''),
|
||
checksum=state_data.get('checksum', '')
|
||
)
|
||
|
||
# 验证校验和
|
||
if not self._validate_checksum(state):
|
||
print(f"警告: Actor {state.actor_id} 状态校验和不匹配,丢弃数据")
|
||
return
|
||
|
||
# 存储接收到的状态
|
||
self.network_states[state.actor_id] = state
|
||
|
||
# 添加到缓冲区用于插值
|
||
if state.actor_id in self.state_buffers:
|
||
self.state_buffers[state.actor_id].add_state(state)
|
||
|
||
print(f"接收Actor {state.actor_id} 状态更新 (时间: {state.timestamp:.3f})")
|
||
|
||
def _apply_actor_state(self, actor_id: str, state: NetworkActorState) -> None:
|
||
"""
|
||
应用Actor状态
|
||
:param actor_id: Actor ID
|
||
:param state: 状态数据
|
||
"""
|
||
actor_info = self.core_system.actors.get(actor_id)
|
||
if not actor_info:
|
||
return
|
||
|
||
actor = actor_info.actor
|
||
|
||
# 应用变换
|
||
actor.setPos(*state.position)
|
||
actor.setHpr(*state.rotation)
|
||
actor.setScale(*state.scale)
|
||
|
||
# 应用动画状态
|
||
for anim_name, anim_state in state.active_animations.items():
|
||
if anim_name in actor.getAnimNames():
|
||
# 设置动画帧
|
||
if 'frame' in anim_state:
|
||
actor.pose(anim_name, anim_state['frame'])
|
||
|
||
# 设置播放速度
|
||
if 'play_rate' in anim_state:
|
||
actor.setPlayRate(anim_state['play_rate'], anim_name)
|
||
|
||
# 恢复播放状态
|
||
if anim_state.get('state') in ['PLAYING', 'LOOPING']:
|
||
if anim_state.get('loop', False):
|
||
actor.loop(anim_name, restart=False)
|
||
else:
|
||
actor.play(anim_name)
|
||
|
||
print(f"应用Actor {actor_id} 网络状态 (时间: {state.timestamp:.3f})")
|
||
|
||
def _apply_interpolated_state(self, actor_id: str, target_time: float) -> None:
|
||
"""
|
||
应用插值状态
|
||
:param actor_id: Actor ID
|
||
:param target_time: 目标时间
|
||
"""
|
||
if actor_id not in self.state_buffers:
|
||
return
|
||
|
||
# 获取插值状态
|
||
interpolated_state = self.state_buffers[actor_id].get_interpolated_state(target_time)
|
||
if interpolated_state:
|
||
self._apply_actor_state(actor_id, interpolated_state)
|
||
|
||
def update_network_sync(self, task) -> int:
|
||
"""
|
||
更新网络同步
|
||
:param task: 任务对象
|
||
"""
|
||
current_time = globalClock.getRealTime()
|
||
|
||
# 检查同步间隔
|
||
if current_time - self.last_sync_time < self.config.sync_interval:
|
||
return task.cont
|
||
|
||
self.last_sync_time = current_time
|
||
|
||
if self.config.mode == NetworkMode.SERVER:
|
||
# 服务器:发送本地Actor状态
|
||
for actor_id in self.core_system.actors.keys():
|
||
self.send_actor_state(actor_id)
|
||
|
||
elif self.config.mode == NetworkMode.CLIENT:
|
||
# 客户端:应用接收到的状态
|
||
current_network_time = globalClock.getRealTime()
|
||
|
||
# 根据同步模式应用状态
|
||
if self.config.sync_mode == SyncMode.INTERPOLATION:
|
||
# 使用插值
|
||
for actor_id in self.network_states.keys():
|
||
self._apply_interpolated_state(actor_id, current_network_time)
|
||
else:
|
||
# 直接应用最新状态
|
||
for actor_id, state in self.network_states.items():
|
||
self._apply_actor_state(actor_id, state)
|
||
|
||
return task.cont
|
||
|
||
def start_sync_task(self) -> None:
|
||
"""
|
||
启动同步任务
|
||
"""
|
||
if self.sync_task is None:
|
||
self.sync_task = self.world.taskMgr.add(
|
||
self.update_network_sync,
|
||
"animationNetworkSyncTask",
|
||
sort=40 # 确保在网络更新之后执行
|
||
)
|
||
print("动画网络同步任务已启动")
|
||
|
||
def stop_sync_task(self) -> None:
|
||
"""
|
||
停止同步任务
|
||
"""
|
||
if self.sync_task:
|
||
self.world.taskMgr.remove(self.sync_task)
|
||
self.sync_task = None
|
||
print("动画网络同步任务已停止")
|
||
|
||
def get_network_stats(self) -> Dict[str, Any]:
|
||
"""
|
||
获取网络同步统计信息
|
||
:return: 统计信息
|
||
"""
|
||
return {
|
||
'mode': self.config.mode.name,
|
||
'sync_mode': self.config.sync_mode.name,
|
||
'connected': self.is_connected,
|
||
'sync_interval': self.config.sync_interval,
|
||
'interpolation_mode': self.config.interpolation_mode,
|
||
'compression_enabled': self.config.enable_compression,
|
||
'network_actors': len(self.network_states),
|
||
'buffered_states': sum(len(buffer.buffer) for buffer in self.state_buffers.values())
|
||
}
|
||
|
||
def add_sync_callback(self, actor_id: str, callback: Callable) -> None:
|
||
"""
|
||
添加同步回调
|
||
:param actor_id: Actor ID
|
||
:param callback: 回调函数
|
||
"""
|
||
if actor_id not in self.sync_callbacks:
|
||
self.sync_callbacks[actor_id] = []
|
||
self.sync_callbacks[actor_id].append(callback)
|
||
|
||
def remove_sync_callback(self, actor_id: str, callback: Callable) -> None:
|
||
"""
|
||
移除同步回调
|
||
:param actor_id: Actor ID
|
||
:param callback: 回调函数
|
||
"""
|
||
if actor_id in self.sync_callbacks and callback in self.sync_callbacks[actor_id]:
|
||
self.sync_callbacks[actor_id].remove(callback)
|
||
|
||
def _trigger_sync_callbacks(self, actor_id: str, state: NetworkActorState) -> None:
|
||
"""
|
||
触发同步回调
|
||
:param actor_id: Actor ID
|
||
:param state: 状态数据
|
||
"""
|
||
if actor_id in self.sync_callbacks:
|
||
for callback in self.sync_callbacks[actor_id]:
|
||
try:
|
||
callback(actor_id, state)
|
||
except Exception as e:
|
||
print(f"执行同步回调失败: {e}")
|
||
|
||
# 使用示例和测试代码
|
||
def example_network_sync_usage(world, core_system):
|
||
"""
|
||
网络同步使用示例
|
||
"""
|
||
print("=== 动画网络同步使用示例 ===")
|
||
|
||
# 创建网络同步系统
|
||
network_sync = AnimationNetworkSync(world, core_system)
|
||
|
||
# 设置为服务器模式
|
||
network_sync.set_network_mode(NetworkMode.SERVER)
|
||
network_sync.set_sync_mode(SyncMode.STATE_SYNC)
|
||
network_sync.configure_sync(sync_interval=0.05, interpolation_mode='linear')
|
||
|
||
# 获取网络统计
|
||
stats = network_sync.get_network_stats()
|
||
print(f"网络统计: {stats}")
|
||
|
||
print("网络同步示例完成")
|
||
return network_sync
|
||
|
||
if __name__ == "__main__":
|
||
print("动画网络同步模块加载完成") |