Files
g0v0-server/app/signalr/hub/multiplayer_packet_cleaner.py
2025-08-22 14:58:13 +08:00

222 lines
8.4 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.
"""
多人游戏数据包清理管理器
基于osu-server源码实现的数据包清理逻辑
"""
from __future__ import annotations
import asyncio
from datetime import UTC, datetime, timedelta
from typing import Dict, List, Optional, Set
from collections import defaultdict
import logging
logger = logging.getLogger(__name__)
class MultiplayerPacketCleaner:
"""多人游戏数据包清理管理器基于osu源码设计"""
def __init__(self):
# 待清理的数据包队列
self.cleanup_queue: Dict[int, List[Dict]] = defaultdict(list)
# 清理任务映射
self.cleanup_tasks: Dict[int, asyncio.Task] = {}
# 延迟清理时间(秒)
self.cleanup_delay = 5.0
# 强制清理时间(秒)
self.force_cleanup_delay = 30.0
async def schedule_cleanup(self, room_id: int, packet_data: Dict):
"""安排数据包清理参考osu源码的清理调度"""
self.cleanup_queue[room_id].append({
**packet_data,
'scheduled_at': datetime.now(UTC),
'room_id': room_id
})
# 如果没有正在进行的清理任务,开始新的清理任务
if room_id not in self.cleanup_tasks or self.cleanup_tasks[room_id].done():
self.cleanup_tasks[room_id] = asyncio.create_task(
self._delayed_cleanup_task(room_id)
)
logger.debug(f"[PacketCleaner] Scheduled cleanup task for room {room_id}")
async def _delayed_cleanup_task(self, room_id: int):
"""延迟清理任务类似osu源码的延迟清理机制"""
try:
# 等待延迟时间
await asyncio.sleep(self.cleanup_delay)
# 执行清理
await self._execute_cleanup(room_id)
except asyncio.CancelledError:
logger.debug(f"[PacketCleaner] Cleanup task for room {room_id} was cancelled")
raise
except Exception as e:
logger.error(f"[PacketCleaner] Error during cleanup for room {room_id}: {e}")
async def _execute_cleanup(self, room_id: int):
"""执行实际的清理操作"""
if room_id not in self.cleanup_queue:
return
packets_to_clean = self.cleanup_queue.pop(room_id, [])
if not packets_to_clean:
return
logger.info(f"[PacketCleaner] Cleaning {len(packets_to_clean)} packets for room {room_id}")
# 按类型分组处理清理
score_packets = []
state_packets = []
leaderboard_packets = []
for packet in packets_to_clean:
packet_type = packet.get('type', 'unknown')
if packet_type == 'score':
score_packets.append(packet)
elif packet_type == 'state':
state_packets.append(packet)
elif packet_type == 'leaderboard':
leaderboard_packets.append(packet)
# 清理分数数据包
if score_packets:
await self._cleanup_score_packets(room_id, score_packets)
# 清理状态数据包
if state_packets:
await self._cleanup_state_packets(room_id, state_packets)
# 清理排行榜数据包
if leaderboard_packets:
await self._cleanup_leaderboard_packets(room_id, leaderboard_packets)
async def _cleanup_score_packets(self, room_id: int, packets: List[Dict]):
"""清理分数相关数据包"""
user_ids = set(p.get('user_id') for p in packets if p.get('user_id'))
logger.debug(f"[PacketCleaner] Cleaning score packets for {len(user_ids)} users in room {room_id}")
# 这里可以添加具体的清理逻辑,比如:
# - 清理过期的分数帧
# - 压缩历史分数数据
# - 清理缓存
async def _cleanup_state_packets(self, room_id: int, packets: List[Dict]):
"""清理状态相关数据包"""
logger.debug(f"[PacketCleaner] Cleaning {len(packets)} state packets for room {room_id}")
# 这里可以添加状态清理逻辑,比如:
# - 清理过期状态数据
# - 重置用户状态缓存
async def _cleanup_leaderboard_packets(self, room_id: int, packets: List[Dict]):
"""清理排行榜相关数据包"""
logger.debug(f"[PacketCleaner] Cleaning {len(packets)} leaderboard packets for room {room_id}")
# 这里可以添加排行榜清理逻辑
async def force_cleanup(self, room_id: int):
"""强制立即清理指定房间的数据包"""
# 取消延迟清理任务
if room_id in self.cleanup_tasks:
task = self.cleanup_tasks.pop(room_id)
if not task.done():
task.cancel()
try:
await task
except asyncio.CancelledError:
pass
# 立即执行清理
await self._execute_cleanup(room_id)
logger.info(f"[PacketCleaner] Force cleaned packets for room {room_id}")
async def cleanup_all_for_room(self, room_id: int):
"""清理房间的所有数据包(房间结束时调用)"""
await self.force_cleanup(room_id)
# 清理任务引用
self.cleanup_tasks.pop(room_id, None)
self.cleanup_queue.pop(room_id, None)
logger.info(f"[PacketCleaner] Completed full cleanup for room {room_id}")
async def cleanup_expired_packets(self):
"""定期清理过期的数据包"""
current_time = datetime.now(UTC)
expired_rooms = []
for room_id, packets in self.cleanup_queue.items():
# 查找超过强制清理时间的数据包
expired_packets = [
p for p in packets
if (current_time - p['scheduled_at']).total_seconds() > self.force_cleanup_delay
]
if expired_packets:
expired_rooms.append(room_id)
# 强制清理过期数据包
for room_id in expired_rooms:
await self.force_cleanup(room_id)
def get_cleanup_stats(self) -> Dict:
"""获取清理统计信息"""
return {
'active_cleanup_tasks': len([t for t in self.cleanup_tasks.values() if not t.done()]),
'pending_packets': sum(len(packets) for packets in self.cleanup_queue.values()),
'rooms_with_pending_cleanup': len(self.cleanup_queue),
}
# 全局实例
packet_cleaner = MultiplayerPacketCleaner()
class GameSessionCleaner:
"""游戏会话清理器参考osu源码的会话管理"""
@staticmethod
async def cleanup_game_session(room_id: int, game_completed: bool = False):
"""清理游戏会话数据(每局游戏结束后调用)"""
try:
# 安排数据包清理
await packet_cleaner.schedule_cleanup(room_id, {
'type': 'game_session_end',
'completed': game_completed,
'timestamp': datetime.now(UTC).isoformat()
})
logger.info(f"[GameSessionCleaner] Scheduled cleanup for game session in room {room_id} (completed: {game_completed})")
except Exception as e:
logger.error(f"[GameSessionCleaner] Failed to cleanup game session for room {room_id}: {e}")
@staticmethod
async def cleanup_user_session(room_id: int, user_id: int):
"""清理单个用户的会话数据"""
try:
await packet_cleaner.schedule_cleanup(room_id, {
'type': 'user_session_end',
'user_id': user_id,
'timestamp': datetime.now(UTC).isoformat()
})
logger.debug(f"[GameSessionCleaner] Scheduled cleanup for user {user_id} in room {room_id}")
except Exception as e:
logger.error(f"[GameSessionCleaner] Failed to cleanup user session {user_id} in room {room_id}: {e}")
@staticmethod
async def cleanup_room_fully(room_id: int):
"""完全清理房间数据(房间关闭时调用)"""
try:
await packet_cleaner.cleanup_all_for_room(room_id)
logger.info(f"[GameSessionCleaner] Completed full room cleanup for {room_id}")
except Exception as e:
logger.error(f"[GameSessionCleaner] Failed to fully cleanup room {room_id}: {e}")