EG/plugins/user/matchmaking_system/queue/queue_manager.py
2025-10-30 15:01:29 +08:00

664 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.

"""
队列管理器模块
管理等待匹配的玩家队列
"""
import time
import threading
import uuid
from typing import Dict, Any, List, Optional
from collections import deque
class QueueManager:
"""
队列管理器
管理等待匹配的玩家队列
"""
def __init__(self, plugin):
"""
初始化队列管理器
Args:
plugin: 匹配系统插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 队列配置
self.queue_config = {
"default_queue_timeout": 300.0, # 5分钟
"queue_processing_interval": 2.0,
"max_players_per_match": 100,
"min_players_per_match": 2,
"enable_queue_prioritization": True,
"enable_skill_based_queue": True,
"enable_region_based_queue": True
}
# 队列状态
self.queue_state = {
"total_players": 0,
"active_queues": 0,
"players_matched": 0,
"players_timed_out": 0
}
# 队列存储
self.queues = {} # 按游戏模式分类的队列
self.player_queues = {} # 玩家所在的队列映射
self.queue_lock = threading.RLock()
# 队列统计
self.queue_stats = {
"players_added": 0,
"players_removed": 0,
"matches_created": 0,
"queue_timeouts": 0,
"queue_errors": 0
}
# 回调函数
self.queue_callbacks = {
"player_queued": [],
"player_dequeued": [],
"match_found": [],
"queue_timeout": [],
"queue_error": []
}
# 时间戳记录
self.last_queue_process = 0.0
self.last_cleanup = 0.0
print("✓ 队列管理器已创建")
def initialize(self) -> bool:
"""
初始化队列管理器
Returns:
是否初始化成功
"""
try:
print("正在初始化队列管理器...")
self.initialized = True
print("✓ 队列管理器初始化完成")
return True
except Exception as e:
print(f"✗ 队列管理器初始化失败: {e}")
self.queue_stats["queue_errors"] += 1
import traceback
traceback.print_exc()
return False
def enable(self) -> bool:
"""
启用队列管理器
Returns:
是否启用成功
"""
try:
if not self.initialized:
print("✗ 队列管理器未初始化")
return False
self.enabled = True
print("✓ 队列管理器已启用")
return True
except Exception as e:
print(f"✗ 队列管理器启用失败: {e}")
self.queue_stats["queue_errors"] += 1
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用队列管理器"""
try:
self.enabled = False
# 清空所有队列
with self.queue_lock:
self.queues.clear()
self.player_queues.clear()
print("✓ 队列管理器已禁用")
except Exception as e:
print(f"✗ 队列管理器禁用失败: {e}")
self.queue_stats["queue_errors"] += 1
import traceback
traceback.print_exc()
def finalize(self):
"""清理队列管理器资源"""
try:
# 禁用队列管理器
if self.enabled:
self.disable()
# 清理回调
self.queue_callbacks.clear()
self.initialized = False
print("✓ 队列管理器资源已清理")
except Exception as e:
print(f"✗ 队列管理器资源清理失败: {e}")
import traceback
traceback.print_exc()
def update(self, dt: float):
"""
更新队列管理器状态
Args:
dt: 时间增量(秒)
"""
try:
if not self.enabled:
return
current_time = time.time()
# 定期处理队列
if current_time - self.last_queue_process >= self.queue_config["queue_processing_interval"]:
self.process_queues()
self.last_queue_process = current_time
# 定期清理
if current_time - self.last_cleanup >= 60.0: # 每分钟清理一次
self._cleanup_expired_players(current_time)
self.last_cleanup = current_time
except Exception as e:
print(f"✗ 队列管理器更新失败: {e}")
self.queue_stats["queue_errors"] += 1
import traceback
traceback.print_exc()
def _cleanup_expired_players(self, current_time: float):
"""清理过期玩家"""
try:
expired_players = []
with self.queue_lock:
for player_id, queue_info in self.player_queues.items():
join_time = queue_info["join_time"]
timeout = queue_info.get("timeout", self.queue_config["default_queue_timeout"])
if current_time - join_time > timeout:
expired_players.append(player_id)
# 移除过期玩家
for player_id in expired_players:
self._remove_player_from_queue(player_id, "timeout")
self.queue_stats["queue_timeouts"] += 1
# 触发队列超时回调
self._trigger_queue_callback("queue_timeout", {
"player_id": player_id,
"timestamp": current_time
})
except Exception as e:
print(f"✗ 过期玩家清理失败: {e}")
self.queue_stats["queue_errors"] += 1
def add_players_to_queue(self, player_ids: List[str], queue_params: Dict[str, Any]) -> bool:
"""
添加玩家到队列
Args:
player_ids: 玩家ID列表
queue_params: 队列参数
Returns:
是否添加成功
"""
try:
if not self.enabled:
return False
queue_name = queue_params.get("queue_name", "default")
game_mode = queue_params.get("game_mode", "default")
region = queue_params.get("region", "global")
skill_level = queue_params.get("skill_level", 0)
timeout = queue_params.get("timeout", self.queue_config["default_queue_timeout"])
with self.queue_lock:
# 创建队列(如果不存在)
if queue_name not in self.queues:
self.queues[queue_name] = {
"name": queue_name,
"game_mode": game_mode,
"region": region,
"players": deque(),
"created_time": time.time()
}
self.queue_state["active_queues"] += 1
# 添加玩家到队列
queue = self.queues[queue_name]
for player_id in player_ids:
player_info = {
"player_id": player_id,
"skill_level": skill_level,
"region": region,
"join_time": time.time(),
"timeout": timeout,
"queue_params": queue_params
}
queue["players"].append(player_info)
self.player_queues[player_id] = {
"queue_name": queue_name,
"join_time": time.time(),
"timeout": timeout
}
self.queue_state["total_players"] += len(player_ids)
self.queue_stats["players_added"] += len(player_ids)
# 触发玩家入队回调
self._trigger_queue_callback("player_queued", {
"player_ids": player_ids,
"queue_name": queue_name,
"game_mode": game_mode,
"timestamp": time.time()
})
print(f"{len(player_ids)}名玩家已添加到队列: {queue_name}")
return True
except Exception as e:
print(f"✗ 玩家添加到队列失败: {e}")
self.queue_stats["queue_errors"] += 1
return False
def remove_players_from_queue(self, match_id: str = None, player_ids: List[str] = None) -> bool:
"""
从队列移除玩家
Args:
match_id: 匹配ID可选
player_ids: 玩家ID列表可选
Returns:
是否移除成功
"""
try:
if not self.enabled:
return False
removed_count = 0
if player_ids:
# 移除指定玩家
for player_id in player_ids:
if self._remove_player_from_queue(player_id):
removed_count += 1
else:
# 移除所有玩家(这种情况较少使用)
with self.queue_lock:
player_ids = list(self.player_queues.keys())
for player_id in player_ids:
if self._remove_player_from_queue(player_id):
removed_count += 1
self.queue_stats["players_removed"] += removed_count
# 触发玩家离队回调
if player_ids:
self._trigger_queue_callback("player_dequeued", {
"player_ids": player_ids,
"match_id": match_id,
"timestamp": time.time()
})
print(f"{removed_count}名玩家已从队列移除")
return removed_count > 0
except Exception as e:
print(f"✗ 玩家从队列移除失败: {e}")
self.queue_stats["queue_errors"] += 1
return False
def _remove_player_from_queue(self, player_id: str, reason: str = "manual") -> bool:
"""
从队列移除单个玩家
Args:
player_id: 玩家ID
reason: 移除原因
Returns:
是否移除成功
"""
try:
with self.queue_lock:
if player_id not in self.player_queues:
return False
queue_name = self.player_queues[player_id]["queue_name"]
del self.player_queues[player_id]
# 从队列中移除玩家
if queue_name in self.queues:
queue = self.queues[queue_name]
players = queue["players"]
# 查找并移除玩家
player_found = False
for i, player_info in enumerate(list(players)):
if player_info["player_id"] == player_id:
del players[i]
player_found = True
break
# 如果队列为空,移除队列
if len(players) == 0:
del self.queues[queue_name]
self.queue_state["active_queues"] -= 1
if player_found:
self.queue_state["total_players"] -= 1
return True
return False
except Exception as e:
print(f"✗ 玩家从队列移除失败: {e}")
self.queue_stats["queue_errors"] += 1
return False
def process_queues(self):
"""处理队列(寻找匹配)"""
try:
if not self.enabled:
return
with self.queue_lock:
queue_names = list(self.queues.keys())
for queue_name in queue_names:
self._process_queue(queue_name)
except Exception as e:
print(f"✗ 队列处理失败: {e}")
self.queue_stats["queue_errors"] += 1
def _process_queue(self, queue_name: str):
"""
处理单个队列
Args:
queue_name: 队列名称
"""
try:
with self.queue_lock:
if queue_name not in self.queues:
return
queue = self.queues[queue_name]
players = list(queue["players"])
# 检查是否有足够的玩家进行匹配
min_players = self.queue_config["min_players_per_match"]
max_players = self.queue_config["max_players_per_match"]
if len(players) >= min_players:
# 确定匹配的玩家数量
match_player_count = min(len(players), max_players)
# 选择玩家(简化实现,实际中可能需要考虑技能匹配等因素)
selected_players = players[:match_player_count]
# 创建匹配
if self._create_match(selected_players, queue):
# 从队列中移除已匹配的玩家
for player_info in selected_players:
self._remove_player_from_queue(player_info["player_id"])
self.queue_state["players_matched"] += len(selected_players)
self.queue_stats["matches_created"] += 1
except Exception as e:
print(f"✗ 队列处理失败: {e}")
self.queue_stats["queue_errors"] += 1
def _create_match(self, players: List[Dict[str, Any]], queue: Dict[str, Any]) -> bool:
"""
创建匹配
Args:
players: 玩家列表
queue: 队列信息
Returns:
是否创建成功
"""
try:
# 生成匹配ID
match_id = f"match_{int(time.time() * 1000000)}"
# 提取玩家ID
player_ids = [player["player_id"] for player in players]
# 创建房间
room_id = None
if self.plugin.room_allocator:
room_settings = {
"game_mode": queue["game_mode"],
"region": queue["region"],
"max_players": len(players)
}
room_id = self.plugin.room_allocator.create_room(
room_name=f"Match_{match_id[:8]}",
room_settings=room_settings
)
# 添加玩家到房间
if room_id and self.plugin.room_allocator:
for player_id in player_ids:
self.plugin.room_allocator.add_client_to_room(room_id, player_id)
# 通知匹配管理器匹配完成
if self.plugin.match_manager:
self.plugin.match_manager.complete_match(match_id, room_id or "")
# 触发匹配找到回调
self._trigger_queue_callback("match_found", {
"match_id": match_id,
"room_id": room_id,
"player_ids": player_ids,
"queue_name": queue["name"],
"timestamp": time.time()
})
print(f"✓ 匹配已创建: {match_id} ({len(player_ids)}名玩家)")
return True
except Exception as e:
print(f"✗ 匹配创建失败: {e}")
self.queue_stats["queue_errors"] += 1
return False
def get_queue_info(self, queue_name: str = None) -> Dict[str, Any]:
"""
获取队列信息
Args:
queue_name: 队列名称(可选,如果未指定则返回所有队列信息)
Returns:
队列信息字典
"""
try:
with self.queue_lock:
if queue_name:
if queue_name in self.queues:
queue = self.queues[queue_name]
return {
"name": queue["name"],
"game_mode": queue["game_mode"],
"region": queue["region"],
"player_count": len(queue["players"]),
"created_time": queue["created_time"]
}
else:
return {}
else:
# 返回所有队列信息
queue_info = {}
for name, queue in self.queues.items():
queue_info[name] = {
"name": queue["name"],
"game_mode": queue["game_mode"],
"region": queue["region"],
"player_count": len(queue["players"]),
"created_time": queue["created_time"]
}
return queue_info
except Exception as e:
print(f"✗ 队列信息获取失败: {e}")
self.queue_stats["queue_errors"] += 1
return {}
def get_player_queue_info(self, player_id: str) -> Optional[Dict[str, Any]]:
"""
获取玩家队列信息
Args:
player_id: 玩家ID
Returns:
玩家队列信息或None
"""
try:
with self.queue_lock:
if player_id in self.player_queues:
return self.player_queues[player_id].copy()
else:
return None
except Exception as e:
print(f"✗ 玩家队列信息获取失败: {e}")
self.queue_stats["queue_errors"] += 1
return None
def get_queue_stats(self) -> Dict[str, Any]:
"""
获取队列统计信息
Returns:
队列统计字典
"""
return {
"state": self.queue_state.copy(),
"stats": self.queue_stats.copy(),
"config": self.queue_config.copy(),
"current_queues": len(self.queues),
"current_players": len(self.player_queues)
}
def reset_stats(self):
"""重置队列统计信息"""
try:
self.queue_stats = {
"players_added": 0,
"players_removed": 0,
"matches_created": 0,
"queue_timeouts": 0,
"queue_errors": 0
}
print("✓ 队列统计信息已重置")
except Exception as e:
print(f"✗ 队列统计信息重置失败: {e}")
def set_queue_config(self, config: Dict[str, Any]) -> bool:
"""
设置队列配置
Args:
config: 队列配置字典
Returns:
是否设置成功
"""
try:
self.queue_config.update(config)
print(f"✓ 队列配置已更新: {self.queue_config}")
return True
except Exception as e:
print(f"✗ 队列配置设置失败: {e}")
return False
def get_queue_config(self) -> Dict[str, Any]:
"""
获取队列配置
Returns:
队列配置字典
"""
return self.queue_config.copy()
def _trigger_queue_callback(self, callback_type: str, data: Dict[str, Any]):
"""
触发队列回调
Args:
callback_type: 回调类型
data: 回调数据
"""
try:
if callback_type in self.queue_callbacks:
for callback in self.queue_callbacks[callback_type]:
try:
callback(data)
except Exception as e:
print(f"✗ 队列回调执行失败: {callback_type} - {e}")
except Exception as e:
print(f"✗ 队列回调触发失败: {e}")
def register_queue_callback(self, callback_type: str, callback: callable):
"""
注册队列回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.queue_callbacks:
self.queue_callbacks[callback_type].append(callback)
print(f"✓ 队列回调已注册: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 队列回调注册失败: {e}")
def unregister_queue_callback(self, callback_type: str, callback: callable):
"""
注销队列回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.queue_callbacks:
if callback in self.queue_callbacks[callback_type]:
self.queue_callbacks[callback_type].remove(callback)
print(f"✓ 队列回调已注销: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 队列回调注销失败: {e}")