EG/plugins/user/advanced_skeletal_animation/network.py
2025-12-12 16:16:15 +08:00

553 lines
20 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 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("动画网络同步模块加载完成")