回滚代码

This commit is contained in:
咕谷酱
2025-08-22 14:58:13 +08:00
parent 8c6f7aa0ef
commit b1f0cbfed1
7 changed files with 1114 additions and 290 deletions

View File

@@ -0,0 +1,221 @@
"""
多人游戏数据包清理管理器
基于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}")