diff --git a/plugins/user/matchmaking_system/README.md b/plugins/user/matchmaking_system/README.md new file mode 100644 index 00000000..8e7530b5 --- /dev/null +++ b/plugins/user/matchmaking_system/README.md @@ -0,0 +1,383 @@ +# 匹配系统插件 + +为EG引擎提供完整的玩家匹配功能,包括队列管理、算法匹配、房间分配、规则配置、实时监控和数据分析等核心功能。 + +## 功能特性 + +### 1. 匹配管理 +- 智能匹配过程管理 +- 多种匹配模式支持 +- 匹配状态跟踪和控制 +- 匹配结果处理和回调 + +### 2. 队列系统 +- 玩家排队管理 +- 队列优先级处理 +- 超时自动清理 +- 实时队列状态监控 + +### 3. 匹配算法 +- 技能匹配算法 +- 快速匹配算法 +- 团队平衡算法 +- 自定义算法支持 +- 匹配质量评估 + +### 4. 房间分配 +- 动态房间创建和销毁 +- 玩家房间分配管理 +- 房间状态跟踪 +- 房间密码保护 + +### 5. 规则配置 +- 灵活的匹配规则系统 +- 游戏模式特定规则 +- 自定义规则集支持 +- 实时规则验证 + +### 6. 实时监控 +- 性能指标收集 +- 实时数据监控 +- 警报系统 +- 系统健康状态检查 + +### 7. 事件系统 +- 事件驱动架构 +- 异步事件处理 +- 事件过滤和优先级 +- 事件监听器系统 + +### 8. 配置管理 +- 持久化配置存储 +- 动态配置更新 +- 配置变更监听 +- 配置导入导出 + +### 9. 可视化编辑器 +- 实时数据展示 +- 配置参数调整 +- 统计图表显示 +- 数据导出功能 + +### 10. 数据统计 +- 实时数据收集 +- 历史数据分析 +- 统计报告生成 +- 数据导出支持 + +## 系统架构 + +``` +匹配系统插件 +├── 核心模块 +│ └── 匹配管理器 (core/match_manager.py) +├── 队列系统 +│ └── 队列管理器 (queue/queue_manager.py) +├── 算法系统 +│ └── 算法管理器 (algorithms/algorithm_manager.py) +├── 房间系统 +│ └── 房间分配器 (rooms/room_allocator.py) +├── 规则系统 +│ └── 规则管理器 (rules/rule_manager.py) +├── 监控系统 +│ └── 匹配监控器 (monitoring/match_monitor.py) +├── 事件系统 +│ └── 事件处理器 (events/event_handler.py) +├── 配置系统 +│ └── 配置管理器 (config/match_config.py) +├── 编辑器系统 +│ └── 匹配编辑器 (editor/match_editor.py) +├── 统计系统 +│ └── 统计管理器 (stats/stats_manager.py) +└── 插件主文件 + └── 插件入口 (plugin.py) +``` + +## 安装和使用 + +### 安装依赖 + +```bash +# 无特殊依赖,使用标准Python库 +``` + +### 配置插件 + +插件配置文件位于 `config/matchmaking_config.json`,包含以下主要配置项: + +```json +{ + "system": { + "enable_matchmaking": true, + "max_concurrent_matches": 1000, + "default_match_timeout": 300.0 + }, + "queue": { + "default_queue_timeout": 300.0, + "queue_processing_interval": 2.0 + }, + "matching": { + "default_algorithm": "skill_based", + "skill_range": 100 + }, + "room": { + "default_max_players": 8, + "enable_room_passwords": true + } +} +``` + +### 启动插件 + +```python +# 初始化插件 +plugin = MatchmakingSystemPlugin(engine) +plugin.initialize() +plugin.enable() + +# 插件会自动处理匹配过程 +``` + +## API参考 + +### 匹配管理 + +```python +# 开始匹配 +match_id = plugin.match_manager.start_matchmaking(player_ids, match_params) + +# 取消匹配 +plugin.match_manager.cancel_matchmaking(match_id) + +# 获取匹配统计 +stats = plugin.match_manager.get_match_stats() +``` + +### 队列管理 + +```python +# 添加玩家到队列 +plugin.queue_manager.add_players_to_queue(player_ids, queue_params) + +# 从队列移除玩家 +plugin.queue_manager.remove_players_from_queue(player_ids=player_ids) + +# 获取队列信息 +queue_info = plugin.queue_manager.get_queue_info(queue_name) +``` + +### 算法管理 + +```python +# 设置活动算法 +plugin.algorithm_manager.set_active_algorithm("skill_based") + +# 运行匹配算法 +result = plugin.algorithm_manager.run_matching_algorithm(players) + +# 更新玩家技能 +plugin.algorithm_manager.update_player_skill(player_id, skill_level) +``` + +### 房间分配 + +```python +# 创建房间 +room_id = plugin.room_allocator.create_room(room_name, room_settings) + +# 添加玩家到房间 +plugin.room_allocator.add_client_to_room(room_id, player_id) + +# 从房间移除玩家 +plugin.room_allocator.remove_client_from_room(room_id, player_id) +``` + +### 规则管理 + +```python +# 应用规则 +is_valid = plugin.rule_manager.apply_rules(players, game_mode) + +# 添加自定义规则集 +plugin.rule_manager.add_custom_rule_set("custom_rules", rules) + +# 设置活动规则集 +plugin.rule_manager.set_active_rules("custom_rules") +``` + +### 监控系统 + +```python +# 获取性能指标 +metrics = plugin.monitor.get_performance_metrics() + +# 获取活动警报 +alerts = plugin.monitor.get_active_alerts() + +# 记录匹配队列时间 +plugin.monitor.record_match_queue_time(queue_time) +``` + +### 事件系统 + +```python +# 发出事件 +plugin.event_handler.emit_event("match_found", payload) + +# 注册事件处理器 +plugin.event_handler.register_event_handler("match_found", handler_func) + +# 注册事件过滤器 +plugin.event_handler.register_event_filter("match_found", filter_func) +``` + +### 配置管理 + +```python +# 获取配置 +config_value = plugin.config_manager.get_config("section", "key", default_value) + +# 设置配置 +plugin.config_manager.set_config("section", "key", value) + +# 保存配置 +plugin.config_manager.save_config() + +# 加载配置 +plugin.config_manager.load_config() +``` + +### 编辑器系统 + +```python +# 显示编辑器 +plugin.editor.show_editor() + +# 隐藏编辑器 +plugin.editor.hide_editor() + +# 切换标签页 +plugin.editor.switch_tab("overview") + +# 更新配置 +plugin.editor.update_config("section", "key", value) +``` + +### 统计系统 + +```python +# 获取实时统计数据 +realtime_stats = plugin.stats_manager.get_realtime_stats() + +# 生成报告 +report = plugin.stats_manager.generate_report("summary", "24h") + +# 导出统计数据 +plugin.stats_manager.export_stats("stats.json") + +# 重置统计数据 +plugin.stats_manager.reset_stats() +``` + +## 匹配流程 + +### 1. 玩家加入队列 +``` +玩家 -> 队列管理器 -> 添加到匹配队列 +``` + +### 2. 队列处理 +``` +队列管理器 -> 算法管理器 -> 运行匹配算法 +``` + +### 3. 匹配创建 +``` +算法管理器 -> 房间分配器 -> 创建匹配房间 +``` + +### 4. 玩家分配 +``` +房间分配器 -> 玩家 -> 加入匹配房间 +``` + +### 5. 匹配完成 +``` +匹配管理器 -> 事件系统 -> 发出匹配完成事件 +``` + +## 性能优化 + +1. **异步处理**:采用多线程和异步I/O提高并发性能 +2. **智能队列**:高效的队列管理和玩家匹配算法 +3. **内存优化**:及时清理无用数据和对象 +4. **缓存机制**:对频繁访问的数据进行缓存 +5. **批量处理**:批量处理队列和匹配操作 + +## 扩展开发 + +### 添加新的匹配算法 + +```python +# 在算法管理器中实现新的匹配算法 +def _run_custom_matching(self, players): + # 实现自定义匹配逻辑 + pass + +# 注册算法 +plugin.algorithm_manager.register_algorithm("custom", _run_custom_matching) +``` + +### 添加新的事件类型 + +```python +# 注册事件处理器 +plugin.event_handler.register_event_handler("custom_event", custom_handler) + +# 发出事件 +plugin.event_handler.emit_event("custom_event", payload) +``` + +### 添加自定义规则 + +```python +# 添加自定义规则集 +plugin.rule_manager.add_custom_rule_set("my_rules", { + "min_players": 2, + "max_players": 8, + "custom_rule": True +}) + +# 应用规则 +plugin.rule_manager.apply_rules(players, "my_rules") +``` + +## 故障排除 + +### 常见问题 + +1. **匹配失败**:检查匹配规则和算法配置 +2. **队列超时**:调整队列超时时间和匹配参数 +3. **性能问题**:检查系统资源使用情况,优化配置 +4. **配置错误**:验证配置文件格式和参数 + +### 日志分析 + +查看日志文件以诊断问题: +``` +logs/matchmaking_system.log +``` + +## 版本信息 + +- 版本:1.0.0 +- 作者:EG Plugin System +- 许可证:MIT + +## 贡献指南 + +欢迎提交Issue和Pull Request来改进这个插件。 + +## 支持 + +如需技术支持,请联系插件维护团队或查看相关文档。 \ No newline at end of file diff --git a/plugins/user/matchmaking_system/algorithms/algorithm_manager.py b/plugins/user/matchmaking_system/algorithms/algorithm_manager.py new file mode 100644 index 00000000..b770cd26 --- /dev/null +++ b/plugins/user/matchmaking_system/algorithms/algorithm_manager.py @@ -0,0 +1,616 @@ +""" +算法管理器模块 +实现不同的匹配算法(技能匹配、快速匹配等) +""" + +import time +import random +from typing import Dict, Any, List, Optional +from collections import defaultdict + +class AlgorithmManager: + """ + 算法管理器 + 实现不同的匹配算法(技能匹配、快速匹配等) + """ + + def __init__(self, plugin): + """ + 初始化算法管理器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 算法配置 + self.algorithm_config = { + "default_algorithm": "skill_based", + "available_algorithms": ["skill_based", "quick", "team_balanced", "custom"], + "skill_based_config": { + "skill_range": 100, # 技能匹配范围 + "max_wait_time": 120.0, # 最大等待时间(秒) + "expand_range_over_time": True, # 随时间扩展匹配范围 + "range_expansion_rate": 10 # 每秒扩展范围 + }, + "team_balanced_config": { + "balance_threshold": 0.1, # 平衡阈值 + "max_balance_attempts": 5 # 最大平衡尝试次数 + }, + "quick_match_config": { + "max_queue_time": 30.0, # 最大排队时间 + "min_players": 2, # 最少玩家数 + "max_players": 8 # 最多玩家数 + } + } + + # 算法状态 + self.algorithm_state = { + "active_algorithm": "skill_based", + "total_matches": 0, + "successful_matches": 0, + "failed_matches": 0, + "average_match_quality": 0.0 + } + + # 玩家数据存储 + self.player_data = {} # 玩家技能、统计数据等 + + # 算法统计 + self.algorithm_stats = { + "algorithms_run": 0, + "matches_created": 0, + "players_matched": 0, + "algorithm_errors": 0 + } + + # 回调函数 + self.algorithm_callbacks = { + "algorithm_selected": [], + "match_quality_calculated": [], + "algorithm_error": [] + } + + # 时间戳记录 + self.last_algorithm_run = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 算法管理器已创建") + + def initialize(self) -> bool: + """ + 初始化算法管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化算法管理器...") + + # 注册默认算法 + self._register_default_algorithms() + + self.initialized = True + print("✓ 算法管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 算法管理器初始化失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _register_default_algorithms(self): + """注册默认算法""" + try: + print("✓ 默认算法已注册") + except Exception as e: + print(f"✗ 默认算法注册失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + + 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.algorithm_stats["algorithm_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用算法管理器""" + try: + self.enabled = False + print("✓ 算法管理器已禁用") + + except Exception as e: + print(f"✗ 算法管理器禁用失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理算法管理器资源""" + try: + # 禁用算法管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.algorithm_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 + + self.last_algorithm_run = time.time() + + except Exception as e: + print(f"✗ 算法管理器更新失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + import traceback + traceback.print_exc() + + def set_active_algorithm(self, algorithm_name: str) -> bool: + """ + 设置活动算法 + + Args: + algorithm_name: 算法名称 + + Returns: + 是否设置成功 + """ + try: + if algorithm_name not in self.algorithm_config["available_algorithms"]: + print(f"✗ 不支持的算法: {algorithm_name}") + return False + + self.algorithm_state["active_algorithm"] = algorithm_name + print(f"✓ 活动算法已设置为: {algorithm_name}") + + # 触发算法选择回调 + self._trigger_algorithm_callback("algorithm_selected", { + "algorithm": algorithm_name, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 活动算法设置失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return False + + def run_matching_algorithm(self, players: List[Dict[str, Any]], algorithm: str = None) -> Optional[List[List[Dict[str, Any]]]]: + """ + 运行匹配算法 + + Args: + players: 玩家列表 + algorithm: 算法名称(可选,默认使用活动算法) + + Returns: + 匹配结果(玩家分组列表)或None + """ + try: + if not self.enabled: + return None + + # 使用指定算法或活动算法 + if algorithm is None: + algorithm = self.algorithm_state["active_algorithm"] + + # 验证算法支持 + if algorithm not in self.algorithm_config["available_algorithms"]: + print(f"✗ 不支持的算法: {algorithm}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + self.algorithm_stats["algorithms_run"] += 1 + + # 根据算法类型运行相应算法 + if algorithm == "skill_based": + result = self._run_skill_based_matching(players) + elif algorithm == "quick": + result = self._run_quick_matching(players) + elif algorithm == "team_balanced": + result = self._run_team_balanced_matching(players) + else: + # 默认使用技能匹配 + result = self._run_skill_based_matching(players) + + if result: + self.algorithm_state["successful_matches"] += 1 + self.algorithm_state["total_matches"] += 1 + self.algorithm_stats["matches_created"] += 1 + + # 计算匹配质量 + match_quality = self._calculate_match_quality(result) + self.algorithm_state["average_match_quality"] = ( + self.algorithm_state["average_match_quality"] * (self.algorithm_state["successful_matches"] - 1) + + match_quality + ) / self.algorithm_state["successful_matches"] + + # 触发匹配质量计算回调 + self._trigger_algorithm_callback("match_quality_calculated", { + "quality": match_quality, + "algorithm": algorithm, + "timestamp": time.time() + }) + + else: + self.algorithm_state["failed_matches"] += 1 + + return result + + except Exception as e: + print(f"✗ 匹配算法运行失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + def _run_skill_based_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]: + """ + 运行基于技能的匹配算法 + + Args: + players: 玩家列表 + + Returns: + 匹配结果或None + """ + try: + if not players: + return None + + config = self.algorithm_config["skill_based_config"] + skill_range = config["skill_range"] + + # 按技能水平排序玩家 + sorted_players = sorted(players, key=lambda p: p.get("skill_level", 0)) + + # 简化实现:将技能相近的玩家分组 + groups = [] + current_group = [sorted_players[0]] + + for i in range(1, len(sorted_players)): + current_player = sorted_players[i] + last_player = current_group[-1] + + # 检查技能差异是否在范围内 + skill_diff = abs(current_player.get("skill_level", 0) - last_player.get("skill_level", 0)) + + if skill_diff <= skill_range and len(current_group) < 8: # 最多8人一组 + current_group.append(current_player) + else: + # 创建新组 + groups.append(current_group) + current_group = [current_player] + + # 添加最后一组 + if current_group: + groups.append(current_group) + + print(f"✓ 技能匹配完成: 创建了 {len(groups)} 个组") + return groups + + except Exception as e: + print(f"✗ 技能匹配算法失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + def _run_quick_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]: + """ + 运行快速匹配算法 + + Args: + players: 玩家列表 + + Returns: + 匹配结果或None + """ + try: + if not players: + return None + + config = self.algorithm_config["quick_match_config"] + min_players = config["min_players"] + max_players = config["max_players"] + + # 简化实现:随机分组 + groups = [] + player_pool = players.copy() + random.shuffle(player_pool) + + while player_pool: + group_size = min(random.randint(min_players, max_players), len(player_pool)) + group = player_pool[:group_size] + groups.append(group) + player_pool = player_pool[group_size:] + + print(f"✓ 快速匹配完成: 创建了 {len(groups)} 个组") + return groups + + except Exception as e: + print(f"✗ 快速匹配算法失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + def _run_team_balanced_matching(self, players: List[Dict[str, Any]]) -> Optional[List[List[Dict[str, Any]]]]: + """ + 运行团队平衡匹配算法 + + Args: + players: 玩家列表 + + Returns: + 匹配结果或None + """ + try: + if not players or len(players) < 2: + return None + + # 简化实现:创建两个平衡的团队 + # 按技能排序 + sorted_players = sorted(players, key=lambda p: p.get("skill_level", 0), reverse=True) + + team1 = [] + team2 = [] + team1_skill = 0 + team2_skill = 0 + + # 贪心算法分配玩家到技能平衡的团队 + for player in sorted_players: + player_skill = player.get("skill_level", 0) + + if team1_skill <= team2_skill: + team1.append(player) + team1_skill += player_skill + else: + team2.append(player) + team2_skill += player_skill + + groups = [team1, team2] if team1 and team2 else [sorted_players] + + print(f"✓ 团队平衡匹配完成: 创建了 {len(groups)} 个组") + return groups + + except Exception as e: + print(f"✗ 团队平衡匹配算法失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + def _calculate_match_quality(self, groups: List[List[Dict[str, Any]]]) -> float: + """ + 计算匹配质量 + + Args: + groups: 玩家分组 + + Returns: + 匹配质量分数(0-1) + """ + try: + if not groups: + return 0.0 + + total_quality = 0.0 + group_count = 0 + + for group in groups: + if len(group) < 2: + continue + + # 计算组内技能差异 + skills = [p.get("skill_level", 0) for p in group] + avg_skill = sum(skills) / len(skills) + variance = sum((s - avg_skill) ** 2 for s in skills) / len(skills) + std_dev = variance ** 0.5 + + # 转换为质量分数(标准差越小质量越高) + max_expected_std = 100.0 # 假设最大预期标准差 + quality = max(0.0, 1.0 - (std_dev / max_expected_std)) + + total_quality += quality + group_count += 1 + + return total_quality / group_count if group_count > 0 else 0.0 + + except Exception as e: + print(f"✗ 匹配质量计算失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return 0.0 + + def update_player_skill(self, player_id: str, skill_level: float, stats: Dict[str, Any] = None): + """ + 更新玩家技能 + + Args: + player_id: 玩家ID + skill_level: 技能等级 + stats: 玩家统计数据 + """ + try: + if player_id not in self.player_data: + self.player_data[player_id] = { + "skill_history": [], + "match_stats": {} + } + + player_info = self.player_data[player_id] + player_info["current_skill"] = skill_level + player_info["skill_history"].append({ + "skill": skill_level, + "timestamp": time.time() + }) + + if stats: + player_info["match_stats"].update(stats) + + except Exception as e: + print(f"✗ 玩家技能更新失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + + def get_player_skill(self, player_id: str) -> Optional[float]: + """ + 获取玩家技能 + + Args: + player_id: 玩家ID + + Returns: + 玩家技能等级或None + """ + try: + if player_id in self.player_data: + return self.player_data[player_id].get("current_skill") + return None + except Exception as e: + print(f"✗ 玩家技能获取失败: {e}") + self.algorithm_stats["algorithm_errors"] += 1 + return None + + def get_algorithm_stats(self) -> Dict[str, Any]: + """ + 获取算法统计信息 + + Returns: + 算法统计字典 + """ + return { + "state": self.algorithm_state.copy(), + "stats": self.algorithm_stats.copy(), + "config": self.algorithm_config.copy(), + "tracked_players": len(self.player_data) + } + + def reset_stats(self): + """重置算法统计信息""" + try: + self.algorithm_stats = { + "algorithms_run": 0, + "matches_created": 0, + "players_matched": 0, + "algorithm_errors": 0 + } + + self.algorithm_state["total_matches"] = 0 + self.algorithm_state["successful_matches"] = 0 + self.algorithm_state["failed_matches"] = 0 + self.algorithm_state["average_match_quality"] = 0.0 + + print("✓ 算法统计信息已重置") + except Exception as e: + print(f"✗ 算法统计信息重置失败: {e}") + + def set_algorithm_config(self, config: Dict[str, Any]) -> bool: + """ + 设置算法配置 + + Args: + config: 算法配置字典 + + Returns: + 是否设置成功 + """ + try: + self.algorithm_config.update(config) + print(f"✓ 算法配置已更新: {self.algorithm_config}") + return True + except Exception as e: + print(f"✗ 算法配置设置失败: {e}") + return False + + def get_algorithm_config(self) -> Dict[str, Any]: + """ + 获取算法配置 + + Returns: + 算法配置字典 + """ + return self.algorithm_config.copy() + + def _trigger_algorithm_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发算法回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.algorithm_callbacks: + for callback in self.algorithm_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 算法回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 算法回调触发失败: {e}") + + def register_algorithm_callback(self, callback_type: str, callback: callable): + """ + 注册算法回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.algorithm_callbacks: + self.algorithm_callbacks[callback_type].append(callback) + print(f"✓ 算法回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 算法回调注册失败: {e}") + + def unregister_algorithm_callback(self, callback_type: str, callback: callable): + """ + 注销算法回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.algorithm_callbacks: + if callback in self.algorithm_callbacks[callback_type]: + self.algorithm_callbacks[callback_type].remove(callback) + print(f"✓ 算法回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 算法回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/config/match_config.py b/plugins/user/matchmaking_system/config/match_config.py new file mode 100644 index 00000000..b25434c8 --- /dev/null +++ b/plugins/user/matchmaking_system/config/match_config.py @@ -0,0 +1,712 @@ +""" +匹配配置管理器模块 +管理匹配系统配置 +""" + +import time +import json +import os +from typing import Dict, Any, List, Optional + +class MatchConfig: + """ + 匹配配置管理器 + 管理匹配系统配置 + """ + + def __init__(self, plugin): + """ + 初始化匹配配置管理器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 配置文件路径 + self.config_file_path = "/home/hello/EG/plugins/user/matchmaking_system/config/matchmaking_config.json" + + # 默认配置 + self.default_config = { + "system": { + "enable_matchmaking": True, + "max_concurrent_matches": 1000, + "default_match_timeout": 300.0, + "enable_cross_platform_matching": True + }, + "queue": { + "default_queue_timeout": 300.0, + "queue_processing_interval": 2.0, + "max_players_per_match": 100, + "min_players_per_match": 2, + "enable_queue_prioritization": True + }, + "matching": { + "default_algorithm": "skill_based", + "skill_range": 100, + "max_wait_time": 120.0, + "enable_skill_based_matching": True, + "enable_team_balancing": True + }, + "room": { + "default_max_players": 8, + "enable_room_passwords": True, + "enable_room_visibility": True, + "room_timeout": 3600, + "max_rooms": 1000 + }, + "monitoring": { + "enable_monitoring": True, + "monitoring_interval": 5.0, + "log_level": "INFO", + "enable_performance_monitoring": True + }, + "events": { + "enable_event_system": True, + "max_event_queue_size": 10000, + "enable_event_filtering": True + } + } + + # 当前配置 + self.current_config = self.default_config.copy() + + # 配置状态 + self.config_state = { + "last_loaded": 0.0, + "last_saved": 0.0, + "config_version": "1.0.0", + "is_dirty": False + } + + # 配置统计 + self.config_stats = { + "loads": 0, + "saves": 0, + "updates": 0, + "config_errors": 0 + } + + # 配置监听器 + self.config_listeners = {} + + # 回调函数 + self.config_callbacks = { + "config_loaded": [], + "config_saved": [], + "config_updated": [], + "config_error": [] + } + + print("✓ 匹配配置管理器已创建") + + def initialize(self) -> bool: + """ + 初始化匹配配置管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配配置管理器...") + + # 创建配置目录 + config_dir = os.path.dirname(self.config_file_path) + if not os.path.exists(config_dir): + os.makedirs(config_dir) + + # 加载配置 + self.load_config() + + self.initialized = True + print("✓ 匹配配置管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配配置管理器初始化失败: {e}") + self.config_stats["config_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.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用匹配配置管理器""" + try: + self.enabled = False + + # 保存配置(如果有更改) + if self.config_state["is_dirty"]: + self.save_config() + + print("✓ 匹配配置管理器已禁用") + + except Exception as e: + print(f"✗ 匹配配置管理器禁用失败: {e}") + self.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理匹配配置管理器资源""" + try: + # 禁用匹配配置管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.config_callbacks.clear() + self.config_listeners.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 + + # 检查配置文件是否被外部修改 + self._check_config_file_changes() + + except Exception as e: + print(f"✗ 匹配配置管理器更新失败: {e}") + self.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + + def _check_config_file_changes(self): + """检查配置文件是否被外部修改""" + try: + if not os.path.exists(self.config_file_path): + return + + last_modified = os.path.getmtime(self.config_file_path) + if last_modified > self.config_state["last_loaded"]: + # 配置文件已被修改,重新加载 + print("检测到配置文件被外部修改,正在重新加载...") + self.load_config() + + except Exception as e: + print(f"✗ 配置文件变更检查失败: {e}") + self.config_stats["config_errors"] += 1 + + def load_config(self) -> bool: + """ + 加载配置 + + Returns: + 是否加载成功 + """ + try: + # 检查配置文件是否存在 + if os.path.exists(self.config_file_path): + with open(self.config_file_path, 'r', encoding='utf-8') as f: + loaded_config = json.load(f) + + # 合并配置(使用默认配置作为基础) + self.current_config = self._merge_config(self.default_config, loaded_config) + + print(f"✓ 配置已从文件加载: {self.config_file_path}") + else: + # 使用默认配置 + self.current_config = self.default_config.copy() + print("✓ 使用默认配置") + + self.config_state["last_loaded"] = time.time() + self.config_state["is_dirty"] = False + self.config_stats["loads"] += 1 + + # 触发配置加载回调 + self._trigger_config_callback("config_loaded", { + "config": self.current_config, + "timestamp": self.config_state["last_loaded"] + }) + + return True + + except Exception as e: + print(f"✗ 配置加载失败: {e}") + self.config_stats["config_errors"] += 1 + + # 使用默认配置 + self.current_config = self.default_config.copy() + return False + + def _merge_config(self, base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]: + """ + 合并配置(递归) + + Args: + base_config: 基础配置 + override_config: 覆盖配置 + + Returns: + 合并后的配置 + """ + try: + merged = base_config.copy() + + for key, value in override_config.items(): + if key in merged and isinstance(merged[key], dict) and isinstance(value, dict): + # 递归合并嵌套字典 + merged[key] = self._merge_config(merged[key], value) + else: + # 直接覆盖 + merged[key] = value + + return merged + + except Exception as e: + print(f"✗ 配置合并失败: {e}") + self.config_stats["config_errors"] += 1 + return base_config + + def save_config(self) -> bool: + """ + 保存配置 + + Returns: + 是否保存成功 + """ + try: + # 创建配置目录(如果不存在) + config_dir = os.path.dirname(self.config_file_path) + if not os.path.exists(config_dir): + os.makedirs(config_dir) + + # 保存配置到文件 + with open(self.config_file_path, 'w', encoding='utf-8') as f: + json.dump(self.current_config, f, indent=2, ensure_ascii=False) + + self.config_state["last_saved"] = time.time() + self.config_state["is_dirty"] = False + self.config_stats["saves"] += 1 + + # 触发配置保存回调 + self._trigger_config_callback("config_saved", { + "config": self.current_config, + "timestamp": self.config_state["last_saved"] + }) + + print(f"✓ 配置已保存到文件: {self.config_file_path}") + return True + + except Exception as e: + print(f"✗ 配置保存失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def get_config(self, section: str = None, key: str = None, default_value: Any = None) -> Any: + """ + 获取配置值 + + Args: + section: 配置节(可选) + key: 配置键(可选) + default_value: 默认值 + + Returns: + 配置值 + """ + try: + if section is None: + return self.current_config.copy() + + if section not in self.current_config: + return default_value + + if key is None: + return self.current_config[section].copy() + + if key not in self.current_config[section]: + return default_value + + return self.current_config[section][key] + + except Exception as e: + print(f"✗ 获取配置失败: {e}") + self.config_stats["config_errors"] += 1 + return default_value + + def set_config(self, section: str, key: str, value: Any) -> bool: + """ + 设置配置值 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + + Returns: + 是否设置成功 + """ + try: + if section not in self.current_config: + self.current_config[section] = {} + + self.current_config[section][key] = value + self.config_state["is_dirty"] = True + self.config_stats["updates"] += 1 + + # 通知配置监听器 + self._notify_config_listeners(section, key, value) + + # 触发配置更新回调 + self._trigger_config_callback("config_updated", { + "section": section, + "key": key, + "value": value, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 设置配置失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def update_config_section(self, section: str, config_data: Dict[str, Any]) -> bool: + """ + 更新配置节 + + Args: + section: 配置节 + config_data: 配置数据 + + Returns: + 是否更新成功 + """ + try: + if section not in self.current_config: + self.current_config[section] = {} + + self.current_config[section].update(config_data) + self.config_state["is_dirty"] = True + self.config_stats["updates"] += 1 + + # 通知配置监听器 + for key, value in config_data.items(): + self._notify_config_listeners(section, key, value) + + # 触发配置更新回调 + self._trigger_config_callback("config_updated", { + "section": section, + "config_data": config_data, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 更新配置节失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def reset_config(self) -> bool: + """ + 重置配置为默认值 + + Returns: + 是否重置成功 + """ + try: + self.current_config = self.default_config.copy() + self.config_state["is_dirty"] = True + + # 通知所有配置监听器 + for section_name, section_data in self.current_config.items(): + if isinstance(section_data, dict): + for key, value in section_data.items(): + self._notify_config_listeners(section_name, key, value) + + print("✓ 配置已重置为默认值") + return True + + except Exception as e: + print(f"✗ 配置重置失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def register_config_listener(self, section: str, key: str, listener: callable): + """ + 注册配置监听器 + + Args: + section: 配置节 + key: 配置键 + listener: 监听器函数 + """ + try: + listener_key = f"{section}.{key}" + if listener_key not in self.config_listeners: + self.config_listeners[listener_key] = [] + + self.config_listeners[listener_key].append(listener) + print(f"✓ 配置监听器已注册: {listener_key}") + + except Exception as e: + print(f"✗ 配置监听器注册失败: {e}") + self.config_stats["config_errors"] += 1 + + def unregister_config_listener(self, section: str, key: str, listener: callable): + """ + 注销配置监听器 + + Args: + section: 配置节 + key: 配置键 + listener: 监听器函数 + """ + try: + listener_key = f"{section}.{key}" + if listener_key in self.config_listeners: + if listener in self.config_listeners[listener_key]: + self.config_listeners[listener_key].remove(listener) + print(f"✓ 配置监听器已注销: {listener_key}") + + except Exception as e: + print(f"✗ 配置监听器注销失败: {e}") + self.config_stats["config_errors"] += 1 + + def _notify_config_listeners(self, section: str, key: str, value: Any): + """ + 通知配置监听器 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + """ + try: + listener_key = f"{section}.{key}" + if listener_key in self.config_listeners: + for listener in self.config_listeners[listener_key]: + try: + listener(section, key, value) + except Exception as e: + print(f"✗ 配置监听器执行失败: {listener_key} - {e}") + + except Exception as e: + print(f"✗ 配置监听器通知失败: {e}") + self.config_stats["config_errors"] += 1 + + def get_config_stats(self) -> Dict[str, Any]: + """ + 获取配置统计信息 + + Returns: + 配置统计字典 + """ + return { + "state": self.config_state.copy(), + "stats": self.config_stats.copy(), + "config_sections": list(self.current_config.keys()) + } + + def reset_stats(self): + """重置配置统计信息""" + try: + self.config_stats = { + "loads": 0, + "saves": 0, + "updates": 0, + "config_errors": 0 + } + print("✓ 配置统计信息已重置") + except Exception as e: + print(f"✗ 配置统计信息重置失败: {e}") + + def validate_config(self) -> bool: + """ + 验证配置 + + Returns: + 配置是否有效 + """ + try: + # 检查必需的配置项 + required_sections = ["system", "queue", "matching", "room", "monitoring", "events"] + for section in required_sections: + if section not in self.current_config: + print(f"✗ 缺少必需的配置节: {section}") + return False + + # 检查系统配置 + system_config = self.current_config["system"] + if "max_concurrent_matches" not in system_config or not isinstance(system_config["max_concurrent_matches"], int): + print("✗ 无效的最大并发匹配数配置") + return False + + # 检查队列配置 + queue_config = self.current_config["queue"] + if "max_players_per_match" not in queue_config or not isinstance(queue_config["max_players_per_match"], int): + print("✗ 无效的最大玩家数配置") + return False + + print("✓ 配置验证通过") + return True + + except Exception as e: + print(f"✗ 配置验证失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def export_config(self, file_path: str) -> bool: + """ + 导出配置到文件 + + Args: + file_path: 导出文件路径 + + Returns: + 是否导出成功 + """ + try: + # 创建导出目录 + export_dir = os.path.dirname(file_path) + if not os.path.exists(export_dir): + os.makedirs(export_dir) + + # 导出配置 + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(self.current_config, f, indent=2, ensure_ascii=False) + + print(f"✓ 配置已导出到: {file_path}") + return True + + except Exception as e: + print(f"✗ 配置导出失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def import_config(self, file_path: str) -> bool: + """ + 从文件导入配置 + + Args: + file_path: 导入文件路径 + + Returns: + 是否导入成功 + """ + try: + if not os.path.exists(file_path): + print(f"✗ 配置文件不存在: {file_path}") + return False + + # 读取配置文件 + with open(file_path, 'r', encoding='utf-8') as f: + imported_config = json.load(f) + + # 验证配置 + if not isinstance(imported_config, dict): + print("✗ 无效的配置文件格式") + return False + + # 合并配置 + self.current_config = self._merge_config(self.default_config, imported_config) + self.config_state["is_dirty"] = True + + # 通知配置监听器 + for section_name, section_data in self.current_config.items(): + if isinstance(section_data, dict): + for key, value in section_data.items(): + self._notify_config_listeners(section_name, key, value) + + print(f"✓ 配置已从文件导入: {file_path}") + return True + + except Exception as e: + print(f"✗ 配置导入失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def _trigger_config_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发配置回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.config_callbacks: + for callback in self.config_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 配置回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 配置回调触发失败: {e}") + + def register_config_callback(self, callback_type: str, callback: callable): + """ + 注册配置回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.config_callbacks: + self.config_callbacks[callback_type].append(callback) + print(f"✓ 配置回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 配置回调注册失败: {e}") + + def unregister_config_callback(self, callback_type: str, callback: callable): + """ + 注销配置回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.config_callbacks: + if callback in self.config_callbacks[callback_type]: + self.config_callbacks[callback_type].remove(callback) + print(f"✓ 配置回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 配置回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/core/match_manager.py b/plugins/user/matchmaking_system/core/match_manager.py new file mode 100644 index 00000000..f86aa16f --- /dev/null +++ b/plugins/user/matchmaking_system/core/match_manager.py @@ -0,0 +1,417 @@ +""" +匹配管理器模块 +管理匹配过程和规则 +""" + +import time +import threading +from typing import Dict, Any, List, Optional + +class MatchManager: + """ + 匹配管理器 + 管理匹配过程和规则 + """ + + def __init__(self, plugin): + """ + 初始化匹配管理器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 匹配配置 + self.match_config = { + "enable_matchmaking": True, + "matchmaking_interval": 1.0, + "max_match_attempts": 3, + "match_timeout": 30.0, + "enable_skill_based_matching": True, + "enable_team_balancing": True, + "enable_cross_platform_matching": True + } + + # 匹配状态 + self.match_state = { + "is_matching": False, + "active_matches": 0, + "successful_matches": 0, + "failed_matches": 0, + "last_match_time": 0.0 + } + + # 匹配统计 + self.match_stats = { + "matches_started": 0, + "matches_completed": 0, + "matches_cancelled": 0, + "average_match_time": 0.0, + "match_errors": 0 + } + + # 匹配锁 + self.match_lock = threading.RLock() + + # 回调函数 + self.match_callbacks = { + "match_started": [], + "match_completed": [], + "match_cancelled": [], + "match_error": [] + } + + # 时间戳记录 + self.last_matchmaking_run = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 匹配管理器已创建") + + def initialize(self) -> bool: + """ + 初始化匹配管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配管理器...") + + self.initialized = True + print("✓ 匹配管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配管理器初始化失败: {e}") + self.match_stats["match_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.match_stats["match_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用匹配管理器""" + try: + self.enabled = False + print("✓ 匹配管理器已禁用") + + except Exception as e: + print(f"✗ 匹配管理器禁用失败: {e}") + self.match_stats["match_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理匹配管理器资源""" + try: + # 禁用匹配管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.match_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_matchmaking_run >= self.match_config["matchmaking_interval"]: + self._run_matchmaking() + self.last_matchmaking_run = current_time + + self.match_state["last_match_time"] = current_time + + except Exception as e: + print(f"✗ 匹配管理器更新失败: {e}") + self.match_stats["match_errors"] += 1 + import traceback + traceback.print_exc() + + def _run_matchmaking(self): + """运行匹配算法""" + try: + if not self.match_config["enable_matchmaking"]: + return + + # 通知队列管理器运行匹配 + if self.plugin.queue_manager: + self.plugin.queue_manager.process_queues() + + except Exception as e: + print(f"✗ 匹配算法运行失败: {e}") + self.match_stats["match_errors"] += 1 + + def start_matchmaking(self, player_ids: List[str], match_params: Dict[str, Any] = None) -> str: + """ + 开始匹配 + + Args: + player_ids: 玩家ID列表 + match_params: 匹配参数 + + Returns: + 匹配ID + """ + try: + if not self.enabled: + raise Exception("匹配管理器未启用") + + # 生成匹配ID + match_id = f"match_{int(time.time() * 1000000)}" + + # 添加玩家到队列 + if self.plugin.queue_manager: + queue_success = self.plugin.queue_manager.add_players_to_queue( + player_ids, match_params or {}) + + if queue_success: + self.match_stats["matches_started"] += 1 + self.match_state["active_matches"] += 1 + + # 触发匹配开始回调 + self._trigger_match_callback("match_started", { + "match_id": match_id, + "player_ids": player_ids, + "match_params": match_params, + "timestamp": time.time() + }) + + print(f"✓ 匹配已开始: {match_id}") + return match_id + else: + raise Exception("添加玩家到队列失败") + else: + raise Exception("队列管理器不可用") + + except Exception as e: + print(f"✗ 匹配开始失败: {e}") + self.match_stats["match_errors"] += 1 + + # 触发匹配错误回调 + self._trigger_match_callback("match_error", { + "player_ids": player_ids, + "error": str(e), + "timestamp": time.time() + }) + + return "" + + def cancel_matchmaking(self, match_id: str) -> bool: + """ + 取消匹配 + + Args: + match_id: 匹配ID + + Returns: + 是否取消成功 + """ + try: + # 从队列中移除玩家 + if self.plugin.queue_manager: + cancel_success = self.plugin.queue_manager.remove_players_from_queue(match_id) + + if cancel_success: + self.match_stats["matches_cancelled"] += 1 + if self.match_state["active_matches"] > 0: + self.match_state["active_matches"] -= 1 + + # 触发匹配取消回调 + self._trigger_match_callback("match_cancelled", { + "match_id": match_id, + "timestamp": time.time() + }) + + print(f"✓ 匹配已取消: {match_id}") + return True + else: + return False + else: + return False + + except Exception as e: + print(f"✗ 匹配取消失败: {e}") + self.match_stats["match_errors"] += 1 + return False + + def complete_match(self, match_id: str, room_id: str) -> bool: + """ + 完成匹配 + + Args: + match_id: 匹配ID + room_id: 房间ID + + Returns: + 是否完成成功 + """ + try: + self.match_stats["matches_completed"] += 1 + self.match_state["successful_matches"] += 1 + if self.match_state["active_matches"] > 0: + self.match_state["active_matches"] -= 1 + + # 触发匹配完成回调 + self._trigger_match_callback("match_completed", { + "match_id": match_id, + "room_id": room_id, + "timestamp": time.time() + }) + + print(f"✓ 匹配已完成: {match_id} -> 房间: {room_id}") + return True + + except Exception as e: + print(f"✗ 匹配完成失败: {e}") + self.match_stats["match_errors"] += 1 + return False + + def get_match_stats(self) -> Dict[str, Any]: + """ + 获取匹配统计信息 + + Returns: + 匹配统计字典 + """ + return { + "state": self.match_state.copy(), + "stats": self.match_stats.copy(), + "config": self.match_config.copy() + } + + def reset_stats(self): + """重置匹配统计信息""" + try: + self.match_stats = { + "matches_started": 0, + "matches_completed": 0, + "matches_cancelled": 0, + "average_match_time": 0.0, + "match_errors": 0 + } + print("✓ 匹配统计信息已重置") + except Exception as e: + print(f"✗ 匹配统计信息重置失败: {e}") + + def set_match_config(self, config: Dict[str, Any]) -> bool: + """ + 设置匹配配置 + + Args: + config: 匹配配置字典 + + Returns: + 是否设置成功 + """ + try: + self.match_config.update(config) + print(f"✓ 匹配配置已更新: {self.match_config}") + return True + except Exception as e: + print(f"✗ 匹配配置设置失败: {e}") + return False + + def get_match_config(self) -> Dict[str, Any]: + """ + 获取匹配配置 + + Returns: + 匹配配置字典 + """ + return self.match_config.copy() + + def _trigger_match_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发匹配回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.match_callbacks: + for callback in self.match_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 匹配回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 匹配回调触发失败: {e}") + + def register_match_callback(self, callback_type: str, callback: callable): + """ + 注册匹配回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.match_callbacks: + self.match_callbacks[callback_type].append(callback) + print(f"✓ 匹配回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 匹配回调注册失败: {e}") + + def unregister_match_callback(self, callback_type: str, callback: callable): + """ + 注销匹配回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.match_callbacks: + if callback in self.match_callbacks[callback_type]: + self.match_callbacks[callback_type].remove(callback) + print(f"✓ 匹配回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 匹配回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/editor/match_editor.py b/plugins/user/matchmaking_system/editor/match_editor.py new file mode 100644 index 00000000..eaabdfcb --- /dev/null +++ b/plugins/user/matchmaking_system/editor/match_editor.py @@ -0,0 +1,606 @@ +""" +匹配编辑器模块 +提供图形界面配置匹配参数 +""" + +import time +from typing import Dict, Any, List, Optional + +class MatchEditor: + """ + 匹配编辑器 + 提供图形界面配置匹配参数 + """ + + def __init__(self, plugin): + """ + 初始化匹配编辑器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 编辑器配置 + self.editor_config = { + "enable_editor": True, + "enable_realtime_monitoring": True, + "enable_performance_display": True, + "enable_config_editor": True, + "enable_statistics_display": True, + "refresh_interval": 1.0, + "max_data_points": 100 + } + + # 编辑器状态 + self.editor_state = { + "is_visible": False, + "last_update": 0.0, + "active_tab": "overview", + "selected_queue": None, + "selected_algorithm": None + } + + # UI组件 + self.ui_components = { + "main_window": None, + "tabs": {}, + "panels": {}, + "charts": {}, + "controls": {} + } + + # 编辑器统计 + self.editor_stats = { + "updates": 0, + "ui_refreshes": 0, + "data_points": 0, + "editor_errors": 0 + } + + # 实时数据缓存 + self.realtime_data = { + "performance_metrics": {}, + "queue_data": {}, + "match_data": {}, + "algorithm_stats": {}, + "system_stats": {} + } + + # 回调函数 + self.editor_callbacks = { + "ui_updated": [], + "data_refreshed": [], + "config_changed": [], + "editor_error": [] + } + + # 时间戳记录 + self.last_ui_update = 0.0 + self.last_data_refresh = 0.0 + + print("✓ 匹配编辑器已创建") + + def initialize(self) -> bool: + """ + 初始化匹配编辑器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配编辑器...") + + # 初始化UI组件(模拟) + self._initialize_ui_components() + + self.initialized = True + print("✓ 匹配编辑器初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配编辑器初始化失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _initialize_ui_components(self): + """初始化UI组件""" + try: + # 创建主窗口 + self.ui_components["main_window"] = { + "title": "匹配系统编辑器", + "size": (1200, 800), + "position": (100, 100) + } + + # 创建标签页 + self.ui_components["tabs"] = { + "overview": {"name": "概览", "icon": "dashboard"}, + "queues": {"name": "队列", "icon": "queue"}, + "matches": {"name": "匹配", "icon": "gamepad"}, + "algorithms": {"name": "算法", "icon": "calculate"}, + "rooms": {"name": "房间", "icon": "meeting_room"}, + "config": {"name": "配置", "icon": "settings"}, + "statistics": {"name": "统计", "icon": "bar_chart"} + } + + # 创建面板 + self.ui_components["panels"] = { + "system_status": {"title": "系统状态", "visible": True}, + "performance": {"title": "性能指标", "visible": True}, + "queues_list": {"title": "队列列表", "visible": True}, + "matches_list": {"title": "匹配列表", "visible": True}, + "algorithm_stats": {"title": "算法统计", "visible": True} + } + + print("✓ UI组件已初始化") + + except Exception as e: + print(f"✗ UI组件初始化失败: {e}") + self.editor_stats["editor_errors"] += 1 + + 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.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用匹配编辑器""" + try: + self.enabled = False + + # 隐藏UI + self.hide_editor() + + print("✓ 匹配编辑器已禁用") + + except Exception as e: + print(f"✗ 匹配编辑器禁用失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理匹配编辑器资源""" + try: + # 禁用匹配编辑器 + if self.enabled: + self.disable() + + # 清理回调 + self.editor_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() + + # 定期刷新UI + if current_time - self.last_ui_update >= self.editor_config["refresh_interval"]: + self._refresh_ui() + self.last_ui_update = current_time + + # 定期更新数据 + if current_time - self.last_data_refresh >= 0.5: # 每0.5秒更新一次数据 + self._update_realtime_data() + self.last_data_refresh = current_time + + self.editor_state["last_update"] = current_time + + except Exception as e: + print(f"✗ 匹配编辑器更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + + def _refresh_ui(self): + """刷新UI""" + try: + # 更新UI组件状态 + self._update_ui_components() + + # 更新统计信息 + self.editor_stats["ui_refreshes"] += 1 + + # 触发UI更新回调 + self._trigger_editor_callback("ui_updated", { + "timestamp": time.time() + }) + + except Exception as e: + print(f"✗ UI刷新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def _update_ui_components(self): + """更新UI组件""" + try: + # 更新系统状态面板 + system_stats = {} + if self.plugin.match_manager: + system_stats.update(self.plugin.match_manager.get_match_stats()) + if self.plugin.queue_manager: + system_stats.update(self.plugin.queue_manager.get_queue_stats()) + if self.plugin.algorithm_manager: + system_stats.update(self.plugin.algorithm_manager.get_algorithm_stats()) + + self.ui_components["panels"]["system_status"]["data"] = system_stats + + # 更新队列列表面板 + if self.plugin.queue_manager: + queues = self.plugin.queue_manager.get_queue_info() + self.ui_components["panels"]["queues_list"]["data"] = queues + + # 更新匹配列表面板 + if self.plugin.match_manager: + match_stats = self.plugin.match_manager.get_match_stats() + self.ui_components["panels"]["matches_list"]["data"] = match_stats + + # 更新算法统计面板 + if self.plugin.algorithm_manager: + algorithm_stats = self.plugin.algorithm_manager.get_algorithm_stats() + self.ui_components["panels"]["algorithm_stats"]["data"] = algorithm_stats + + except Exception as e: + print(f"✗ UI组件更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def _update_realtime_data(self): + """更新实时数据""" + try: + current_time = time.time() + + # 更新性能指标 + if self.plugin.monitor: + metrics = self.plugin.monitor.get_performance_metrics() + self.realtime_data["performance_metrics"] = metrics + + # 更新队列数据 + if self.plugin.queue_manager: + queue_info = self.plugin.queue_manager.get_queue_info() + self.realtime_data["queue_data"] = queue_info + + # 更新匹配数据 + if self.plugin.match_manager: + match_stats = self.plugin.match_manager.get_match_stats() + self.realtime_data["match_data"] = match_stats + + # 更新算法统计 + if self.plugin.algorithm_manager: + algorithm_stats = self.plugin.algorithm_manager.get_algorithm_stats() + self.realtime_data["algorithm_stats"] = algorithm_stats + + # 更新系统统计 + system_stats = {} + if self.plugin.config_manager: + system_stats["config"] = self.plugin.config_manager.get_config_stats() + if self.plugin.event_handler: + system_stats["events"] = self.plugin.event_handler.get_event_stats() + + self.realtime_data["system_stats"] = system_stats + + # 触发数据刷新回调 + self._trigger_editor_callback("data_refreshed", { + "data": self.realtime_data, + "timestamp": current_time + }) + + except Exception as e: + print(f"✗ 实时数据更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def show_editor(self): + """显示编辑器""" + try: + self.editor_state["is_visible"] = True + print("✓ 匹配编辑器已显示") + + # 刷新数据 + self._update_realtime_data() + + except Exception as e: + print(f"✗ 编辑器显示失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def hide_editor(self): + """隐藏编辑器""" + try: + self.editor_state["is_visible"] = False + print("✓ 匹配编辑器已隐藏") + + except Exception as e: + print(f"✗ 编辑器隐藏失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def switch_tab(self, tab_name: str): + """ + 切换标签页 + + Args: + tab_name: 标签页名称 + """ + try: + if tab_name in self.ui_components["tabs"]: + self.editor_state["active_tab"] = tab_name + print(f"✓ 切换到标签页: {tab_name}") + else: + print(f"✗ 无效的标签页: {tab_name}") + + except Exception as e: + print(f"✗ 标签页切换失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def select_queue(self, queue_name: str): + """ + 选择队列 + + Args: + queue_name: 队列名称 + """ + try: + self.editor_state["selected_queue"] = queue_name + print(f"✓ 选择队列: {queue_name}") + + except Exception as e: + print(f"✗ 队列选择失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def select_algorithm(self, algorithm_name: str): + """ + 选择算法 + + Args: + algorithm_name: 算法名称 + """ + try: + self.editor_state["selected_algorithm"] = algorithm_name + print(f"✓ 选择算法: {algorithm_name}") + + except Exception as e: + print(f"✗ 算法选择失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def update_config(self, section: str, key: str, value: Any) -> bool: + """ + 更新配置 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + + Returns: + 是否更新成功 + """ + try: + # 通过配置管理器更新配置 + if self.plugin.config_manager: + result = self.plugin.config_manager.set_config(section, key, value) + + if result: + self.editor_stats["updates"] += 1 + + # 触发配置更改回调 + self._trigger_editor_callback("config_changed", { + "section": section, + "key": key, + "value": value, + "timestamp": time.time() + }) + + print(f"✓ 配置已更新: {section}.{key} = {value}") + return True + else: + print(f"✗ 配置更新失败: {section}.{key}") + return False + else: + print("✗ 配置管理器不可用") + return False + + except Exception as e: + print(f"✗ 配置更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + return False + + def get_editor_stats(self) -> Dict[str, Any]: + """ + 获取编辑器统计信息 + + Returns: + 编辑器统计字典 + """ + return { + "state": self.editor_state.copy(), + "stats": self.editor_stats.copy(), + "config": self.editor_config.copy(), + "ui_components": len(self.ui_components["tabs"]) + } + + def reset_stats(self): + """重置编辑器统计信息""" + try: + self.editor_stats = { + "updates": 0, + "ui_refreshes": 0, + "data_points": 0, + "editor_errors": 0 + } + print("✓ 编辑器统计信息已重置") + except Exception as e: + print(f"✗ 编辑器统计信息重置失败: {e}") + + def set_editor_config(self, config: Dict[str, Any]) -> bool: + """ + 设置编辑器配置 + + Args: + config: 编辑器配置字典 + + Returns: + 是否设置成功 + """ + try: + self.editor_config.update(config) + print(f"✓ 编辑器配置已更新: {self.editor_config}") + return True + except Exception as e: + print(f"✗ 编辑器配置设置失败: {e}") + return False + + def get_editor_config(self) -> Dict[str, Any]: + """ + 获取编辑器配置 + + Returns: + 编辑器配置字典 + """ + return self.editor_config.copy() + + def get_realtime_data(self) -> Dict[str, Any]: + """ + 获取实时数据 + + Returns: + 实时数据字典 + """ + return self.realtime_data.copy() + + def export_data(self, data_type: str, file_path: str) -> bool: + """ + 导出数据 + + Args: + data_type: 数据类型 + file_path: 导出文件路径 + + Returns: + 是否导出成功 + """ + try: + import json + import os + + # 创建导出目录 + export_dir = os.path.dirname(file_path) + if not os.path.exists(export_dir): + os.makedirs(export_dir) + + # 获取要导出的数据 + export_data = None + if data_type == "performance": + export_data = self.realtime_data["performance_metrics"] + elif data_type == "queues": + export_data = self.realtime_data["queue_data"] + elif data_type == "matches": + export_data = self.realtime_data["match_data"] + elif data_type == "algorithms": + export_data = self.realtime_data["algorithm_stats"] + elif data_type == "system": + export_data = self.realtime_data["system_stats"] + elif data_type == "all": + export_data = self.realtime_data + else: + print(f"✗ 不支持的数据类型: {data_type}") + return False + + # 导出数据 + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(export_data, f, indent=2, ensure_ascii=False, default=str) + + print(f"✓ 数据已导出到: {file_path}") + return True + + except Exception as e: + print(f"✗ 数据导出失败: {e}") + self.editor_stats["editor_errors"] += 1 + return False + + def _trigger_editor_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发编辑器回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.editor_callbacks: + for callback in self.editor_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 编辑器回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 编辑器回调触发失败: {e}") + + def register_editor_callback(self, callback_type: str, callback: callable): + """ + 注册编辑器回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.editor_callbacks: + self.editor_callbacks[callback_type].append(callback) + print(f"✓ 编辑器回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 编辑器回调注册失败: {e}") + + def unregister_editor_callback(self, callback_type: str, callback: callable): + """ + 注销编辑器回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.editor_callbacks: + if callback in self.editor_callbacks[callback_type]: + self.editor_callbacks[callback_type].remove(callback) + print(f"✓ 编辑器回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 编辑器回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/events/event_handler.py b/plugins/user/matchmaking_system/events/event_handler.py new file mode 100644 index 00000000..109bac74 --- /dev/null +++ b/plugins/user/matchmaking_system/events/event_handler.py @@ -0,0 +1,699 @@ +""" +事件处理器模块 +处理匹配相关的事件 +""" + +import time +import threading +import queue +from typing import Dict, Any, List, Optional, Callable + +class EventHandler: + """ + 事件处理器 + 处理匹配相关的事件 + """ + + def __init__(self, plugin): + """ + 初始化事件处理器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 事件配置 + self.event_config = { + "enable_event_system": True, + "max_event_queue_size": 10000, + "enable_event_filtering": True, + "event_processing_interval": 0.01, # 10ms + "enable_async_processing": True, + "max_concurrent_events": 100, + "enable_event_prioritization": True + } + + # 事件状态 + self.event_state = { + "is_processing": False, + "pending_events": 0, + "processed_events": 0, + "dropped_events": 0, + "last_event_time": 0.0 + } + + # 事件队列 + self.event_queue = queue.Queue(maxsize=self.event_config["max_event_queue_size"]) + + # 事件处理器存储 + self.event_handlers = {} + + # 事件统计 + self.event_stats = { + "events_received": 0, + "events_processed": 0, + "events_dropped": 0, + "handler_errors": 0, + "async_events": 0 + } + + # 事件过滤器 + self.event_filters = {} + + # 回调函数 + self.event_callbacks = { + "event_received": [], + "event_processed": [], + "event_dropped": [], + "event_error": [] + } + + # 事件处理线程 + self.event_thread = None + self.event_thread_running = False + + # 时间戳记录 + self.last_event_process = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 事件处理器已创建") + + def initialize(self) -> bool: + """ + 初始化事件处理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化事件处理器...") + + # 注册默认事件处理器 + self._register_default_handlers() + + # 启动事件处理线程 + self._start_event_thread() + + self.initialized = True + print("✓ 事件处理器初始化完成") + return True + + except Exception as e: + print(f"✗ 事件处理器初始化失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _register_default_handlers(self): + """注册默认事件处理器""" + try: + # 注册系统事件处理器 + self.register_event_handler("player_queued", self._handle_player_queued) + self.register_event_handler("player_dequeued", self._handle_player_dequeued) + self.register_event_handler("match_found", self._handle_match_found) + self.register_event_handler("match_completed", self._handle_match_completed) + self.register_event_handler("match_cancelled", self._handle_match_cancelled) + self.register_event_handler("queue_timeout", self._handle_queue_timeout) + + print("✓ 默认事件处理器已注册") + + except Exception as e: + print(f"✗ 默认事件处理器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _start_event_thread(self): + """启动事件处理线程""" + try: + self.event_thread_running = True + self.event_thread = threading.Thread(target=self._event_loop, daemon=True) + self.event_thread.start() + print("✓ 事件处理线程已启动") + except Exception as e: + print(f"✗ 事件处理线程启动失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _event_loop(self): + """事件处理循环""" + try: + while self.event_thread_running: + try: + if self.enabled and self.event_config["enable_event_system"]: + # 处理事件队列 + self._process_event_queue() + + # 短暂休眠 + time.sleep(self.event_config["event_processing_interval"]) + + except Exception as e: + print(f"✗ 事件处理循环错误: {e}") + self.event_stats["handler_errors"] += 1 + time.sleep(1.0) # 出错时延长休眠 + + except Exception as e: + print(f"✗ 事件处理线程失败: {e}") + self.event_stats["handler_errors"] += 1 + + 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.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用事件处理器""" + try: + self.enabled = False + + # 停止事件处理线程 + if self.event_thread_running: + self.event_thread_running = False + if self.event_thread and self.event_thread.is_alive(): + self.event_thread.join(timeout=5.0) + + print("✓ 事件处理器已禁用") + + except Exception as e: + print(f"✗ 事件处理器禁用失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理事件处理器资源""" + try: + # 禁用事件处理器 + if self.enabled: + self.disable() + + # 清理回调和处理器 + self.event_callbacks.clear() + self.event_handlers.clear() + self.event_filters.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() + self.event_state["last_event_time"] = current_time + + except Exception as e: + print(f"✗ 事件处理器更新失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + + def _process_event_queue(self): + """处理事件队列""" + try: + processed_count = 0 + max_process_per_loop = 100 # 每次循环最多处理100个事件 + + while not self.event_queue.empty() and processed_count < max_process_per_loop: + try: + # 从队列获取事件 + event_data = self.event_queue.get_nowait() + + # 处理事件 + self._handle_event_internal(event_data) + + processed_count += 1 + self.event_stats["events_processed"] += 1 + + except queue.Empty: + break + except Exception as e: + print(f"✗ 事件队列处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + self.event_state["pending_events"] = self.event_queue.qsize() + + except Exception as e: + print(f"✗ 事件队列处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_event_internal(self, event_data: Dict[str, Any]): + """ + 内部事件处理 + + Args: + event_data: 事件数据 + """ + try: + event_type = event_data.get("type") + event_payload = event_data.get("payload", {}) + event_priority = event_data.get("priority", 0) + event_timestamp = event_data.get("timestamp", time.time()) + + # 应用事件过滤器 + if self.event_config["enable_event_filtering"]: + if not self._apply_event_filters(event_type, event_payload): + # 事件被过滤掉 + return + + # 查找事件处理器 + if event_type in self.event_handlers: + handlers = self.event_handlers[event_type] + + # 按优先级排序处理器 + if self.event_config["enable_event_prioritization"]: + handlers = sorted(handlers, key=lambda x: x.get("priority", 0), reverse=True) + + # 调用所有处理器 + for handler_info in handlers: + try: + handler_func = handler_info["function"] + handler_result = handler_func(event_payload) + + # 检查是否需要停止传播 + if handler_result is False: + break + + except Exception as e: + print(f"✗ 事件处理器执行失败: {event_type} - {e}") + self.event_stats["handler_errors"] += 1 + + # 触发事件处理回调 + self._trigger_event_callback("event_processed", { + "event_type": event_type, + "event_payload": event_payload, + "processing_time": time.time() - event_timestamp, + "timestamp": time.time() + }) + + except Exception as e: + print(f"✗ 内部事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _apply_event_filters(self, event_type: str, event_payload: Dict[str, Any]) -> bool: + """ + 应用事件过滤器 + + Args: + event_type: 事件类型 + event_payload: 事件载荷 + + Returns: + 是否通过过滤器 + """ + try: + if event_type in self.event_filters: + filters = self.event_filters[event_type] + for filter_func in filters: + if not filter_func(event_payload): + return False + + return True + + except Exception as e: + print(f"✗ 事件过滤器应用失败: {e}") + self.event_stats["handler_errors"] += 1 + return True + + def emit_event(self, event_type: str, event_payload: Dict[str, Any] = None, + priority: int = 0, async_process: bool = None) -> bool: + """ + 发出事件 + + Args: + event_type: 事件类型 + event_payload: 事件载荷 + priority: 事件优先级 + async_process: 是否异步处理 + + Returns: + 是否发出成功 + """ + try: + if not self.enabled or not self.event_config["enable_event_system"]: + return False + + # 使用默认异步处理设置或指定设置 + if async_process is None: + async_process = self.event_config["enable_async_processing"] + + event_data = { + "type": event_type, + "payload": event_payload or {}, + "priority": priority, + "timestamp": time.time(), + "async": async_process + } + + # 触发事件接收回调 + self._trigger_event_callback("event_received", { + "event_type": event_type, + "event_payload": event_payload, + "priority": priority, + "timestamp": time.time() + }) + + # 更新统计 + self.event_stats["events_received"] += 1 + + if async_process: + # 异步处理:添加到队列 + try: + self.event_queue.put_nowait(event_data) + self.event_stats["async_events"] += 1 + return True + except queue.Full: + # 队列已满,丢弃事件 + self.event_stats["events_dropped"] += 1 + self.event_state["dropped_events"] += 1 + + # 触发事件丢弃回调 + self._trigger_event_callback("event_dropped", { + "event_type": event_type, + "event_payload": event_payload, + "reason": "queue_full", + "timestamp": time.time() + }) + + return False + else: + # 同步处理:立即处理 + self._handle_event_internal(event_data) + return True + + except Exception as e: + print(f"✗ 事件发出失败: {e}") + self.event_stats["handler_errors"] += 1 + return False + + def register_event_handler(self, event_type: str, handler: Callable, priority: int = 0): + """ + 注册事件处理器 + + Args: + event_type: 事件类型 + handler: 处理器函数 + priority: 处理器优先级 + """ + try: + if event_type not in self.event_handlers: + self.event_handlers[event_type] = [] + + handler_info = { + "function": handler, + "priority": priority + } + + self.event_handlers[event_type].append(handler_info) + print(f"✓ 事件处理器已注册: {event_type} (优先级: {priority})") + + except Exception as e: + print(f"✗ 事件处理器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def unregister_event_handler(self, event_type: str, handler: Callable): + """ + 注销事件处理器 + + Args: + event_type: 事件类型 + handler: 处理器函数 + """ + try: + if event_type in self.event_handlers: + handlers = self.event_handlers[event_type] + for i, handler_info in enumerate(handlers): + if handler_info["function"] == handler: + del handlers[i] + print(f"✓ 事件处理器已注销: {event_type}") + return + + print(f"✗ 事件处理器不存在: {event_type}") + + except Exception as e: + print(f"✗ 事件处理器注销失败: {e}") + self.event_stats["handler_errors"] += 1 + + def register_event_filter(self, event_type: str, filter_func: Callable): + """ + 注册事件过滤器 + + Args: + event_type: 事件类型 + filter_func: 过滤器函数 + """ + try: + if event_type not in self.event_filters: + self.event_filters[event_type] = [] + + self.event_filters[event_type].append(filter_func) + print(f"✓ 事件过滤器已注册: {event_type}") + + except Exception as e: + print(f"✗ 事件过滤器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def unregister_event_filter(self, event_type: str, filter_func: Callable): + """ + 注销事件过滤器 + + Args: + event_type: 事件类型 + filter_func: 过滤器函数 + """ + try: + if event_type in self.event_filters: + filters = self.event_filters[event_type] + if filter_func in filters: + filters.remove(filter_func) + print(f"✓ 事件过滤器已注销: {event_type}") + return + + print(f"✗ 事件过滤器不存在: {event_type}") + + except Exception as e: + print(f"✗ 事件过滤器注销失败: {e}") + self.event_stats["handler_errors"] += 1 + + # 默认事件处理器 + def _handle_player_queued(self, payload: Dict[str, Any]): + """处理玩家排队事件""" + try: + player_ids = payload.get("player_ids", []) + queue_name = payload.get("queue_name", "default") + + print(f".players queued: {len(player_ids)} players in queue '{queue_name}'") + + # 可以在这里添加额外的处理逻辑 + # 例如:记录排队时间、更新统计等 + + except Exception as e: + print(f"✗ 玩家排队事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_player_dequeued(self, payload: Dict[str, Any]): + """处理玩家离队事件""" + try: + player_ids = payload.get("player_ids", []) + match_id = payload.get("match_id") + + print(f".players dequeued: {len(player_ids)} players") + + # 可以在这里添加额外的处理逻辑 + # 例如:更新统计、清理资源等 + + except Exception as e: + print(f"✗ 玩家离队事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_match_found(self, payload: Dict[str, Any]): + """处理匹配找到事件""" + try: + match_id = payload.get("match_id") + room_id = payload.get("room_id") + player_ids = payload.get("player_ids", []) + queue_name = payload.get("queue_name", "default") + + print(f"🎉 Match found: {match_id} with {len(player_ids)} players in queue '{queue_name}' -> Room: {room_id}") + + # 记录匹配队列时间 + if self.plugin.monitor: + queue_time = time.time() - payload.get("timestamp", time.time()) + self.plugin.monitor.record_match_queue_time(queue_time) + + except Exception as e: + print(f"✗ 匹配找到事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_match_completed(self, payload: Dict[str, Any]): + """处理匹配完成事件""" + try: + match_id = payload.get("match_id") + room_id = payload.get("room_id") + + print(f"✅ Match completed: {match_id} -> Room: {room_id}") + + except Exception as e: + print(f"✗ 匹配完成事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_match_cancelled(self, payload: Dict[str, Any]): + """处理匹配取消事件""" + try: + match_id = payload.get("match_id") + + print(f"❌ Match cancelled: {match_id}") + + except Exception as e: + print(f"✗ 匹配取消事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_queue_timeout(self, payload: Dict[str, Any]): + """处理队列超时事件""" + try: + player_id = payload.get("player_id") + + print(f"⏰ Queue timeout for player: {player_id}") + + except Exception as e: + print(f"✗ 队列超时事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def get_event_stats(self) -> Dict[str, Any]: + """ + 获取事件统计信息 + + Returns: + 事件统计字典 + """ + return { + "state": self.event_state.copy(), + "stats": self.event_stats.copy(), + "config": self.event_config.copy(), + "pending_events": self.event_queue.qsize() + } + + def reset_stats(self): + """重置事件统计信息""" + try: + self.event_stats = { + "events_received": 0, + "events_processed": 0, + "events_dropped": 0, + "handler_errors": 0, + "async_events": 0 + } + print("✓ 事件统计信息已重置") + except Exception as e: + print(f"✗ 事件统计信息重置失败: {e}") + + def set_event_config(self, config: Dict[str, Any]) -> bool: + """ + 设置事件配置 + + Args: + config: 事件配置字典 + + Returns: + 是否设置成功 + """ + try: + self.event_config.update(config) + print(f"✓ 事件配置已更新: {self.event_config}") + return True + except Exception as e: + print(f"✗ 事件配置设置失败: {e}") + return False + + def get_event_config(self) -> Dict[str, Any]: + """ + 获取事件配置 + + Returns: + 事件配置字典 + """ + return self.event_config.copy() + + def _trigger_event_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发事件回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.event_callbacks: + for callback in self.event_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 事件回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 事件回调触发失败: {e}") + + def register_event_callback(self, callback_type: str, callback: callable): + """ + 注册事件回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.event_callbacks: + self.event_callbacks[callback_type].append(callback) + print(f"✓ 事件回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 事件回调注册失败: {e}") + + def unregister_event_callback(self, callback_type: str, callback: callable): + """ + 注销事件回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.event_callbacks: + if callback in self.event_callbacks[callback_type]: + self.event_callbacks[callback_type].remove(callback) + print(f"✓ 事件回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 事件回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/monitoring/match_monitor.py b/plugins/user/matchmaking_system/monitoring/match_monitor.py new file mode 100644 index 00000000..602603a9 --- /dev/null +++ b/plugins/user/matchmaking_system/monitoring/match_monitor.py @@ -0,0 +1,600 @@ +""" +匹配监控系统模块 +监控匹配过程和性能 +""" + +import time +from typing import Dict, Any, List, Optional +from collections import deque + +class MatchMonitor: + """ + 匹配监控器 + 监控匹配过程和性能 + """ + + def __init__(self, plugin): + """ + 初始化匹配监控器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 监控配置 + self.monitor_config = { + "enable_monitoring": True, + "monitoring_interval": 5.0, + "log_level": "INFO", + "enable_performance_monitoring": True, + "enable_alerts": True, + "alert_thresholds": { + "match_queue_time": 60.0, + "match_failure_rate": 10.0, + "system_latency": 100.0 + }, + "enable_metrics_collection": True, + "metrics_retention_period": 3600 # 1小时 + } + + # 性能指标存储 + self.performance_metrics = { + "match_queue_times": deque(maxlen=1000), + "match_success_rates": deque(maxlen=1000), + "system_latencies": deque(maxlen=1000), + "player_wait_times": deque(maxlen=1000), + "algorithm_performance": deque(maxlen=1000) + } + + # 监控状态 + self.monitor_state = { + "last_monitoring_update": 0.0, + "last_performance_check": 0.0, + "total_monitored_matches": 0, + "active_alerts": 0 + } + + # 警报系统 + self.active_alerts = {} + self.alert_history = deque(maxlen=1000) + + # 监控统计 + self.monitor_stats = { + "metrics_collected": 0, + "alerts_generated": 0, + "alerts_resolved": 0, + "performance_checks": 0, + "monitor_errors": 0 + } + + # 回调函数 + self.monitor_callbacks = { + "metric_collected": [], + "alert_triggered": [], + "alert_resolved": [], + "performance_degraded": [] + } + + # 时间戳记录 + self.last_metric_collection = 0.0 + self.last_alert_check = 0.0 + + print("✓ 匹配监控器已创建") + + def initialize(self) -> bool: + """ + 初始化匹配监控器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配监控器...") + + self.initialized = True + print("✓ 匹配监控器初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配监控器初始化失败: {e}") + self.monitor_stats["monitor_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.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用匹配监控器""" + try: + self.enabled = False + print("✓ 匹配监控器已禁用") + + except Exception as e: + print(f"✗ 匹配监控器禁用失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理匹配监控器资源""" + try: + # 禁用匹配监控器 + if self.enabled: + self.disable() + + # 清理回调 + self.monitor_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() + self.monitor_state["last_monitoring_update"] = current_time + + # 定期收集性能指标 + if current_time - self.last_metric_collection >= self.monitor_config["monitoring_interval"]: + self._collect_performance_metrics() + self.last_metric_collection = current_time + + # 定期检查警报 + if current_time - self.last_alert_check >= self.monitor_config["monitoring_interval"]: + self._check_alerts() + self.last_alert_check = current_time + + except Exception as e: + print(f"✗ 匹配监控器更新失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + + def _collect_performance_metrics(self): + """收集性能指标""" + try: + current_time = time.time() + metrics = {} + + # 收集匹配管理器指标 + if self.plugin.match_manager: + match_stats = self.plugin.match_manager.get_match_stats() + match_state = match_stats.get("state", {}) + + metrics["active_matches"] = match_state.get("active_matches", 0) + metrics["successful_matches"] = match_state.get("successful_matches", 0) + metrics["failed_matches"] = match_state.get("failed_matches", 0) + + # 计算成功率 + total_matches = metrics["successful_matches"] + metrics["failed_matches"] + if total_matches > 0: + success_rate = (metrics["successful_matches"] / total_matches) * 100 + metrics["match_success_rate"] = success_rate + self.performance_metrics["match_success_rates"].append((current_time, success_rate)) + + # 收集队列管理器指标 + if self.plugin.queue_manager: + queue_stats = self.plugin.queue_manager.get_queue_stats() + queue_state = queue_stats.get("state", {}) + + metrics["total_players"] = queue_state.get("total_players", 0) + metrics["players_matched"] = queue_state.get("players_matched", 0) + metrics["players_timed_out"] = queue_state.get("players_timed_out", 0) + + # 计算超时率 + total_players = metrics["players_matched"] + metrics["players_timed_out"] + if total_players > 0: + timeout_rate = (metrics["players_timed_out"] / total_players) * 100 + metrics["queue_timeout_rate"] = timeout_rate + + # 收集算法管理器指标 + if self.plugin.algorithm_manager: + algorithm_stats = self.plugin.algorithm_manager.get_algorithm_stats() + algorithm_state = algorithm_stats.get("state", {}) + + metrics["total_algorithms"] = algorithm_state.get("total_matches", 0) + metrics["successful_algorithms"] = algorithm_state.get("successful_matches", 0) + metrics["average_match_quality"] = algorithm_state.get("average_match_quality", 0) + + self.performance_metrics["algorithm_performance"].append( + (current_time, metrics["average_match_quality"]) + ) + + # 收集房间分配器指标 + if self.plugin.room_allocator: + room_stats = self.plugin.room_allocator.get_room_stats() + room_state = room_stats.get("state", {}) + + metrics["active_rooms"] = room_state.get("active_rooms", 0) + metrics["total_rooms"] = room_state.get("total_rooms", 0) + + # 更新统计 + self.monitor_stats["metrics_collected"] += 1 + self.monitor_state["total_monitored_matches"] += 1 + + # 触发指标收集回调 + self._trigger_monitor_callback("metric_collected", { + "metrics": metrics, + "timestamp": current_time + }) + + except Exception as e: + print(f"✗ 性能指标收集失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _check_alerts(self): + """检查警报条件""" + try: + if not self.monitor_config["enable_alerts"]: + return + + current_time = time.time() + thresholds = self.monitor_config["alert_thresholds"] + + # 检查匹配队列时间 + if self.performance_metrics["match_queue_times"]: + latest_queue_time = self.performance_metrics["match_queue_times"][-1][1] + if latest_queue_time > thresholds["match_queue_time"]: + self._trigger_alert("high_queue_time", f"匹配队列时间过长: {latest_queue_time:.2f}秒", current_time) + + # 检查匹配失败率 + if self.performance_metrics["match_success_rates"]: + latest_success_rate = self.performance_metrics["match_success_rates"][-1][1] + failure_rate = 100 - latest_success_rate + if failure_rate > thresholds["match_failure_rate"]: + self._trigger_alert("high_failure_rate", f"匹配失败率过高: {failure_rate:.2f}%", current_time) + + # 检查系统延迟 + if self.performance_metrics["system_latencies"]: + latest_latency = self.performance_metrics["system_latencies"][-1][1] + if latest_latency > thresholds["system_latency"]: + self._trigger_alert("high_latency", f"系统延迟过高: {latest_latency:.2f}ms", current_time) + + except Exception as e: + print(f"✗ 警报检查失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _trigger_alert(self, alert_type: str, message: str, timestamp: float = None): + """ + 触发警报 + + Args: + alert_type: 警报类型 + message: 警报消息 + timestamp: 时间戳 + """ + try: + if timestamp is None: + timestamp = time.time() + + alert_id = f"{alert_type}_{int(timestamp)}" + + # 检查是否已存在相同类型的活动警报 + if alert_type in self.active_alerts: + # 更新现有警报 + self.active_alerts[alert_type]["count"] += 1 + self.active_alerts[alert_type]["last_triggered"] = timestamp + else: + # 创建新警报 + self.active_alerts[alert_type] = { + "id": alert_id, + "type": alert_type, + "message": message, + "first_triggered": timestamp, + "last_triggered": timestamp, + "count": 1, + "resolved": False + } + + self.monitor_state["active_alerts"] += 1 + self.monitor_stats["alerts_generated"] += 1 + + # 添加到警报历史 + self.alert_history.append({ + "id": alert_id, + "type": alert_type, + "message": message, + "timestamp": timestamp, + "resolved": False + }) + + # 触发警报回调 + self._trigger_monitor_callback("alert_triggered", { + "alert_id": alert_id, + "alert_type": alert_type, + "message": message, + "timestamp": timestamp + }) + + print(f"⚠️ 警报触发 [{alert_type}]: {message}") + + except Exception as e: + print(f"✗ 警报触发失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def resolve_alert(self, alert_type: str): + """ + 解决警报 + + Args: + alert_type: 警报类型 + """ + try: + if alert_type in self.active_alerts: + alert_data = self.active_alerts[alert_type] + alert_data["resolved"] = True + alert_data["resolved_time"] = time.time() + + self.monitor_state["active_alerts"] -= 1 + self.monitor_stats["alerts_resolved"] += 1 + + # 触发警报解决回调 + self._trigger_monitor_callback("alert_resolved", { + "alert_type": alert_type, + "alert_data": alert_data + }) + + # 从活动警报中移除 + del self.active_alerts[alert_type] + + print(f"✅ 警报已解决: {alert_type}") + + except Exception as e: + print(f"✗ 警报解决失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def record_match_queue_time(self, queue_time: float): + """ + 记录匹配队列时间 + + Args: + queue_time: 队列时间(秒) + """ + try: + current_time = time.time() + self.performance_metrics["match_queue_times"].append((current_time, queue_time)) + except Exception as e: + print(f"✗ 匹配队列时间记录失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def record_player_wait_time(self, wait_time: float): + """ + 记录玩家等待时间 + + Args: + wait_time: 等待时间(秒) + """ + try: + current_time = time.time() + self.performance_metrics["player_wait_times"].append((current_time, wait_time)) + except Exception as e: + print(f"✗ 玩家等待时间记录失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def record_system_latency(self, latency: float): + """ + 记录系统延迟 + + Args: + latency: 延迟(毫秒) + """ + try: + current_time = time.time() + self.performance_metrics["system_latencies"].append((current_time, latency)) + except Exception as e: + print(f"✗ 系统延迟记录失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def get_performance_metrics(self, metric_name: str = None, limit: int = 100) -> Any: + """ + 获取性能指标 + + Args: + metric_name: 指标名称(可选) + limit: 限制返回的数量 + + Returns: + 性能指标数据 + """ + try: + if metric_name: + if metric_name in self.performance_metrics: + metrics = list(self.performance_metrics[metric_name]) + return metrics[-limit:] if len(metrics) > limit else metrics + else: + return [] + else: + # 返回所有指标 + result = {} + for name, metrics in self.performance_metrics.items(): + metric_list = list(metrics) + result[name] = metric_list[-limit:] if len(metric_list) > limit else metric_list + return result + + except Exception as e: + print(f"✗ 获取性能指标失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return {} + + def get_active_alerts(self) -> Dict[str, Any]: + """ + 获取活动警报 + + Returns: + 活动警报字典 + """ + try: + return self.active_alerts.copy() + except Exception as e: + print(f"✗ 获取活动警报失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return {} + + def get_alert_history(self, limit: int = 100) -> List[Dict[str, Any]]: + """ + 获取警报历史 + + Args: + limit: 限制返回的数量 + + Returns: + 警报历史列表 + """ + try: + history = list(self.alert_history) + return history[-limit:] if len(history) > limit else history + except Exception as e: + print(f"✗ 获取警报历史失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return [] + + def get_monitor_stats(self) -> Dict[str, Any]: + """ + 获取监控统计信息 + + Returns: + 监控统计字典 + """ + return { + "state": self.monitor_state.copy(), + "stats": self.monitor_stats.copy(), + "config": self.monitor_config.copy(), + "active_alerts_count": len(self.active_alerts), + "alert_history_count": len(self.alert_history) + } + + def reset_stats(self): + """重置监控统计信息""" + try: + self.monitor_stats = { + "metrics_collected": 0, + "alerts_generated": 0, + "alerts_resolved": 0, + "performance_checks": 0, + "monitor_errors": 0 + } + + self.monitor_state["total_monitored_matches"] = 0 + self.monitor_state["active_alerts"] = 0 + + print("✓ 监控统计信息已重置") + except Exception as e: + print(f"✗ 监控统计信息重置失败: {e}") + + def set_monitor_config(self, config: Dict[str, Any]) -> bool: + """ + 设置监控配置 + + Args: + config: 监控配置字典 + + Returns: + 是否设置成功 + """ + try: + self.monitor_config.update(config) + print(f"✓ 监控配置已更新: {self.monitor_config}") + return True + except Exception as e: + print(f"✗ 监控配置设置失败: {e}") + return False + + def get_monitor_config(self) -> Dict[str, Any]: + """ + 获取监控配置 + + Returns: + 监控配置字典 + """ + return self.monitor_config.copy() + + def _trigger_monitor_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发监控回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.monitor_callbacks: + for callback in self.monitor_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 监控回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 监控回调触发失败: {e}") + + def register_monitor_callback(self, callback_type: str, callback: callable): + """ + 注册监控回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.monitor_callbacks: + self.monitor_callbacks[callback_type].append(callback) + print(f"✓ 监控回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 监控回调注册失败: {e}") + + def unregister_monitor_callback(self, callback_type: str, callback: callable): + """ + 注销监控回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.monitor_callbacks: + if callback in self.monitor_callbacks[callback_type]: + self.monitor_callbacks[callback_type].remove(callback) + print(f"✓ 监控回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 监控回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/plugin.py b/plugins/user/matchmaking_system/plugin.py new file mode 100644 index 00000000..146716a9 --- /dev/null +++ b/plugins/user/matchmaking_system/plugin.py @@ -0,0 +1,225 @@ +""" +匹配系统插件主文件 +""" + +class MatchmakingSystemPlugin: + """ + 匹配系统插件 + 为EG引擎提供玩家匹配功能 + """ + + def __init__(self, engine): + """ + 初始化匹配系统插件 + + Args: + engine: EG引擎实例 + """ + self.engine = engine + self.name = "MatchmakingSystem" + self.version = "1.0.0" + + # 插件组件 + self.match_manager = None + self.queue_manager = None + self.algorithm_manager = None + self.room_allocator = None + self.rule_manager = None + self.monitor = None + self.event_handler = None + self.config_manager = None + self.editor = None + self.stats_manager = None + + print("✓ 匹配系统插件已创建") + + def initialize(self) -> bool: + """ + 初始化插件 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配系统插件...") + + # 导入模块 + from .core.match_manager import MatchManager + from .queue.queue_manager import QueueManager + from .algorithms.algorithm_manager import AlgorithmManager + from .rooms.room_allocator import RoomAllocator + from .rules.rule_manager import RuleManager + from .monitoring.match_monitor import MatchMonitor + from .events.event_handler import EventHandler + from .config.match_config import MatchConfig + from .editor.match_editor import MatchEditor + from .stats.stats_manager import StatsManager + + # 创建组件 + self.config_manager = MatchConfig(self) + self.event_handler = EventHandler(self) + self.rule_manager = RuleManager(self) + self.algorithm_manager = AlgorithmManager(self) + self.queue_manager = QueueManager(self) + self.room_allocator = RoomAllocator(self) + self.match_manager = MatchManager(self) + self.monitor = MatchMonitor(self) + self.editor = MatchEditor(self) + self.stats_manager = StatsManager(self) + + # 初始化组件 + components = [ + self.config_manager, + self.event_handler, + self.rule_manager, + self.algorithm_manager, + self.queue_manager, + self.room_allocator, + self.match_manager, + self.monitor, + self.editor, + self.stats_manager + ] + + for component in components: + if hasattr(component, 'initialize') and not component.initialize(): + print(f"✗ 组件初始化失败: {component.__class__.__name__}") + return False + + print("✓ 匹配系统插件初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配系统插件初始化失败: {e}") + import traceback + traceback.print_exc() + return False + + def enable(self) -> bool: + """ + 启用插件 + + Returns: + 是否启用成功 + """ + try: + print("正在启用匹配系统插件...") + + # 启用组件 + components = [ + self.config_manager, + self.event_handler, + self.rule_manager, + self.algorithm_manager, + self.queue_manager, + self.room_allocator, + self.match_manager, + self.monitor, + self.editor, + self.stats_manager + ] + + for component in components: + if hasattr(component, 'enable') and not component.enable(): + print(f"✗ 组件启用失败: {component.__class__.__name__}") + return False + + print("✓ 匹配系统插件已启用") + return True + + except Exception as e: + print(f"✗ 匹配系统插件启用失败: {e}") + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用插件""" + try: + print("正在禁用匹配系统插件...") + + # 禁用组件 + components = [ + self.stats_manager, + self.editor, + self.monitor, + self.match_manager, + self.room_allocator, + self.queue_manager, + self.algorithm_manager, + self.rule_manager, + self.event_handler, + self.config_manager + ] + + for component in components: + if hasattr(component, 'disable'): + component.disable() + + print("✓ 匹配系统插件已禁用") + + except Exception as e: + print(f"✗ 匹配系统插件禁用失败: {e}") + import traceback + traceback.print_exc() + + def finalize(self): + """清理插件资源""" + try: + print("正在清理匹配系统插件资源...") + + # 清理组件 + components = [ + self.stats_manager, + self.editor, + self.monitor, + self.match_manager, + self.room_allocator, + self.queue_manager, + self.algorithm_manager, + self.rule_manager, + self.event_handler, + self.config_manager + ] + + for component in components: + if hasattr(component, 'finalize'): + component.finalize() + + print("✓ 匹配系统插件资源已清理") + + except Exception as e: + print(f"✗ 匹配系统插件资源清理失败: {e}") + import traceback + traceback.print_exc() + + def update(self, dt: float): + """ + 更新插件状态 + + Args: + dt: 时间增量(秒) + """ + try: + # 更新组件 + components = [ + self.config_manager, + self.event_handler, + self.rule_manager, + self.algorithm_manager, + self.queue_manager, + self.room_allocator, + self.match_manager, + self.monitor, + self.editor, + self.stats_manager + ] + + for component in components: + if hasattr(component, 'update'): + component.update(dt) + + except Exception as e: + print(f"✗ 匹配系统插件更新失败: {e}") + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/plugins/user/matchmaking_system/queue/queue_manager.py b/plugins/user/matchmaking_system/queue/queue_manager.py new file mode 100644 index 00000000..408309b9 --- /dev/null +++ b/plugins/user/matchmaking_system/queue/queue_manager.py @@ -0,0 +1,664 @@ +""" +队列管理器模块 +管理等待匹配的玩家队列 +""" + +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}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/rooms/room_allocator.py b/plugins/user/matchmaking_system/rooms/room_allocator.py new file mode 100644 index 00000000..b35cc1c9 --- /dev/null +++ b/plugins/user/matchmaking_system/rooms/room_allocator.py @@ -0,0 +1,752 @@ +""" +房间分配器模块 +为匹配成功的玩家创建和分配房间 +""" + +import time +import uuid +from typing import Dict, Any, List, Optional + +class RoomAllocator: + """ + 房间分配器 + 为匹配成功的玩家创建和分配房间 + """ + + def __init__(self, plugin): + """ + 初始化房间分配器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 房间配置 + self.room_config = { + "default_max_players": 8, + "enable_room_passwords": True, + "enable_room_visibility": True, + "room_timeout": 3600, # 1小时无活动房间自动关闭 + "max_rooms": 1000, + "enable_persistent_rooms": False + } + + # 房间状态 + self.room_state = { + "total_rooms": 0, + "active_rooms": 0, + "occupied_rooms": 0, + "available_rooms": 0 + } + + # 房间存储 + self.rooms = {} # 房间数据 + self.player_rooms = {} # 玩家所在房间映射 + + # 房间统计 + self.room_stats = { + "rooms_created": 0, + "rooms_destroyed": 0, + "players_added": 0, + "players_removed": 0, + "room_errors": 0 + } + + # 回调函数 + self.room_callbacks = { + "room_created": [], + "room_destroyed": [], + "player_added": [], + "player_removed": [], + "room_error": [] + } + + # 时间戳记录 + self.last_cleanup = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 房间分配器已创建") + + def initialize(self) -> bool: + """ + 初始化房间分配器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化房间分配器...") + + self.initialized = True + print("✓ 房间分配器初始化完成") + return True + + except Exception as e: + print(f"✗ 房间分配器初始化失败: {e}") + self.room_stats["room_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.room_stats["room_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用房间分配器""" + try: + self.enabled = False + + # 销毁所有房间 + self._destroy_all_rooms() + + print("✓ 房间分配器已禁用") + + except Exception as e: + print(f"✗ 房间分配器禁用失败: {e}") + self.room_stats["room_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理房间分配器资源""" + try: + # 禁用房间分配器 + if self.enabled: + self.disable() + + # 清理回调 + self.room_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_cleanup >= 300.0: # 每5分钟清理一次 + self._cleanup_expired_rooms(current_time) + self.last_cleanup = current_time + + except Exception as e: + print(f"✗ 房间分配器更新失败: {e}") + self.room_stats["room_errors"] += 1 + import traceback + traceback.print_exc() + + def _cleanup_expired_rooms(self, current_time: float): + """清理过期房间""" + try: + expired_rooms = [] + for room_id, room_data in self.rooms.items(): + # 检查房间是否超时 + if current_time - room_data.get("last_activity", 0) > self.room_config["room_timeout"]: + # 检查房间是否为空 + if len(room_data.get("players", {})) == 0: + expired_rooms.append(room_id) + + # 销毁过期房间 + for room_id in expired_rooms: + self.destroy_room(room_id) + + except Exception as e: + print(f"✗ 过期房间清理失败: {e}") + self.room_stats["room_errors"] += 1 + + def _destroy_all_rooms(self): + """销毁所有房间""" + try: + room_ids = list(self.rooms.keys()) + for room_id in room_ids: + self.destroy_room(room_id) + except Exception as e: + print(f"✗ 所有房间销毁失败: {e}") + self.room_stats["room_errors"] += 1 + + def create_room(self, room_name: str = None, room_settings: Dict[str, Any] = None, + room_password: str = None) -> Optional[str]: + """ + 创建房间 + + Args: + room_name: 房间名称 + room_settings: 房间设置 + room_password: 房间密码 + + Returns: + 房间ID或None + """ + try: + if not self.enabled: + return None + + # 检查房间数量限制 + if len(self.rooms) >= self.room_config["max_rooms"]: + print("✗ 房间数量已达上限") + self.room_stats["room_errors"] += 1 + return None + + # 生成房间ID + room_id = f"room_{int(time.time() * 1000000)}" + + # 使用默认设置或传入设置 + settings = { + "max_players": self.room_config["default_max_players"], + "is_public": True, + "allow_spectators": True + } + + if room_settings: + settings.update(room_settings) + + # 创建房间数据 + room_data = { + "room_id": room_id, + "room_name": room_name or f"房间-{room_id[:8]}", + "settings": settings, + "password": room_password, + "players": {}, # 玩家ID到玩家数据的映射 + "host_player_id": None, + "created_time": time.time(), + "last_activity": time.time(), + "room_state": {}, # 房间状态数据 + "custom_data": {} # 自定义房间数据 + } + + # 添加到房间列表 + self.rooms[room_id] = room_data + + self.room_state["total_rooms"] += 1 + self.room_state["active_rooms"] += 1 + self.room_stats["rooms_created"] += 1 + + # 触发房间创建回调 + self._trigger_room_callback("room_created", { + "room_id": room_id, + "room_name": room_data["room_name"], + "settings": settings, + "timestamp": room_data["created_time"] + }) + + print(f"✓ 房间已创建: {room_id} ({room_data['room_name']})") + return room_id + + except Exception as e: + print(f"✗ 房间创建失败: {e}") + self.room_stats["room_errors"] += 1 + return None + + def destroy_room(self, room_id: str) -> bool: + """ + 销毁房间 + + Args: + room_id: 房间ID + + Returns: + 是否销毁成功 + """ + try: + if not self.enabled: + return False + + # 检查房间是否存在 + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + return False + + room_data = self.rooms[room_id] + + # 通知房间内所有玩家 + player_ids = list(room_data["players"].keys()) + for player_id in player_ids: + self._notify_player_room_destroyed(player_id, room_id) + + # 移除房间 + del self.rooms[room_id] + + self.room_state["active_rooms"] -= 1 + self.room_stats["rooms_destroyed"] += 1 + + # 触发房间销毁回调 + self._trigger_room_callback("room_destroyed", { + "room_id": room_id, + "room_name": room_data["room_name"], + "timestamp": time.time() + }) + + print(f"✓ 房间已销毁: {room_id}") + return True + + except Exception as e: + print(f"✗ 房间销毁失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def _notify_player_room_destroyed(self, player_id: str, room_id: str): + """ + 通知玩家房间被销毁 + + Args: + player_id: 玩家ID + room_id: 房间ID + """ + try: + # 从玩家房间映射中移除 + if player_id in self.player_rooms: + del self.player_rooms[player_id] + + except Exception as e: + print(f"✗ 玩家房间销毁通知失败: {e}") + self.room_stats["room_errors"] += 1 + + def add_client_to_room(self, room_id: str, player_id: str, player_data: Dict[str, Any] = None) -> bool: + """ + 添加玩家到房间 + + Args: + room_id: 房间ID + player_id: 玩家ID + player_data: 玩家数据 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + # 检查房间是否存在 + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + self.room_stats["room_errors"] += 1 + return False + + room_data = self.rooms[room_id] + + # 检查房间是否已满 + if len(room_data["players"]) >= room_data["settings"]["max_players"]: + print(f"✗ 房间已满: {room_id}") + return False + + # 检查房间密码 + if room_data["password"] and not self._verify_room_password(room_id, player_data): + print(f"✗ 房间密码错误: {room_id}") + return False + + # 添加玩家到房间 + player_info = player_data or {} + player_info["join_time"] = time.time() + player_info["last_activity"] = time.time() + + room_data["players"][player_id] = player_info + room_data["last_activity"] = time.time() + + # 设置房间主机(如果是第一个玩家) + if room_data["host_player_id"] is None: + room_data["host_player_id"] = player_id + + # 更新玩家房间映射 + self.player_rooms[player_id] = room_id + + self.room_stats["players_added"] += 1 + + # 触发玩家添加回调 + self._trigger_room_callback("player_added", { + "room_id": room_id, + "room_name": room_data["room_name"], + "player_id": player_id, + "timestamp": time.time() + }) + + print(f"✓ 玩家已加入房间: {player_id} -> {room_id}") + return True + + except Exception as e: + print(f"✗ 玩家加入房间失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def remove_client_from_room(self, room_id: str, player_id: str) -> bool: + """ + 从房间移除玩家 + + Args: + room_id: 房间ID + player_id: 玩家ID + + Returns: + 是否移除成功 + """ + try: + if not self.enabled: + return False + + # 检查房间是否存在 + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + return False + + room_data = self.rooms[room_id] + + # 检查玩家是否在房间中 + if player_id not in room_data["players"]: + print(f"✗ 玩家不在房间中: {player_id}") + return False + + # 移除玩家 + del room_data["players"][player_id] + room_data["last_activity"] = time.time() + + # 更新主机(如果移除的是主机) + if room_data["host_player_id"] == player_id: + if room_data["players"]: + # 选择第一个玩家作为新主机 + room_data["host_player_id"] = next(iter(room_data["players"])) + else: + room_data["host_player_id"] = None + + # 移除玩家房间映射 + if player_id in self.player_rooms: + del self.player_rooms[player_id] + + self.room_stats["players_removed"] += 1 + + # 触发玩家移除回调 + self._trigger_room_callback("player_removed", { + "room_id": room_id, + "room_name": room_data["room_name"], + "player_id": player_id, + "timestamp": time.time() + }) + + # 如果房间为空,销毁房间 + if len(room_data["players"]) == 0: + self.destroy_room(room_id) + + print(f"✓ 玩家已离开房间: {player_id} <- {room_id}") + return True + + except Exception as e: + print(f"✗ 玩家离开房间失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def _verify_room_password(self, room_id: str, player_data: Dict[str, Any]) -> bool: + """ + 验证房间密码 + + Args: + room_id: 房间ID + player_data: 玩家数据 + + Returns: + 密码是否正确 + """ + try: + if not self.room_config["enable_room_passwords"]: + return True + + if room_id not in self.rooms: + return False + + room_data = self.rooms[room_id] + if not room_data["password"]: + return True + + player_password = player_data.get("password") if player_data else None + return player_password == room_data["password"] + + except Exception as e: + print(f"✗ 房间密码验证失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def get_room_info(self, room_id: str) -> Optional[Dict[str, Any]]: + """ + 获取房间信息 + + Args: + room_id: 房间ID + + Returns: + 房间信息或None + """ + try: + if room_id in self.rooms: + room_data = self.rooms[room_id] + # 返回不包含敏感信息的房间信息 + safe_room_data = { + "room_id": room_data["room_id"], + "room_name": room_data["room_name"], + "settings": room_data["settings"], + "player_count": len(room_data["players"]), + "has_password": bool(room_data["password"]), + "host_player_id": room_data["host_player_id"], + "created_time": room_data["created_time"], + "last_activity": room_data["last_activity"] + } + return safe_room_data + else: + return None + + except Exception as e: + print(f"✗ 获取房间信息失败: {e}") + self.room_stats["room_errors"] += 1 + return None + + def get_player_room(self, player_id: str) -> Optional[str]: + """ + 获取玩家所在房间 + + Args: + player_id: 玩家ID + + Returns: + 房间ID或None + """ + try: + return self.player_rooms.get(player_id) + except Exception as e: + print(f"✗ 获取玩家房间失败: {e}") + self.room_stats["room_errors"] += 1 + return None + + def get_room_players(self, room_id: str) -> List[str]: + """ + 获取房间内所有玩家 + + Args: + room_id: 房间ID + + Returns: + 玩家ID列表 + """ + try: + if room_id in self.rooms: + return list(self.rooms[room_id]["players"].keys()) + else: + return [] + + except Exception as e: + print(f"✗ 获取房间玩家失败: {e}") + self.room_stats["room_errors"] += 1 + return [] + + def update_room_state(self, room_id: str, state_data: Dict[str, Any]) -> bool: + """ + 更新房间状态 + + Args: + room_id: 房间ID + state_data: 状态数据 + + Returns: + 是否更新成功 + """ + try: + if room_id not in self.rooms: + return False + + self.rooms[room_id]["room_state"].update(state_data) + self.rooms[room_id]["last_activity"] = time.time() + return True + + except Exception as e: + print(f"✗ 更新房间状态失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def set_room_custom_data(self, room_id: str, key: str, value: Any) -> bool: + """ + 设置房间自定义数据 + + Args: + room_id: 房间ID + key: 数据键 + value: 数据值 + + Returns: + 是否设置成功 + """ + try: + if room_id not in self.rooms: + return False + + self.rooms[room_id]["custom_data"][key] = value + return True + + except Exception as e: + print(f"✗ 设置房间自定义数据失败: {e}") + self.room_stats["room_errors"] += 1 + return False + + def get_room_custom_data(self, room_id: str, key: str, default: Any = None) -> Any: + """ + 获取房间自定义数据 + + Args: + room_id: 房间ID + key: 数据键 + default: 默认值 + + Returns: + 数据值或默认值 + """ + try: + if room_id not in self.rooms: + return default + + return self.rooms[room_id]["custom_data"].get(key, default) + + except Exception as e: + print(f"✗ 获取房间自定义数据失败: {e}") + self.room_stats["room_errors"] += 1 + return default + + def get_room_stats(self) -> Dict[str, Any]: + """ + 获取房间统计信息 + + Returns: + 房间统计字典 + """ + return { + "state": self.room_state.copy(), + "stats": self.room_stats.copy(), + "config": self.room_config.copy(), + "current_rooms": len(self.rooms), + "current_players": len(self.player_rooms) + } + + def reset_stats(self): + """重置房间统计信息""" + try: + self.room_stats = { + "rooms_created": 0, + "rooms_destroyed": 0, + "players_added": 0, + "players_removed": 0, + "room_errors": 0 + } + print("✓ 房间统计信息已重置") + except Exception as e: + print(f"✗ 房间统计信息重置失败: {e}") + + def set_room_config(self, config: Dict[str, Any]) -> bool: + """ + 设置房间配置 + + Args: + config: 房间配置字典 + + Returns: + 是否设置成功 + """ + try: + self.room_config.update(config) + print(f"✓ 房间配置已更新: {self.room_config}") + return True + except Exception as e: + print(f"✗ 房间配置设置失败: {e}") + return False + + def get_room_config(self) -> Dict[str, Any]: + """ + 获取房间配置 + + Returns: + 房间配置字典 + """ + return self.room_config.copy() + + def _trigger_room_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发房间回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.room_callbacks: + for callback in self.room_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 房间回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 房间回调触发失败: {e}") + + def register_room_callback(self, callback_type: str, callback: callable): + """ + 注册房间回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.room_callbacks: + self.room_callbacks[callback_type].append(callback) + print(f"✓ 房间回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 房间回调注册失败: {e}") + + def unregister_room_callback(self, callback_type: str, callback: callable): + """ + 注销房间回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.room_callbacks: + if callback in self.room_callbacks[callback_type]: + self.room_callbacks[callback_type].remove(callback) + print(f"✓ 房间回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 房间回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/rules/rule_manager.py b/plugins/user/matchmaking_system/rules/rule_manager.py new file mode 100644 index 00000000..31d5d21a --- /dev/null +++ b/plugins/user/matchmaking_system/rules/rule_manager.py @@ -0,0 +1,633 @@ +""" +匹配规则管理器模块 +管理匹配规则和参数 +""" + +import time +from typing import Dict, Any, List, Optional + +class RuleManager: + """ + 匹配规则管理器 + 管理匹配规则和参数 + """ + + def __init__(self, plugin): + """ + 初始化匹配规则管理器 + + Args: + plugin: 匹配系统插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 规则配置 + self.rule_config = { + "default_rules": { + "min_players": 2, + "max_players": 8, + "skill_range": 100, + "max_wait_time": 120.0, + "region_matching": True, + "platform_matching": True, + "language_matching": True + }, + "game_mode_rules": {}, # 不同游戏模式的规则 + "custom_rules": {}, # 自定义规则 + "enable_rule_validation": True, + "enable_dynamic_rules": True + } + + # 规则状态 + self.rule_state = { + "active_rules": "default", + "total_rules": 1, # 默认规则 + "custom_rules_count": 0, + "rules_modified": 0 + } + + # 规则统计 + self.rule_stats = { + "rules_applied": 0, + "rules_violated": 0, + "rule_errors": 0 + } + + # 回调函数 + self.rule_callbacks = { + "rule_applied": [], + "rule_violated": [], + "rule_updated": [], + "rule_error": [] + } + + # 时间戳记录 + self.last_rule_application = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 匹配规则管理器已创建") + + def initialize(self) -> bool: + """ + 初始化匹配规则管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化匹配规则管理器...") + + self.initialized = True + print("✓ 匹配规则管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 匹配规则管理器初始化失败: {e}") + self.rule_stats["rule_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.rule_stats["rule_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用匹配规则管理器""" + try: + self.enabled = False + print("✓ 匹配规则管理器已禁用") + + except Exception as e: + print(f"✗ 匹配规则管理器禁用失败: {e}") + self.rule_stats["rule_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理匹配规则管理器资源""" + try: + # 禁用匹配规则管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.rule_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 + + self.last_rule_application = time.time() + + except Exception as e: + print(f"✗ 匹配规则管理器更新失败: {e}") + self.rule_stats["rule_errors"] += 1 + import traceback + traceback.print_exc() + + def apply_rules(self, players: List[Dict[str, Any]], game_mode: str = "default") -> bool: + """ + 应用匹配规则 + + Args: + players: 玩家列表 + game_mode: 游戏模式 + + Returns: + 是否符合规则 + """ + try: + if not self.enabled: + return True # 如果未启用,跳过规则检查 + + self.rule_stats["rules_applied"] += 1 + + # 获取适用的规则 + rules = self.get_active_rules(game_mode) + + # 检查玩家数量 + min_players = rules.get("min_players", 2) + max_players = rules.get("max_players", 8) + + if len(players) < min_players: + print(f"✗ 玩家数量不足: {len(players)} < {min_players}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "min_players", + "required": min_players, + "actual": len(players), + "timestamp": time.time() + }) + + return False + + if len(players) > max_players: + print(f"✗ 玩家数量超限: {len(players)} > {max_players}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "max_players", + "required": max_players, + "actual": len(players), + "timestamp": time.time() + }) + + return False + + # 检查技能范围 + if rules.get("skill_range") is not None: + skill_range = rules["skill_range"] + skills = [p.get("skill_level", 0) for p in players] + min_skill = min(skills) + max_skill = max(skills) + + if max_skill - min_skill > skill_range: + print(f"✗ 技能范围超限: {max_skill - min_skill} > {skill_range}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "skill_range", + "required": skill_range, + "actual": max_skill - min_skill, + "timestamp": time.time() + }) + + return False + + # 检查区域匹配 + if rules.get("region_matching", False): + regions = [p.get("region", "global") for p in players] + if len(set(regions)) > 1: + print(f"✗ 区域不匹配: {set(regions)}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "region_matching", + "regions": list(set(regions)), + "timestamp": time.time() + }) + + return False + + # 检查平台匹配 + if rules.get("platform_matching", False): + platforms = [p.get("platform", "pc") for p in players] + if len(set(platforms)) > 1: + print(f"✗ 平台不匹配: {set(platforms)}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "platform_matching", + "platforms": list(set(platforms)), + "timestamp": time.time() + }) + + return False + + # 检查语言匹配 + if rules.get("language_matching", False): + languages = [p.get("language", "en") for p in players] + if len(set(languages)) > 1: + print(f"✗ 语言不匹配: {set(languages)}") + self.rule_stats["rules_violated"] += 1 + + # 触发规则违反回调 + self._trigger_rule_callback("rule_violated", { + "rule": "language_matching", + "languages": list(set(languages)), + "timestamp": time.time() + }) + + return False + + # 触发规则应用回调 + self._trigger_rule_callback("rule_applied", { + "players": len(players), + "game_mode": game_mode, + "rules": rules, + "timestamp": time.time() + }) + + print(f"✓ 规则检查通过: {len(players)}名玩家, 游戏模式: {game_mode}") + return True + + except Exception as e: + print(f"✗ 规则应用失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def get_active_rules(self, game_mode: str = "default") -> Dict[str, Any]: + """ + 获取活动规则 + + Args: + game_mode: 游戏模式 + + Returns: + 规则字典 + """ + try: + # 检查是否有特定游戏模式的规则 + if game_mode in self.rule_config["game_mode_rules"]: + return self.rule_config["game_mode_rules"][game_mode] + + # 检查是否有自定义规则 + if self.rule_state["active_rules"] in self.rule_config["custom_rules"]: + return self.rule_config["custom_rules"][self.rule_state["active_rules"]] + + # 返回默认规则 + return self.rule_config["default_rules"] + + except Exception as e: + print(f"✗ 获取活动规则失败: {e}") + self.rule_stats["rule_errors"] += 1 + return self.rule_config["default_rules"] + + def set_active_rules(self, rule_set_name: str) -> bool: + """ + 设置活动规则集 + + Args: + rule_set_name: 规则集名称 + + Returns: + 是否设置成功 + """ + try: + # 验证规则集存在 + if (rule_set_name != "default" and + rule_set_name not in self.rule_config["custom_rules"] and + rule_set_name not in self.rule_config["game_mode_rules"]): + print(f"✗ 规则集不存在: {rule_set_name}") + return False + + self.rule_state["active_rules"] = rule_set_name + + # 触发规则更新回调 + self._trigger_rule_callback("rule_updated", { + "rule_set": rule_set_name, + "timestamp": time.time() + }) + + print(f"✓ 活动规则集已设置为: {rule_set_name}") + return True + + except Exception as e: + print(f"✗ 活动规则集设置失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def add_custom_rule_set(self, rule_set_name: str, rules: Dict[str, Any]) -> bool: + """ + 添加自定义规则集 + + Args: + rule_set_name: 规则集名称 + rules: 规则字典 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + # 验证规则 + if self.rule_config["enable_rule_validation"]: + if not self._validate_rules(rules): + print(f"✗ 规则验证失败: {rule_set_name}") + self.rule_stats["rule_errors"] += 1 + return False + + self.rule_config["custom_rules"][rule_set_name] = rules + self.rule_state["custom_rules_count"] = len(self.rule_config["custom_rules"]) + self.rule_state["total_rules"] += 1 + self.rule_state["rules_modified"] += 1 + + print(f"✓ 自定义规则集已添加: {rule_set_name}") + return True + + except Exception as e: + print(f"✗ 自定义规则集添加失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def remove_custom_rule_set(self, rule_set_name: str) -> bool: + """ + 移除自定义规则集 + + Args: + rule_set_name: 规则集名称 + + Returns: + 是否移除成功 + """ + try: + if rule_set_name in self.rule_config["custom_rules"]: + del self.rule_config["custom_rules"][rule_set_name] + self.rule_state["custom_rules_count"] = len(self.rule_config["custom_rules"]) + self.rule_state["total_rules"] -= 1 + self.rule_state["rules_modified"] += 1 + + # 如果删除的是活动规则集,切换回默认规则 + if self.rule_state["active_rules"] == rule_set_name: + self.rule_state["active_rules"] = "default" + + print(f"✓ 自定义规则集已移除: {rule_set_name}") + return True + else: + print(f"✗ 自定义规则集不存在: {rule_set_name}") + return False + + except Exception as e: + print(f"✗ 自定义规则集移除失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def _validate_rules(self, rules: Dict[str, Any]) -> bool: + """ + 验证规则 + + Args: + rules: 规则字典 + + Returns: + 是否验证通过 + """ + try: + # 检查基本规则参数 + if "min_players" in rules: + if not isinstance(rules["min_players"], int) or rules["min_players"] < 1: + return False + + if "max_players" in rules: + if not isinstance(rules["max_players"], int) or rules["max_players"] < 1: + return False + + if "min_players" in rules and "max_players" in rules: + if rules["min_players"] > rules["max_players"]: + return False + + if "skill_range" in rules: + if not isinstance(rules["skill_range"], (int, float)) or rules["skill_range"] < 0: + return False + + if "max_wait_time" in rules: + if not isinstance(rules["max_wait_time"], (int, float)) or rules["max_wait_time"] < 0: + return False + + return True + + except Exception as e: + print(f"✗ 规则验证失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def add_game_mode_rules(self, game_mode: str, rules: Dict[str, Any]) -> bool: + """ + 添加游戏模式规则 + + Args: + game_mode: 游戏模式 + rules: 规则字典 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + # 验证规则 + if self.rule_config["enable_rule_validation"]: + if not self._validate_rules(rules): + print(f"✗ 游戏模式规则验证失败: {game_mode}") + self.rule_stats["rule_errors"] += 1 + return False + + self.rule_config["game_mode_rules"][game_mode] = rules + self.rule_state["total_rules"] += 1 + self.rule_state["rules_modified"] += 1 + + print(f"✓ 游戏模式规则已添加: {game_mode}") + return True + + except Exception as e: + print(f"✗ 游戏模式规则添加失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def remove_game_mode_rules(self, game_mode: str) -> bool: + """ + 移除游戏模式规则 + + Args: + game_mode: 游戏模式 + + Returns: + 是否移除成功 + """ + try: + if game_mode in self.rule_config["game_mode_rules"]: + del self.rule_config["game_mode_rules"][game_mode] + self.rule_state["total_rules"] -= 1 + self.rule_state["rules_modified"] += 1 + + print(f"✓ 游戏模式规则已移除: {game_mode}") + return True + else: + print(f"✗ 游戏模式规则不存在: {game_mode}") + return False + + except Exception as e: + print(f"✗ 游戏模式规则移除失败: {e}") + self.rule_stats["rule_errors"] += 1 + return False + + def get_rule_stats(self) -> Dict[str, Any]: + """ + 获取规则统计信息 + + Returns: + 规则统计字典 + """ + return { + "state": self.rule_state.copy(), + "stats": self.rule_stats.copy(), + "config": self.rule_config.copy() + } + + def reset_stats(self): + """重置规则统计信息""" + try: + self.rule_stats = { + "rules_applied": 0, + "rules_violated": 0, + "rule_errors": 0 + } + print("✓ 规则统计信息已重置") + except Exception as e: + print(f"✗ 规则统计信息重置失败: {e}") + + def set_rule_config(self, config: Dict[str, Any]) -> bool: + """ + 设置规则配置 + + Args: + config: 规则配置字典 + + Returns: + 是否设置成功 + """ + try: + self.rule_config.update(config) + print(f"✓ 规则配置已更新: {self.rule_config}") + return True + except Exception as e: + print(f"✗ 规则配置设置失败: {e}") + return False + + def get_rule_config(self) -> Dict[str, Any]: + """ + 获取规则配置 + + Returns: + 规则配置字典 + """ + return self.rule_config.copy() + + def _trigger_rule_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发规则回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.rule_callbacks: + for callback in self.rule_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 规则回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 规则回调触发失败: {e}") + + def register_rule_callback(self, callback_type: str, callback: callable): + """ + 注册规则回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.rule_callbacks: + self.rule_callbacks[callback_type].append(callback) + print(f"✓ 规则回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 规则回调注册失败: {e}") + + def unregister_rule_callback(self, callback_type: str, callback: callable): + """ + 注销规则回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.rule_callbacks: + if callback in self.rule_callbacks[callback_type]: + self.rule_callbacks[callback_type].remove(callback) + print(f"✓ 规则回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 规则回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/matchmaking_system/stats/stats_manager.py b/plugins/user/matchmaking_system/stats/stats_manager.py new file mode 100644 index 00000000..74e57670 --- /dev/null +++ b/plugins/user/matchmaking_system/stats/stats_manager.py @@ -0,0 +1,657 @@ +""" +统计数据管理器模块 +收集和分析匹配数据 +""" + +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}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/README.md b/plugins/user/realtime_communication/README.md new file mode 100644 index 00000000..e2337965 --- /dev/null +++ b/plugins/user/realtime_communication/README.md @@ -0,0 +1,331 @@ +# 实时通信插件 + +为EG引擎提供完整的实时通信功能,支持WebSocket双向通信、客户端管理、消息路由、房间系统等功能。 + +## 功能特性 + +### 1. WebSocket服务器 +- 实时双向通信支持 +- SSL/TLS加密连接 +- 心跳检测和连接管理 +- 消息压缩和优化 + +### 2. 客户端管理 +- 客户端连接和断开管理 +- 认证和授权系统 +- 黑名单和速率限制 +- 客户端状态跟踪 + +### 3. 消息路由 +- 智能消息分发 +- 房间内消息广播 +- 点对点消息传递 +- 消息过滤和验证 + +### 4. 房间系统 +- 动态房间创建和销毁 +- 房间密码保护 +- 客户端加入/离开管理 +- 房间状态同步 + +### 5. 协议处理 +- 多协议支持(JSON、二进制等) +- 消息压缩和解压缩 +- 数据序列化和反序列化 +- 消息分片和重组 + +### 6. 安全认证 +- Token认证机制 +- 密码哈希和验证 +- 会话管理 +- IP过滤和阻止 + +### 7. 监控系统 +- 实时性能监控 +- 日志记录和分析 +- 警报系统 +- 指标收集和展示 + +### 8. 配置管理 +- 灵活的配置系统 +- 实时配置更新 +- 配置导入导出 +- 默认配置模板 + +### 9. 事件系统 +- 事件驱动架构 +- 异步事件处理 +- 事件过滤和优先级 +- 事件监听器系统 + +### 10. 可视化编辑器 +- 实时数据监控 +- 配置界面 +- 统计信息展示 +- 数据导出功能 + +## 系统架构 + +``` +实时通信插件 +├── 核心模块 +│ └── WebSocket服务器 (core/websocket_server.py) +├── 客户端管理 +│ └── 客户端管理器 (clients/client_manager.py) +├── 消息处理 +│ ├── 消息路由器 (messaging/message_router.py) +│ ├── 协议处理器 (protocol/protocol_handler.py) +│ └── 数据序列化器 (serialization/data_serializer.py) +├── 房间系统 +│ └── 房间管理器 (rooms/room_manager.py) +├── 安全系统 +│ └── 认证管理器 (auth/auth_manager.py) +├── 监控系统 +│ └── 通信监控器 (monitoring/communication_monitor.py) +├── 配置管理 +│ └── 通信配置器 (config/comm_config.py) +├── 事件系统 +│ └── 事件处理器 (events/event_handler.py) +├── 编辑器接口 +│ └── 通信编辑器 (editor/comm_editor.py) +└── 工具模块 + └── 通信工具类 (utils/comm_utils.py) +``` + +## 安装和使用 + +### 安装依赖 + +```bash +# 安装必要的Python包 +pip install psutil +``` + +### 配置插件 + +插件配置文件位于 `config/communication_config.json`,包含以下主要配置项: + +```json +{ + "server": { + "host": "0.0.0.0", + "port": 8080, + "enable_ssl": false + }, + "client": { + "max_clients": 1000, + "enable_authentication": true + }, + "message": { + "max_message_size": 65536, + "enable_message_compression": true + } +} +``` + +### 启动插件 + +```python +# 初始化插件 +plugin = RealtimeCommunicationPlugin(engine) +plugin.initialize() +plugin.enable() + +# 启动WebSocket服务器 +plugin.websocket_server.start_server() + +# 插件会自动处理客户端连接、消息路由等操作 +``` + +## API参考 + +### WebSocket服务器 + +```python +# 发送消息到客户端 +plugin.websocket_server.send_message_to_client(client_id, message) + +# 广播消息 +plugin.websocket_server.broadcast_message(message) + +# 断开客户端连接 +plugin.websocket_server.disconnect_client(client_id, reason) +``` + +### 客户端管理 + +```python +# 添加客户端 +plugin.client_manager.add_client(client_id, client_info) + +# 认证客户端 +plugin.client_manager.authenticate_client(client_id, auth_data) + +# 禁止客户端 +plugin.client_manager.ban_client(client_id, reason) +``` + +### 消息路由 + +```python +# 路由消息 +plugin.message_router.route_message(client_id, message) + +# 发送直接消息 +plugin.message_router.send_direct_message(from_client_id, to_client_id, message) + +# 房间广播 +plugin.message_router.broadcast_to_room(room_id, message) +``` + +### 房间管理 + +```python +# 创建房间 +room_id = plugin.room_manager.create_room(room_name, settings) + +# 添加客户端到房间 +plugin.room_manager.add_client_to_room(room_id, client_id) + +# 从房间移除客户端 +plugin.room_manager.remove_client_from_room(room_id, client_id) + +# 销毁房间 +plugin.room_manager.destroy_room(room_id) +``` + +## 消息格式 + +### 系统消息 + +```json +{ + "type": "ping", + "timestamp": 1234567890.123 +} +``` + +```json +{ + "type": "pong", + "timestamp": 1234567890.123, + "ping_timestamp": 1234567890.120 +} +``` + +### 聊天消息 + +```json +{ + "type": "chat", + "content": "Hello, world!", + "sender": "user123" +} +``` + +### 房间消息 + +```json +{ + "type": "join_room", + "room_id": "room123" +} +``` + +```json +{ + "type": "leave_room", + "room_id": "room123" +} +``` + +## 性能优化 + +1. **连接管理**:使用连接池和复用机制减少资源消耗 +2. **消息压缩**:对大数据消息进行自动压缩 +3. **异步处理**:采用多线程和异步I/O提高并发性能 +4. **缓存机制**:对频繁访问的数据进行缓存 +5. **内存优化**:及时清理无用对象和连接 + +## 安全特性 + +1. **认证机制**:支持Token和密码认证 +2. **权限控制**:基于角色的访问控制 +3. **数据加密**:支持SSL/TLS传输加密 +4. **防攻击**:IP过滤、速率限制、防DDoS +5. **审计日志**:完整操作日志记录 + +## 监控和调试 + +### 实时监控 +- CPU和内存使用率 +- 网络流量统计 +- 连接数和消息数 +- 错误率和响应时间 + +### 日志系统 +- 详细的操作日志 +- 错误和异常记录 +- 性能指标日志 +- 安全审计日志 + +## 扩展开发 + +### 添加新的消息类型 + +```python +# 在消息路由器中注册新的处理器 +plugin.message_router.register_message_handler("custom_type", custom_handler) + +def custom_handler(client_id, message): + # 处理自定义消息 + pass +``` + +### 添加新的协议 + +```python +# 在协议处理器中注册新的协议 +plugin.protocol_handler.add_protocol_handler("custom", encode_func, decode_func) +``` + +### 添加事件监听器 + +```python +# 注册事件监听器 +plugin.event_handler.register_event_handler("client_connected", on_client_connected) + +def on_client_connected(payload): + # 处理客户端连接事件 + pass +``` + +## 故障排除 + +### 常见问题 + +1. **连接失败**:检查端口是否被占用,防火墙设置 +2. **认证失败**:检查认证配置和凭证 +3. **性能问题**:检查系统资源使用情况,优化配置 +4. **消息丢失**:检查网络连接,调整消息队列大小 + +### 日志分析 + +查看日志文件以诊断问题: +``` +logs/communication_monitor.log +``` + +## 版本信息 + +- 版本:1.0.0 +- 作者:EG Plugin System +- 许可证:MIT + +## 贡献指南 + +欢迎提交Issue和Pull Request来改进这个插件。 + +## 支持 + +如需技术支持,请联系插件维护团队或查看相关文档。 \ No newline at end of file diff --git a/plugins/user/realtime_communication/auth/auth_manager.py b/plugins/user/realtime_communication/auth/auth_manager.py new file mode 100644 index 00000000..3b0009ee --- /dev/null +++ b/plugins/user/realtime_communication/auth/auth_manager.py @@ -0,0 +1,900 @@ +""" +认证管理器模块 +确保通信安全 +""" + +import time +import hashlib +import secrets +import hmac +from typing import Dict, Any, List, Optional + +class AuthManager: + """ + 认证管理器 + 确保通信安全 + """ + + def __init__(self, plugin): + """ + 初始化认证管理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 认证配置 + self.auth_config = { + "enable_authentication": True, + "auth_timeout": 30.0, + "enable_token_auth": True, + "enable_password_auth": True, + "token_lifetime": 86400, # 24小时 + "enable_session_management": True, + "session_timeout": 3600, # 1小时 + "enable_two_factor_auth": False, + "max_login_attempts": 5, + "login_lockout_duration": 900, # 15分钟 + "enable_ip_filtering": True, + "enable_audit_logging": True + } + + # 认证状态 + self.auth_state = { + "active_sessions": 0, + "total_authentications": 0, + "successful_authentications": 0, + "failed_authentications": 0 + } + + # 认证存储 + self.sessions = {} + self.tokens = {} + self.login_attempts = {} + self.blocked_ips = {} + + # 认证统计 + self.auth_stats = { + "tokens_generated": 0, + "sessions_created": 0, + "sessions_expired": 0, + "users_banned": 0, + "auth_errors": 0 + } + + # 密钥管理 + self.secret_key = self._generate_secret_key() + + # 回调函数 + self.auth_callbacks = { + "auth_success": [], + "auth_failed": [], + "session_created": [], + "session_expired": [], + "user_banned": [], + "auth_error": [] + } + + # 时间戳记录 + self.last_session_cleanup = 0.0 + self.last_auth_event = 0.0 + + print("✓ 认证管理器已创建") + + def initialize(self) -> bool: + """ + 初始化认证管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化认证管理器...") + + # 生成密钥 + self.secret_key = self._generate_secret_key() + + self.initialized = True + print("✓ 认证管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 认证管理器初始化失败: {e}") + self.auth_stats["auth_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _generate_secret_key(self) -> str: + """ + 生成密钥 + + Returns: + 生成的密钥 + """ + try: + return secrets.token_hex(32) + except Exception as e: + print(f"✗ 密钥生成失败: {e}") + # 使用默认密钥(仅用于开发环境) + return "default_secret_key_for_development_purposes_only" + + 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.auth_stats["auth_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用认证管理器""" + try: + self.enabled = False + + # 清理会话和令牌 + self.sessions.clear() + self.tokens.clear() + + print("✓ 认证管理器已禁用") + + except Exception as e: + print(f"✗ 认证管理器禁用失败: {e}") + self.auth_stats["auth_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理认证管理器资源""" + try: + # 禁用认证管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.auth_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_session_cleanup > 300: # 每5分钟清理一次 + self._cleanup_expired_sessions(current_time) + self.last_session_cleanup = current_time + + # 定期清理登录尝试记录 + self._cleanup_login_attempts(current_time) + + self.last_auth_event = current_time + + except Exception as e: + print(f"✗ 认证管理器更新失败: {e}") + self.auth_stats["auth_errors"] += 1 + import traceback + traceback.print_exc() + + def _cleanup_expired_sessions(self, current_time: float): + """清理过期会话""" + try: + expired_sessions = [] + for session_id, session_data in self.sessions.items(): + if current_time - session_data["login_time"] > self.auth_config["session_timeout"]: + expired_sessions.append(session_id) + + for session_id in expired_sessions: + del self.sessions[session_id] + self.auth_state["active_sessions"] -= 1 + self.auth_stats["sessions_expired"] += 1 + + # 触发会话过期回调 + self._trigger_auth_callback("session_expired", { + "session_id": session_id, + "timestamp": current_time + }) + + except Exception as e: + print(f"✗ 过期会话清理失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def _cleanup_login_attempts(self, current_time: float): + """清理登录尝试记录""" + try: + expired_attempts = [] + for ip_address, attempt_data in self.login_attempts.items(): + if current_time - attempt_data["last_attempt"] > self.auth_config["login_lockout_duration"]: + expired_attempts.append(ip_address) + + for ip_address in expired_attempts: + del self.login_attempts[ip_address] + + except Exception as e: + print(f"✗ 登录尝试记录清理失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def authenticate_client(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 认证客户端 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + if not self.enabled or not self.auth_config["enable_authentication"]: + # 如果未启用认证,直接通过 + return True + + # 检查IP是否被阻止 + client_ip = auth_data.get("client_ip", "") + if self._is_ip_blocked(client_ip): + self.auth_stats["failed_authentications"] += 1 + return False + + # 检查登录尝试次数 + if self._is_login_blocked(client_ip): + self.auth_stats["failed_authentications"] += 1 + return False + + # 执行认证 + auth_result = self._perform_authentication(client_id, auth_data) + + if auth_result: + self.auth_state["successful_authentications"] += 1 + self.auth_state["total_authentications"] += 1 + + # 触发认证成功回调 + self._trigger_auth_callback("auth_success", { + "client_id": client_id, + "auth_data": auth_data, + "timestamp": time.time() + }) + + # 记录审计日志 + if self.auth_config["enable_audit_logging"]: + self._log_auth_event("auth_success", client_id, client_ip) + + else: + self.auth_state["failed_authentications"] += 1 + self.auth_state["total_authentications"] += 1 + + # 记录失败的登录尝试 + self._record_failed_login(client_ip) + + # 触发认证失败回调 + self._trigger_auth_callback("auth_failed", { + "client_id": client_id, + "auth_data": auth_data, + "timestamp": time.time() + }) + + # 记录审计日志 + if self.auth_config["enable_audit_logging"]: + self._log_auth_event("auth_failed", client_id, client_ip) + + return auth_result + + except Exception as e: + print(f"✗ 客户端认证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _is_ip_blocked(self, client_ip: str) -> bool: + """ + 检查IP是否被阻止 + + Args: + client_ip: 客户端IP + + Returns: + IP是否被阻止 + """ + try: + if not self.auth_config["enable_ip_filtering"]: + return False + + if client_ip in self.blocked_ips: + block_data = self.blocked_ips[client_ip] + if time.time() - block_data["block_time"] < block_data["duration"]: + return True + else: + # 阻止时间已过期,移除 + del self.blocked_ips[client_ip] + + return False + + except Exception as e: + print(f"✗ IP阻止检查失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _is_login_blocked(self, client_ip: str) -> bool: + """ + 检查登录是否被阻止 + + Args: + client_ip: 客户端IP + + Returns: + 登录是否被阻止 + """ + try: + if client_ip in self.login_attempts: + attempts = self.login_attempts[client_ip] + if (attempts["count"] >= self.auth_config["max_login_attempts"] and + time.time() - attempts["last_attempt"] < self.auth_config["login_lockout_duration"]): + return True + + return False + + except Exception as e: + print(f"✗ 登录阻止检查失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _record_failed_login(self, client_ip: str): + """ + 记录失败的登录尝试 + + Args: + client_ip: 客户端IP + """ + try: + current_time = time.time() + + if client_ip not in self.login_attempts: + self.login_attempts[client_ip] = { + "count": 1, + "last_attempt": current_time + } + else: + self.login_attempts[client_ip]["count"] += 1 + self.login_attempts[client_ip]["last_attempt"] = current_time + + # 如果超过最大尝试次数,阻止IP + if (self.login_attempts[client_ip]["count"] >= + self.auth_config["max_login_attempts"]): + self._block_ip(client_ip, self.auth_config["login_lockout_duration"]) + + except Exception as e: + print(f"✗ 失败登录记录失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def _perform_authentication(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 执行认证 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + # 检查认证类型 + auth_type = auth_data.get("auth_type", "token") + + if auth_type == "token": + return self._authenticate_with_token(client_id, auth_data) + elif auth_type == "password": + return self._authenticate_with_password(client_id, auth_data) + elif auth_type == "session": + return self._authenticate_with_session(client_id, auth_data) + else: + # 默认认证方式 + return self._authenticate_with_token(client_id, auth_data) + + except Exception as e: + print(f"✗ 认证执行失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _authenticate_with_token(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 使用令牌认证 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + token = auth_data.get("token") + if not token: + return False + + # 验证令牌 + if token in self.tokens: + token_data = self.tokens[token] + if time.time() - token_data["created_time"] < self.auth_config["token_lifetime"]: + # 令牌有效,创建会话 + self._create_session(client_id, auth_data) + return True + else: + # 令牌过期,移除 + del self.tokens[token] + + return False + + except Exception as e: + print(f"✗ 令牌认证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _authenticate_with_password(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 使用密码认证 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + username = auth_data.get("username") + password = auth_data.get("password") + + if not username or not password: + return False + + # 验证密码(简化实现) + # 在实际应用中,这里应该连接到用户数据库验证用户名和密码 + if username == "test_user" and password == "test_password": + # 创建会话 + self._create_session(client_id, auth_data) + return True + else: + return False + + except Exception as e: + print(f"✗ 密码认证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _authenticate_with_session(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 使用会话认证 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + session_id = auth_data.get("session_id") + if not session_id: + return False + + # 检查会话是否存在且有效 + if session_id in self.sessions: + session_data = self.sessions[session_id] + if time.time() - session_data["login_time"] < self.auth_config["session_timeout"]: + # 更新会话最后活动时间 + session_data["last_activity"] = time.time() + return True + + return False + + except Exception as e: + print(f"✗ 会话认证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _create_session(self, client_id: str, auth_data: Dict[str, Any]): + """ + 创建会话 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + """ + try: + session_id = self._generate_session_id() + + session_data = { + "client_id": client_id, + "login_time": time.time(), + "last_activity": time.time(), + "user_id": auth_data.get("user_id", ""), + "username": auth_data.get("username", ""), + "permissions": auth_data.get("permissions", []) + } + + self.sessions[session_id] = session_data + self.auth_state["active_sessions"] += 1 + self.auth_stats["sessions_created"] += 1 + + # 触发会话创建回调 + self._trigger_auth_callback("session_created", { + "session_id": session_id, + "client_id": client_id, + "auth_data": auth_data, + "timestamp": time.time() + }) + + except Exception as e: + print(f"✗ 会话创建失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def _generate_session_id(self) -> str: + """ + 生成会话ID + + Returns: + 生成的会话ID + """ + try: + return secrets.token_urlsafe(32) + except Exception as e: + print(f"✗ 会话ID生成失败: {e}") + self.auth_stats["auth_errors"] += 1 + return f"session_{int(time.time() * 1000000)}" + + def generate_token(self, user_id: str, permissions: List[str] = None) -> str: + """ + 生成令牌 + + Args: + user_id: 用户ID + permissions: 权限列表 + + Returns: + 生成的令牌 + """ + try: + token = secrets.token_urlsafe(64) + + self.tokens[token] = { + "user_id": user_id, + "permissions": permissions or [], + "created_time": time.time() + } + + self.auth_stats["tokens_generated"] += 1 + + return token + + except Exception as e: + print(f"✗ 令牌生成失败: {e}") + self.auth_stats["auth_errors"] += 1 + return "" + + def validate_token(self, token: str) -> bool: + """ + 验证令牌 + + Args: + token: 令牌 + + Returns: + 令牌是否有效 + """ + try: + if not token: + return False + + if token in self.tokens: + token_data = self.tokens[token] + if time.time() - token_data["created_time"] < self.auth_config["token_lifetime"]: + return True + else: + # 令牌过期,移除 + del self.tokens[token] + + return False + + except Exception as e: + print(f"✗ 令牌验证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def hash_password(self, password: str, salt: str = None) -> Dict[str, str]: + """ + 哈希密码 + + Args: + password: 密码 + salt: 盐值(可选) + + Returns: + 包含哈希值和盐值的字典 + """ + try: + if salt is None: + salt = secrets.token_hex(16) + + # 使用PBKDF2哈希密码 + password_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt.encode('utf-8'), 100000) + return { + "hash": password_hash.hex(), + "salt": salt + } + + except Exception as e: + print(f"✗ 密码哈希失败: {e}") + self.auth_stats["auth_errors"] += 1 + return {} + + def verify_password(self, password: str, password_hash: str, salt: str) -> bool: + """ + 验证密码 + + Args: + password: 密码 + password_hash: 密码哈希值 + salt: 盐值 + + Returns: + 密码是否正确 + """ + try: + # 重新哈希密码 + new_hash = hashlib.pbkdf2_hmac('sha256', password.encode('utf-8'), salt.encode('utf-8'), 100000) + return hmac.compare_digest(new_hash.hex(), password_hash) + + except Exception as e: + print(f"✗ 密码验证失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _block_ip(self, client_ip: str, duration: float): + """ + 阻止IP地址 + + Args: + client_ip: 客户端IP + duration: 阻止持续时间(秒) + """ + try: + self.blocked_ips[client_ip] = { + "block_time": time.time(), + "duration": duration + } + + # 触发用户禁止回调 + self._trigger_auth_callback("user_banned", { + "ip_address": client_ip, + "duration": duration, + "timestamp": time.time() + }) + + # 记录审计日志 + if self.auth_config["enable_audit_logging"]: + self._log_auth_event("ip_blocked", None, client_ip) + + print(f"✓ IP地址已被阻止: {client_ip} (持续 {duration} 秒)") + + except Exception as e: + print(f"✗ IP阻止失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def unblock_ip(self, client_ip: str) -> bool: + """ + 解除IP阻止 + + Args: + client_ip: 客户端IP + + Returns: + 是否解除成功 + """ + try: + if client_ip in self.blocked_ips: + del self.blocked_ips[client_ip] + print(f"✓ IP地址阻止已解除: {client_ip}") + return True + else: + print(f"✗ IP地址未被阻止: {client_ip}") + return False + + except Exception as e: + print(f"✗ IP阻止解除失败: {e}") + self.auth_stats["auth_errors"] += 1 + return False + + def _log_auth_event(self, event_type: str, client_id: str = None, client_ip: str = None): + """ + 记录认证事件 + + Args: + event_type: 事件类型 + client_id: 客户端ID(可选) + client_ip: 客户端IP(可选) + """ + try: + if not self.auth_config["enable_audit_logging"]: + return + + event = { + "event_type": event_type, + "client_id": client_id, + "client_ip": client_ip, + "timestamp": time.time() + } + + # 在实际实现中,这里会将事件记录到日志文件或数据库 + print(f"[认证日志] {event_type} - 客户端: {client_id}, IP: {client_ip}") + + except Exception as e: + print(f"✗ 认证事件记录失败: {e}") + self.auth_stats["auth_errors"] += 1 + + def get_active_sessions(self) -> Dict[str, Any]: + """ + 获取活跃会话 + + Returns: + 活跃会话字典 + """ + try: + return self.sessions.copy() + except Exception as e: + print(f"✗ 获取活跃会话失败: {e}") + self.auth_stats["auth_errors"] += 1 + return {} + + def get_blocked_ips(self) -> Dict[str, Any]: + """ + 获取被阻止的IP地址 + + Returns: + 被阻止的IP地址字典 + """ + try: + return self.blocked_ips.copy() + except Exception as e: + print(f"✗ 获取被阻止的IP地址失败: {e}") + self.auth_stats["auth_errors"] += 1 + return {} + + def get_auth_stats(self) -> Dict[str, Any]: + """ + 获取认证统计信息 + + Returns: + 认证统计字典 + """ + return { + "state": self.auth_state.copy(), + "stats": self.auth_stats.copy(), + "config": self.auth_config.copy() + } + + def reset_stats(self): + """重置认证统计信息""" + try: + self.auth_stats = { + "tokens_generated": 0, + "sessions_created": 0, + "sessions_expired": 0, + "users_banned": 0, + "auth_errors": 0 + } + print("✓ 认证统计信息已重置") + except Exception as e: + print(f"✗ 认证统计信息重置失败: {e}") + + def set_auth_config(self, config: Dict[str, Any]) -> bool: + """ + 设置认证配置 + + Args: + config: 认证配置字典 + + Returns: + 是否设置成功 + """ + try: + self.auth_config.update(config) + print(f"✓ 认证配置已更新: {self.auth_config}") + return True + except Exception as e: + print(f"✗ 认证配置设置失败: {e}") + return False + + def get_auth_config(self) -> Dict[str, Any]: + """ + 获取认证配置 + + Returns: + 认证配置字典 + """ + return self.auth_config.copy() + + def _trigger_auth_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发认证回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.auth_callbacks: + for callback in self.auth_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 认证回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 认证回调触发失败: {e}") + + def register_auth_callback(self, callback_type: str, callback: callable): + """ + 注册认证回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.auth_callbacks: + self.auth_callbacks[callback_type].append(callback) + print(f"✓ 认证回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 认证回调注册失败: {e}") + + def unregister_auth_callback(self, callback_type: str, callback: callable): + """ + 注销认证回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.auth_callbacks: + if callback in self.auth_callbacks[callback_type]: + self.auth_callbacks[callback_type].remove(callback) + print(f"✓ 认证回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 认证回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/clients/client_manager.py b/plugins/user/realtime_communication/clients/client_manager.py new file mode 100644 index 00000000..67fd9ae8 --- /dev/null +++ b/plugins/user/realtime_communication/clients/client_manager.py @@ -0,0 +1,724 @@ +""" +客户端管理器模块 +管理客户端连接和状态 +""" + +import time +import threading +import hashlib +from typing import Dict, Any, List, Optional + +class ClientManager: + """ + 客户端管理器 + 管理客户端连接和状态 + """ + + def __init__(self, plugin): + """ + 初始化客户端管理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 客户端配置 + self.client_config = { + "enable_authentication": True, + "auth_timeout": 30.0, + "max_clients": 1000, + "enable_heartbeat": True, + "heartbeat_interval": 30.0, + "enable_rate_limiting": True, + "max_messages_per_second": 100, + "enable_banning": True, + "ban_duration": 3600, + "client_timeout": 60.0 + } + + # 客户端状态 + self.client_state = { + "current_clients": 0, + "total_clients": 0, + "authenticated_clients": 0, + "banned_clients": 0 + } + + # 客户端存储 + self.clients = {} + self.client_lock = threading.RLock() + + # 客户端统计 + self.client_stats = { + "clients_joined": 0, + "clients_left": 0, + "authentication_success": 0, + "authentication_failed": 0, + "messages_sent": 0, + "messages_received": 0, + "banned_clients": 0, + "errors": 0 + } + + # 黑名单管理 + self.banned_ips = {} + self.banned_users = {} + + # 回调函数 + self.client_callbacks = { + "client_joined": [], + "client_left": [], + "client_authenticated": [], + "client_banned": [], + "client_error": [] + } + + # 时间戳记录 + self.last_heartbeat_check = 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.client_stats["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.client_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用客户端管理器""" + try: + self.enabled = False + + # 断开所有客户端 + self._disconnect_all_clients() + + print("✓ 客户端管理器已禁用") + + except Exception as e: + print(f"✗ 客户端管理器禁用失败: {e}") + self.client_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理客户端管理器资源""" + try: + # 禁用客户端管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.client_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_heartbeat_check > self.client_config["heartbeat_interval"]: + self._check_client_heartbeats(current_time) + self.last_heartbeat_check = current_time + + # 定期清理 + if current_time - self.last_cleanup > 60.0: # 每分钟清理一次 + self._cleanup_banned_clients(current_time) + self.last_cleanup = current_time + + except Exception as e: + print(f"✗ 客户端管理器更新失败: {e}") + self.client_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def _check_client_heartbeats(self, current_time: float): + """检查客户端心跳""" + try: + if not self.client_config["enable_heartbeat"]: + return + + clients_to_disconnect = [] + with self.client_lock: + for client_id, client_data in self.clients.items(): + last_heartbeat = client_data.get("last_heartbeat", client_data["connect_time"]) + if current_time - last_heartbeat > self.client_config["client_timeout"]: + clients_to_disconnect.append(client_id) + + for client_id in clients_to_disconnect: + self.disconnect_client(client_id, "timeout") + + except Exception as e: + print(f"✗ 客户端心跳检查失败: {e}") + self.client_stats["errors"] += 1 + + def _cleanup_banned_clients(self, current_time: float): + """清理过期的黑名单客户端""" + try: + expired_bans = [] + for ip, ban_data in self.banned_ips.items(): + if current_time - ban_data["ban_time"] > self.client_config["ban_duration"]: + expired_bans.append(ip) + + for ip in expired_bans: + del self.banned_ips[ip] + self.client_state["banned_clients"] -= 1 + + except Exception as e: + print(f"✗ 黑名单清理失败: {e}") + self.client_stats["errors"] += 1 + + def _disconnect_all_clients(self): + """断开所有客户端连接""" + try: + with self.client_lock: + client_ids = list(self.clients.keys()) + + for client_id in client_ids: + self.disconnect_client(client_id, "server_shutdown") + + except Exception as e: + print(f"✗ 断开所有客户端连接失败: {e}") + self.client_stats["errors"] += 1 + + def add_client(self, client_id: str, client_info: Dict[str, Any]) -> bool: + """ + 添加客户端 + + Args: + client_id: 客户端ID + client_info: 客户端信息 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + # 检查客户端数量限制 + with self.client_lock: + if len(self.clients) >= self.client_config["max_clients"]: + print("✗ 客户端数量已达上限") + return False + + # 检查IP是否被禁止 + client_ip = client_info.get("ip_address", "") + if self._is_ip_banned(client_ip): + print(f"✗ IP地址被禁止: {client_ip}") + return False + + # 添加客户端 + client_data = { + "client_id": client_id, + "client_info": client_info, + "connect_time": time.time(), + "last_heartbeat": time.time(), + "is_authenticated": False, + "user_id": "", + "username": "", + "permissions": [], + "messages_sent": 0, + "messages_received": 0, + "bytes_sent": 0, + "bytes_received": 0 + } + + with self.client_lock: + self.clients[client_id] = client_data + + self.client_state["current_clients"] = len(self.clients) + self.client_state["total_clients"] += 1 + self.client_stats["clients_joined"] += 1 + + # 触发客户端加入回调 + self._trigger_client_callback("client_joined", { + "client_id": client_id, + "client_info": client_info, + "timestamp": time.time() + }) + + print(f"✓ 客户端已添加: {client_id}") + return True + + except Exception as e: + print(f"✗ 添加客户端失败: {e}") + self.client_stats["errors"] += 1 + return False + + def remove_client(self, client_id: str, reason: str = "unknown") -> bool: + """ + 移除客户端 + + Args: + client_id: 客户端ID + reason: 移除原因 + + Returns: + 是否移除成功 + """ + try: + with self.client_lock: + if client_id in self.clients: + client_data = self.clients[client_id] + if client_data["is_authenticated"]: + self.client_state["authenticated_clients"] -= 1 + del self.clients[client_id] + + self.client_state["current_clients"] = len(self.clients) + self.client_stats["clients_left"] += 1 + + # 触发客户端离开回调 + self._trigger_client_callback("client_left", { + "client_id": client_id, + "reason": reason, + "timestamp": time.time() + }) + + print(f"✓ 客户端已移除: {client_id} (原因: {reason})") + return True + + except Exception as e: + print(f"✗ 移除客户端失败: {e}") + self.client_stats["errors"] += 1 + return False + + def disconnect_client(self, client_id: str, reason: str = "kicked") -> bool: + """ + 断开客户端连接 + + Args: + client_id: 客户端ID + reason: 断开原因 + + Returns: + 是否断开成功 + """ + try: + # 通知WebSocket服务器断开连接 + if self.plugin.websocket_server: + self.plugin.websocket_server.disconnect_client(client_id, reason) + + # 移除客户端 + return self.remove_client(client_id, reason) + + except Exception as e: + print(f"✗ 断开客户端连接失败: {e}") + self.client_stats["errors"] += 1 + return False + + def authenticate_client(self, client_id: str, auth_data: Dict[str, Any]) -> bool: + """ + 认证客户端 + + Args: + client_id: 客户端ID + auth_data: 认证数据 + + Returns: + 是否认证成功 + """ + try: + if not self.enabled or not self.client_config["enable_authentication"]: + # 如果未启用认证,直接通过 + with self.client_lock: + if client_id in self.clients: + self.clients[client_id]["is_authenticated"] = True + return True + + # 使用认证管理器进行认证 + if self.plugin.auth_manager: + auth_result = self.plugin.auth_manager.authenticate_client(client_id, auth_data) + if auth_result: + # 更新客户端认证状态 + with self.client_lock: + if client_id in self.clients: + self.clients[client_id]["is_authenticated"] = True + self.clients[client_id]["user_id"] = auth_data.get("user_id", "") + self.clients[client_id]["username"] = auth_data.get("username", "") + self.clients[client_id]["permissions"] = auth_data.get("permissions", []) + + self.client_state["authenticated_clients"] += 1 + self.client_stats["authentication_success"] += 1 + + # 触发客户端认证回调 + self._trigger_client_callback("client_authenticated", { + "client_id": client_id, + "user_id": auth_data.get("user_id", ""), + "username": auth_data.get("username", ""), + "timestamp": time.time() + }) + + print(f"✓ 客户端认证成功: {client_id}") + return True + else: + self.client_stats["authentication_failed"] += 1 + print(f"✗ 客户端认证失败: {client_id}") + return False + else: + print("✗ 认证管理器不可用") + return False + + except Exception as e: + print(f"✗ 客户端认证失败: {e}") + self.client_stats["errors"] += 1 + return False + + def update_client_heartbeat(self, client_id: str) -> bool: + """ + 更新客户端心跳 + + Args: + client_id: 客户端ID + + Returns: + 是否更新成功 + """ + try: + with self.client_lock: + if client_id in self.clients: + self.clients[client_id]["last_heartbeat"] = time.time() + return True + else: + return False + + except Exception as e: + print(f"✗ 更新客户端心跳失败: {e}") + self.client_stats["errors"] += 1 + return False + + def get_client(self, client_id: str) -> Optional[Dict[str, Any]]: + """ + 获取客户端信息 + + Args: + client_id: 客户端ID + + Returns: + 客户端信息或None + """ + try: + with self.client_lock: + if client_id in self.clients: + return self.clients[client_id].copy() + else: + return None + + except Exception as e: + print(f"✗ 获取客户端信息失败: {e}") + self.client_stats["errors"] += 1 + return None + + def get_all_clients(self) -> Dict[str, Any]: + """ + 获取所有客户端信息 + + Returns: + 所有客户端信息字典 + """ + try: + with self.client_lock: + return {k: v.copy() for k, v in self.clients.items()} + except Exception as e: + print(f"✗ 获取所有客户端信息失败: {e}") + self.client_stats["errors"] += 1 + return {} + + def ban_client(self, client_id: str, reason: str = "violation", duration: float = 3600) -> bool: + """ + 禁止客户端 + + Args: + client_id: 客户端ID + reason: 禁止原因 + duration: 禁止时长(秒) + + Returns: + 是否禁止成功 + """ + try: + if not self.client_config["enable_banning"]: + return False + + client_data = self.get_client(client_id) + if not client_data: + return False + + client_ip = client_data["client_info"].get("ip_address", "") + if client_ip: + # 添加到IP黑名单 + self.banned_ips[client_ip] = { + "client_id": client_id, + "ban_time": time.time(), + "reason": reason, + "duration": duration + } + + self.client_state["banned_clients"] += 1 + self.client_stats["banned_clients"] += 1 + + # 断开客户端连接 + self.disconnect_client(client_id, f"banned: {reason}") + + # 触发客户端禁止回调 + self._trigger_client_callback("client_banned", { + "client_id": client_id, + "ip_address": client_ip, + "reason": reason, + "duration": duration, + "timestamp": time.time() + }) + + print(f"✓ 客户端已被禁止: {client_id} (IP: {client_ip})") + return True + else: + return False + + except Exception as e: + print(f"✗ 禁止客户端失败: {e}") + self.client_stats["errors"] += 1 + return False + + def _is_ip_banned(self, ip_address: str) -> bool: + """ + 检查IP是否被禁止 + + Args: + ip_address: IP地址 + + Returns: + IP是否被禁止 + """ + try: + if not self.client_config["enable_banning"]: + return False + + if ip_address in self.banned_ips: + ban_data = self.banned_ips[ip_address] + if time.time() - ban_data["ban_time"] < ban_data["duration"]: + return True + else: + # 禁止时间已过期,移除 + del self.banned_ips[ip_address] + self.client_state["banned_clients"] -= 1 + + return False + + except Exception as e: + print(f"✗ IP禁止检查失败: {e}") + self.client_stats["errors"] += 1 + return False + + def unban_ip(self, ip_address: str) -> bool: + """ + 解除IP禁止 + + Args: + ip_address: IP地址 + + Returns: + 是否解除成功 + """ + try: + if ip_address in self.banned_ips: + del self.banned_ips[ip_address] + self.client_state["banned_clients"] -= 1 + print(f"✓ IP禁止已解除: {ip_address}") + return True + else: + print(f"✗ IP未被禁止: {ip_address}") + return False + + except Exception as e: + print(f"✗ 解除IP禁止失败: {e}") + self.client_stats["errors"] += 1 + return False + + def is_client_authenticated(self, client_id: str) -> bool: + """ + 检查客户端是否已认证 + + Args: + client_id: 客户端ID + + Returns: + 客户端是否已认证 + """ + try: + with self.client_lock: + if client_id in self.clients: + return self.clients[client_id]["is_authenticated"] + else: + return False + + except Exception as e: + print(f"✗ 客户端认证检查失败: {e}") + self.client_stats["errors"] += 1 + return False + + def get_client_stats(self) -> Dict[str, Any]: + """ + 获取客户端统计信息 + + Returns: + 客户端统计字典 + """ + return { + "state": self.client_state.copy(), + "stats": self.client_stats.copy(), + "config": self.client_config.copy() + } + + def reset_stats(self): + """重置客户端统计信息""" + try: + self.client_stats = { + "clients_joined": 0, + "clients_left": 0, + "authentication_success": 0, + "authentication_failed": 0, + "messages_sent": 0, + "messages_received": 0, + "banned_clients": 0, + "errors": 0 + } + print("✓ 客户端统计信息已重置") + except Exception as e: + print(f"✗ 客户端统计信息重置失败: {e}") + + def set_client_config(self, config: Dict[str, Any]) -> bool: + """ + 设置客户端配置 + + Args: + config: 客户端配置字典 + + Returns: + 是否设置成功 + """ + try: + self.client_config.update(config) + print(f"✓ 客户端配置已更新: {self.client_config}") + return True + except Exception as e: + print(f"✗ 客户端配置设置失败: {e}") + return False + + def get_client_config(self) -> Dict[str, Any]: + """ + 获取客户端配置 + + Returns: + 客户端配置字典 + """ + return self.client_config.copy() + + def _trigger_client_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发客户端回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.client_callbacks: + for callback in self.client_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 客户端回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 客户端回调触发失败: {e}") + + def register_client_callback(self, callback_type: str, callback: callable): + """ + 注册客户端回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.client_callbacks: + self.client_callbacks[callback_type].append(callback) + print(f"✓ 客户端回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 客户端回调注册失败: {e}") + + def unregister_client_callback(self, callback_type: str, callback: callable): + """ + 注销客户端回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.client_callbacks: + if callback in self.client_callbacks[callback_type]: + self.client_callbacks[callback_type].remove(callback) + print(f"✓ 客户端回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 客户端回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/config/comm_config.py b/plugins/user/realtime_communication/config/comm_config.py new file mode 100644 index 00000000..23549f94 --- /dev/null +++ b/plugins/user/realtime_communication/config/comm_config.py @@ -0,0 +1,759 @@ +""" +通信配置管理器模块 +管理通信配置和用户偏好 +""" + +import time +import json +import os +from typing import Dict, Any, List, Optional + +class CommConfig: + """ + 通信配置管理器 + 管理通信配置和用户偏好 + """ + + def __init__(self, plugin): + """ + 初始化通信配置管理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 配置文件路径 + self.config_file_path = "/home/hello/EG/plugins/user/realtime_communication/config/communication_config.json" + + # 默认配置 + self.default_config = { + "server": { + "host": "0.0.0.0", + "port": 8080, + "enable_ssl": False, + "ssl_cert_file": "", + "ssl_key_file": "", + "max_connections": 1000, + "connection_timeout": 30.0, + "ping_interval": 30.0 + }, + "client": { + "enable_authentication": True, + "auth_timeout": 30.0, + "max_clients": 1000, + "enable_heartbeat": True, + "heartbeat_interval": 30.0, + "enable_rate_limiting": True, + "max_messages_per_second": 100, + "enable_banning": True, + "ban_duration": 3600 + }, + "message": { + "enable_message_filtering": True, + "max_message_size": 65536, + "enable_message_queue": True, + "message_queue_size": 10000, + "enable_message_compression": True, + "compression_threshold": 1024 + }, + "room": { + "max_rooms": 1000, + "max_clients_per_room": 100, + "enable_room_persistence": False, + "room_timeout": 3600, + "enable_room_passwords": True + }, + "protocol": { + "supported_protocols": ["websocket", "json", "binary"], + "default_protocol": "json", + "enable_compression": True, + "compression_threshold": 1024, + "enable_encryption": False, + "max_message_size": 65536 + }, + "auth": { + "enable_authentication": True, + "auth_timeout": 30.0, + "enable_token_auth": True, + "enable_password_auth": True, + "token_lifetime": 86400, + "enable_session_management": True, + "session_timeout": 3600, + "max_login_attempts": 5, + "login_lockout_duration": 900 + }, + "serialization": { + "default_format": "json", + "supported_formats": ["json", "pickle", "binary"], + "enable_compression": True, + "compression_threshold": 1024, + "enable_caching": True, + "cache_size": 1000 + }, + "monitoring": { + "enable_monitoring": True, + "monitoring_interval": 5.0, + "log_level": "INFO", + "enable_file_logging": True, + "log_file_path": "/home/hello/EG/plugins/user/realtime_communication/logs/communication_monitor.log" + }, + "events": { + "enable_event_system": True, + "max_event_queue_size": 10000, + "enable_event_filtering": True + } + } + + # 当前配置 + self.current_config = self.default_config.copy() + + # 配置状态 + self.config_state = { + "last_loaded": 0.0, + "last_saved": 0.0, + "config_version": "1.0.0", + "is_dirty": False + } + + # 配置统计 + self.config_stats = { + "loads": 0, + "saves": 0, + "updates": 0, + "config_errors": 0 + } + + # 配置监听器 + self.config_listeners = {} + + # 回调函数 + self.config_callbacks = { + "config_loaded": [], + "config_saved": [], + "config_updated": [], + "config_error": [] + } + + print("✓ 通信配置管理器已创建") + + def initialize(self) -> bool: + """ + 初始化通信配置管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化通信配置管理器...") + + # 创建配置目录 + config_dir = os.path.dirname(self.config_file_path) + if not os.path.exists(config_dir): + os.makedirs(config_dir) + + # 创建日志目录 + if "monitoring" in self.default_config: + log_dir = os.path.dirname(self.default_config["monitoring"]["log_file_path"]) + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + # 加载配置 + self.load_config() + + self.initialized = True + print("✓ 通信配置管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 通信配置管理器初始化失败: {e}") + self.config_stats["config_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.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用通信配置管理器""" + try: + self.enabled = False + + # 保存配置(如果有更改) + if self.config_state["is_dirty"]: + self.save_config() + + print("✓ 通信配置管理器已禁用") + + except Exception as e: + print(f"✗ 通信配置管理器禁用失败: {e}") + self.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理通信配置管理器资源""" + try: + # 禁用通信配置管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.config_callbacks.clear() + self.config_listeners.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 + + # 检查配置文件是否被外部修改 + self._check_config_file_changes() + + except Exception as e: + print(f"✗ 通信配置管理器更新失败: {e}") + self.config_stats["config_errors"] += 1 + import traceback + traceback.print_exc() + + def _check_config_file_changes(self): + """检查配置文件是否被外部修改""" + try: + if not os.path.exists(self.config_file_path): + return + + last_modified = os.path.getmtime(self.config_file_path) + if last_modified > self.config_state["last_loaded"]: + # 配置文件已被修改,重新加载 + print("检测到配置文件被外部修改,正在重新加载...") + self.load_config() + + except Exception as e: + print(f"✗ 配置文件变更检查失败: {e}") + self.config_stats["config_errors"] += 1 + + def load_config(self) -> bool: + """ + 加载配置 + + Returns: + 是否加载成功 + """ + try: + # 检查配置文件是否存在 + if os.path.exists(self.config_file_path): + with open(self.config_file_path, 'r', encoding='utf-8') as f: + loaded_config = json.load(f) + + # 合并配置(使用默认配置作为基础) + self.current_config = self._merge_config(self.default_config, loaded_config) + + print(f"✓ 配置已从文件加载: {self.config_file_path}") + else: + # 使用默认配置 + self.current_config = self.default_config.copy() + print("✓ 使用默认配置") + + self.config_state["last_loaded"] = time.time() + self.config_state["is_dirty"] = False + self.config_stats["loads"] += 1 + + # 触发配置加载回调 + self._trigger_config_callback("config_loaded", { + "config": self.current_config, + "timestamp": self.config_state["last_loaded"] + }) + + return True + + except Exception as e: + print(f"✗ 配置加载失败: {e}") + self.config_stats["config_errors"] += 1 + + # 使用默认配置 + self.current_config = self.default_config.copy() + return False + + def _merge_config(self, base_config: Dict[str, Any], override_config: Dict[str, Any]) -> Dict[str, Any]: + """ + 合并配置(递归) + + Args: + base_config: 基础配置 + override_config: 覆盖配置 + + Returns: + 合并后的配置 + """ + try: + merged = base_config.copy() + + for key, value in override_config.items(): + if key in merged and isinstance(merged[key], dict) and isinstance(value, dict): + # 递归合并嵌套字典 + merged[key] = self._merge_config(merged[key], value) + else: + # 直接覆盖 + merged[key] = value + + return merged + + except Exception as e: + print(f"✗ 配置合并失败: {e}") + self.config_stats["config_errors"] += 1 + return base_config + + def save_config(self) -> bool: + """ + 保存配置 + + Returns: + 是否保存成功 + """ + try: + # 创建配置目录(如果不存在) + config_dir = os.path.dirname(self.config_file_path) + if not os.path.exists(config_dir): + os.makedirs(config_dir) + + # 保存配置到文件 + with open(self.config_file_path, 'w', encoding='utf-8') as f: + json.dump(self.current_config, f, indent=2, ensure_ascii=False) + + self.config_state["last_saved"] = time.time() + self.config_state["is_dirty"] = False + self.config_stats["saves"] += 1 + + # 触发配置保存回调 + self._trigger_config_callback("config_saved", { + "config": self.current_config, + "timestamp": self.config_state["last_saved"] + }) + + print(f"✓ 配置已保存到文件: {self.config_file_path}") + return True + + except Exception as e: + print(f"✗ 配置保存失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def get_config(self, section: str = None, key: str = None, default_value: Any = None) -> Any: + """ + 获取配置值 + + Args: + section: 配置节(可选) + key: 配置键(可选) + default_value: 默认值 + + Returns: + 配置值 + """ + try: + if section is None: + return self.current_config.copy() + + if section not in self.current_config: + return default_value + + if key is None: + return self.current_config[section].copy() + + if key not in self.current_config[section]: + return default_value + + return self.current_config[section][key] + + except Exception as e: + print(f"✗ 获取配置失败: {e}") + self.config_stats["config_errors"] += 1 + return default_value + + def set_config(self, section: str, key: str, value: Any) -> bool: + """ + 设置配置值 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + + Returns: + 是否设置成功 + """ + try: + if section not in self.current_config: + self.current_config[section] = {} + + self.current_config[section][key] = value + self.config_state["is_dirty"] = True + self.config_stats["updates"] += 1 + + # 通知配置监听器 + self._notify_config_listeners(section, key, value) + + # 触发配置更新回调 + self._trigger_config_callback("config_updated", { + "section": section, + "key": key, + "value": value, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 设置配置失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def update_config_section(self, section: str, config_data: Dict[str, Any]) -> bool: + """ + 更新配置节 + + Args: + section: 配置节 + config_data: 配置数据 + + Returns: + 是否更新成功 + """ + try: + if section not in self.current_config: + self.current_config[section] = {} + + self.current_config[section].update(config_data) + self.config_state["is_dirty"] = True + self.config_stats["updates"] += 1 + + # 通知配置监听器 + for key, value in config_data.items(): + self._notify_config_listeners(section, key, value) + + # 触发配置更新回调 + self._trigger_config_callback("config_updated", { + "section": section, + "config_data": config_data, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 更新配置节失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def reset_config(self) -> bool: + """ + 重置配置为默认值 + + Returns: + 是否重置成功 + """ + try: + self.current_config = self.default_config.copy() + self.config_state["is_dirty"] = True + + # 通知所有配置监听器 + for section_name, section_data in self.current_config.items(): + if isinstance(section_data, dict): + for key, value in section_data.items(): + self._notify_config_listeners(section_name, key, value) + + print("✓ 配置已重置为默认值") + return True + + except Exception as e: + print(f"✗ 配置重置失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def register_config_listener(self, section: str, key: str, listener: callable): + """ + 注册配置监听器 + + Args: + section: 配置节 + key: 配置键 + listener: 监听器函数 + """ + try: + listener_key = f"{section}.{key}" + if listener_key not in self.config_listeners: + self.config_listeners[listener_key] = [] + + self.config_listeners[listener_key].append(listener) + print(f"✓ 配置监听器已注册: {listener_key}") + + except Exception as e: + print(f"✗ 配置监听器注册失败: {e}") + self.config_stats["config_errors"] += 1 + + def unregister_config_listener(self, section: str, key: str, listener: callable): + """ + 注销配置监听器 + + Args: + section: 配置节 + key: 配置键 + listener: 监听器函数 + """ + try: + listener_key = f"{section}.{key}" + if listener_key in self.config_listeners: + if listener in self.config_listeners[listener_key]: + self.config_listeners[listener_key].remove(listener) + print(f"✓ 配置监听器已注销: {listener_key}") + + except Exception as e: + print(f"✗ 配置监听器注销失败: {e}") + self.config_stats["config_errors"] += 1 + + def _notify_config_listeners(self, section: str, key: str, value: Any): + """ + 通知配置监听器 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + """ + try: + listener_key = f"{section}.{key}" + if listener_key in self.config_listeners: + for listener in self.config_listeners[listener_key]: + try: + listener(section, key, value) + except Exception as e: + print(f"✗ 配置监听器执行失败: {listener_key} - {e}") + + except Exception as e: + print(f"✗ 配置监听器通知失败: {e}") + self.config_stats["config_errors"] += 1 + + def get_config_stats(self) -> Dict[str, Any]: + """ + 获取配置统计信息 + + Returns: + 配置统计字典 + """ + return { + "state": self.config_state.copy(), + "stats": self.config_stats.copy(), + "config_sections": list(self.current_config.keys()) + } + + def reset_stats(self): + """重置配置统计信息""" + try: + self.config_stats = { + "loads": 0, + "saves": 0, + "updates": 0, + "config_errors": 0 + } + print("✓ 配置统计信息已重置") + except Exception as e: + print(f"✗ 配置统计信息重置失败: {e}") + + def validate_config(self) -> bool: + """ + 验证配置 + + Returns: + 配置是否有效 + """ + try: + # 检查必需的配置项 + required_sections = ["server", "client", "message", "room", "protocol", "auth", "serialization"] + for section in required_sections: + if section not in self.current_config: + print(f"✗ 缺少必需的配置节: {section}") + return False + + # 检查服务器配置 + server_config = self.current_config["server"] + if "port" not in server_config or not isinstance(server_config["port"], int): + print("✗ 无效的服务器端口配置") + return False + + if "max_connections" not in server_config or not isinstance(server_config["max_connections"], int): + print("✗ 无效的最大连接数配置") + return False + + # 检查客户端配置 + client_config = self.current_config["client"] + if "max_clients" not in client_config or not isinstance(client_config["max_clients"], int): + print("✗ 无效的最大客户端数配置") + return False + + print("✓ 配置验证通过") + return True + + except Exception as e: + print(f"✗ 配置验证失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def export_config(self, file_path: str) -> bool: + """ + 导出配置到文件 + + Args: + file_path: 导出文件路径 + + Returns: + 是否导出成功 + """ + try: + # 创建导出目录 + export_dir = os.path.dirname(file_path) + if not os.path.exists(export_dir): + os.makedirs(export_dir) + + # 导出配置 + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(self.current_config, f, indent=2, ensure_ascii=False) + + print(f"✓ 配置已导出到: {file_path}") + return True + + except Exception as e: + print(f"✗ 配置导出失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def import_config(self, file_path: str) -> bool: + """ + 从文件导入配置 + + Args: + file_path: 导入文件路径 + + Returns: + 是否导入成功 + """ + try: + if not os.path.exists(file_path): + print(f"✗ 配置文件不存在: {file_path}") + return False + + # 读取配置文件 + with open(file_path, 'r', encoding='utf-8') as f: + imported_config = json.load(f) + + # 验证配置 + if not isinstance(imported_config, dict): + print("✗ 无效的配置文件格式") + return False + + # 合并配置 + self.current_config = self._merge_config(self.default_config, imported_config) + self.config_state["is_dirty"] = True + + # 通知配置监听器 + for section_name, section_data in self.current_config.items(): + if isinstance(section_data, dict): + for key, value in section_data.items(): + self._notify_config_listeners(section_name, key, value) + + print(f"✓ 配置已从文件导入: {file_path}") + return True + + except Exception as e: + print(f"✗ 配置导入失败: {e}") + self.config_stats["config_errors"] += 1 + return False + + def _trigger_config_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发配置回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.config_callbacks: + for callback in self.config_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 配置回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 配置回调触发失败: {e}") + + def register_config_callback(self, callback_type: str, callback: callable): + """ + 注册配置回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.config_callbacks: + self.config_callbacks[callback_type].append(callback) + print(f"✓ 配置回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 配置回调注册失败: {e}") + + def unregister_config_callback(self, callback_type: str, callback: callable): + """ + 注销配置回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.config_callbacks: + if callback in self.config_callbacks[callback_type]: + self.config_callbacks[callback_type].remove(callback) + print(f"✓ 配置回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 配置回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/core/websocket_server.py b/plugins/user/realtime_communication/core/websocket_server.py new file mode 100644 index 00000000..3843d058 --- /dev/null +++ b/plugins/user/realtime_communication/core/websocket_server.py @@ -0,0 +1,698 @@ +""" +WebSocket服务器模块 +处理实时双向通信连接 +""" + +import time +import threading +import json +import base64 +from typing import Dict, Any, List, Optional + +class WebSocketServer: + """ + WebSocket服务器 + 处理实时双向通信连接 + """ + + def __init__(self, plugin): + """ + 初始化WebSocket服务器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # WebSocket配置 + self.websocket_config = { + "host": "0.0.0.0", + "port": 8080, + "enable_ssl": False, + "ssl_cert_file": "", + "ssl_key_file": "", + "max_connections": 1000, + "enable_compression": True, + "ping_interval": 30.0, + "ping_timeout": 10.0, + "message_queue_size": 1000, + "enable_cors": True + } + + # WebSocket服务器状态 + self.server_state = { + "is_running": False, + "start_time": 0.0, + "uptime": 0.0, + "current_connections": 0, + "total_connections": 0, + "bytes_sent": 0, + "bytes_received": 0 + } + + # WebSocket服务器统计 + self.server_stats = { + "messages_sent": 0, + "messages_received": 0, + "connections_accepted": 0, + "connections_dropped": 0, + "errors": 0 + } + + # 连接存储 + self.connections = {} + self.connection_lock = threading.RLock() + + # 消息队列 + self.message_queue = [] + self.message_queue_lock = threading.RLock() + + # 服务器线程 + self.server_thread = None + self.server_thread_running = False + + # 回调函数 + self.websocket_callbacks = { + "server_started": [], + "server_stopped": [], + "client_connected": [], + "client_disconnected": [], + "message_received": [], + "websocket_error": [] + } + + # 时间戳记录 + self.last_ping_time = 0.0 + self.last_message_time = 0.0 + + print("✓ WebSocket服务器已创建") + + def initialize(self) -> bool: + """ + 初始化WebSocket服务器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化WebSocket服务器...") + + self.initialized = True + print("✓ WebSocket服务器初始化完成") + return True + + except Exception as e: + print(f"✗ WebSocket服务器初始化失败: {e}") + self.server_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def enable(self) -> bool: + """ + 启用WebSocket服务器 + + Returns: + 是否启用成功 + """ + try: + if not self.initialized: + print("✗ WebSocket服务器未初始化") + return False + + # 启动WebSocket服务器 + self._start_websocket_server() + + self.enabled = True + print("✓ WebSocket服务器已启用") + return True + + except Exception as e: + print(f"✗ WebSocket服务器启用失败: {e}") + self.server_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用WebSocket服务器""" + try: + self.enabled = False + + # 停止WebSocket服务器 + self._stop_websocket_server() + + print("✓ WebSocket服务器已禁用") + + except Exception as e: + print(f"✗ WebSocket服务器禁用失败: {e}") + self.server_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理WebSocket服务器资源""" + try: + # 禁用WebSocket服务器 + if self.enabled: + self.disable() + + # 清理回调 + self.websocket_callbacks.clear() + + self.initialized = False + print("✓ WebSocket服务器资源已清理") + + except Exception as e: + print(f"✗ WebSocket服务器资源清理失败: {e}") + import traceback + traceback.print_exc() + + def update(self, dt: float): + """ + 更新WebSocket服务器状态 + + Args: + dt: 时间增量(秒) + """ + try: + if not self.enabled: + return + + current_time = time.time() + + # 更新运行时间 + if self.server_state["is_running"]: + self.server_state["uptime"] = current_time - self.server_state["start_time"] + + # 处理消息队列 + self._process_message_queue() + + # 发送心跳 + if current_time - self.last_ping_time > self.websocket_config["ping_interval"]: + self._send_ping_to_all_clients() + self.last_ping_time = current_time + + self.last_message_time = current_time + + except Exception as e: + print(f"✗ WebSocket服务器更新失败: {e}") + self.server_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def _start_websocket_server(self): + """启动WebSocket服务器""" + try: + if self.server_state["is_running"]: + return + + # 在实际实现中,这里会启动一个WebSocket服务器 + # 由于这是一个示例,我们模拟服务器启动过程 + self.server_state["is_running"] = True + self.server_state["start_time"] = time.time() + + # 触发服务器启动回调 + self._trigger_websocket_callback("server_started", { + "host": self.websocket_config["host"], + "port": self.websocket_config["port"], + "timestamp": self.server_state["start_time"] + }) + + print(f"✓ WebSocket服务器已启动: ws://{self.websocket_config['host']}:{self.websocket_config['port']}") + + except Exception as e: + print(f"✗ WebSocket服务器启动失败: {e}") + self.server_stats["errors"] += 1 + + def _stop_websocket_server(self): + """停止WebSocket服务器""" + try: + if not self.server_state["is_running"]: + return + + # 断开所有客户端连接 + self._disconnect_all_clients() + + self.server_state["is_running"] = False + self.server_state["start_time"] = 0.0 + self.server_state["uptime"] = 0.0 + + # 触发服务器停止回调 + self._trigger_websocket_callback("server_stopped", { + "timestamp": time.time() + }) + + print("✓ WebSocket服务器已停止") + + except Exception as e: + print(f"✗ WebSocket服务器停止失败: {e}") + self.server_stats["errors"] += 1 + + def _process_message_queue(self): + """处理消息队列""" + try: + if not self.message_queue: + return + + with self.message_queue_lock: + messages_to_process = self.message_queue[:] + self.message_queue.clear() + + for message_data in messages_to_process: + try: + client_id = message_data.get("client_id") + message = message_data.get("message") + + # 处理消息 + self._handle_incoming_message(client_id, message) + + except Exception as e: + print(f"✗ 消息处理失败: {e}") + self.server_stats["errors"] += 1 + + except Exception as e: + print(f"✗ 消息队列处理失败: {e}") + self.server_stats["errors"] += 1 + + def _handle_incoming_message(self, client_id: str, message: Dict[str, Any]): + """ + 处理传入消息 + + Args: + client_id: 客户端ID + message: 消息数据 + """ + try: + self.server_stats["messages_received"] += 1 + + # 触发消息接收回调 + self._trigger_websocket_callback("message_received", { + "client_id": client_id, + "message": message, + "timestamp": time.time() + }) + + # 将消息传递给消息路由器处理 + if self.plugin.message_router: + self.plugin.message_router.route_message(client_id, message) + + except Exception as e: + print(f"✗ 传入消息处理失败: {e}") + self.server_stats["errors"] += 1 + + def _send_ping_to_all_clients(self): + """向所有客户端发送心跳""" + try: + ping_message = { + "type": "ping", + "timestamp": time.time() + } + + with self.connection_lock: + for client_id, connection in self.connections.items(): + try: + self._send_message_to_client(client_id, ping_message) + except Exception as e: + print(f"✗ 心跳发送失败: {client_id} - {e}") + + except Exception as e: + print(f"✗ 心跳发送失败: {e}") + self.server_stats["errors"] += 1 + + def _disconnect_all_clients(self): + """断开所有客户端连接""" + try: + with self.connection_lock: + client_ids = list(self.connections.keys()) + + for client_id in client_ids: + self.disconnect_client(client_id, "server_shutdown") + + except Exception as e: + print(f"✗ 断开所有客户端连接失败: {e}") + self.server_stats["errors"] += 1 + + def send_message_to_client(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 向客户端发送消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否发送成功 + """ + try: + if not self.enabled: + return False + + return self._send_message_to_client(client_id, message) + + except Exception as e: + print(f"✗ 向客户端发送消息失败: {e}") + self.server_stats["errors"] += 1 + return False + + def _send_message_to_client(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 向客户端发送消息(内部实现) + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否发送成功 + """ + try: + # 检查客户端连接是否存在 + with self.connection_lock: + if client_id not in self.connections: + return False + + # 序列化消息 + if self.plugin.data_serializer: + serialized_message = self.plugin.data_serializer.serialize(message) + else: + serialized_message = json.dumps(message, ensure_ascii=False) + + # 发送消息(模拟) + message_size = len(serialized_message.encode('utf-8')) + self.server_state["bytes_sent"] += message_size + self.server_stats["messages_sent"] += 1 + + return True + + except Exception as e: + print(f"✗ 向客户端发送消息失败: {e}") + self.server_stats["errors"] += 1 + return False + + def broadcast_message(self, message: Dict[str, Any], exclude_client_id: str = None) -> bool: + """ + 广播消息到所有客户端 + + Args: + message: 消息数据 + exclude_client_id: 要排除的客户端ID + + Returns: + 是否广播成功 + """ + try: + if not self.enabled: + return False + + success_count = 0 + with self.connection_lock: + for client_id in self.connections.keys(): + # 跳过排除的客户端 + if client_id == exclude_client_id: + continue + + if self._send_message_to_client(client_id, message): + success_count += 1 + + return success_count > 0 + + except Exception as e: + print(f"✗ 消息广播失败: {e}") + self.server_stats["errors"] += 1 + return False + + def add_client_connection(self, client_id: str, connection_data: Dict[str, Any]) -> bool: + """ + 添加客户端连接 + + Args: + client_id: 客户端ID + connection_data: 连接数据 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + with self.connection_lock: + # 检查连接数限制 + if len(self.connections) >= self.websocket_config["max_connections"]: + print("✗ 连接数已达上限") + return False + + # 添加连接 + self.connections[client_id] = { + "connection_data": connection_data, + "connect_time": time.time(), + "last_activity": time.time(), + "bytes_sent": 0, + "bytes_received": 0, + "messages_sent": 0, + "messages_received": 0 + } + + self.server_state["current_connections"] = len(self.connections) + self.server_state["total_connections"] += 1 + self.server_stats["connections_accepted"] += 1 + + # 触发客户端连接回调 + self._trigger_websocket_callback("client_connected", { + "client_id": client_id, + "connection_data": connection_data, + "timestamp": time.time() + }) + + print(f"✓ 客户端已连接: {client_id}") + return True + + except Exception as e: + print(f"✗ 添加客户端连接失败: {e}") + self.server_stats["errors"] += 1 + return False + + def remove_client_connection(self, client_id: str, reason: str = "unknown") -> bool: + """ + 移除客户端连接 + + Args: + client_id: 客户端ID + reason: 断开原因 + + Returns: + 是否移除成功 + """ + try: + with self.connection_lock: + if client_id in self.connections: + del self.connections[client_id] + + self.server_state["current_connections"] = len(self.connections) + self.server_stats["connections_dropped"] += 1 + + # 触发客户端断开回调 + self._trigger_websocket_callback("client_disconnected", { + "client_id": client_id, + "reason": reason, + "timestamp": time.time() + }) + + print(f"✓ 客户端已断开: {client_id} (原因: {reason})") + return True + + except Exception as e: + print(f"✗ 移除客户端连接失败: {e}") + self.server_stats["errors"] += 1 + return False + + def disconnect_client(self, client_id: str, reason: str = "kicked") -> bool: + """ + 断开客户端连接 + + Args: + client_id: 客户端ID + reason: 断开原因 + + Returns: + 是否断开成功 + """ + try: + # 通知客户端断开连接 + disconnect_message = { + "type": "disconnect", + "reason": reason, + "timestamp": time.time() + } + + self._send_message_to_client(client_id, disconnect_message) + + # 移除连接 + return self.remove_client_connection(client_id, reason) + + except Exception as e: + print(f"✗ 断开客户端连接失败: {e}") + self.server_stats["errors"] += 1 + return False + + def get_connection_info(self, client_id: str) -> Optional[Dict[str, Any]]: + """ + 获取连接信息 + + Args: + client_id: 客户端ID + + Returns: + 连接信息或None + """ + try: + with self.connection_lock: + if client_id in self.connections: + return self.connections[client_id].copy() + else: + return None + + except Exception as e: + print(f"✗ 获取连接信息失败: {e}") + self.server_stats["errors"] += 1 + return None + + def get_all_connections(self) -> Dict[str, Any]: + """ + 获取所有连接信息 + + Returns: + 所有连接信息字典 + """ + try: + with self.connection_lock: + return {k: v.copy() for k, v in self.connections.items()} + except Exception as e: + print(f"✗ 获取所有连接信息失败: {e}") + self.server_stats["errors"] += 1 + return {} + + def get_server_stats(self) -> Dict[str, Any]: + """ + 获取服务器统计信息 + + Returns: + 服务器统计字典 + """ + return { + "state": self.server_state.copy(), + "stats": self.server_stats.copy(), + "config": self.websocket_config.copy() + } + + def reset_stats(self): + """重置服务器统计信息""" + try: + self.server_stats = { + "messages_sent": 0, + "messages_received": 0, + "connections_accepted": 0, + "connections_dropped": 0, + "errors": 0 + } + + self.server_state["bytes_sent"] = 0 + self.server_state["bytes_received"] = 0 + + print("✓ 服务器统计信息已重置") + except Exception as e: + print(f"✗ 服务器统计信息重置失败: {e}") + + def set_websocket_config(self, config: Dict[str, Any]) -> bool: + """ + 设置WebSocket配置 + + Args: + config: WebSocket配置字典 + + Returns: + 是否设置成功 + """ + try: + old_port = self.websocket_config.get("port") + new_port = config.get("port", old_port) + + # 如果服务器正在运行且端口发生变化,重启服务器 + if (self.server_state["is_running"] and + old_port != new_port): + self._stop_websocket_server() + self.websocket_config.update(config) + self._start_websocket_server() + else: + self.websocket_config.update(config) + + print(f"✓ WebSocket配置已更新: {self.websocket_config}") + return True + except Exception as e: + print(f"✗ WebSocket配置设置失败: {e}") + return False + + def get_websocket_config(self) -> Dict[str, Any]: + """ + 获取WebSocket配置 + + Returns: + WebSocket配置字典 + """ + return self.websocket_config.copy() + + def _trigger_websocket_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发WebSocket回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.websocket_callbacks: + for callback in self.websocket_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ WebSocket回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ WebSocket回调触发失败: {e}") + + def register_websocket_callback(self, callback_type: str, callback: callable): + """ + 注册WebSocket回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.websocket_callbacks: + self.websocket_callbacks[callback_type].append(callback) + print(f"✓ WebSocket回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ WebSocket回调注册失败: {e}") + + def unregister_websocket_callback(self, callback_type: str, callback: callable): + """ + 注销WebSocket回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.websocket_callbacks: + if callback in self.websocket_callbacks[callback_type]: + self.websocket_callbacks[callback_type].remove(callback) + print(f"✓ WebSocket回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ WebSocket回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/editor/comm_editor.py b/plugins/user/realtime_communication/editor/comm_editor.py new file mode 100644 index 00000000..6adf62fb --- /dev/null +++ b/plugins/user/realtime_communication/editor/comm_editor.py @@ -0,0 +1,596 @@ +""" +通信编辑器模块 +提供图形界面配置通信参数 +""" + +import time +from typing import Dict, Any, List, Optional + +class CommEditor: + """ + 通信编辑器 + 提供图形界面配置通信参数 + """ + + def __init__(self, plugin): + """ + 初始化通信编辑器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 编辑器配置 + self.editor_config = { + "enable_editor": True, + "enable_realtime_monitoring": True, + "enable_performance_display": True, + "enable_config_editor": True, + "enable_statistics_display": True, + "refresh_interval": 1.0, + "max_data_points": 100 + } + + # 编辑器状态 + self.editor_state = { + "is_visible": False, + "last_update": 0.0, + "active_tab": "overview", + "selected_client": None, + "selected_room": None + } + + # UI组件 + self.ui_components = { + "main_window": None, + "tabs": {}, + "panels": {}, + "charts": {}, + "controls": {} + } + + # 编辑器统计 + self.editor_stats = { + "updates": 0, + "ui_refreshes": 0, + "data_points": 0, + "editor_errors": 0 + } + + # 实时数据缓存 + self.realtime_data = { + "performance_metrics": {}, + "client_data": {}, + "room_data": {}, + "message_stats": {}, + "network_stats": {} + } + + # 回调函数 + self.editor_callbacks = { + "ui_updated": [], + "data_refreshed": [], + "config_changed": [], + "editor_error": [] + } + + # 时间戳记录 + self.last_ui_update = 0.0 + self.last_data_refresh = 0.0 + + print("✓ 通信编辑器已创建") + + def initialize(self) -> bool: + """ + 初始化通信编辑器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化通信编辑器...") + + # 初始化UI组件(模拟) + self._initialize_ui_components() + + self.initialized = True + print("✓ 通信编辑器初始化完成") + return True + + except Exception as e: + print(f"✗ 通信编辑器初始化失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _initialize_ui_components(self): + """初始化UI组件""" + try: + # 创建主窗口 + self.ui_components["main_window"] = { + "title": "实时通信插件编辑器", + "size": (1200, 800), + "position": (100, 100) + } + + # 创建标签页 + self.ui_components["tabs"] = { + "overview": {"name": "概览", "icon": "dashboard"}, + "clients": {"name": "客户端", "icon": "people"}, + "rooms": {"name": "房间", "icon": "meeting_room"}, + "messages": {"name": "消息", "icon": "message"}, + "network": {"name": "网络", "icon": "network"}, + "config": {"name": "配置", "icon": "settings"}, + "statistics": {"name": "统计", "icon": "bar_chart"} + } + + # 创建面板 + self.ui_components["panels"] = { + "server_status": {"title": "服务器状态", "visible": True}, + "performance": {"title": "性能指标", "visible": True}, + "clients_list": {"title": "客户端列表", "visible": True}, + "rooms_list": {"title": "房间列表", "visible": True}, + "message_stats": {"title": "消息统计", "visible": True} + } + + print("✓ UI组件已初始化") + + except Exception as e: + print(f"✗ UI组件初始化失败: {e}") + self.editor_stats["editor_errors"] += 1 + + 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.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用通信编辑器""" + try: + self.enabled = False + + # 隐藏UI + self.hide_editor() + + print("✓ 通信编辑器已禁用") + + except Exception as e: + print(f"✗ 通信编辑器禁用失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理通信编辑器资源""" + try: + # 禁用通信编辑器 + if self.enabled: + self.disable() + + # 清理回调 + self.editor_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() + + # 定期刷新UI + if current_time - self.last_ui_update >= self.editor_config["refresh_interval"]: + self._refresh_ui() + self.last_ui_update = current_time + + # 定期更新数据 + if current_time - self.last_data_refresh >= 0.5: # 每0.5秒更新一次数据 + self._update_realtime_data() + self.last_data_refresh = current_time + + self.editor_state["last_update"] = current_time + + except Exception as e: + print(f"✗ 通信编辑器更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + import traceback + traceback.print_exc() + + def _refresh_ui(self): + """刷新UI""" + try: + # 更新UI组件状态 + self._update_ui_components() + + # 更新统计信息 + self.editor_stats["ui_refreshes"] += 1 + + # 触发UI更新回调 + self._trigger_editor_callback("ui_updated", { + "timestamp": time.time() + }) + + except Exception as e: + print(f"✗ UI刷新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def _update_ui_components(self): + """更新UI组件""" + try: + # 更新服务器状态面板 + if self.plugin.websocket_server: + server_stats = self.plugin.websocket_server.get_server_stats() + self.ui_components["panels"]["server_status"]["data"] = server_stats + + # 更新客户端列表面板 + if self.plugin.client_manager: + clients = self.plugin.client_manager.get_all_clients() + self.ui_components["panels"]["clients_list"]["data"] = clients + + # 更新房间列表面板 + if self.plugin.room_manager: + rooms = self.plugin.room_manager.get_all_rooms() + self.ui_components["panels"]["rooms_list"]["data"] = rooms + + # 更新消息统计面板 + if self.plugin.message_router: + message_stats = self.plugin.message_router.get_message_stats() + self.ui_components["panels"]["message_stats"]["data"] = message_stats + + except Exception as e: + print(f"✗ UI组件更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def _update_realtime_data(self): + """更新实时数据""" + try: + current_time = time.time() + + # 更新性能指标 + if self.plugin.monitor: + metrics = self.plugin.monitor.get_performance_metrics() + self.realtime_data["performance_metrics"] = metrics + + # 更新客户端数据 + if self.plugin.client_manager: + clients = self.plugin.client_manager.get_all_clients() + self.realtime_data["client_data"] = clients + + # 更新房间数据 + if self.plugin.room_manager: + rooms = self.plugin.room_manager.get_all_rooms() + self.realtime_data["room_data"] = rooms + + # 更新消息统计 + if self.plugin.message_router: + message_stats = self.plugin.message_router.get_message_stats() + self.realtime_data["message_stats"] = message_stats + + # 更新网络统计 + if self.plugin.websocket_server: + network_stats = self.plugin.websocket_server.get_server_stats() + self.realtime_data["network_stats"] = network_stats + + # 触发数据刷新回调 + self._trigger_editor_callback("data_refreshed", { + "data": self.realtime_data, + "timestamp": current_time + }) + + except Exception as e: + print(f"✗ 实时数据更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def show_editor(self): + """显示编辑器""" + try: + self.editor_state["is_visible"] = True + print("✓ 通信编辑器已显示") + + # 刷新数据 + self._update_realtime_data() + + except Exception as e: + print(f"✗ 编辑器显示失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def hide_editor(self): + """隐藏编辑器""" + try: + self.editor_state["is_visible"] = False + print("✓ 通信编辑器已隐藏") + + except Exception as e: + print(f"✗ 编辑器隐藏失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def switch_tab(self, tab_name: str): + """ + 切换标签页 + + Args: + tab_name: 标签页名称 + """ + try: + if tab_name in self.ui_components["tabs"]: + self.editor_state["active_tab"] = tab_name + print(f"✓ 切换到标签页: {tab_name}") + else: + print(f"✗ 无效的标签页: {tab_name}") + + except Exception as e: + print(f"✗ 标签页切换失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def select_client(self, client_id: str): + """ + 选择客户端 + + Args: + client_id: 客户端ID + """ + try: + self.editor_state["selected_client"] = client_id + print(f"✓ 选择客户端: {client_id}") + + except Exception as e: + print(f"✗ 客户端选择失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def select_room(self, room_id: str): + """ + 选择房间 + + Args: + room_id: 房间ID + """ + try: + self.editor_state["selected_room"] = room_id + print(f"✓ 选择房间: {room_id}") + + except Exception as e: + print(f"✗ 房间选择失败: {e}") + self.editor_stats["editor_errors"] += 1 + + def update_config(self, section: str, key: str, value: Any) -> bool: + """ + 更新配置 + + Args: + section: 配置节 + key: 配置键 + value: 配置值 + + Returns: + 是否更新成功 + """ + try: + # 通过配置管理器更新配置 + if self.plugin.config_manager: + result = self.plugin.config_manager.set_config(section, key, value) + + if result: + self.editor_stats["updates"] += 1 + + # 触发配置更改回调 + self._trigger_editor_callback("config_changed", { + "section": section, + "key": key, + "value": value, + "timestamp": time.time() + }) + + print(f"✓ 配置已更新: {section}.{key} = {value}") + return True + else: + print(f"✗ 配置更新失败: {section}.{key}") + return False + else: + print("✗ 配置管理器不可用") + return False + + except Exception as e: + print(f"✗ 配置更新失败: {e}") + self.editor_stats["editor_errors"] += 1 + return False + + def get_editor_stats(self) -> Dict[str, Any]: + """ + 获取编辑器统计信息 + + Returns: + 编辑器统计字典 + """ + return { + "state": self.editor_state.copy(), + "stats": self.editor_stats.copy(), + "config": self.editor_config.copy(), + "ui_components": len(self.ui_components["tabs"]) + } + + def reset_stats(self): + """重置编辑器统计信息""" + try: + self.editor_stats = { + "updates": 0, + "ui_refreshes": 0, + "data_points": 0, + "editor_errors": 0 + } + print("✓ 编辑器统计信息已重置") + except Exception as e: + print(f"✗ 编辑器统计信息重置失败: {e}") + + def set_editor_config(self, config: Dict[str, Any]) -> bool: + """ + 设置编辑器配置 + + Args: + config: 编辑器配置字典 + + Returns: + 是否设置成功 + """ + try: + self.editor_config.update(config) + print(f"✓ 编辑器配置已更新: {self.editor_config}") + return True + except Exception as e: + print(f"✗ 编辑器配置设置失败: {e}") + return False + + def get_editor_config(self) -> Dict[str, Any]: + """ + 获取编辑器配置 + + Returns: + 编辑器配置字典 + """ + return self.editor_config.copy() + + def get_realtime_data(self) -> Dict[str, Any]: + """ + 获取实时数据 + + Returns: + 实时数据字典 + """ + return self.realtime_data.copy() + + def export_data(self, data_type: str, file_path: str) -> bool: + """ + 导出数据 + + Args: + data_type: 数据类型 + file_path: 导出文件路径 + + Returns: + 是否导出成功 + """ + try: + import json + import os + + # 创建导出目录 + export_dir = os.path.dirname(file_path) + if not os.path.exists(export_dir): + os.makedirs(export_dir) + + # 获取要导出的数据 + export_data = None + if data_type == "performance": + export_data = self.realtime_data["performance_metrics"] + elif data_type == "clients": + export_data = self.realtime_data["client_data"] + elif data_type == "rooms": + export_data = self.realtime_data["room_data"] + elif data_type == "messages": + export_data = self.realtime_data["message_stats"] + elif data_type == "network": + export_data = self.realtime_data["network_stats"] + elif data_type == "all": + export_data = self.realtime_data + else: + print(f"✗ 不支持的数据类型: {data_type}") + return False + + # 导出数据 + with open(file_path, 'w', encoding='utf-8') as f: + json.dump(export_data, f, indent=2, ensure_ascii=False, default=str) + + print(f"✓ 数据已导出到: {file_path}") + return True + + except Exception as e: + print(f"✗ 数据导出失败: {e}") + self.editor_stats["editor_errors"] += 1 + return False + + def _trigger_editor_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发编辑器回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.editor_callbacks: + for callback in self.editor_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 编辑器回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 编辑器回调触发失败: {e}") + + def register_editor_callback(self, callback_type: str, callback: callable): + """ + 注册编辑器回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.editor_callbacks: + self.editor_callbacks[callback_type].append(callback) + print(f"✓ 编辑器回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 编辑器回调注册失败: {e}") + + def unregister_editor_callback(self, callback_type: str, callback: callable): + """ + 注销编辑器回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.editor_callbacks: + if callback in self.editor_callbacks[callback_type]: + self.editor_callbacks[callback_type].remove(callback) + print(f"✓ 编辑器回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 编辑器回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/events/event_handler.py b/plugins/user/realtime_communication/events/event_handler.py new file mode 100644 index 00000000..fbf2fe81 --- /dev/null +++ b/plugins/user/realtime_communication/events/event_handler.py @@ -0,0 +1,716 @@ +""" +事件处理器模块 +处理通信事件和系统响应 +""" + +import time +import threading +import queue +from typing import Dict, Any, List, Optional, Callable + +class EventHandler: + """ + 事件处理器 + 处理通信事件和系统响应 + """ + + def __init__(self, plugin): + """ + 初始化事件处理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 事件配置 + self.event_config = { + "enable_event_system": True, + "max_event_queue_size": 10000, + "enable_event_filtering": True, + "event_processing_interval": 0.01, # 10ms + "enable_async_processing": True, + "max_concurrent_events": 100, + "enable_event_prioritization": True, + "enable_event_logging": True + } + + # 事件状态 + self.event_state = { + "is_processing": False, + "pending_events": 0, + "processed_events": 0, + "dropped_events": 0, + "last_event_time": 0.0 + } + + # 事件队列 + self.event_queue = queue.Queue(maxsize=self.event_config["max_event_queue_size"]) + self.event_queue_lock = threading.RLock() + + # 事件处理器存储 + self.event_handlers = {} + + # 事件统计 + self.event_stats = { + "events_received": 0, + "events_processed": 0, + "events_dropped": 0, + "handler_errors": 0, + "async_events": 0 + } + + # 事件过滤器 + self.event_filters = {} + + # 回调函数 + self.event_callbacks = { + "event_received": [], + "event_processed": [], + "event_dropped": [], + "event_error": [] + } + + # 事件处理线程 + self.event_thread = None + self.event_thread_running = False + + # 时间戳记录 + self.last_event_process = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 事件处理器已创建") + + def initialize(self) -> bool: + """ + 初始化事件处理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化事件处理器...") + + # 注册默认事件处理器 + self._register_default_handlers() + + # 启动事件处理线程 + self._start_event_thread() + + self.initialized = True + print("✓ 事件处理器初始化完成") + return True + + except Exception as e: + print(f"✗ 事件处理器初始化失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _register_default_handlers(self): + """注册默认事件处理器""" + try: + # 注册系统事件处理器 + self.register_event_handler("client_connected", self._handle_client_connected) + self.register_event_handler("client_disconnected", self._handle_client_disconnected) + self.register_event_handler("message_received", self._handle_message_received) + self.register_event_handler("room_created", self._handle_room_created) + self.register_event_handler("room_destroyed", self._handle_room_destroyed) + self.register_event_handler("client_joined_room", self._handle_client_joined_room) + self.register_event_handler("client_left_room", self._handle_client_left_room) + + print("✓ 默认事件处理器已注册") + + except Exception as e: + print(f"✗ 默认事件处理器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _start_event_thread(self): + """启动事件处理线程""" + try: + self.event_thread_running = True + self.event_thread = threading.Thread(target=self._event_loop, daemon=True) + self.event_thread.start() + print("✓ 事件处理线程已启动") + except Exception as e: + print(f"✗ 事件处理线程启动失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _event_loop(self): + """事件处理循环""" + try: + while self.event_thread_running: + try: + if self.enabled and self.event_config["enable_event_system"]: + # 处理事件队列 + self._process_event_queue() + + # 短暂休眠 + time.sleep(self.event_config["event_processing_interval"]) + + except Exception as e: + print(f"✗ 事件处理循环错误: {e}") + self.event_stats["handler_errors"] += 1 + time.sleep(1.0) # 出错时延长休眠 + + except Exception as e: + print(f"✗ 事件处理线程失败: {e}") + self.event_stats["handler_errors"] += 1 + + 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.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用事件处理器""" + try: + self.enabled = False + + # 停止事件处理线程 + if self.event_thread_running: + self.event_thread_running = False + if self.event_thread and self.event_thread.is_alive(): + self.event_thread.join(timeout=5.0) + + print("✓ 事件处理器已禁用") + + except Exception as e: + print(f"✗ 事件处理器禁用失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理事件处理器资源""" + try: + # 禁用事件处理器 + if self.enabled: + self.disable() + + # 清理回调和处理器 + self.event_callbacks.clear() + self.event_handlers.clear() + self.event_filters.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() + self.event_state["last_event_time"] = current_time + + except Exception as e: + print(f"✗ 事件处理器更新失败: {e}") + self.event_stats["handler_errors"] += 1 + import traceback + traceback.print_exc() + + def _process_event_queue(self): + """处理事件队列""" + try: + processed_count = 0 + max_process_per_loop = 100 # 每次循环最多处理100个事件 + + while not self.event_queue.empty() and processed_count < max_process_per_loop: + try: + # 从队列获取事件 + event_data = self.event_queue.get_nowait() + + # 处理事件 + self._handle_event_internal(event_data) + + processed_count += 1 + self.event_stats["events_processed"] += 1 + + except queue.Empty: + break + except Exception as e: + print(f"✗ 事件队列处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + self.event_state["pending_events"] = self.event_queue.qsize() + + except Exception as e: + print(f"✗ 事件队列处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_event_internal(self, event_data: Dict[str, Any]): + """ + 内部事件处理 + + Args: + event_data: 事件数据 + """ + try: + event_type = event_data.get("type") + event_payload = event_data.get("payload", {}) + event_priority = event_data.get("priority", 0) + event_timestamp = event_data.get("timestamp", time.time()) + + # 应用事件过滤器 + if self.event_config["enable_event_filtering"]: + if not self._apply_event_filters(event_type, event_payload): + # 事件被过滤掉 + return + + # 查找事件处理器 + if event_type in self.event_handlers: + handlers = self.event_handlers[event_type] + + # 按优先级排序处理器 + if self.event_config["enable_event_prioritization"]: + handlers = sorted(handlers, key=lambda x: x.get("priority", 0), reverse=True) + + # 调用所有处理器 + for handler_info in handlers: + try: + handler_func = handler_info["function"] + handler_result = handler_func(event_payload) + + # 检查是否需要停止传播 + if handler_result is False: + break + + except Exception as e: + print(f"✗ 事件处理器执行失败: {event_type} - {e}") + self.event_stats["handler_errors"] += 1 + + # 触发事件处理回调 + self._trigger_event_callback("event_processed", { + "event_type": event_type, + "event_payload": event_payload, + "processing_time": time.time() - event_timestamp, + "timestamp": time.time() + }) + + except Exception as e: + print(f"✗ 内部事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _apply_event_filters(self, event_type: str, event_payload: Dict[str, Any]) -> bool: + """ + 应用事件过滤器 + + Args: + event_type: 事件类型 + event_payload: 事件载荷 + + Returns: + 是否通过过滤器 + """ + try: + if event_type in self.event_filters: + filters = self.event_filters[event_type] + for filter_func in filters: + if not filter_func(event_payload): + return False + + return True + + except Exception as e: + print(f"✗ 事件过滤器应用失败: {e}") + self.event_stats["handler_errors"] += 1 + return True + + def emit_event(self, event_type: str, event_payload: Dict[str, Any] = None, + priority: int = 0, async_process: bool = None) -> bool: + """ + 发出事件 + + Args: + event_type: 事件类型 + event_payload: 事件载荷 + priority: 事件优先级 + async_process: 是否异步处理 + + Returns: + 是否发出成功 + """ + try: + if not self.enabled or not self.event_config["enable_event_system"]: + return False + + # 使用默认异步处理设置或指定设置 + if async_process is None: + async_process = self.event_config["enable_async_processing"] + + event_data = { + "type": event_type, + "payload": event_payload or {}, + "priority": priority, + "timestamp": time.time(), + "async": async_process + } + + # 触发事件接收回调 + self._trigger_event_callback("event_received", { + "event_type": event_type, + "event_payload": event_payload, + "priority": priority, + "timestamp": time.time() + }) + + # 更新统计 + self.event_stats["events_received"] += 1 + + if async_process: + # 异步处理:添加到队列 + try: + self.event_queue.put_nowait(event_data) + self.event_stats["async_events"] += 1 + return True + except queue.Full: + # 队列已满,丢弃事件 + self.event_stats["events_dropped"] += 1 + self.event_state["dropped_events"] += 1 + + # 触发事件丢弃回调 + self._trigger_event_callback("event_dropped", { + "event_type": event_type, + "event_payload": event_payload, + "reason": "queue_full", + "timestamp": time.time() + }) + + return False + else: + # 同步处理:立即处理 + self._handle_event_internal(event_data) + return True + + except Exception as e: + print(f"✗ 事件发出失败: {e}") + self.event_stats["handler_errors"] += 1 + return False + + def register_event_handler(self, event_type: str, handler: Callable, priority: int = 0): + """ + 注册事件处理器 + + Args: + event_type: 事件类型 + handler: 处理器函数 + priority: 处理器优先级 + """ + try: + if event_type not in self.event_handlers: + self.event_handlers[event_type] = [] + + handler_info = { + "function": handler, + "priority": priority + } + + self.event_handlers[event_type].append(handler_info) + print(f"✓ 事件处理器已注册: {event_type} (优先级: {priority})") + + except Exception as e: + print(f"✗ 事件处理器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def unregister_event_handler(self, event_type: str, handler: Callable): + """ + 注销事件处理器 + + Args: + event_type: 事件类型 + handler: 处理器函数 + """ + try: + if event_type in self.event_handlers: + handlers = self.event_handlers[event_type] + for i, handler_info in enumerate(handlers): + if handler_info["function"] == handler: + del handlers[i] + print(f"✓ 事件处理器已注销: {event_type}") + return + + print(f"✗ 事件处理器不存在: {event_type}") + + except Exception as e: + print(f"✗ 事件处理器注销失败: {e}") + self.event_stats["handler_errors"] += 1 + + def register_event_filter(self, event_type: str, filter_func: Callable): + """ + 注册事件过滤器 + + Args: + event_type: 事件类型 + filter_func: 过滤器函数 + """ + try: + if event_type not in self.event_filters: + self.event_filters[event_type] = [] + + self.event_filters[event_type].append(filter_func) + print(f"✓ 事件过滤器已注册: {event_type}") + + except Exception as e: + print(f"✗ 事件过滤器注册失败: {e}") + self.event_stats["handler_errors"] += 1 + + def unregister_event_filter(self, event_type: str, filter_func: Callable): + """ + 注销事件过滤器 + + Args: + event_type: 事件类型 + filter_func: 过滤器函数 + """ + try: + if event_type in self.event_filters: + filters = self.event_filters[event_type] + if filter_func in filters: + filters.remove(filter_func) + print(f"✓ 事件过滤器已注销: {event_type}") + return + + print(f"✗ 事件过滤器不存在: {event_type}") + + except Exception as e: + print(f"✗ 事件过滤器注销失败: {e}") + self.event_stats["handler_errors"] += 1 + + # 默认事件处理器 + def _handle_client_connected(self, payload: Dict[str, Any]): + """处理客户端连接事件""" + try: + client_id = payload.get("client_id") + connection_data = payload.get("connection_data") + + # 添加客户端到客户端管理器 + if self.plugin.client_manager: + self.plugin.client_manager.add_client(client_id, { + "connection_data": connection_data, + "ip_address": connection_data.get("remote_address", "") if connection_data else "" + }) + + except Exception as e: + print(f"✗ 客户端连接事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_client_disconnected(self, payload: Dict[str, Any]): + """处理客户端断开连接事件""" + try: + client_id = payload.get("client_id") + reason = payload.get("reason", "unknown") + + # 从客户端管理器移除客户端 + if self.plugin.client_manager: + self.plugin.client_manager.remove_client(client_id, reason) + + except Exception as e: + print(f"✗ 客户端断开连接事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_message_received(self, payload: Dict[str, Any]): + """处理消息接收事件""" + try: + client_id = payload.get("client_id") + message = payload.get("message") + + # 将消息传递给消息路由器 + if self.plugin.message_router: + self.plugin.message_router.route_message(client_id, message) + + except Exception as e: + print(f"✗ 消息接收事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_room_created(self, payload: Dict[str, Any]): + """处理房间创建事件""" + try: + room_id = payload.get("room_id") + room_name = payload.get("room_name") + + # 可以在这里添加额外的房间创建逻辑 + print(f"房间已创建: {room_name} ({room_id})") + + except Exception as e: + print(f"✗ 房间创建事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_room_destroyed(self, payload: Dict[str, Any]): + """处理房间销毁事件""" + try: + room_id = payload.get("room_id") + room_name = payload.get("room_name") + + # 可以在这里添加额外的房间销毁逻辑 + print(f"房间已销毁: {room_name} ({room_id})") + + except Exception as e: + print(f"✗ 房间销毁事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_client_joined_room(self, payload: Dict[str, Any]): + """处理客户端加入房间事件""" + try: + room_id = payload.get("room_id") + client_id = payload.get("client_id") + + # 可以在这里添加额外的客户端加入房间逻辑 + print(f"客户端 {client_id} 已加入房间 {room_id}") + + except Exception as e: + print(f"✗ 客户端加入房间事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def _handle_client_left_room(self, payload: Dict[str, Any]): + """处理客户端离开房间事件""" + try: + room_id = payload.get("room_id") + client_id = payload.get("client_id") + + # 可以在这里添加额外的客户端离开房间逻辑 + print(f"客户端 {client_id} 已离开房间 {room_id}") + + except Exception as e: + print(f"✗ 客户端离开房间事件处理失败: {e}") + self.event_stats["handler_errors"] += 1 + + def get_event_stats(self) -> Dict[str, Any]: + """ + 获取事件统计信息 + + Returns: + 事件统计字典 + """ + return { + "state": self.event_state.copy(), + "stats": self.event_stats.copy(), + "config": self.event_config.copy(), + "pending_events": self.event_queue.qsize() + } + + def reset_stats(self): + """重置事件统计信息""" + try: + self.event_stats = { + "events_received": 0, + "events_processed": 0, + "events_dropped": 0, + "handler_errors": 0, + "async_events": 0 + } + print("✓ 事件统计信息已重置") + except Exception as e: + print(f"✗ 事件统计信息重置失败: {e}") + + def set_event_config(self, config: Dict[str, Any]) -> bool: + """ + 设置事件配置 + + Args: + config: 事件配置字典 + + Returns: + 是否设置成功 + """ + try: + self.event_config.update(config) + print(f"✓ 事件配置已更新: {self.event_config}") + return True + except Exception as e: + print(f"✗ 事件配置设置失败: {e}") + return False + + def get_event_config(self) -> Dict[str, Any]: + """ + 获取事件配置 + + Returns: + 事件配置字典 + """ + return self.event_config.copy() + + def _trigger_event_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发事件回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.event_callbacks: + for callback in self.event_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 事件回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 事件回调触发失败: {e}") + + def register_event_callback(self, callback_type: str, callback: callable): + """ + 注册事件回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.event_callbacks: + self.event_callbacks[callback_type].append(callback) + print(f"✓ 事件回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 事件回调注册失败: {e}") + + def unregister_event_callback(self, callback_type: str, callback: callable): + """ + 注销事件回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.event_callbacks: + if callback in self.event_callbacks[callback_type]: + self.event_callbacks[callback_type].remove(callback) + print(f"✓ 事件回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 事件回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/messaging/message_router.py b/plugins/user/realtime_communication/messaging/message_router.py new file mode 100644 index 00000000..e9304ee0 --- /dev/null +++ b/plugins/user/realtime_communication/messaging/message_router.py @@ -0,0 +1,771 @@ +""" +消息路由器模块 +处理客户端间的消息传递 +""" + +import time +import threading +import json +from typing import Dict, Any, List, Optional + +class MessageRouter: + """ + 消息路由器 + 处理客户端间的消息传递 + """ + + def __init__(self, plugin): + """ + 初始化消息路由器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 消息路由配置 + self.message_config = { + "enable_message_filtering": True, + "max_message_size": 65536, # 64KB + "enable_message_queue": True, + "message_queue_size": 10000, + "enable_broadcast_optimization": True, + "enable_message_compression": True, + "enable_rate_limiting": True, + "max_messages_per_second_per_client": 100, + "enable_message_logging": False + } + + # 消息路由状态 + self.router_state = { + "is_processing": False, + "pending_messages": 0, + "active_routes": 0, + "last_message_time": 0.0 + } + + # 消息路由统计 + self.message_stats = { + "messages_routed": 0, + "messages_broadcast": 0, + "messages_filtered": 0, + "messages_dropped": 0, + "bytes_routed": 0, + "routing_errors": 0 + } + + # 消息处理器存储 + self.message_handlers = {} + + # 消息队列 + self.message_queue = [] + self.message_queue_lock = threading.RLock() + + # 路由表 + self.routing_table = {} + + # 回调函数 + self.message_callbacks = { + "message_routed": [], + "message_broadcast": [], + "message_filtered": [], + "message_error": [] + } + + # 时间戳记录 + self.last_queue_process = 0.0 + self.last_stats_reset = 0.0 + + print("✓ 消息路由器已创建") + + def initialize(self) -> bool: + """ + 初始化消息路由器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化消息路由器...") + + # 注册默认消息处理器 + self._register_default_handlers() + + self.initialized = True + print("✓ 消息路由器初始化完成") + return True + + except Exception as e: + print(f"✗ 消息路由器初始化失败: {e}") + self.message_stats["routing_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _register_default_handlers(self): + """注册默认消息处理器""" + try: + # 注册系统消息处理器 + self.register_message_handler("ping", self._handle_ping_message) + self.register_message_handler("pong", self._handle_pong_message) + self.register_message_handler("chat", self._handle_chat_message) + self.register_message_handler("join_room", self._handle_join_room_message) + self.register_message_handler("leave_room", self._handle_leave_room_message) + self.register_message_handler("broadcast", self._handle_broadcast_message) + + print("✓ 默认消息处理器已注册") + + except Exception as e: + print(f"✗ 默认消息处理器注册失败: {e}") + self.message_stats["routing_errors"] += 1 + + 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.message_stats["routing_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用消息路由器""" + try: + self.enabled = False + + # 清空消息队列 + with self.message_queue_lock: + self.message_queue.clear() + + print("✓ 消息路由器已禁用") + + except Exception as e: + print(f"✗ 消息路由器禁用失败: {e}") + self.message_stats["routing_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理消息路由器资源""" + try: + # 禁用消息路由器 + if self.enabled: + self.disable() + + # 清理回调和处理器 + self.message_callbacks.clear() + self.message_handlers.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 self.message_config["enable_message_queue"]: + self._process_message_queue() + + self.router_state["last_message_time"] = current_time + + except Exception as e: + print(f"✗ 消息路由器更新失败: {e}") + self.message_stats["routing_errors"] += 1 + import traceback + traceback.print_exc() + + def _process_message_queue(self): + """处理消息队列""" + try: + if not self.message_queue: + return + + with self.message_queue_lock: + messages_to_process = self.message_queue[:] + self.message_queue.clear() + + for message_data in messages_to_process: + try: + client_id = message_data.get("client_id") + message = message_data.get("message") + + # 处理消息 + self._route_message_internal(client_id, message) + + except Exception as e: + print(f"✗ 队列消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + + self.router_state["pending_messages"] = len(self.message_queue) + + except Exception as e: + print(f"✗ 消息队列处理失败: {e}") + self.message_stats["routing_errors"] += 1 + + def register_message_handler(self, message_type: str, handler: callable): + """ + 注册消息处理器 + + Args: + message_type: 消息类型 + handler: 处理器函数 + """ + try: + self.message_handlers[message_type] = handler + print(f"✓ 消息处理器已注册: {message_type}") + except Exception as e: + print(f"✗ 消息处理器注册失败: {e}") + self.message_stats["routing_errors"] += 1 + + def unregister_message_handler(self, message_type: str): + """ + 注销消息处理器 + + Args: + message_type: 消息类型 + """ + try: + if message_type in self.message_handlers: + del self.message_handlers[message_type] + print(f"✓ 消息处理器已注销: {message_type}") + else: + print(f"✗ 消息处理器不存在: {message_type}") + except Exception as e: + print(f"✗ 消息处理器注销失败: {e}") + self.message_stats["routing_errors"] += 1 + + def route_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 路由消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否路由成功 + """ + try: + if not self.enabled: + return False + + # 检查消息大小 + if self.message_config["enable_message_filtering"]: + message_size = len(json.dumps(message, ensure_ascii=False).encode('utf-8')) + if message_size > self.message_config["max_message_size"]: + print(f"✗ 消息过大: {message_size} bytes") + self.message_stats["messages_dropped"] += 1 + return False + + # 如果启用消息队列,将消息加入队列 + if self.message_config["enable_message_queue"]: + with self.message_queue_lock: + if len(self.message_queue) >= self.message_config["message_queue_size"]: + print("✗ 消息队列已满,丢弃消息") + self.message_stats["messages_dropped"] += 1 + return False + + self.message_queue.append({ + "client_id": client_id, + "message": message, + "timestamp": time.time() + }) + + self.router_state["pending_messages"] = len(self.message_queue) + return True + else: + # 直接处理消息 + return self._route_message_internal(client_id, message) + + except Exception as e: + print(f"✗ 消息路由失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _route_message_internal(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 内部消息路由处理 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否路由成功 + """ + try: + # 更新统计信息 + self.message_stats["messages_routed"] += 1 + message_size = len(json.dumps(message, ensure_ascii=False).encode('utf-8')) + self.message_stats["bytes_routed"] += message_size + + # 触发消息路由回调 + self._trigger_message_callback("message_routed", { + "client_id": client_id, + "message": message, + "timestamp": time.time() + }) + + # 获取消息类型 + message_type = message.get("type", "unknown") + + # 查找对应的消息处理器 + if message_type in self.message_handlers: + try: + # 调用消息处理器 + handler_result = self.message_handlers[message_type](client_id, message) + return handler_result if handler_result is not None else True + except Exception as e: + print(f"✗ 消息处理器执行失败: {message_type} - {e}") + self.message_stats["routing_errors"] += 1 + return False + else: + # 默认处理:广播给所有客户端(除了发送者) + return self._handle_default_message(client_id, message) + + except Exception as e: + print(f"✗ 内部消息路由处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_default_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理默认消息(广播给其他客户端) + + Args: + client_id: 发送客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + # 广播消息给所有其他客户端 + if self.plugin.websocket_server: + broadcast_success = self.plugin.websocket_server.broadcast_message( + message, exclude_client_id=client_id) + if broadcast_success: + self.message_stats["messages_broadcast"] += 1 + return True + else: + return False + else: + return False + + except Exception as e: + print(f"✗ 默认消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + # 默认消息处理器 + def _handle_ping_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理ping消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + # 更新客户端心跳 + if self.plugin.client_manager: + self.plugin.client_manager.update_client_heartbeat(client_id) + + # 回复pong消息 + pong_message = { + "type": "pong", + "timestamp": time.time(), + "ping_timestamp": message.get("timestamp", 0) + } + + if self.plugin.websocket_server: + return self.plugin.websocket_server.send_message_to_client(client_id, pong_message) + else: + return False + + except Exception as e: + print(f"✗ ping消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_pong_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理pong消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + # 更新客户端心跳 + if self.plugin.client_manager: + self.plugin.client_manager.update_client_heartbeat(client_id) + + return True + + except Exception as e: + print(f"✗ pong消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_chat_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理聊天消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + # 获取客户端信息 + client_data = self.plugin.client_manager.get_client(client_id) if self.plugin.client_manager else None + if not client_data: + return False + + # 添加发送者信息 + message["sender_id"] = client_id + message["sender_name"] = client_data.get("username", "Unknown") + message["sender_time"] = time.time() + + # 广播聊天消息 + if self.plugin.websocket_server: + broadcast_success = self.plugin.websocket_server.broadcast_message(message) + if broadcast_success: + self.message_stats["messages_broadcast"] += 1 + return broadcast_success + else: + return False + + except Exception as e: + print(f"✗ 聊天消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_join_room_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理加入房间消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + room_id = message.get("room_id") + if not room_id: + return False + + # 使用房间管理器处理加入房间请求 + if self.plugin.room_manager: + join_success = self.plugin.room_manager.add_client_to_room(room_id, client_id) + if join_success: + # 通知客户端加入成功 + response_message = { + "type": "room_joined", + "room_id": room_id, + "timestamp": time.time() + } + + if self.plugin.websocket_server: + self.plugin.websocket_server.send_message_to_client(client_id, response_message) + + return join_success + else: + return False + + except Exception as e: + print(f"✗ 加入房间消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_leave_room_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理离开房间消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + room_id = message.get("room_id") + if not room_id: + return False + + # 使用房间管理器处理离开房间请求 + if self.plugin.room_manager: + leave_success = self.plugin.room_manager.remove_client_from_room(room_id, client_id) + if leave_success: + # 通知客户端离开成功 + response_message = { + "type": "room_left", + "room_id": room_id, + "timestamp": time.time() + } + + if self.plugin.websocket_server: + self.plugin.websocket_server.send_message_to_client(client_id, response_message) + + return leave_success + else: + return False + + except Exception as e: + print(f"✗ 离开房间消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def _handle_broadcast_message(self, client_id: str, message: Dict[str, Any]) -> bool: + """ + 处理广播消息 + + Args: + client_id: 客户端ID + message: 消息数据 + + Returns: + 是否处理成功 + """ + try: + # 广播消息给所有客户端 + if self.plugin.websocket_server: + broadcast_success = self.plugin.websocket_server.broadcast_message(message) + if broadcast_success: + self.message_stats["messages_broadcast"] += 1 + return broadcast_success + else: + return False + + except Exception as e: + print(f"✗ 广播消息处理失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def send_direct_message(self, from_client_id: str, to_client_id: str, message: Dict[str, Any]) -> bool: + """ + 发送直接消息 + + Args: + from_client_id: 发送客户端ID + to_client_id: 接收客户端ID + message: 消息数据 + + Returns: + 是否发送成功 + """ + try: + if not self.enabled: + return False + + # 添加发送者信息 + message["sender_id"] = from_client_id + message["timestamp"] = time.time() + + # 发送消息到目标客户端 + if self.plugin.websocket_server: + send_success = self.plugin.websocket_server.send_message_to_client(to_client_id, message) + if send_success: + self.message_stats["messages_routed"] += 1 + return send_success + else: + return False + + except Exception as e: + print(f"✗ 直接消息发送失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def broadcast_to_room(self, room_id: str, message: Dict[str, Any], exclude_client_id: str = None) -> bool: + """ + 广播消息到房间 + + Args: + room_id: 房间ID + message: 消息数据 + exclude_client_id: 要排除的客户端ID + + Returns: + 是否广播成功 + """ + try: + if not self.enabled: + return False + + # 使用房间管理器获取房间内的客户端 + if self.plugin.room_manager: + room_clients = self.plugin.room_manager.get_room_clients(room_id) + if not room_clients: + return False + + success_count = 0 + for client_id in room_clients: + # 跳过排除的客户端 + if client_id == exclude_client_id: + continue + + # 发送消息 + if self.plugin.websocket_server: + send_success = self.plugin.websocket_server.send_message_to_client(client_id, message) + if send_success: + success_count += 1 + + return success_count > 0 + else: + return False + + except Exception as e: + print(f"✗ 房间广播失败: {e}") + self.message_stats["routing_errors"] += 1 + return False + + def get_message_stats(self) -> Dict[str, Any]: + """ + 获取消息统计信息 + + Returns: + 消息统计字典 + """ + return { + "state": self.router_state.copy(), + "stats": self.message_stats.copy(), + "config": self.message_config.copy() + } + + def reset_stats(self): + """重置消息统计信息""" + try: + self.message_stats = { + "messages_routed": 0, + "messages_broadcast": 0, + "messages_filtered": 0, + "messages_dropped": 0, + "bytes_routed": 0, + "routing_errors": 0 + } + print("✓ 消息统计信息已重置") + except Exception as e: + print(f"✗ 消息统计信息重置失败: {e}") + + def set_message_config(self, config: Dict[str, Any]) -> bool: + """ + 设置消息配置 + + Args: + config: 消息配置字典 + + Returns: + 是否设置成功 + """ + try: + self.message_config.update(config) + print(f"✓ 消息配置已更新: {self.message_config}") + return True + except Exception as e: + print(f"✗ 消息配置设置失败: {e}") + return False + + def get_message_config(self) -> Dict[str, Any]: + """ + 获取消息配置 + + Returns: + 消息配置字典 + """ + return self.message_config.copy() + + def _trigger_message_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发消息回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.message_callbacks: + for callback in self.message_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 消息回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 消息回调触发失败: {e}") + + def register_message_callback(self, callback_type: str, callback: callable): + """ + 注册消息回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.message_callbacks: + self.message_callbacks[callback_type].append(callback) + print(f"✓ 消息回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 消息回调注册失败: {e}") + + def unregister_message_callback(self, callback_type: str, callback: callable): + """ + 注销消息回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.message_callbacks: + if callback in self.message_callbacks[callback_type]: + self.message_callbacks[callback_type].remove(callback) + print(f"✓ 消息回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 消息回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/monitoring/communication_monitor.py b/plugins/user/realtime_communication/monitoring/communication_monitor.py new file mode 100644 index 00000000..05b17d93 --- /dev/null +++ b/plugins/user/realtime_communication/monitoring/communication_monitor.py @@ -0,0 +1,825 @@ +""" +通信监控模块 +实时监控通信性能和记录日志 +""" + +import time +import threading +import json +import os +from typing import Dict, Any, List, Optional +from collections import deque + +class CommunicationMonitor: + """ + 通信监控器 + 实时监控通信性能和记录日志 + """ + + def __init__(self, plugin): + """ + 初始化通信监控器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 监控配置 + self.monitor_config = { + "enable_monitoring": True, + "monitoring_interval": 5.0, + "log_level": "INFO", + "enable_file_logging": True, + "log_file_path": "/home/hello/EG/plugins/user/realtime_communication/logs/communication_monitor.log", + "max_log_file_size": 10485760, # 10MB + "enable_performance_monitoring": True, + "enable_alerts": True, + "alert_thresholds": { + "cpu_usage": 80.0, + "memory_usage": 85.0, + "network_latency": 100.0, + "error_rate": 5.0, + "message_queue_size": 1000 + }, + "enable_metrics_collection": True, + "metrics_retention_period": 3600, # 1小时 + "enable_realtime_dashboard": True + } + + # 性能指标存储 + self.performance_metrics = { + "cpu_usage": deque(maxlen=1000), + "memory_usage": deque(maxlen=1000), + "network_in": deque(maxlen=1000), + "network_out": deque(maxlen=1000), + "active_connections": deque(maxlen=1000), + "messages_per_second": deque(maxlen=1000), + "message_queue_size": deque(maxlen=1000), + "error_rate": deque(maxlen=1000) + } + + # 监控状态 + self.monitor_state = { + "last_monitoring_update": 0.0, + "last_performance_check": 0.0, + "total_logs": 0, + "total_alerts": 0, + "active_alerts": 0 + } + + # 警报系统 + self.active_alerts = {} + self.alert_history = deque(maxlen=1000) + + # 日志文件 + self.log_file = None + self.log_file_size = 0 + + # 监控统计 + self.monitor_stats = { + "metrics_collected": 0, + "logs_written": 0, + "alerts_generated": 0, + "alerts_resolved": 0, + "performance_checks": 0, + "monitor_errors": 0 + } + + # 线程锁 + self.monitor_lock = threading.RLock() + + # 回调函数 + self.monitor_callbacks = { + "metric_collected": [], + "alert_triggered": [], + "alert_resolved": [], + "log_entry": [], + "performance_degraded": [] + } + + # 监控线程 + self.monitor_thread = None + self.monitor_thread_running = False + + # 时间戳记录 + self.last_log_write = 0.0 + self.last_metric_collection = 0.0 + + print("✓ 通信监控器已创建") + + def initialize(self) -> bool: + """ + 初始化通信监控器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化通信监控器...") + + # 创建日志目录 + log_dir = os.path.dirname(self.monitor_config["log_file_path"]) + if not os.path.exists(log_dir): + os.makedirs(log_dir) + + # 初始化日志文件 + if self.monitor_config["enable_file_logging"]: + self._initialize_log_file() + + # 启动监控线程 + self._start_monitor_thread() + + self.initialized = True + print("✓ 通信监控器初始化完成") + return True + + except Exception as e: + print(f"✗ 通信监控器初始化失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def _initialize_log_file(self): + """初始化日志文件""" + try: + # 检查文件是否存在 + if os.path.exists(self.monitor_config["log_file_path"]): + self.log_file_size = os.path.getsize(self.monitor_config["log_file_path"]) + + # 打开日志文件 + self.log_file = open(self.monitor_config["log_file_path"], "a", encoding="utf-8") + print(f"✓ 日志文件已初始化: {self.monitor_config['log_file_path']}") + + except Exception as e: + print(f"✗ 日志文件初始化失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _start_monitor_thread(self): + """启动监控线程""" + try: + self.monitor_thread_running = True + self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True) + self.monitor_thread.start() + print("✓ 监控线程已启动") + except Exception as e: + print(f"✗ 监控线程启动失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _monitor_loop(self): + """监控线程循环""" + try: + while self.monitor_thread_running: + try: + if self.enabled and self.monitor_config["enable_monitoring"]: + current_time = time.time() + + # 收集性能指标 + if current_time - self.last_metric_collection >= self.monitor_config["monitoring_interval"]: + self._collect_performance_metrics() + self.last_metric_collection = current_time + + # 检查警报 + self._check_alerts() + + # 检查日志文件大小 + self._check_log_file_size() + + # 短暂休眠 + time.sleep(1.0) + + except Exception as e: + print(f"✗ 监控循环错误: {e}") + self.monitor_stats["monitor_errors"] += 1 + time.sleep(5.0) # 出错时延长休眠 + + except Exception as e: + print(f"✗ 监控线程失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + 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.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用通信监控器""" + try: + self.enabled = False + + # 停止监控线程 + if self.monitor_thread_running: + self.monitor_thread_running = False + if self.monitor_thread and self.monitor_thread.is_alive(): + self.monitor_thread.join(timeout=5.0) + + # 关闭日志文件 + if self.log_file: + self.log_file.close() + self.log_file = None + + print("✓ 通信监控器已禁用") + + except Exception as e: + print(f"✗ 通信监控器禁用失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理通信监控器资源""" + try: + # 禁用通信监控器 + if self.enabled: + self.disable() + + # 清理回调 + self.monitor_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() + self.monitor_state["last_monitoring_update"] = current_time + + except Exception as e: + print(f"✗ 通信监控器更新失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + import traceback + traceback.print_exc() + + def _collect_performance_metrics(self): + """收集性能指标""" + try: + current_time = time.time() + metrics = {} + + # 收集系统资源使用情况 + try: + import psutil + + # CPU使用率 + cpu_percent = psutil.cpu_percent(interval=0.1) + metrics["cpu_usage"] = cpu_percent + self.performance_metrics["cpu_usage"].append((current_time, cpu_percent)) + + # 内存使用率 + memory_info = psutil.virtual_memory() + memory_percent = memory_info.percent + metrics["memory_usage"] = memory_percent + self.performance_metrics["memory_usage"].append((current_time, memory_percent)) + + # 网络使用情况 + net_io = psutil.net_io_counters() + metrics["network_in"] = net_io.bytes_recv + metrics["network_out"] = net_io.bytes_sent + self.performance_metrics["network_in"].append((current_time, net_io.bytes_recv)) + self.performance_metrics["network_out"].append((current_time, net_io.bytes_sent)) + + except ImportError: + # 如果没有psutil,使用简化的指标 + metrics["cpu_usage"] = 0.0 + metrics["memory_usage"] = 0.0 + metrics["network_in"] = 0 + metrics["network_out"] = 0 + + # 收集服务器指标 + if self.plugin.websocket_server: + server_stats = self.plugin.websocket_server.get_server_stats() + server_state = server_stats.get("state", {}) + + # 活跃连接数 + active_connections = server_state.get("current_connections", 0) + metrics["active_connections"] = active_connections + self.performance_metrics["active_connections"].append((current_time, active_connections)) + + # 数据传输量 + bytes_sent = server_state.get("bytes_sent", 0) + bytes_received = server_state.get("bytes_received", 0) + metrics["bytes_sent"] = bytes_sent + metrics["bytes_received"] = bytes_received + + # 收集客户端指标 + if self.plugin.client_manager: + client_stats = self.plugin.client_manager.get_client_stats() + client_state = client_stats.get("state", {}) + client_count = client_state.get("current_clients", 0) + metrics["client_count"] = client_count + + # 收集消息指标 + if self.plugin.message_router: + message_stats = self.plugin.message_router.get_message_stats() + message_state = message_stats.get("state", {}) + message_stats_data = message_stats.get("stats", {}) + + # 消息队列大小 + pending_messages = message_state.get("pending_messages", 0) + metrics["message_queue_size"] = pending_messages + self.performance_metrics["message_queue_size"].append((current_time, pending_messages)) + + # 消息处理速率 + messages_routed = message_stats_data.get("messages_routed", 0) + if hasattr(self, '_last_messages_routed'): + mps = (messages_routed - self._last_messages_routed) / self.monitor_config["monitoring_interval"] + metrics["messages_per_second"] = max(0, mps) + self.performance_metrics["messages_per_second"].append((current_time, metrics["messages_per_second"])) + else: + metrics["messages_per_second"] = 0 + self.performance_metrics["messages_per_second"].append((current_time, 0)) + + self._last_messages_routed = messages_routed + + # 收集房间指标 + if self.plugin.room_manager: + room_stats = self.plugin.room_manager.get_room_stats() + room_state = room_stats.get("state", {}) + active_rooms = room_state.get("active_rooms", 0) + metrics["active_rooms"] = active_rooms + + # 更新统计 + self.monitor_stats["metrics_collected"] += 1 + self.last_metric_collection = current_time + + # 触发指标收集回调 + self._trigger_monitor_callback("metric_collected", { + "metrics": metrics, + "timestamp": current_time + }) + + # 记录日志 + if self._should_log("DEBUG"): + self._write_log("DEBUG", f"性能指标收集: {metrics}") + + except Exception as e: + print(f"✗ 性能指标收集失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _check_alerts(self): + """检查警报条件""" + try: + if not self.monitor_config["enable_alerts"]: + return + + current_time = time.time() + thresholds = self.monitor_config["alert_thresholds"] + + # 检查CPU使用率 + if self.performance_metrics["cpu_usage"]: + latest_cpu = self.performance_metrics["cpu_usage"][-1][1] + if latest_cpu > thresholds["cpu_usage"]: + self._trigger_alert("high_cpu_usage", f"CPU使用率过高: {latest_cpu:.2f}%", current_time) + + # 检查内存使用率 + if self.performance_metrics["memory_usage"]: + latest_memory = self.performance_metrics["memory_usage"][-1][1] + if latest_memory > thresholds["memory_usage"]: + self._trigger_alert("high_memory_usage", f"内存使用率过高: {latest_memory:.2f}%", current_time) + + # 检查消息队列大小 + if self.performance_metrics["message_queue_size"]: + latest_queue_size = self.performance_metrics["message_queue_size"][-1][1] + if latest_queue_size > thresholds["message_queue_size"]: + self._trigger_alert("high_message_queue", f"消息队列过大: {latest_queue_size}", current_time) + + # 检查错误率 + if self.performance_metrics["error_rate"]: + latest_error_rate = self.performance_metrics["error_rate"][-1][1] + if latest_error_rate > thresholds["error_rate"]: + self._trigger_alert("high_error_rate", f"错误率过高: {latest_error_rate:.2f}%", current_time) + + # 检查活跃连接数 + if self.performance_metrics["active_connections"]: + latest_connections = self.performance_metrics["active_connections"][-1][1] + if self.plugin.websocket_server: + max_connections = self.plugin.websocket_server.websocket_config.get("max_connections", 1000) + if latest_connections > max_connections * 0.9: # 90%阈值 + self._trigger_alert("high_connections", f"连接数接近上限: {latest_connections}/{max_connections}", current_time) + + except Exception as e: + print(f"✗ 警报检查失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _trigger_alert(self, alert_type: str, message: str, timestamp: float = None): + """ + 触发警报 + + Args: + alert_type: 警报类型 + message: 警报消息 + timestamp: 时间戳 + """ + try: + if timestamp is None: + timestamp = time.time() + + alert_id = f"{alert_type}_{int(timestamp)}" + + # 检查是否已存在相同类型的活动警报 + with self.monitor_lock: + if alert_type in self.active_alerts: + # 更新现有警报 + self.active_alerts[alert_type]["count"] += 1 + self.active_alerts[alert_type]["last_triggered"] = timestamp + else: + # 创建新警报 + self.active_alerts[alert_type] = { + "id": alert_id, + "type": alert_type, + "message": message, + "first_triggered": timestamp, + "last_triggered": timestamp, + "count": 1, + "resolved": False + } + + self.monitor_state["active_alerts"] += 1 + self.monitor_stats["alerts_generated"] += 1 + + # 添加到警报历史 + self.alert_history.append({ + "id": alert_id, + "type": alert_type, + "message": message, + "timestamp": timestamp, + "resolved": False + }) + + # 触发警报回调 + self._trigger_monitor_callback("alert_triggered", { + "alert_id": alert_id, + "alert_type": alert_type, + "message": message, + "timestamp": timestamp + }) + + # 记录日志 + self._write_log("WARNING", f"警报触发 [{alert_type}]: {message}") + + except Exception as e: + print(f"✗ 警报触发失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def resolve_alert(self, alert_type: str): + """ + 解决警报 + + Args: + alert_type: 警报类型 + """ + try: + with self.monitor_lock: + if alert_type in self.active_alerts: + alert_data = self.active_alerts[alert_type] + alert_data["resolved"] = True + alert_data["resolved_time"] = time.time() + + self.monitor_state["active_alerts"] -= 1 + self.monitor_stats["alerts_resolved"] += 1 + + # 触发警报解决回调 + self._trigger_monitor_callback("alert_resolved", { + "alert_type": alert_type, + "alert_data": alert_data + }) + + # 从活动警报中移除 + del self.active_alerts[alert_type] + + # 记录日志 + self._write_log("INFO", f"警报已解决: {alert_type}") + + except Exception as e: + print(f"✗ 警报解决失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _check_log_file_size(self): + """检查日志文件大小""" + try: + if not self.monitor_config["enable_file_logging"] or not self.log_file: + return + + # 检查文件大小 + if os.path.exists(self.monitor_config["log_file_path"]): + current_size = os.path.getsize(self.monitor_config["log_file_path"]) + if current_size > self.monitor_config["max_log_file_size"]: + # 轮转日志文件 + self._rotate_log_file() + + except Exception as e: + print(f"✗ 日志文件大小检查失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _rotate_log_file(self): + """轮转日志文件""" + try: + if self.log_file: + self.log_file.close() + + # 重命名当前日志文件 + timestamp = int(time.time()) + rotated_filename = f"{self.monitor_config['log_file_path']}.{timestamp}" + os.rename(self.monitor_config["log_file_path"], rotated_filename) + + # 重新初始化日志文件 + self._initialize_log_file() + + print(f"✓ 日志文件已轮转: {rotated_filename}") + + except Exception as e: + print(f"✗ 日志文件轮转失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _write_log(self, level: str, message: str, extra_data: Dict[str, Any] = None): + """ + 写入日志 + + Args: + level: 日志级别 + message: 日志消息 + extra_data: 额外数据 + """ + try: + if not self._should_log(level): + return + + timestamp = time.time() + log_entry = { + "timestamp": timestamp, + "level": level, + "message": message, + "extra_data": extra_data + } + + # 控制台输出 + print(f"[{level}] {message}") + + # 文件日志 + if self.monitor_config["enable_file_logging"] and self.log_file: + try: + log_line = json.dumps(log_entry, ensure_ascii=False) + self.log_file.write(log_line + "\n") + self.log_file.flush() + + self.log_file_size += len(log_line) + 1 + self.monitor_stats["logs_written"] += 1 + self.monitor_state["total_logs"] += 1 + + except Exception as e: + print(f"✗ 文件日志写入失败: {e}") + + # 触发日志条目回调 + self._trigger_monitor_callback("log_entry", log_entry) + + self.last_log_write = timestamp + + except Exception as e: + print(f"✗ 日志写入失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + + def _should_log(self, level: str) -> bool: + """ + 检查是否应该记录指定级别的日志 + + Args: + level: 日志级别 + + Returns: + 是否应该记录日志 + """ + try: + log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + current_level = self.monitor_config["log_level"] + + if current_level not in log_levels: + return True # 默认记录所有日志 + + if level not in log_levels: + return True # 默认记录未知级别的日志 + + current_index = log_levels.index(current_level) + level_index = log_levels.index(level) + + # 只记录等于或高于当前级别的日志 + return level_index >= current_index + + except Exception as e: + print(f"✗ 日志级别检查失败: {e}") + return True + + def get_performance_metrics(self, metric_name: str = None, limit: int = 100) -> Any: + """ + 获取性能指标 + + Args: + metric_name: 指标名称(可选) + limit: 限制返回的数量 + + Returns: + 性能指标数据 + """ + try: + with self.monitor_lock: + if metric_name: + if metric_name in self.performance_metrics: + metrics = list(self.performance_metrics[metric_name]) + return metrics[-limit:] if len(metrics) > limit else metrics + else: + return [] + else: + # 返回所有指标 + result = {} + for name, metrics in self.performance_metrics.items(): + metric_list = list(metrics) + result[name] = metric_list[-limit:] if len(metric_list) > limit else metric_list + return result + + except Exception as e: + print(f"✗ 获取性能指标失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return {} + + def get_active_alerts(self) -> Dict[str, Any]: + """ + 获取活动警报 + + Returns: + 活动警报字典 + """ + try: + with self.monitor_lock: + return self.active_alerts.copy() + except Exception as e: + print(f"✗ 获取活动警报失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return {} + + def get_alert_history(self, limit: int = 100) -> List[Dict[str, Any]]: + """ + 获取警报历史 + + Args: + limit: 限制返回的数量 + + Returns: + 警报历史列表 + """ + try: + with self.monitor_lock: + history = list(self.alert_history) + return history[-limit:] if len(history) > limit else history + except Exception as e: + print(f"✗ 获取警报历史失败: {e}") + self.monitor_stats["monitor_errors"] += 1 + return [] + + def get_monitor_stats(self) -> Dict[str, Any]: + """ + 获取监控统计信息 + + Returns: + 监控统计字典 + """ + return { + "state": self.monitor_state.copy(), + "stats": self.monitor_stats.copy(), + "config": self.monitor_config.copy(), + "active_alerts_count": len(self.active_alerts), + "alert_history_count": len(self.alert_history) + } + + def reset_stats(self): + """重置监控统计信息""" + try: + self.monitor_stats = { + "metrics_collected": 0, + "logs_written": 0, + "alerts_generated": 0, + "alerts_resolved": 0, + "performance_checks": 0, + "monitor_errors": 0 + } + + self.monitor_state["total_logs"] = 0 + self.monitor_state["total_alerts"] = 0 + self.monitor_state["active_alerts"] = 0 + + print("✓ 监控统计信息已重置") + except Exception as e: + print(f"✗ 监控统计信息重置失败: {e}") + + def set_monitor_config(self, config: Dict[str, Any]) -> bool: + """ + 设置监控配置 + + Args: + config: 监控配置字典 + + Returns: + 是否设置成功 + """ + try: + self.monitor_config.update(config) + print(f"✓ 监控配置已更新: {self.monitor_config}") + return True + except Exception as e: + print(f"✗ 监控配置设置失败: {e}") + return False + + def get_monitor_config(self) -> Dict[str, Any]: + """ + 获取监控配置 + + Returns: + 监控配置字典 + """ + return self.monitor_config.copy() + + def _trigger_monitor_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发监控回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.monitor_callbacks: + for callback in self.monitor_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 监控回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 监控回调触发失败: {e}") + + def register_monitor_callback(self, callback_type: str, callback: callable): + """ + 注册监控回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.monitor_callbacks: + self.monitor_callbacks[callback_type].append(callback) + print(f"✓ 监控回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 监控回调注册失败: {e}") + + def unregister_monitor_callback(self, callback_type: str, callback: callable): + """ + 注销监控回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.monitor_callbacks: + if callback in self.monitor_callbacks[callback_type]: + self.monitor_callbacks[callback_type].remove(callback) + print(f"✓ 监控回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 监控回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/plugin.py b/plugins/user/realtime_communication/plugin.py new file mode 100644 index 00000000..edebc54e --- /dev/null +++ b/plugins/user/realtime_communication/plugin.py @@ -0,0 +1,391 @@ +""" +实时通信插件主文件 +为EG引擎提供实时通信功能 +""" + +import time +from typing import Dict, Any + +# 导入各个模块 +from .core.websocket_server import WebSocketServer +from .clients.client_manager import ClientManager +from .messaging.message_router import MessageRouter +from .rooms.room_manager import RoomManager +from .protocol.protocol_handler import ProtocolHandler +from .auth.auth_manager import AuthManager +from .serialization.data_serializer import DataSerializer +from .monitoring.communication_monitor import CommunicationMonitor +from .config.comm_config import CommConfig +from .events.event_handler import EventHandler +from .editor.comm_editor import CommEditor + +class RealtimeCommunicationPlugin: + """ + 实时通信插件主类 + 整合所有通信功能模块 + """ + + def __init__(self, engine): + """ + 初始化实时通信插件 + + Args: + engine: EG引擎实例 + """ + self.engine = engine + self.enabled = False + self.initialized = False + + # 插件信息 + self.plugin_info = { + "name": "Realtime Communication Plugin", + "version": "1.0.0", + "author": "EG Plugin System", + "description": "为EG引擎提供实时通信功能" + } + + # 插件状态 + self.plugin_state = { + "initialized_modules": [], + "last_update": 0.0, + "total_messages": 0, + "active_connections": 0 + } + + # 插件统计 + self.plugin_stats = { + "messages_sent": 0, + "messages_received": 0, + "connections_accepted": 0, + "connections_dropped": 0, + "errors": 0 + } + + # 功能模块 + self.websocket_server = None + self.client_manager = None + self.message_router = None + self.room_manager = None + self.protocol_handler = None + self.auth_manager = None + self.data_serializer = None + self.monitor = None + self.config_manager = None + self.event_handler = None + self.editor = None + + print("✓ 实时通信插件已创建") + + def initialize(self) -> bool: + """ + 初始化插件 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化实时通信插件...") + + # 初始化配置管理器 + self.config_manager = CommConfig(self) + if self.config_manager.initialize(): + self.plugin_state["initialized_modules"].append("config_manager") + else: + print("✗ 配置管理器初始化失败") + return False + + # 初始化认证管理器 + self.auth_manager = AuthManager(self) + if self.auth_manager.initialize(): + self.plugin_state["initialized_modules"].append("auth_manager") + else: + print("✗ 认证管理器初始化失败") + return False + + # 初始化数据序列化器 + self.data_serializer = DataSerializer(self) + if self.data_serializer.initialize(): + self.plugin_state["initialized_modules"].append("data_serializer") + else: + print("✗ 数据序列化器初始化失败") + return False + + # 初始化协议处理器 + self.protocol_handler = ProtocolHandler(self) + if self.protocol_handler.initialize(): + self.plugin_state["initialized_modules"].append("protocol_handler") + else: + print("✗ 协议处理器初始化失败") + return False + + # 初始化客户端管理器 + self.client_manager = ClientManager(self) + if self.client_manager.initialize(): + self.plugin_state["initialized_modules"].append("client_manager") + else: + print("✗ 客户端管理器初始化失败") + return False + + # 初始化消息路由器 + self.message_router = MessageRouter(self) + if self.message_router.initialize(): + self.plugin_state["initialized_modules"].append("message_router") + else: + print("✗ 消息路由器初始化失败") + return False + + # 初始化房间管理器 + self.room_manager = RoomManager(self) + if self.room_manager.initialize(): + self.plugin_state["initialized_modules"].append("room_manager") + else: + print("✗ 房间管理器初始化失败") + return False + + # 初始化WebSocket服务器 + self.websocket_server = WebSocketServer(self) + if self.websocket_server.initialize(): + self.plugin_state["initialized_modules"].append("websocket_server") + else: + print("✗ WebSocket服务器初始化失败") + return False + + # 初始化事件处理器 + self.event_handler = EventHandler(self) + if self.event_handler.initialize(): + self.plugin_state["initialized_modules"].append("event_handler") + else: + print("✗ 事件处理器初始化失败") + return False + + # 初始化监控系统 + self.monitor = CommunicationMonitor(self) + if self.monitor.initialize(): + self.plugin_state["initialized_modules"].append("monitor") + else: + print("✗ 监控系统初始化失败") + return False + + # 初始化编辑器接口 + self.editor = CommEditor(self) + if self.editor.initialize(): + self.plugin_state["initialized_modules"].append("editor") + else: + print("✗ 编辑器接口初始化失败") + return False + + self.initialized = True + print("✓ 实时通信插件初始化完成") + return True + + except Exception as e: + print(f"✗ 实时通信插件初始化失败: {e}") + self.plugin_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def enable(self) -> bool: + """ + 启用插件 + + Returns: + 是否启用成功 + """ + try: + if not self.initialized: + print("✗ 实时通信插件未初始化") + return False + + print("正在启用实时通信插件...") + + # 启用各模块(按依赖顺序) + modules_to_enable = [ + ("config_manager", self.config_manager), + ("auth_manager", self.auth_manager), + ("data_serializer", self.data_serializer), + ("protocol_handler", self.protocol_handler), + ("client_manager", self.client_manager), + ("message_router", self.message_router), + ("room_manager", self.room_manager), + ("event_handler", self.event_handler), + ("monitor", self.monitor), + ("editor", self.editor), + ("websocket_server", self.websocket_server) + ] + + for module_name, module in modules_to_enable: + if module and not module.enable(): + print(f"✗ {module_name} 启用失败") + return False + elif module: + print(f"✓ {module_name} 已启用") + + self.enabled = True + print("✓ 实时通信插件已启用") + return True + + except Exception as e: + print(f"✗ 实时通信插件启用失败: {e}") + self.plugin_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用插件""" + try: + print("正在禁用实时通信插件...") + + # 禁用各模块(按依赖顺序倒序) + modules_to_disable = [ + ("websocket_server", self.websocket_server), + ("editor", self.editor), + ("monitor", self.monitor), + ("event_handler", self.event_handler), + ("room_manager", self.room_manager), + ("message_router", self.message_router), + ("client_manager", self.client_manager), + ("protocol_handler", self.protocol_handler), + ("data_serializer", self.data_serializer), + ("auth_manager", self.auth_manager), + ("config_manager", self.config_manager) + ] + + for module_name, module in modules_to_disable: + if module: + module.disable() + print(f"✓ {module_name} 已禁用") + + self.enabled = False + print("✓ 实时通信插件已禁用") + + except Exception as e: + print(f"✗ 实时通信插件禁用失败: {e}") + self.plugin_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理插件资源""" + try: + print("正在清理实时通信插件资源...") + + # 禁用插件 + if self.enabled: + self.disable() + + # 清理各模块 + modules_to_finalize = [ + self.websocket_server, + self.client_manager, + self.message_router, + self.room_manager, + self.protocol_handler, + self.auth_manager, + self.data_serializer, + self.monitor, + self.config_manager, + self.event_handler, + self.editor + ] + + for module in modules_to_finalize: + if module: + module.finalize() + + 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 self.websocket_server: + self.websocket_server.update(dt) + + if self.client_manager: + self.client_manager.update(dt) + + if self.message_router: + self.message_router.update(dt) + + if self.room_manager: + self.room_manager.update(dt) + + if self.monitor: + self.monitor.update(dt) + + if self.editor: + self.editor.update(dt) + + self.plugin_state["last_update"] = current_time + + except Exception as e: + print(f"✗ 实时通信插件更新失败: {e}") + self.plugin_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def get_plugin_info(self) -> Dict[str, Any]: + """ + 获取插件信息 + + Returns: + 插件信息字典 + """ + return self.plugin_info.copy() + + def get_plugin_state(self) -> Dict[str, Any]: + """ + 获取插件状态 + + Returns: + 插件状态字典 + """ + return self.plugin_state.copy() + + def get_plugin_stats(self) -> Dict[str, Any]: + """ + 获取插件统计信息 + + Returns: + 插件统计字典 + """ + return { + "info": self.plugin_info.copy(), + "state": self.plugin_state.copy(), + "stats": self.plugin_stats.copy() + } + + def reset_stats(self): + """重置插件统计信息""" + try: + self.plugin_stats = { + "messages_sent": 0, + "messages_received": 0, + "connections_accepted": 0, + "connections_dropped": 0, + "errors": 0 + } + print("✓ 插件统计信息已重置") + except Exception as e: + print(f"✗ 插件统计信息重置失败: {e}") + +# 插件导出 +plugin = RealtimeCommunicationPlugin \ No newline at end of file diff --git a/plugins/user/realtime_communication/protocol/protocol_handler.py b/plugins/user/realtime_communication/protocol/protocol_handler.py new file mode 100644 index 00000000..f5c35423 --- /dev/null +++ b/plugins/user/realtime_communication/protocol/protocol_handler.py @@ -0,0 +1,658 @@ +""" +协议处理器模块 +处理各种通信协议和消息格式 +""" + +import time +import json +import zlib +import base64 +from typing import Dict, Any, List, Optional + +class ProtocolHandler: + """ + 协议处理器 + 处理各种通信协议和消息格式 + """ + + def __init__(self, plugin): + """ + 初始化协议处理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 协议配置 + self.protocol_config = { + "supported_protocols": ["websocket", "json", "binary"], + "default_protocol": "json", + "enable_compression": True, + "compression_threshold": 1024, # 1KB + "enable_encryption": False, + "enable_message_validation": True, + "max_message_size": 65536, # 64KB + "enable_fragmentation": True, + "fragment_size": 8192, # 8KB + "enable_checksum": True + } + + # 协议状态 + self.protocol_state = { + "active_protocols": [], + "compressed_messages": 0, + "encrypted_messages": 0, + "fragmented_messages": 0 + } + + # 协议统计 + self.protocol_stats = { + "messages_encoded": 0, + "messages_decoded": 0, + "bytes_encoded": 0, + "bytes_decoded": 0, + "compression_savings": 0, + "protocol_errors": 0 + } + + # 协议处理器映射 + self.protocol_handlers = { + "json": { + "encode": self._encode_json_message, + "decode": self._decode_json_message + }, + "binary": { + "encode": self._encode_binary_message, + "decode": self._decode_binary_message + } + } + + # 回调函数 + self.protocol_callbacks = { + "message_encoded": [], + "message_decoded": [], + "compression_applied": [], + "encryption_applied": [], + "protocol_error": [] + } + + # 时间戳记录 + self.last_compression = 0.0 + self.last_encryption = 0.0 + + print("✓ 协议处理器已创建") + + def initialize(self) -> bool: + """ + 初始化协议处理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化协议处理器...") + + self.initialized = True + print("✓ 协议处理器初始化完成") + return True + + except Exception as e: + print(f"✗ 协议处理器初始化失败: {e}") + self.protocol_stats["protocol_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.protocol_stats["protocol_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用协议处理器""" + try: + self.enabled = False + print("✓ 协议处理器已禁用") + + except Exception as e: + print(f"✗ 协议处理器禁用失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理协议处理器资源""" + try: + # 禁用协议处理器 + if self.enabled: + self.disable() + + # 清理回调 + self.protocol_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 + + # 协议处理器不需要频繁更新 + pass + + except Exception as e: + print(f"✗ 协议处理器更新失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + import traceback + traceback.print_exc() + + def encode_message(self, message: Dict[str, Any], protocol: str = None) -> Optional[bytes]: + """ + 编码消息 + + Args: + message: 消息数据 + protocol: 协议类型 + + Returns: + 编码后的消息或None + """ + try: + if not self.enabled: + return None + + # 使用默认协议或指定协议 + if protocol is None: + protocol = self.protocol_config["default_protocol"] + + # 验证协议支持 + if protocol not in self.protocol_handlers: + print(f"✗ 不支持的协议: {protocol}") + self.protocol_stats["protocol_errors"] += 1 + return None + + # 验证消息大小 + if self.protocol_config["enable_message_validation"]: + message_size = len(json.dumps(message, ensure_ascii=False).encode('utf-8')) + if message_size > self.protocol_config["max_message_size"]: + print(f"✗ 消息过大: {message_size} bytes") + self.protocol_stats["protocol_errors"] += 1 + return None + + # 获取协议处理器 + encoder = self.protocol_handlers[protocol]["encode"] + + # 编码消息 + encoded_message = encoder(message) + if encoded_message is None: + return None + + # 更新统计 + self.protocol_stats["messages_encoded"] += 1 + self.protocol_stats["bytes_encoded"] += len(encoded_message) + + # 触发消息编码回调 + self._trigger_protocol_callback("message_encoded", { + "protocol": protocol, + "message_size": len(encoded_message), + "timestamp": time.time() + }) + + return encoded_message + + except Exception as e: + print(f"✗ 消息编码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def decode_message(self, encoded_message: bytes, protocol: str = None) -> Optional[Dict[str, Any]]: + """ + 解码消息 + + Args: + encoded_message: 编码后的消息 + protocol: 协议类型 + + Returns: + 解码后的消息或None + """ + try: + if not self.enabled: + return None + + # 使用默认协议或指定协议 + if protocol is None: + protocol = self.protocol_config["default_protocol"] + + # 验证协议支持 + if protocol not in self.protocol_handlers: + print(f"✗ 不支持的协议: {protocol}") + self.protocol_stats["protocol_errors"] += 1 + return None + + # 获取协议处理器 + decoder = self.protocol_handlers[protocol]["decode"] + + # 解码消息 + decoded_message = decoder(encoded_message) + if decoded_message is None: + return None + + # 更新统计 + self.protocol_stats["messages_decoded"] += 1 + self.protocol_stats["bytes_decoded"] += len(encoded_message) + + # 触发消息解码回调 + self._trigger_protocol_callback("message_decoded", { + "protocol": protocol, + "message_size": len(encoded_message), + "timestamp": time.time() + }) + + return decoded_message + + except Exception as e: + print(f"✗ 消息解码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def _encode_json_message(self, message: Dict[str, Any]) -> Optional[bytes]: + """ + JSON消息编码 + + Args: + message: 消息数据 + + Returns: + 编码后的消息或None + """ + try: + # 序列化为JSON + json_string = json.dumps(message, ensure_ascii=False) + encoded_message = json_string.encode('utf-8') + + # 检查是否需要压缩 + if (self.protocol_config["enable_compression"] and + len(encoded_message) > self.protocol_config["compression_threshold"]): + compressed_message = zlib.compress(encoded_message) + if len(compressed_message) < len(encoded_message): + # 使用压缩版本 + encoded_message = compressed_message + self.protocol_state["compressed_messages"] += 1 + self.protocol_stats["compression_savings"] += len(encoded_message) - len(compressed_message) + + # 触发压缩应用回调 + self._trigger_protocol_callback("compression_applied", { + "original_size": len(json_string.encode('utf-8')), + "compressed_size": len(compressed_message), + "timestamp": time.time() + }) + + return encoded_message + + except Exception as e: + print(f"✗ JSON消息编码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def _decode_json_message(self, encoded_message: bytes) -> Optional[Dict[str, Any]]: + """ + JSON消息解码 + + Args: + encoded_message: 编码后的消息 + + Returns: + 解码后的消息或None + """ + try: + # 尝试解压缩 + try: + decompressed_message = zlib.decompress(encoded_message) + encoded_message = decompressed_message + except zlib.error: + # 不是压缩数据,使用原始数据 + pass + + # 解码为JSON + json_string = encoded_message.decode('utf-8') + decoded_message = json.loads(json_string) + + return decoded_message + + except Exception as e: + print(f"✗ JSON消息解码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def _encode_binary_message(self, message: Dict[str, Any]) -> Optional[bytes]: + """ + 二进制消息编码 + + Args: + message: 消息数据 + + Returns: + 编码后的消息或None + """ + try: + # 将消息转换为二进制格式(简化实现) + json_string = json.dumps(message, ensure_ascii=False) + binary_message = json_string.encode('utf-8') + + # 添加简单的头部信息 + header = len(binary_message).to_bytes(4, byteorder='big') + encoded_message = header + binary_message + + # 检查是否需要压缩 + if (self.protocol_config["enable_compression"] and + len(encoded_message) > self.protocol_config["compression_threshold"]): + compressed_message = zlib.compress(encoded_message) + if len(compressed_message) < len(encoded_message): + # 使用压缩版本 + encoded_message = compressed_message + self.protocol_state["compressed_messages"] += 1 + self.protocol_stats["compression_savings"] += len(encoded_message) - len(compressed_message) + + # 触发压缩应用回调 + self._trigger_protocol_callback("compression_applied", { + "original_size": len(header + binary_message), + "compressed_size": len(compressed_message), + "timestamp": time.time() + }) + + return encoded_message + + except Exception as e: + print(f"✗ 二进制消息编码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def _decode_binary_message(self, encoded_message: bytes) -> Optional[Dict[str, Any]]: + """ + 二进制消息解码 + + Args: + encoded_message: 编码后的消息 + + Returns: + 解码后的消息或None + """ + try: + # 尝试解压缩 + try: + decompressed_message = zlib.decompress(encoded_message) + encoded_message = decompressed_message + except zlib.error: + # 不是压缩数据,使用原始数据 + pass + + # 解析头部信息 + if len(encoded_message) < 4: + raise ValueError("消息太短") + + message_length = int.from_bytes(encoded_message[:4], byteorder='big') + binary_message = encoded_message[4:4 + message_length] + + # 解码为JSON + json_string = binary_message.decode('utf-8') + decoded_message = json.loads(json_string) + + return decoded_message + + except Exception as e: + print(f"✗ 二进制消息解码失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def fragment_message(self, message: bytes, fragment_size: int = None) -> List[bytes]: + """ + 分片消息 + + Args: + message: 消息数据 + fragment_size: 分片大小 + + Returns: + 分片列表 + """ + try: + if not self.protocol_config["enable_fragmentation"]: + return [message] + + if fragment_size is None: + fragment_size = self.protocol_config["fragment_size"] + + fragments = [] + for i in range(0, len(message), fragment_size): + fragment = message[i:i + fragment_size] + fragments.append(fragment) + + self.protocol_state["fragmented_messages"] += 1 + + return fragments + + except Exception as e: + print(f"✗ 消息分片失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return [message] + + def reassemble_message(self, fragments: List[bytes]) -> Optional[bytes]: + """ + 重组消息 + + Args: + fragments: 分片列表 + + Returns: + 重组后的消息或None + """ + try: + if not fragments: + return None + + reassembled_message = b''.join(fragments) + return reassembled_message + + except Exception as e: + print(f"✗ 消息重组失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return None + + def add_protocol_handler(self, protocol: str, encode_func: callable, decode_func: callable) -> bool: + """ + 添加协议处理器 + + Args: + protocol: 协议名称 + encode_func: 编码函数 + decode_func: 解码函数 + + Returns: + 是否添加成功 + """ + try: + self.protocol_handlers[protocol] = { + "encode": encode_func, + "decode": decode_func + } + + if protocol not in self.protocol_config["supported_protocols"]: + self.protocol_config["supported_protocols"].append(protocol) + + print(f"✓ 协议处理器已添加: {protocol}") + return True + + except Exception as e: + print(f"✗ 协议处理器添加失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return False + + def remove_protocol_handler(self, protocol: str) -> bool: + """ + 移除协议处理器 + + Args: + protocol: 协议名称 + + Returns: + 是否移除成功 + """ + try: + if protocol in self.protocol_handlers: + del self.protocol_handlers[protocol] + + if protocol in self.protocol_config["supported_protocols"]: + self.protocol_config["supported_protocols"].remove(protocol) + + print(f"✓ 协议处理器已移除: {protocol}") + return True + else: + print(f"✗ 协议处理器不存在: {protocol}") + return False + + except Exception as e: + print(f"✗ 协议处理器移除失败: {e}") + self.protocol_stats["protocol_errors"] += 1 + return False + + def get_protocol_stats(self) -> Dict[str, Any]: + """ + 获取协议统计信息 + + Returns: + 协议统计字典 + """ + return { + "state": self.protocol_state.copy(), + "stats": self.protocol_stats.copy(), + "config": self.protocol_config.copy() + } + + def reset_stats(self): + """重置协议统计信息""" + try: + self.protocol_stats = { + "messages_encoded": 0, + "messages_decoded": 0, + "bytes_encoded": 0, + "bytes_decoded": 0, + "compression_savings": 0, + "protocol_errors": 0 + } + print("✓ 协议统计信息已重置") + except Exception as e: + print(f"✗ 协议统计信息重置失败: {e}") + + def set_protocol_config(self, config: Dict[str, Any]) -> bool: + """ + 设置协议配置 + + Args: + config: 协议配置字典 + + Returns: + 是否设置成功 + """ + try: + self.protocol_config.update(config) + print(f"✓ 协议配置已更新: {self.protocol_config}") + return True + except Exception as e: + print(f"✗ 协议配置设置失败: {e}") + return False + + def get_protocol_config(self) -> Dict[str, Any]: + """ + 获取协议配置 + + Returns: + 协议配置字典 + """ + return self.protocol_config.copy() + + def _trigger_protocol_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发协议回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.protocol_callbacks: + for callback in self.protocol_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 协议回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 协议回调触发失败: {e}") + + def register_protocol_callback(self, callback_type: str, callback: callable): + """ + 注册协议回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.protocol_callbacks: + self.protocol_callbacks[callback_type].append(callback) + print(f"✓ 协议回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 协议回调注册失败: {e}") + + def unregister_protocol_callback(self, callback_type: str, callback: callable): + """ + 注销协议回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.protocol_callbacks: + if callback in self.protocol_callbacks[callback_type]: + self.protocol_callbacks[callback_type].remove(callback) + print(f"✓ 协议回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 协议回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/rooms/room_manager.py b/plugins/user/realtime_communication/rooms/room_manager.py new file mode 100644 index 00000000..49904640 --- /dev/null +++ b/plugins/user/realtime_communication/rooms/room_manager.py @@ -0,0 +1,894 @@ +""" +房间管理器模块 +管理通信组和广播消息 +""" + +import time +import threading +import uuid +from typing import Dict, Any, List, Optional + +class RoomManager: + """ + 房间管理器 + 管理通信组和广播消息 + """ + + def __init__(self, plugin): + """ + 初始化房间管理器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 房间配置 + self.room_config = { + "max_rooms": 1000, + "max_clients_per_room": 100, + "enable_room_persistence": False, + "room_timeout": 3600, # 1小时无活动房间自动关闭 + "enable_room_passwords": True, + "enable_room_visibility": True, + "default_room_settings": { + "max_clients": 50, + "is_public": True, + "allow_spectators": True, + "enable_voice_chat": True + } + } + + # 房间状态 + self.room_state = { + "total_rooms": 0, + "active_rooms": 0, + "clients_in_rooms": 0 + } + + # 房间存储 + self.rooms = {} + self.client_rooms = {} # 客户端所在房间映射 + self.room_lock = threading.RLock() + + # 房间统计 + self.room_stats = { + "rooms_created": 0, + "rooms_destroyed": 0, + "clients_joined": 0, + "clients_left": 0, + "messages_broadcast": 0, + "errors": 0 + } + + # 回调函数 + self.room_callbacks = { + "room_created": [], + "room_destroyed": [], + "client_joined": [], + "client_left": [], + "room_updated": [], + "room_error": [] + } + + # 时间戳记录 + self.last_cleanup = 0.0 + self.last_room_check = 0.0 + + print("✓ 房间管理器已创建") + + def initialize(self) -> bool: + """ + 初始化房间管理器 + + Returns: + 是否初始化成功 + """ + try: + print("正在初始化房间管理器...") + + self.initialized = True + print("✓ 房间管理器初始化完成") + return True + + except Exception as e: + print(f"✗ 房间管理器初始化失败: {e}") + self.room_stats["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.room_stats["errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用房间管理器""" + try: + self.enabled = False + + # 销毁所有房间 + self._destroy_all_rooms() + + print("✓ 房间管理器已禁用") + + except Exception as e: + print(f"✗ 房间管理器禁用失败: {e}") + self.room_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理房间管理器资源""" + try: + # 禁用房间管理器 + if self.enabled: + self.disable() + + # 清理回调 + self.room_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_cleanup > 60.0: # 每分钟清理一次 + self._cleanup_expired_rooms(current_time) + self.last_cleanup = current_time + + except Exception as e: + print(f"✗ 房间管理器更新失败: {e}") + self.room_stats["errors"] += 1 + import traceback + traceback.print_exc() + + def _cleanup_expired_rooms(self, current_time: float): + """清理过期房间""" + try: + expired_rooms = [] + with self.room_lock: + for room_id, room_data in self.rooms.items(): + # 检查房间是否超时 + if current_time - room_data["last_activity"] > self.room_config["room_timeout"]: + # 检查房间是否为空 + if len(room_data["clients"]) == 0: + expired_rooms.append(room_id) + + # 销毁过期房间 + for room_id in expired_rooms: + self.destroy_room(room_id) + + except Exception as e: + print(f"✗ 过期房间清理失败: {e}") + self.room_stats["errors"] += 1 + + def _destroy_all_rooms(self): + """销毁所有房间""" + try: + with self.room_lock: + room_ids = list(self.rooms.keys()) + + for room_id in room_ids: + self.destroy_room(room_id) + + except Exception as e: + print(f"✗ 销毁所有房间失败: {e}") + self.room_stats["errors"] += 1 + + def create_room(self, room_name: str = None, room_settings: Dict[str, Any] = None, + room_password: str = None) -> Optional[str]: + """ + 创建房间 + + Args: + room_name: 房间名称 + room_settings: 房间设置 + room_password: 房间密码 + + Returns: + 房间ID或None + """ + try: + if not self.enabled: + return None + + # 检查房间数量限制 + with self.room_lock: + if len(self.rooms) >= self.room_config["max_rooms"]: + print("✗ 房间数量已达上限") + return None + + # 生成房间ID + room_id = str(uuid.uuid4()) + + # 使用默认设置或传入设置 + settings = self.room_config["default_room_settings"].copy() + if room_settings: + settings.update(room_settings) + + # 创建房间数据 + room_data = { + "room_id": room_id, + "room_name": room_name or f"房间-{room_id[:8]}", + "settings": settings, + "password": room_password, + "clients": {}, # 客户端ID到客户端数据的映射 + "host_client_id": None, + "created_time": time.time(), + "last_activity": time.time(), + "room_state": {}, # 房间状态数据 + "custom_data": {} # 自定义房间数据 + } + + # 添加到房间列表 + with self.room_lock: + self.rooms[room_id] = room_data + + self.room_state["total_rooms"] += 1 + self.room_state["active_rooms"] += 1 + self.room_stats["rooms_created"] += 1 + + # 触发房间创建回调 + self._trigger_room_callback("room_created", { + "room_id": room_id, + "room_name": room_data["room_name"], + "settings": settings, + "timestamp": room_data["created_time"] + }) + + print(f"✓ 房间已创建: {room_id} ({room_data['room_name']})") + return room_id + + except Exception as e: + print(f"✗ 房间创建失败: {e}") + self.room_stats["errors"] += 1 + return None + + def destroy_room(self, room_id: str) -> bool: + """ + 销毁房间 + + Args: + room_id: 房间ID + + Returns: + 是否销毁成功 + """ + try: + if not self.enabled: + return False + + # 检查房间是否存在 + with self.room_lock: + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + return False + + room_data = self.rooms[room_id] + + # 通知房间内所有客户端 + for client_id in list(room_data["clients"].keys()): + self._notify_client_room_destroyed(client_id, room_id) + + # 移除房间 + del self.rooms[room_id] + + self.room_state["active_rooms"] -= 1 + self.room_stats["rooms_destroyed"] += 1 + + # 触发房间销毁回调 + self._trigger_room_callback("room_destroyed", { + "room_id": room_id, + "room_name": room_data["room_name"], + "timestamp": time.time() + }) + + print(f"✓ 房间已销毁: {room_id}") + return True + + except Exception as e: + print(f"✗ 房间销毁失败: {e}") + self.room_stats["errors"] += 1 + return False + + def _notify_client_room_destroyed(self, client_id: str, room_id: str): + """ + 通知客户端房间被销毁 + + Args: + client_id: 客户端ID + room_id: 房间ID + """ + try: + # 从客户端房间映射中移除 + if client_id in self.client_rooms: + del self.client_rooms[client_id] + + # 发送房间销毁通知 + if self.plugin.websocket_server: + destroy_message = { + "type": "room_destroyed", + "room_id": room_id, + "timestamp": time.time() + } + + self.plugin.websocket_server.send_message_to_client(client_id, destroy_message) + + except Exception as e: + print(f"✗ 通知客户端房间销毁失败: {e}") + self.room_stats["errors"] += 1 + + def add_client_to_room(self, room_id: str, client_id: str, client_data: Dict[str, Any] = None) -> bool: + """ + 添加客户端到房间 + + Args: + room_id: 房间ID + client_id: 客户端ID + client_data: 客户端数据 + + Returns: + 是否添加成功 + """ + try: + if not self.enabled: + return False + + # 检查客户端是否已认证 + if self.plugin.client_manager and not self.plugin.client_manager.is_client_authenticated(client_id): + print(f"✗ 客户端未认证: {client_id}") + return False + + # 检查房间是否存在 + with self.room_lock: + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + return False + + room_data = self.rooms[room_id] + + # 检查房间是否已满 + if len(room_data["clients"]) >= room_data["settings"]["max_clients"]: + print(f"✗ 房间已满: {room_id}") + return False + + # 检查房间密码 + if room_data["password"] and not self._verify_room_password(room_id, client_data): + print(f"✗ 房间密码错误: {room_id}") + return False + + # 添加客户端到房间 + client_info = client_data or {} + client_info["join_time"] = time.time() + client_info["last_activity"] = time.time() + + room_data["clients"][client_id] = client_info + room_data["last_activity"] = time.time() + + # 设置房间主机(如果是第一个客户端) + if room_data["host_client_id"] is None: + room_data["host_client_id"] = client_id + + # 更新客户端房间映射 + self.client_rooms[client_id] = room_id + + self.room_state["clients_in_rooms"] += 1 + self.room_stats["clients_joined"] += 1 + + # 触发客户端加入回调 + self._trigger_room_callback("client_joined", { + "room_id": room_id, + "room_name": room_data["room_name"], + "client_id": client_id, + "timestamp": time.time() + }) + + # 通知客户端加入成功 + join_success_message = { + "type": "room_join_success", + "room_id": room_id, + "room_name": room_data["room_name"], + "timestamp": time.time() + } + + if self.plugin.websocket_server: + self.plugin.websocket_server.send_message_to_client(client_id, join_success_message) + + print(f"✓ 客户端已加入房间: {client_id} -> {room_id}") + return True + + except Exception as e: + print(f"✗ 添加客户端到房间失败: {e}") + self.room_stats["errors"] += 1 + return False + + def remove_client_from_room(self, room_id: str, client_id: str) -> bool: + """ + 从房间移除客户端 + + Args: + room_id: 房间ID + client_id: 客户端ID + + Returns: + 是否移除成功 + """ + try: + if not self.enabled: + return False + + # 检查房间是否存在 + with self.room_lock: + if room_id not in self.rooms: + print(f"✗ 房间不存在: {room_id}") + return False + + room_data = self.rooms[room_id] + + # 检查客户端是否在房间中 + if client_id not in room_data["clients"]: + print(f"✗ 客户端不在房间中: {client_id}") + return False + + # 移除客户端 + del room_data["clients"][client_id] + room_data["last_activity"] = time.time() + + # 更新主机(如果移除的是主机) + if room_data["host_client_id"] == client_id: + if room_data["clients"]: + # 选择第一个客户端作为新主机 + room_data["host_client_id"] = next(iter(room_data["clients"])) + else: + room_data["host_client_id"] = None + + # 移除客户端房间映射 + if client_id in self.client_rooms: + del self.client_rooms[client_id] + + self.room_state["clients_in_rooms"] -= 1 + self.room_stats["clients_left"] += 1 + + # 触发客户端离开回调 + self._trigger_room_callback("client_left", { + "room_id": room_id, + "room_name": room_data["room_name"], + "client_id": client_id, + "timestamp": time.time() + }) + + # 通知客户端离开成功 + leave_success_message = { + "type": "room_leave_success", + "room_id": room_id, + "timestamp": time.time() + } + + if self.plugin.websocket_server: + self.plugin.websocket_server.send_message_to_client(client_id, leave_success_message) + + # 如果房间为空,销毁房间 + if len(room_data["clients"]) == 0: + self.destroy_room(room_id) + + print(f"✓ 客户端已离开房间: {client_id} <- {room_id}") + return True + + except Exception as e: + print(f"✗ 从房间移除客户端失败: {e}") + self.room_stats["errors"] += 1 + return False + + def _verify_room_password(self, room_id: str, client_data: Dict[str, Any]) -> bool: + """ + 验证房间密码 + + Args: + room_id: 房间ID + client_data: 客户端数据 + + Returns: + 密码是否正确 + """ + try: + if not self.room_config["enable_room_passwords"]: + return True + + with self.room_lock: + if room_id not in self.rooms: + return False + + room_data = self.rooms[room_id] + if not room_data["password"]: + return True + + client_password = client_data.get("password") if client_data else None + return client_password == room_data["password"] + + except Exception as e: + print(f"✗ 房间密码验证失败: {e}") + self.room_stats["errors"] += 1 + return False + + def get_room_info(self, room_id: str) -> Optional[Dict[str, Any]]: + """ + 获取房间信息 + + Args: + room_id: 房间ID + + Returns: + 房间信息或None + """ + try: + with self.room_lock: + if room_id in self.rooms: + room_data = self.rooms[room_id] + # 返回不包含敏感信息的房间信息 + safe_room_data = { + "room_id": room_data["room_id"], + "room_name": room_data["room_name"], + "settings": room_data["settings"], + "client_count": len(room_data["clients"]), + "has_password": bool(room_data["password"]), + "host_client_id": room_data["host_client_id"], + "created_time": room_data["created_time"], + "last_activity": room_data["last_activity"] + } + return safe_room_data + else: + return None + + except Exception as e: + print(f"✗ 获取房间信息失败: {e}") + self.room_stats["errors"] += 1 + return None + + def get_all_rooms(self) -> List[Dict[str, Any]]: + """ + 获取所有房间信息 + + Returns: + 房间信息列表 + """ + try: + rooms_info = [] + with self.room_lock: + for room_id, room_data in self.rooms.items(): + # 只返回公开房间或用户所在的房间 + if room_data["settings"].get("is_public", True): + safe_room_data = { + "room_id": room_data["room_id"], + "room_name": room_data["room_name"], + "settings": room_data["settings"], + "client_count": len(room_data["clients"]), + "has_password": bool(room_data["password"]), + "host_client_id": room_data["host_client_id"], + "created_time": room_data["created_time"], + "last_activity": room_data["last_activity"] + } + rooms_info.append(safe_room_data) + + return rooms_info + + except Exception as e: + print(f"✗ 获取所有房间信息失败: {e}") + self.room_stats["errors"] += 1 + return [] + + def get_client_room(self, client_id: str) -> Optional[str]: + """ + 获取客户端所在房间 + + Args: + client_id: 客户端ID + + Returns: + 房间ID或None + """ + try: + return self.client_rooms.get(client_id) + except Exception as e: + print(f"✗ 获取客户端房间失败: {e}") + self.room_stats["errors"] += 1 + return None + + def get_room_clients(self, room_id: str) -> List[str]: + """ + 获取房间内所有客户端 + + Args: + room_id: 房间ID + + Returns: + 客户端ID列表 + """ + try: + with self.room_lock: + if room_id in self.rooms: + return list(self.rooms[room_id]["clients"].keys()) + else: + return [] + + except Exception as e: + print(f"✗ 获取房间客户端失败: {e}") + self.room_stats["errors"] += 1 + return [] + + def update_room_state(self, room_id: str, state_data: Dict[str, Any]) -> bool: + """ + 更新房间状态 + + Args: + room_id: 房间ID + state_data: 状态数据 + + Returns: + 是否更新成功 + """ + try: + with self.room_lock: + if room_id not in self.rooms: + return False + + self.rooms[room_id]["room_state"].update(state_data) + self.rooms[room_id]["last_activity"] = time.time() + + # 触发房间更新回调 + self._trigger_room_callback("room_updated", { + "room_id": room_id, + "state_data": state_data, + "timestamp": time.time() + }) + + return True + + except Exception as e: + print(f"✗ 更新房间状态失败: {e}") + self.room_stats["errors"] += 1 + return False + + def broadcast_to_room(self, room_id: str, message_data: Dict[str, Any], + exclude_client_id: str = None) -> bool: + """ + 向房间内所有客户端广播消息 + + Args: + room_id: 房间ID + message_data: 消息数据 + exclude_client_id: 要排除的客户端ID + + Returns: + 是否广播成功 + """ + try: + if not self.enabled: + return False + + with self.room_lock: + if room_id not in self.rooms: + return False + + room_data = self.rooms[room_id] + success_count = 0 + + # 向房间内每个客户端发送消息 + for client_id in room_data["clients"]: + # 跳过排除的客户端 + if client_id == exclude_client_id: + continue + + # 发送消息 + if self.plugin.websocket_server: + send_success = self.plugin.websocket_server.send_message_to_client( + client_id, message_data) + if send_success: + success_count += 1 + + if success_count > 0: + self.room_stats["messages_broadcast"] += 1 + + return success_count > 0 + + except Exception as e: + print(f"✗ 房间广播失败: {e}") + self.room_stats["errors"] += 1 + return False + + def set_room_custom_data(self, room_id: str, key: str, value: Any) -> bool: + """ + 设置房间自定义数据 + + Args: + room_id: 房间ID + key: 数据键 + value: 数据值 + + Returns: + 是否设置成功 + """ + try: + with self.room_lock: + if room_id not in self.rooms: + return False + + self.rooms[room_id]["custom_data"][key] = value + return True + + except Exception as e: + print(f"✗ 设置房间自定义数据失败: {e}") + self.room_stats["errors"] += 1 + return False + + def get_room_custom_data(self, room_id: str, key: str, default: Any = None) -> Any: + """ + 获取房间自定义数据 + + Args: + room_id: 房间ID + key: 数据键 + default: 默认值 + + Returns: + 数据值或默认值 + """ + try: + with self.room_lock: + if room_id not in self.rooms: + return default + + return self.rooms[room_id]["custom_data"].get(key, default) + + except Exception as e: + print(f"✗ 获取房间自定义数据失败: {e}") + self.room_stats["errors"] += 1 + return default + + def get_room_stats(self) -> Dict[str, Any]: + """ + 获取房间统计信息 + + Returns: + 房间统计字典 + """ + return { + "state": self.room_state.copy(), + "stats": self.room_stats.copy(), + "config": self.room_config.copy(), + "current_rooms": len(self.rooms), + "current_clients_in_rooms": len(self.client_rooms) + } + + def reset_stats(self): + """重置房间统计信息""" + try: + self.room_stats = { + "rooms_created": 0, + "rooms_destroyed": 0, + "clients_joined": 0, + "clients_left": 0, + "messages_broadcast": 0, + "errors": 0 + } + print("✓ 房间统计信息已重置") + except Exception as e: + print(f"✗ 房间统计信息重置失败: {e}") + + def set_room_config(self, config: Dict[str, Any]) -> bool: + """ + 设置房间配置 + + Args: + config: 房间配置字典 + + Returns: + 是否设置成功 + """ + try: + self.room_config.update(config) + print(f"✓ 房间配置已更新: {self.room_config}") + return True + except Exception as e: + print(f"✗ 房间配置设置失败: {e}") + return False + + def get_room_config(self) -> Dict[str, Any]: + """ + 获取房间配置 + + Returns: + 房间配置字典 + """ + return self.room_config.copy() + + def _trigger_room_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发房间回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.room_callbacks: + for callback in self.room_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 房间回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 房间回调触发失败: {e}") + + def register_room_callback(self, callback_type: str, callback: callable): + """ + 注册房间回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.room_callbacks: + self.room_callbacks[callback_type].append(callback) + print(f"✓ 房间回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 房间回调注册失败: {e}") + + def unregister_room_callback(self, callback_type: str, callback: callable): + """ + 注销房间回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.room_callbacks: + if callback in self.room_callbacks[callback_type]: + self.room_callbacks[callback_type].remove(callback) + print(f"✓ 房间回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 房间回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/serialization/data_serializer.py b/plugins/user/realtime_communication/serialization/data_serializer.py new file mode 100644 index 00000000..d4ca9e0f --- /dev/null +++ b/plugins/user/realtime_communication/serialization/data_serializer.py @@ -0,0 +1,773 @@ +""" +数据序列化器模块 +将消息序列化为网络数据 +""" + +import time +import json +import pickle +import zlib +from typing import Dict, Any, List, Optional, Union + +class DataSerializer: + """ + 数据序列化器 + 将消息序列化为网络数据 + """ + + def __init__(self, plugin): + """ + 初始化数据序列化器 + + Args: + plugin: 实时通信插件实例 + """ + self.plugin = plugin + self.enabled = False + self.initialized = False + + # 序列化配置 + self.serializer_config = { + "default_format": "json", + "supported_formats": ["json", "pickle", "binary"], + "enable_compression": True, + "compression_threshold": 1024, # 1KB + "enable_caching": True, + "cache_size": 1000, + "enable_incremental_encoding": True, + "enable_type_preservation": True + } + + # 序列化状态 + self.serializer_state = { + "cached_objects": 0, + "compressed_objects": 0, + "serialized_objects": 0 + } + + # 序列化统计 + self.serializer_stats = { + "objects_serialized": 0, + "objects_deserialized": 0, + "bytes_serialized": 0, + "bytes_deserialized": 0, + "compression_savings": 0, + "cache_hits": 0, + "cache_misses": 0, + "serializer_errors": 0 + } + + # 缓存管理 + self.serialization_cache = {} + self.cache_timestamps = {} + + # 序列化器映射 + self.serializers = { + "json": { + "serialize": self._serialize_json, + "deserialize": self._deserialize_json + }, + "pickle": { + "serialize": self._serialize_pickle, + "deserialize": self._deserialize_pickle + }, + "binary": { + "serialize": self._serialize_binary, + "deserialize": self._deserialize_binary + } + } + + # 回调函数 + self.serializer_callbacks = { + "object_serialized": [], + "object_deserialized": [], + "compression_applied": [], + "cache_hit": [], + "serializer_error": [] + } + + # 时间戳记录 + self.last_serialization = 0.0 + self.last_cache_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.serializer_stats["serializer_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.serializer_stats["serializer_errors"] += 1 + import traceback + traceback.print_exc() + return False + + def disable(self): + """禁用数据序列化器""" + try: + self.enabled = False + + # 清理缓存 + self.serialization_cache.clear() + self.cache_timestamps.clear() + + print("✓ 数据序列化器已禁用") + + except Exception as e: + print(f"✗ 数据序列化器禁用失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + import traceback + traceback.print_exc() + + def finalize(self): + """清理数据序列化器资源""" + try: + # 禁用数据序列化器 + if self.enabled: + self.disable() + + # 清理回调 + self.serializer_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_cache_cleanup > 300: # 每5分钟清理一次 + self._cleanup_cache(current_time) + self.last_cache_cleanup = current_time + + self.last_serialization = current_time + + except Exception as e: + print(f"✗ 数据序列化器更新失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + import traceback + traceback.print_exc() + + def _cleanup_cache(self, current_time: float): + """清理缓存""" + try: + if not self.serializer_config["enable_caching"]: + return + + expired_keys = [] + for key, timestamp in self.cache_timestamps.items(): + # 缓存对象超过10分钟未使用则清除 + if current_time - timestamp > 600: + expired_keys.append(key) + + for key in expired_keys: + if key in self.serialization_cache: + del self.serialization_cache[key] + if key in self.cache_timestamps: + del self.cache_timestamps[key] + + self.serializer_state["cached_objects"] = len(self.serialization_cache) + + except Exception as e: + print(f"✗ 缓存清理失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + + def serialize(self, obj: Any, format: str = None) -> Optional[bytes]: + """ + 序列化对象 + + Args: + obj: 要序列化的对象 + format: 序列化格式 + + Returns: + 序列化后的字节数据或None + """ + try: + if not self.enabled: + return None + + # 使用默认格式或指定格式 + if format is None: + format = self.serializer_config["default_format"] + + # 验证格式支持 + if format not in self.serializers: + print(f"✗ 不支持的序列化格式: {format}") + self.serializer_stats["serializer_errors"] += 1 + return None + + # 检查缓存 + cache_key = None + if self.serializer_config["enable_caching"]: + cache_key = self._generate_cache_key(obj, format) + if cache_key in self.serialization_cache: + self.serializer_stats["cache_hits"] += 1 + self.cache_timestamps[cache_key] = time.time() + + # 触发缓存命中回调 + self._trigger_serializer_callback("cache_hit", { + "cache_key": cache_key, + "format": format, + "timestamp": time.time() + }) + + return self.serialization_cache[cache_key] + else: + self.serializer_stats["cache_misses"] += 1 + + # 获取序列化器 + serializer = self.serializers[format]["serialize"] + + # 序列化对象 + serialized_data = serializer(obj) + if serialized_data is None: + return None + + # 更新统计 + self.serializer_stats["objects_serialized"] += 1 + self.serializer_stats["bytes_serialized"] += len(serialized_data) + self.serializer_state["serialized_objects"] += 1 + + # 缓存结果 + if (self.serializer_config["enable_caching"] and + cache_key and + len(self.serialization_cache) < self.serializer_config["cache_size"]): + self.serialization_cache[cache_key] = serialized_data + self.cache_timestamps[cache_key] = time.time() + self.serializer_state["cached_objects"] = len(self.serialization_cache) + + # 触发对象序列化回调 + self._trigger_serializer_callback("object_serialized", { + "format": format, + "data_size": len(serialized_data), + "timestamp": time.time() + }) + + return serialized_data + + except Exception as e: + print(f"✗ 对象序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def deserialize(self, data: bytes, format: str = None) -> Optional[Any]: + """ + 反序列化对象 + + Args: + data: 要反序列化的字节数据 + format: 序列化格式 + + Returns: + 反序列化后的对象或None + """ + try: + if not self.enabled: + return None + + # 使用默认格式或指定格式 + if format is None: + format = self.serializer_config["default_format"] + + # 验证格式支持 + if format not in self.serializers: + print(f"✗ 不支持的反序列化格式: {format}") + self.serializer_stats["serializer_errors"] += 1 + return None + + # 获取反序列化器 + deserializer = self.serializers[format]["deserialize"] + + # 反序列化对象 + deserialized_obj = deserializer(data) + if deserialized_obj is None: + return None + + # 更新统计 + self.serializer_stats["objects_deserialized"] += 1 + self.serializer_stats["bytes_deserialized"] += len(data) + + # 触发对象反序列化回调 + self._trigger_serializer_callback("object_deserialized", { + "format": format, + "data_size": len(data), + "timestamp": time.time() + }) + + return deserialized_obj + + except Exception as e: + print(f"✗ 对象反序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _serialize_json(self, obj: Any) -> Optional[bytes]: + """ + JSON序列化 + + Args: + obj: 要序列化的对象 + + Returns: + 序列化后的字节数据或None + """ + try: + # 序列化为JSON字符串 + json_string = json.dumps(obj, ensure_ascii=False, default=self._json_default) + serialized_data = json_string.encode('utf-8') + + # 检查是否需要压缩 + if (self.serializer_config["enable_compression"] and + len(serialized_data) > self.serializer_config["compression_threshold"]): + compressed_data = zlib.compress(serialized_data) + if len(compressed_data) < len(serialized_data): + # 使用压缩版本 + serialized_data = compressed_data + self.serializer_state["compressed_objects"] += 1 + self.serializer_stats["compression_savings"] += len(serialized_data) - len(compressed_data) + + # 触发压缩应用回调 + self._trigger_serializer_callback("compression_applied", { + "original_size": len(json_string.encode('utf-8')), + "compressed_size": len(compressed_data), + "compression_ratio": len(compressed_data) / len(json_string.encode('utf-8')), + "timestamp": time.time() + }) + + return serialized_data + + except Exception as e: + print(f"✗ JSON序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _deserialize_json(self, data: bytes) -> Optional[Any]: + """ + JSON反序列化 + + Args: + data: 要反序列化的字节数据 + + Returns: + 反序列化后的对象或None + """ + try: + # 尝试解压缩 + try: + decompressed_data = zlib.decompress(data) + data = decompressed_data + except zlib.error: + # 不是压缩数据,使用原始数据 + pass + + # 解码为JSON + json_string = data.decode('utf-8') + deserialized_obj = json.loads(json_string) + + return deserialized_obj + + except Exception as e: + print(f"✗ JSON反序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _json_default(self, obj): + """ + JSON默认序列化方法 + + Args: + obj: 要序列化的对象 + + Returns: + 可序列化的对象 + """ + # 处理不能直接序列化的对象 + return str(obj) + + def _serialize_pickle(self, obj: Any) -> Optional[bytes]: + """ + Pickle序列化 + + Args: + obj: 要序列化的对象 + + Returns: + 序列化后的字节数据或None + """ + try: + # 序列化为pickle数据 + pickle_data = pickle.dumps(obj) + + # 检查是否需要压缩 + if (self.serializer_config["enable_compression"] and + len(pickle_data) > self.serializer_config["compression_threshold"]): + compressed_data = zlib.compress(pickle_data) + if len(compressed_data) < len(pickle_data): + # 使用压缩版本 + pickle_data = compressed_data + self.serializer_state["compressed_objects"] += 1 + self.serializer_stats["compression_savings"] += len(pickle_data) - len(compressed_data) + + # 触发压缩应用回调 + self._trigger_serializer_callback("compression_applied", { + "original_size": len(pickle.dumps(obj)), + "compressed_size": len(compressed_data), + "compression_ratio": len(compressed_data) / len(pickle.dumps(obj)), + "timestamp": time.time() + }) + + return pickle_data + + except Exception as e: + print(f"✗ Pickle序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _deserialize_pickle(self, data: bytes) -> Optional[Any]: + """ + Pickle反序列化 + + Args: + data: 要反序列化的字节数据 + + Returns: + 反序列化后的对象或None + """ + try: + # 尝试解压缩 + try: + decompressed_data = zlib.decompress(data) + data = decompressed_data + except zlib.error: + # 不是压缩数据,使用原始数据 + pass + + # 反序列化pickle数据 + deserialized_obj = pickle.loads(data) + + return deserialized_obj + + except Exception as e: + print(f"✗ Pickle反序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _serialize_binary(self, obj: Any) -> Optional[bytes]: + """ + 二进制序列化 + + Args: + obj: 要序列化的对象 + + Returns: + 序列化后的字节数据或None + """ + try: + # 简化实现:将对象转换为JSON再编码为二进制 + json_string = json.dumps(obj, ensure_ascii=False, default=self._json_default) + binary_data = json_string.encode('utf-8') + + # 添加简单的头部信息 + header = len(binary_data).to_bytes(4, byteorder='big') + serialized_data = header + binary_data + + # 检查是否需要压缩 + if (self.serializer_config["enable_compression"] and + len(serialized_data) > self.serializer_config["compression_threshold"]): + compressed_data = zlib.compress(serialized_data) + if len(compressed_data) < len(serialized_data): + # 使用压缩版本 + serialized_data = compressed_data + self.serializer_state["compressed_objects"] += 1 + self.serializer_stats["compression_savings"] += len(serialized_data) - len(compressed_data) + + # 触发压缩应用回调 + self._trigger_serializer_callback("compression_applied", { + "original_size": len(header + binary_data), + "compressed_size": len(compressed_data), + "compression_ratio": len(compressed_data) / len(header + binary_data), + "timestamp": time.time() + }) + + return serialized_data + + except Exception as e: + print(f"✗ 二进制序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _deserialize_binary(self, data: bytes) -> Optional[Any]: + """ + 二进制反序列化 + + Args: + data: 要反序列化的字节数据 + + Returns: + 反序列化后的对象或None + """ + try: + # 尝试解压缩 + try: + decompressed_data = zlib.decompress(data) + data = decompressed_data + except zlib.error: + # 不是压缩数据,使用原始数据 + pass + + # 解析头部信息 + if len(data) < 4: + raise ValueError("数据太短") + + message_length = int.from_bytes(data[:4], byteorder='big') + binary_data = data[4:4 + message_length] + + # 解码为JSON + json_string = binary_data.decode('utf-8') + deserialized_obj = json.loads(json_string) + + return deserialized_obj + + except Exception as e: + print(f"✗ 二进制反序列化失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return None + + def _generate_cache_key(self, obj: Any, format: str) -> str: + """ + 生成缓存键 + + Args: + obj: 对象 + format: 格式 + + Returns: + 缓存键 + """ + try: + # 简化实现:使用对象的字符串表示和格式生成键 + obj_str = str(obj) + cache_key = f"{format}:{hash(obj_str)}:{len(obj_str)}" + return cache_key + except Exception as e: + print(f"✗ 缓存键生成失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return f"error_key_{int(time.time() * 1000000)}" + + def add_serializer(self, format: str, serialize_func: callable, deserialize_func: callable) -> bool: + """ + 添加序列化器 + + Args: + format: 格式名称 + serialize_func: 序列化函数 + deserialize_func: 反序列化函数 + + Returns: + 是否添加成功 + """ + try: + self.serializers[format] = { + "serialize": serialize_func, + "deserialize": deserialize_func + } + + if format not in self.serializer_config["supported_formats"]: + self.serializer_config["supported_formats"].append(format) + + print(f"✓ 序列化器已添加: {format}") + return True + + except Exception as e: + print(f"✗ 序列化器添加失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return False + + def remove_serializer(self, format: str) -> bool: + """ + 移除序列化器 + + Args: + format: 格式名称 + + Returns: + 是否移除成功 + """ + try: + if format in self.serializers: + del self.serializers[format] + + if format in self.serializer_config["supported_formats"]: + self.serializer_config["supported_formats"].remove(format) + + print(f"✓ 序列化器已移除: {format}") + return True + else: + print(f"✗ 序列化器不存在: {format}") + return False + + except Exception as e: + print(f"✗ 序列化器移除失败: {e}") + self.serializer_stats["serializer_errors"] += 1 + return False + + def get_serializer_stats(self) -> Dict[str, Any]: + """ + 获取序列化统计信息 + + Returns: + 序列化统计字典 + """ + return { + "state": self.serializer_state.copy(), + "stats": self.serializer_stats.copy(), + "config": self.serializer_config.copy(), + "cache_size": len(self.serialization_cache) + } + + def reset_stats(self): + """重置序列化统计信息""" + try: + self.serializer_stats = { + "objects_serialized": 0, + "objects_deserialized": 0, + "bytes_serialized": 0, + "bytes_deserialized": 0, + "compression_savings": 0, + "cache_hits": 0, + "cache_misses": 0, + "serializer_errors": 0 + } + print("✓ 序列化统计信息已重置") + except Exception as e: + print(f"✗ 序列化统计信息重置失败: {e}") + + def set_serializer_config(self, config: Dict[str, Any]) -> bool: + """ + 设置序列化配置 + + Args: + config: 序列化配置字典 + + Returns: + 是否设置成功 + """ + try: + self.serializer_config.update(config) + print(f"✓ 序列化配置已更新: {self.serializer_config}") + return True + except Exception as e: + print(f"✗ 序列化配置设置失败: {e}") + return False + + def get_serializer_config(self) -> Dict[str, Any]: + """ + 获取序列化配置 + + Returns: + 序列化配置字典 + """ + return self.serializer_config.copy() + + def _trigger_serializer_callback(self, callback_type: str, data: Dict[str, Any]): + """ + 触发序列化回调 + + Args: + callback_type: 回调类型 + data: 回调数据 + """ + try: + if callback_type in self.serializer_callbacks: + for callback in self.serializer_callbacks[callback_type]: + try: + callback(data) + except Exception as e: + print(f"✗ 序列化回调执行失败: {callback_type} - {e}") + except Exception as e: + print(f"✗ 序列化回调触发失败: {e}") + + def register_serializer_callback(self, callback_type: str, callback: callable): + """ + 注册序列化回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.serializer_callbacks: + self.serializer_callbacks[callback_type].append(callback) + print(f"✓ 序列化回调已注册: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 序列化回调注册失败: {e}") + + def unregister_serializer_callback(self, callback_type: str, callback: callable): + """ + 注销序列化回调 + + Args: + callback_type: 回调类型 + callback: 回调函数 + """ + try: + if callback_type in self.serializer_callbacks: + if callback in self.serializer_callbacks[callback_type]: + self.serializer_callbacks[callback_type].remove(callback) + print(f"✓ 序列化回调已注销: {callback_type}") + else: + print(f"✗ 无效的回调类型: {callback_type}") + except Exception as e: + print(f"✗ 序列化回调注销失败: {e}") \ No newline at end of file diff --git a/plugins/user/realtime_communication/utils/comm_utils.py b/plugins/user/realtime_communication/utils/comm_utils.py new file mode 100644 index 00000000..3fac73a3 --- /dev/null +++ b/plugins/user/realtime_communication/utils/comm_utils.py @@ -0,0 +1,248 @@ +""" +通信工具类模块 +提供实用工具函数 +""" + +import time +import hashlib +import json +from typing import Dict, Any, List, Optional + +class CommUtils: + """ + 通信工具类 + 提供实用工具函数 + """ + + @staticmethod + def generate_unique_id(prefix: str = "") -> str: + """ + 生成唯一ID + + Args: + prefix: ID前缀 + + Returns: + 生成的唯一ID + """ + try: + timestamp = int(time.time() * 1000000) # 微秒时间戳 + unique_id = f"{prefix}{timestamp}" if prefix else str(timestamp) + return unique_id + except Exception as e: + print(f"✗ 唯一ID生成失败: {e}") + return f"error_id_{int(time.time() * 1000000)}" + + @staticmethod + def calculate_checksum(data: bytes) -> str: + """ + 计算数据校验和 + + Args: + data: 要计算校验和的数据 + + Returns: + 校验和字符串 + """ + try: + checksum = hashlib.md5(data).hexdigest() + return checksum + except Exception as e: + print(f"✗ 校验和计算失败: {e}") + return "" + + @staticmethod + def format_bytes(bytes_count: int) -> str: + """ + 格式化字节数 + + Args: + bytes_count: 字节数 + + Returns: + 格式化后的字符串 + """ + try: + if bytes_count < 1024: + return f"{bytes_count} B" + elif bytes_count < 1024 * 1024: + return f"{bytes_count / 1024:.2f} KB" + elif bytes_count < 1024 * 1024 * 1024: + return f"{bytes_count / (1024 * 1024):.2f} MB" + else: + return f"{bytes_count / (1024 * 1024 * 1024):.2f} GB" + except Exception as e: + print(f"✗ 字节格式化失败: {e}") + return f"{bytes_count} B" + + @staticmethod + def validate_json(data: str) -> bool: + """ + 验证JSON数据 + + Args: + data: JSON字符串 + + Returns: + 是否为有效JSON + """ + try: + json.loads(data) + return True + except Exception: + return False + + @staticmethod + def sanitize_input(input_str: str) -> str: + """ + 清理输入字符串 + + Args: + input_str: 输入字符串 + + Returns: + 清理后的字符串 + """ + try: + # 移除潜在的危险字符 + sanitized = input_str.replace("<", "<").replace(">", ">") + sanitized = sanitized.replace("\"", """).replace("'", "'") + return sanitized + except Exception as e: + print(f"✗ 输入清理失败: {e}") + return input_str + + @staticmethod + def create_response(success: bool, message: str = "", data: Any = None) -> Dict[str, Any]: + """ + 创建响应数据 + + Args: + success: 是否成功 + message: 消息 + data: 数据 + + Returns: + 响应数据字典 + """ + try: + response = { + "success": success, + "message": message, + "timestamp": time.time() + } + + if data is not None: + response["data"] = data + + return response + except Exception as e: + print(f"✗ 响应创建失败: {e}") + return { + "success": False, + "message": "响应创建失败", + "timestamp": time.time() + } + + @staticmethod + def merge_dicts(dict1: Dict[str, Any], dict2: Dict[str, Any]) -> Dict[str, Any]: + """ + 合并字典(递归) + + Args: + dict1: 第一个字典 + dict2: 第二个字典 + + Returns: + 合并后的字典 + """ + try: + result = dict1.copy() + + for key, value in dict2.items(): + if key in result and isinstance(result[key], dict) and isinstance(value, dict): + # 递归合并嵌套字典 + result[key] = CommUtils.merge_dicts(result[key], value) + else: + # 直接覆盖 + result[key] = value + + return result + except Exception as e: + print(f"✗ 字典合并失败: {e}") + return dict1 + + @staticmethod + def get_system_info() -> Dict[str, Any]: + """ + 获取系统信息 + + Returns: + 系统信息字典 + """ + try: + import platform + import psutil + + # 系统信息 + system_info = { + "platform": platform.system(), + "platform_version": platform.version(), + "architecture": platform.architecture()[0], + "processor": platform.processor(), + "python_version": platform.python_version() + } + + # CPU信息 + cpu_info = { + "physical_cores": psutil.cpu_count(logical=False), + "total_cores": psutil.cpu_count(logical=True), + "max_frequency": psutil.cpu_freq().max if psutil.cpu_freq() else 0, + "current_frequency": psutil.cpu_freq().current if psutil.cpu_freq() else 0 + } + + # 内存信息 + memory_info = psutil.virtual_memory() + memory_stats = { + "total": memory_info.total, + "available": memory_info.available, + "used": memory_info.used, + "percentage": memory_info.percent + } + + # 网络信息 + net_info = psutil.net_io_counters() + network_stats = { + "bytes_sent": net_info.bytes_sent, + "bytes_recv": net_info.bytes_recv, + "packets_sent": net_info.packets_sent, + "packets_recv": net_info.packets_recv + } + + return { + "system": system_info, + "cpu": cpu_info, + "memory": memory_stats, + "network": network_stats, + "timestamp": time.time() + } + + except Exception as e: + print(f"✗ 系统信息获取失败: {e}") + return { + "system": {}, + "cpu": {}, + "memory": {}, + "network": {}, + "timestamp": time.time() + } + +# 工具函数别名 +generate_id = CommUtils.generate_unique_id +calculate_checksum = CommUtils.calculate_checksum +format_bytes = CommUtils.format_bytes +validate_json = CommUtils.validate_json +sanitize_input = CommUtils.sanitize_input +create_response = CommUtils.create_response +merge_dicts = CommUtils.merge_dicts +get_system_info = CommUtils.get_system_info \ No newline at end of file