664 lines
22 KiB
Python
664 lines
22 KiB
Python
"""
|
||
队列管理器模块
|
||
管理等待匹配的玩家队列
|
||
"""
|
||
|
||
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}") |