657 lines
23 KiB
Python
657 lines
23 KiB
Python
"""
|
||
统计数据管理器模块
|
||
收集和分析匹配数据
|
||
"""
|
||
|
||
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}") |