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

625 lines
22 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.

"""
Morphing和变形动画插件 - 网络同步模块
提供多人环境中动画状态的同步功能
"""
import json
import time
from typing import Dict, List, Tuple, 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
# 导入核心模块
from .core import MorphingCore
# 定义常量
NETWORK_SETTINGS = {
'sync_interval': 0.1,
'interpolation_buffer': 0.1,
'max_buffer_size': 100,
'enable_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 # 插值同步
class InterpolationMode(Enum):
"""插值模式枚举"""
LINEAR = 0 # 线性插值
SPLINE = 1 # 样条插值
HERMITE = 2 # 埃尔米特插值
@dataclass
class NetworkState:
"""网络状态数据类"""
model_id: str
morph_weights: Dict[str, float] # {target_name: weight}
timestamp: float
sequence_number: int
@dataclass
class StateBufferEntry:
"""状态缓冲区条目"""
state: NetworkState
received_time: float
@dataclass
class NetworkConfig:
"""网络配置数据类"""
mode: NetworkMode = NetworkMode.DISABLED
sync_mode: SyncMode = SyncMode.STATE_SYNC
sync_interval: float = NETWORK_SETTINGS['sync_interval']
interpolation_mode: InterpolationMode = InterpolationMode.LINEAR
enable_compression: bool = NETWORK_SETTINGS['enable_compression']
max_buffer_size: int = NETWORK_SETTINGS['max_buffer_size']
class AnimationNetworkSync(DirectObject):
"""
动画网络同步系统
处理网络环境中的动画同步
"""
def __init__(self, world, morphing_core: MorphingCore):
"""
初始化动画网络同步系统
:param world: 世界对象
:param morphing_core: Morphing核心系统
"""
super().__init__()
self.world = world
self.morphing_core = morphing_core
self.config = NetworkConfig()
self.network_id_map: Dict[str, str] = {} # {local_model_id: network_id}
self.local_id_map: Dict[str, str] = {} # {network_id: local_model_id}
self.state_buffers: Dict[str, List[StateBufferEntry]] = {} # {network_id: [StateBufferEntry]}
self.sequence_numbers: Dict[str, int] = {} # {network_id: sequence_number}
self.is_syncing = False
self.sync_task = None
self.last_sync_time = 0.0
self.network_interface = None # 网络接口(需要根据具体网络库实现)
print("动画网络同步系统初始化完成")
def set_network_mode(self, mode: NetworkMode) -> None:
"""
设置网络模式
:param mode: 网络模式
"""
self.config.mode = mode
print(f"网络模式设置为: {mode.name}")
def set_sync_mode(self, mode: SyncMode) -> None:
"""
设置同步模式
:param mode: 同步模式
"""
self.config.sync_mode = mode
print(f"同步模式设置为: {mode.name}")
def set_sync_interval(self, interval: float) -> None:
"""
设置同步间隔
:param interval: 同步间隔(秒)
"""
self.config.sync_interval = interval
print(f"同步间隔设置为: {interval}")
def set_interpolation_mode(self, mode: InterpolationMode) -> None:
"""
设置插值模式
:param mode: 插值模式
"""
self.config.interpolation_mode = mode
print(f"插值模式设置为: {mode.name}")
def enable_compression(self, enabled: bool = True) -> None:
"""
启用/禁用压缩
:param enabled: 是否启用
"""
self.config.enable_compression = enabled
print(f"压缩已{'启用' if enabled else '禁用'}")
def register_model_for_sync(self, model_id: str, network_id: Optional[str] = None) -> str:
"""
注册模型以进行网络同步
:param model_id: 本地模型ID
:param network_id: 网络ID如果为None则自动生成
:return: 网络ID
"""
if self.config.mode == NetworkMode.DISABLED:
print("错误: 网络同步已禁用")
return ""
if model_id not in self.morphing_core.model_data:
print(f"错误: 模型 {model_id} 未注册到Morphing核心系统")
return ""
if network_id is None:
network_id = f"net_model_{len(self.network_id_map)}"
# 检查ID是否已存在
if network_id in self.local_id_map:
print(f"警告: 网络ID {network_id} 已存在,将被替换")
old_local_id = self.local_id_map[network_id]
if old_local_id in self.network_id_map:
del self.network_id_map[old_local_id]
# 建立映射关系
self.network_id_map[model_id] = network_id
self.local_id_map[network_id] = model_id
# 初始化状态缓冲区
self.state_buffers[network_id] = []
self.sequence_numbers[network_id] = 0
print(f"模型已注册网络同步: {model_id} -> {network_id}")
return network_id
def unregister_model_from_sync(self, model_id: str) -> bool:
"""
从网络同步中注销模型
:param model_id: 本地模型ID
:return: 是否成功注销
"""
if model_id not in self.network_id_map:
print(f"错误: 模型 {model_id} 未注册网络同步")
return False
network_id = self.network_id_map[model_id]
# 清理相关数据
del self.network_id_map[model_id]
if network_id in self.local_id_map:
del self.local_id_map[network_id]
if network_id in self.state_buffers:
del self.state_buffers[network_id]
if network_id in self.sequence_numbers:
del self.sequence_numbers[network_id]
print(f"模型已注销网络同步: {model_id} -> {network_id}")
return True
def send_model_state(self, model_id: str) -> bool:
"""
发送模型状态
:param model_id: 本地模型ID
:return: 是否成功发送
"""
if self.config.mode not in [NetworkMode.SERVER, NetworkMode.PEER_TO_PEER]:
print("错误: 当前网络模式不支持发送状态")
return False
if model_id not in self.network_id_map:
print(f"错误: 模型 {model_id} 未注册网络同步")
return False
network_id = self.network_id_map[model_id]
# 获取模型的当前状态
morph_weights = {}
if model_id in self.morphing_core.morph_targets:
for target_name, morph_target in self.morphing_core.morph_targets[model_id].items():
# 使用平均权重作为代表值
if morph_target.weights:
morph_weights[target_name] = sum(morph_target.weights) / len(morph_target.weights)
# 创建网络状态
state = NetworkState(
model_id=network_id,
morph_weights=morph_weights,
timestamp=globalClockDelta.getRealNetworkTime(bits=32),
sequence_number=self.sequence_numbers.get(network_id, 0)
)
# 更新序列号
self.sequence_numbers[network_id] = self.sequence_numbers.get(network_id, 0) + 1
# 压缩状态数据(如果启用)
state_data = self._serialize_state(state)
if self.config.enable_compression:
state_data = self._compress_state_data(state_data)
# 发送状态数据(需要根据具体网络库实现)
if self.network_interface:
try:
self.network_interface.send_state(network_id, state_data)
print(f"模型状态已发送: {model_id} -> {network_id}")
return True
except Exception as e:
print(f"发送模型状态时出错: {e}")
return False
else:
# 模拟发送(用于测试)
print(f"模拟发送模型状态: {model_id} -> {network_id}")
print(f" 状态数据: {state_data}")
return True
def receive_model_state(self, network_id: str, state_data: bytes) -> bool:
"""
接收模型状态
:param network_id: 网络ID
:param state_data: 状态数据
:return: 是否成功接收
"""
if self.config.mode not in [NetworkMode.CLIENT, NetworkMode.PEER_TO_PEER]:
print("错误: 当前网络模式不支持接收状态")
return False
if network_id not in self.local_id_map:
print(f"警告: 未知的网络ID {network_id},忽略状态数据")
return False
local_id = self.local_id_map[network_id]
# 解压缩状态数据(如果启用)
if self.config.enable_compression:
state_data = self._decompress_state_data(state_data)
# 反序列化状态
state = self._deserialize_state(state_data)
if not state:
print(f"错误: 无法反序列化状态数据")
return False
# 验证序列号
last_sequence = self.sequence_numbers.get(network_id, -1)
if state.sequence_number <= last_sequence:
print(f"警告: 过期的状态数据 (序列号: {state.sequence_number} <= {last_sequence})")
return False
# 更新序列号
self.sequence_numbers[network_id] = state.sequence_number
# 添加到状态缓冲区
buffer_entry = StateBufferEntry(
state=state,
received_time=globalClock.getRealTime()
)
if network_id not in self.state_buffers:
self.state_buffers[network_id] = []
self.state_buffers[network_id].append(buffer_entry)
# 限制缓冲区大小
if len(self.state_buffers[network_id]) > self.config.max_buffer_size:
self.state_buffers[network_id].pop(0)
# 应用状态(根据同步模式)
if self.config.sync_mode == SyncMode.STATE_SYNC:
self._apply_state_immediately(local_id, state)
elif self.config.sync_mode == SyncMode.INTERPOLATION:
# 插值模式下暂不立即应用,等待插值更新
pass
print(f"模型状态已接收: {network_id} -> {local_id}")
return True
def _serialize_state(self, state: NetworkState) -> bytes:
"""
序列化网络状态
:param state: 网络状态
:return: 序列化后的数据
"""
state_dict = {
'model_id': state.model_id,
'morph_weights': state.morph_weights,
'timestamp': state.timestamp,
'sequence_number': state.sequence_number
}
return json.dumps(state_dict).encode('utf-8')
def _deserialize_state(self, state_data: bytes) -> Optional[NetworkState]:
"""
反序列化网络状态
:param state_data: 状态数据
:return: 网络状态对象
"""
try:
state_dict = json.loads(state_data.decode('utf-8'))
return NetworkState(
model_id=state_dict['model_id'],
morph_weights=state_dict['morph_weights'],
timestamp=state_dict['timestamp'],
sequence_number=state_dict['sequence_number']
)
except Exception as e:
print(f"反序列化状态数据时出错: {e}")
return None
def _compress_state_data(self, state_data: bytes) -> bytes:
"""
压缩状态数据
:param state_data: 状态数据
:return: 压缩后的数据
"""
# 简化实现实际项目中可以使用zlib等压缩库
return state_data
def _decompress_state_data(self, state_data: bytes) -> bytes:
"""
解压缩状态数据
:param state_data: 压缩的状态数据
:return: 解压后的数据
"""
# 简化实现,实际项目中需要对应解压
return state_data
def _apply_state_immediately(self, model_id: str, state: NetworkState) -> None:
"""
立即应用网络状态
:param model_id: 本地模型ID
:param state: 网络状态
"""
# 应用Morph目标权重
for target_name, weight in state.morph_weights.items():
self.morphing_core.set_morph_target_weight(model_id, target_name, weight)
def _interpolate_states(self, network_id: str) -> None:
"""
插值状态
:param network_id: 网络ID
"""
if network_id not in self.state_buffers or len(self.state_buffers[network_id]) < 2:
return
local_id = self.local_id_map[network_id]
buffer = self.state_buffers[network_id]
# 获取当前时间
current_time = globalClock.getRealTime()
interpolation_time = current_time - NETWORK_SETTINGS['interpolation_buffer']
# 找到要插值的两个状态
prev_state = None
next_state = None
for entry in buffer:
if entry.received_time <= interpolation_time:
prev_state = entry.state
else:
next_state = entry.state
break
if not prev_state or not next_state:
return
# 计算插值因子
time_diff = next_state.timestamp - prev_state.timestamp
if time_diff <= 0:
return
t = (interpolation_time - prev_state.timestamp) / time_diff
t = max(0.0, min(1.0, t)) # 限制在0-1范围内
# 根据插值模式调整因子
if self.config.interpolation_mode == InterpolationMode.SPLINE:
t = t * t * (3.0 - 2.0 * t)
elif self.config.interpolation_mode == InterpolationMode.HERMITE:
t = t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
# 插值Morph目标权重
all_targets = set(prev_state.morph_weights.keys()) | set(next_state.morph_weights.keys())
for target_name in all_targets:
prev_weight = prev_state.morph_weights.get(target_name, 0.0)
next_weight = next_state.morph_weights.get(target_name, 0.0)
# 计算插值权重
interpolated_weight = prev_weight * (1.0 - t) + next_weight * t
# 应用权重
self.morphing_core.set_morph_target_weight(local_id, target_name, interpolated_weight)
def start_sync_task(self) -> None:
"""
启动同步任务
"""
if self.is_syncing:
return
self.is_syncing = True
# 启动同步任务
self.sync_task = self.world.taskMgr.add(
self._sync_task,
"morphing_network_sync",
sort=40
)
print("动画网络同步任务已启动")
def stop_sync_task(self) -> None:
"""
停止同步任务
"""
if not self.is_syncing:
return
self.is_syncing = False
# 停止同步任务
if self.sync_task:
self.world.taskMgr.remove(self.sync_task)
self.sync_task = None
print("动画网络同步任务已停止")
def _sync_task(self, task) -> int:
"""
网络同步任务
:param task: 任务对象
:return: 任务状态
"""
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:
# 服务器模式:发送所有注册模型的状态
for model_id in self.network_id_map.keys():
self.send_model_state(model_id)
elif self.config.mode == NetworkMode.CLIENT:
# 客户端模式:接收状态并应用插值(如果启用)
if self.config.sync_mode == SyncMode.INTERPOLATION:
for network_id in self.state_buffers.keys():
self._interpolate_states(network_id)
elif self.config.mode == NetworkMode.PEER_TO_PEER:
# 点对点模式:发送和接收状态
for model_id in self.network_id_map.keys():
self.send_model_state(model_id)
if self.config.sync_mode == SyncMode.INTERPOLATION:
for network_id in self.state_buffers.keys():
self._interpolate_states(network_id)
return task.cont
def get_network_info(self) -> Dict[str, Any]:
"""
获取网络同步信息
:return: 网络信息字典
"""
return {
'mode': self.config.mode.name,
'sync_mode': self.config.sync_mode.name,
'sync_interval': self.config.sync_interval,
'interpolation_mode': self.config.interpolation_mode.name,
'compression_enabled': self.config.enable_compression,
'registered_models': len(self.network_id_map),
'network_ids': list(self.network_id_map.values()),
'sync_status': 'active' if self.is_syncing else 'inactive'
}
def get_model_network_info(self, model_id: str) -> Optional[Dict[str, Any]]:
"""
获取模型网络信息
:param model_id: 本地模型ID
:return: 模型网络信息字典
"""
if model_id not in self.network_id_map:
print(f"错误: 模型 {model_id} 未注册网络同步")
return None
network_id = self.network_id_map[model_id]
return {
'local_id': model_id,
'network_id': network_id,
'sequence_number': self.sequence_numbers.get(network_id, 0),
'buffer_size': len(self.state_buffers.get(network_id, [])),
'last_state': self.state_buffers.get(network_id, [])[-1].state if self.state_buffers.get(network_id) else None
}
def set_network_interface(self, network_interface) -> None:
"""
设置网络接口
:param network_interface: 网络接口对象
"""
self.network_interface = network_interface
print("网络接口已设置")
# 网络接口基类(需要根据具体网络库实现)
class NetworkInterface:
"""
网络接口基类
需要根据具体使用的网络库如PyNetGames、Panda3D的PyClientRepository等进行实现
"""
def __init__(self):
"""初始化网络接口"""
pass
def send_state(self, network_id: str, state_data: bytes) -> bool:
"""
发送状态数据
:param network_id: 网络ID
:param state_data: 状态数据
:return: 是否成功发送
"""
# 需要根据具体网络库实现
raise NotImplementedError("需要在子类中实现send_state方法")
def receive_state(self, network_id: str) -> Optional[bytes]:
"""
接收状态数据
:param network_id: 网络ID
:return: 状态数据
"""
# 需要根据具体网络库实现
raise NotImplementedError("需要在子类中实现receive_state方法")
def connect(self, address: str, port: int) -> bool:
"""
连接到服务器
:param address: 服务器地址
:param port: 服务器端口
:return: 是否成功连接
"""
# 需要根据具体网络库实现
raise NotImplementedError("需要在子类中实现connect方法")
def disconnect(self) -> None:
"""
断开连接
"""
# 需要根据具体网络库实现
raise NotImplementedError("需要在子类中实现disconnect方法")
# 使用示例和测试代码
def example_network_usage(world, morphing_core):
"""
动画网络同步系统使用示例
"""
print("=== 动画网络同步系统使用示例 ===")
# 创建网络同步系统
network_sync = AnimationNetworkSync(world, morphing_core)
# 设置网络模式
network_sync.set_network_mode(NetworkMode.SERVER)
# 设置同步模式
network_sync.set_sync_mode(SyncMode.STATE_SYNC)
# 设置同步间隔
network_sync.set_sync_interval(0.05) # 50ms
# 启用压缩
network_sync.enable_compression(True)
# 启动同步任务
network_sync.start_sync_task()
print("网络同步系统配置完成")
print("=== 动画网络同步系统示例结束 ===\n")
return network_sync
if __name__ == "__main__":
print("动画网络同步模块加载完成")