EG/plugins/user/matchmaking_system/stats/stats_manager.py
2025-12-12 16:16:15 +08:00

657 lines
23 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 json
from typing import Dict, Any, List, Optional
from collections import defaultdict, deque
class StatsManager:
"""
统计数据管理器
收集和分析匹配数据
"""
def __init__(self, plugin):
"""
初始化统计数据管理器
Args:
plugin: 匹配系统插件实例
"""
self.plugin = plugin
self.enabled = False
self.initialized = False
# 统计配置
self.stats_config = {
"enable_statistics": True,
"data_retention_period": 86400, # 24小时
"max_data_points": 10000,
"enable_realtime_stats": True,
"stats_collection_interval": 10.0,
"enable_export": True,
"export_format": "json"
}
# 统计数据存储
self.statistics = {
"match_times": deque(maxlen=1000),
"queue_times": deque(maxlen=1000),
"success_rates": deque(maxlen=1000),
"player_counts": deque(maxlen=1000),
"algorithm_performance": deque(maxlen=1000),
"system_performance": deque(maxlen=1000)
}
# 统计状态
self.stats_state = {
"total_matches": 0,
"successful_matches": 0,
"failed_matches": 0,
"total_players": 0,
"matched_players": 0,
"last_stats_update": 0.0
}
# 历史统计数据
self.historical_stats = {
"daily": defaultdict(dict),
"weekly": defaultdict(dict),
"monthly": defaultdict(dict)
}
# 统计报告
self.stat_reports = {
"last_report": None,
"report_cache": {}
}
# 统计回调
self.stats_callbacks = {
"stats_updated": [],
"report_generated": [],
"data_exported": [],
"stats_error": []
}
# 时间戳记录
self.last_stats_collection = 0.0
self.last_report_generation = 0.0
print("✓ 统计数据管理器已创建")
def initialize(self) -> bool:
"""
初始化统计数据管理器
Returns:
是否初始化成功
"""
try:
print("正在初始化统计数据管理器...")
self.initialized = True
print("✓ 统计数据管理器初始化完成")
return True
except Exception as e:
print(f"✗ 统计数据管理器初始化失败: {e}")
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}")
import traceback
traceback.print_exc()
return False
def disable(self):
"""禁用统计数据管理器"""
try:
self.enabled = False
print("✓ 统计数据管理器已禁用")
except Exception as e:
print(f"✗ 统计数据管理器禁用失败: {e}")
import traceback
traceback.print_exc()
def finalize(self):
"""清理统计数据管理器资源"""
try:
# 禁用统计数据管理器
if self.enabled:
self.disable()
# 清理回调
self.stats_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_stats_collection >= self.stats_config["stats_collection_interval"]:
self._collect_statistics()
self.last_stats_collection = current_time
self.stats_state["last_stats_update"] = current_time
except Exception as e:
print(f"✗ 统计数据管理器更新失败: {e}")
import traceback
traceback.print_exc()
def _collect_statistics(self):
"""收集统计数据"""
try:
current_time = time.time()
stats_data = {}
# 收集匹配管理器统计数据
if self.plugin.match_manager:
match_stats = self.plugin.match_manager.get_match_stats()
match_state = match_stats.get("state", {})
stats_data["active_matches"] = match_state.get("active_matches", 0)
stats_data["successful_matches"] = match_state.get("successful_matches", 0)
stats_data["failed_matches"] = match_state.get("failed_matches", 0)
# 更新总计数
self.stats_state["total_matches"] = (
match_state.get("successful_matches", 0) +
match_state.get("failed_matches", 0)
)
self.stats_state["successful_matches"] = match_state.get("successful_matches", 0)
self.stats_state["failed_matches"] = match_state.get("failed_matches", 0)
# 收集队列管理器统计数据
if self.plugin.queue_manager:
queue_stats = self.plugin.queue_manager.get_queue_stats()
queue_state = queue_stats.get("state", {})
stats_data["total_players"] = queue_state.get("total_players", 0)
stats_data["players_matched"] = queue_state.get("players_matched", 0)
stats_data["players_timed_out"] = queue_state.get("players_timed_out", 0)
# 更新总计数
self.stats_state["total_players"] = queue_state.get("total_players", 0)
self.stats_state["matched_players"] = queue_state.get("players_matched", 0)
# 收集算法管理器统计数据
if self.plugin.algorithm_manager:
algorithm_stats = self.plugin.algorithm_manager.get_algorithm_stats()
algorithm_state = algorithm_stats.get("state", {})
stats_data["total_algorithms"] = algorithm_state.get("total_matches", 0)
stats_data["successful_algorithms"] = algorithm_state.get("successful_matches", 0)
stats_data["average_match_quality"] = algorithm_state.get("average_match_quality", 0)
# 记录算法性能
self.statistics["algorithm_performance"].append(
(current_time, stats_data["average_match_quality"])
)
# 收集房间分配器统计数据
if self.plugin.room_allocator:
room_stats = self.plugin.room_allocator.get_room_stats()
room_state = room_stats.get("state", {})
stats_data["active_rooms"] = room_state.get("active_rooms", 0)
stats_data["total_rooms"] = room_state.get("total_rooms", 0)
# 记录统计数据
self.statistics["player_counts"].append((current_time, stats_data.get("total_players", 0)))
# 触发统计更新回调
self._trigger_stats_callback("stats_updated", {
"stats_data": stats_data,
"timestamp": current_time
})
except Exception as e:
print(f"✗ 统计数据收集失败: {e}")
self._trigger_stats_callback("stats_error", {
"error": str(e),
"timestamp": time.time()
})
def record_match_time(self, match_time: float):
"""
记录匹配时间
Args:
match_time: 匹配时间(秒)
"""
try:
current_time = time.time()
self.statistics["match_times"].append((current_time, match_time))
except Exception as e:
print(f"✗ 匹配时间记录失败: {e}")
def record_queue_time(self, queue_time: float):
"""
记录队列时间
Args:
queue_time: 队列时间(秒)
"""
try:
current_time = time.time()
self.statistics["queue_times"].append((current_time, queue_time))
except Exception as e:
print(f"✗ 队列时间记录失败: {e}")
def record_success_rate(self, success_rate: float):
"""
记录成功率
Args:
success_rate: 成功率(百分比)
"""
try:
current_time = time.time()
self.statistics["success_rates"].append((current_time, success_rate))
except Exception as e:
print(f"✗ 成功率记录失败: {e}")
def get_realtime_stats(self) -> Dict[str, Any]:
"""
获取实时统计数据
Returns:
实时统计数据字典
"""
try:
realtime_stats = {
"current_time": time.time(),
"state": self.stats_state.copy(),
"statistics": {}
}
# 获取最新的统计数据
for stat_name, stat_data in self.statistics.items():
if stat_data:
realtime_stats["statistics"][stat_name] = list(stat_data)[-10:] # 最近10个数据点
else:
realtime_stats["statistics"][stat_name] = []
return realtime_stats
except Exception as e:
print(f"✗ 实时统计数据获取失败: {e}")
return {}
def generate_report(self, report_type: str = "summary", time_range: str = "24h") -> Dict[str, Any]:
"""
生成统计报告
Args:
report_type: 报告类型summary, detailed, custom
time_range: 时间范围24h, 7d, 30d
Returns:
统计报告字典
"""
try:
current_time = time.time()
report = {
"report_type": report_type,
"time_range": time_range,
"generated_time": current_time,
"data": {}
}
# 根据报告类型生成不同内容
if report_type == "summary":
report["data"] = self._generate_summary_report()
elif report_type == "detailed":
report["data"] = self._generate_detailed_report()
else:
report["data"] = self._generate_custom_report(report_type)
self.stat_reports["last_report"] = report
self.stat_reports["report_cache"][f"{report_type}_{time_range}"] = report
# 触发报告生成回调
self._trigger_stats_callback("report_generated", {
"report": report,
"timestamp": current_time
})
return report
except Exception as e:
print(f"✗ 统计报告生成失败: {e}")
self._trigger_stats_callback("stats_error", {
"error": str(e),
"timestamp": time.time()
})
return {}
def _generate_summary_report(self) -> Dict[str, Any]:
"""生成摘要报告"""
try:
summary = {
"total_matches": self.stats_state["total_matches"],
"successful_matches": self.stats_state["successful_matches"],
"failed_matches": self.stats_state["failed_matches"],
"match_success_rate": 0.0,
"total_players": self.stats_state["total_players"],
"matched_players": self.stats_state["matched_players"],
"player_match_rate": 0.0,
"average_match_time": 0.0,
"average_queue_time": 0.0
}
# 计算成功率
if self.stats_state["total_matches"] > 0:
summary["match_success_rate"] = (
self.stats_state["successful_matches"] / self.stats_state["total_matches"]
) * 100
# 计算玩家匹配率
if self.stats_state["total_players"] > 0:
summary["player_match_rate"] = (
self.stats_state["matched_players"] / self.stats_state["total_players"]
) * 100
# 计算平均匹配时间
if self.statistics["match_times"]:
times = [t[1] for t in self.statistics["match_times"]]
summary["average_match_time"] = sum(times) / len(times)
# 计算平均队列时间
if self.statistics["queue_times"]:
times = [t[1] for t in self.statistics["queue_times"]]
summary["average_queue_time"] = sum(times) / len(times)
return summary
except Exception as e:
print(f"✗ 摘要报告生成失败: {e}")
return {}
def _generate_detailed_report(self) -> Dict[str, Any]:
"""生成详细报告"""
try:
detailed = {
"summary": self._generate_summary_report(),
"trends": self._generate_trend_data(),
"performance": self._generate_performance_data(),
"historical": self._get_historical_data()
}
return detailed
except Exception as e:
print(f"✗ 详细报告生成失败: {e}")
return {}
def _generate_custom_report(self, report_type: str) -> Dict[str, Any]:
"""生成自定义报告"""
try:
# 简化实现,返回摘要报告
return self._generate_summary_report()
except Exception as e:
print(f"✗ 自定义报告生成失败: {e}")
return {}
def _generate_trend_data(self) -> Dict[str, Any]:
"""生成趋势数据"""
try:
trends = {}
# 匹配趋势
if self.statistics["match_times"]:
recent_matches = list(self.statistics["match_times"])[-100:] # 最近100个数据点
trends["match_times"] = [t[1] for t in recent_matches]
# 成功率趋势
if self.statistics["success_rates"]:
recent_rates = list(self.statistics["success_rates"])[-100:]
trends["success_rates"] = [r[1] for r in recent_rates]
# 玩家数量趋势
if self.statistics["player_counts"]:
recent_players = list(self.statistics["player_counts"])[-100:]
trends["player_counts"] = [p[1] for p in recent_players]
return trends
except Exception as e:
print(f"✗ 趋势数据生成失败: {e}")
return {}
def _generate_performance_data(self) -> Dict[str, Any]:
"""生成性能数据"""
try:
performance = {}
# 算法性能
if self.statistics["algorithm_performance"]:
recent_performance = list(self.statistics["algorithm_performance"])[-100:]
performance["algorithm_performance"] = [p[1] for p in recent_performance]
# 系统性能
if self.statistics["system_performance"]:
recent_system = list(self.statistics["system_performance"])[-100:]
performance["system_performance"] = [s[1] for s in recent_system]
return performance
except Exception as e:
print(f"✗ 性能数据生成失败: {e}")
return {}
def _get_historical_data(self) -> Dict[str, Any]:
"""获取历史数据"""
try:
return {
"daily": dict(self.historical_stats["daily"]),
"weekly": dict(self.historical_stats["weekly"]),
"monthly": dict(self.historical_stats["monthly"])
}
except Exception as e:
print(f"✗ 历史数据获取失败: {e}")
return {}
def export_stats(self, file_path: str, format: str = "json") -> bool:
"""
导出统计数据
Args:
file_path: 导出文件路径
format: 导出格式json, csv
Returns:
是否导出成功
"""
try:
import os
# 创建导出目录
export_dir = os.path.dirname(file_path)
if not os.path.exists(export_dir):
os.makedirs(export_dir)
# 生成统计数据
stats_data = {
"statistics": self.get_realtime_stats(),
"reports": self.stat_reports,
"config": self.stats_config
}
if format.lower() == "json":
# 导出为JSON格式
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(stats_data, f, indent=2, ensure_ascii=False, default=str)
else:
# 默认导出为JSON格式
with open(file_path, 'w', encoding='utf-8') as f:
json.dump(stats_data, f, indent=2, ensure_ascii=False, default=str)
# 触发数据导出回调
self._trigger_stats_callback("data_exported", {
"file_path": file_path,
"format": format,
"timestamp": time.time()
})
print(f"✓ 统计数据已导出到: {file_path}")
return True
except Exception as e:
print(f"✗ 统计数据导出失败: {e}")
self._trigger_stats_callback("stats_error", {
"error": str(e),
"timestamp": time.time()
})
return False
def get_stats_config(self) -> Dict[str, Any]:
"""
获取统计配置
Returns:
统计配置字典
"""
return self.stats_config.copy()
def set_stats_config(self, config: Dict[str, Any]) -> bool:
"""
设置统计配置
Args:
config: 统计配置字典
Returns:
是否设置成功
"""
try:
self.stats_config.update(config)
print(f"✓ 统计配置已更新: {self.stats_config}")
return True
except Exception as e:
print(f"✗ 统计配置设置失败: {e}")
return False
def get_stats_state(self) -> Dict[str, Any]:
"""
获取统计状态
Returns:
统计状态字典
"""
return self.stats_state.copy()
def reset_stats(self):
"""重置统计数据"""
try:
# 重置统计数据
for stat_name in self.statistics:
self.statistics[stat_name].clear()
# 重置状态
self.stats_state = {
"total_matches": 0,
"successful_matches": 0,
"failed_matches": 0,
"total_players": 0,
"matched_players": 0,
"last_stats_update": 0.0
}
print("✓ 统计数据已重置")
except Exception as e:
print(f"✗ 统计数据重置失败: {e}")
def _trigger_stats_callback(self, callback_type: str, data: Dict[str, Any]):
"""
触发统计回调
Args:
callback_type: 回调类型
data: 回调数据
"""
try:
if callback_type in self.stats_callbacks:
for callback in self.stats_callbacks[callback_type]:
try:
callback(data)
except Exception as e:
print(f"✗ 统计回调执行失败: {callback_type} - {e}")
except Exception as e:
print(f"✗ 统计回调触发失败: {e}")
def register_stats_callback(self, callback_type: str, callback: callable):
"""
注册统计回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.stats_callbacks:
self.stats_callbacks[callback_type].append(callback)
print(f"✓ 统计回调已注册: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 统计回调注册失败: {e}")
def unregister_stats_callback(self, callback_type: str, callback: callable):
"""
注销统计回调
Args:
callback_type: 回调类型
callback: 回调函数
"""
try:
if callback_type in self.stats_callbacks:
if callback in self.stats_callbacks[callback_type]:
self.stats_callbacks[callback_type].remove(callback)
print(f"✓ 统计回调已注销: {callback_type}")
else:
print(f"✗ 无效的回调类型: {callback_type}")
except Exception as e:
print(f"✗ 统计回调注销失败: {e}")