diff --git a/Server/ConsoleCommandsAPI.py b/Server/ConsoleCommandsAPI.py new file mode 100644 index 0000000..0e3c2a1 --- /dev/null +++ b/Server/ConsoleCommandsAPI.py @@ -0,0 +1,877 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +萌芽农场服务器控制台命令API模块 +作者: AI Assistant +功能: 提供服务器控制台命令处理功能 +""" + +import os +import json +import sys +from typing import Dict, Any, List, Optional +from datetime import datetime +from SMYMongoDBAPI import SMYMongoDBAPI + +class ConsoleCommandsAPI: + """控制台命令处理类""" + + def __init__(self, server): + """ + 初始化控制台命令API + + Args: + server: 游戏服务器实例 + """ + self.server = server + self.commands = { + "addmoney": self.cmd_add_money, + "addxp": self.cmd_add_experience, + "addlevel": self.cmd_add_level, + "addseed": self.cmd_add_seed, + "lsplayer": self.cmd_list_players, + "playerinfo": self.cmd_player_info, + "resetland": self.cmd_reset_land, + "weather": self.cmd_weather, + "help": self.cmd_help, + "stop": self.cmd_stop, + "save": self.cmd_save_all, + "reload": self.cmd_reload_config, + # MongoDB管理命令 + "dbtest": self.cmd_db_test, + "dbconfig": self.cmd_db_config, + "dbchat": self.cmd_db_chat, + "dbclean": self.cmd_db_clean, + "dbbackup": self.cmd_db_backup + } + + # 初始化MongoDB API + self.mongo_api = None + self._init_mongodb_api() + + def process_command(self, command_line: str) -> bool: + """ + 处理控制台命令 + + Args: + command_line: 命令行字符串 + + Returns: + bool: 命令是否执行成功 + """ + if not command_line.strip(): + return False + + parts = command_line.strip().split() + if not parts: + return False + + # 移除命令前的斜杠(如果有) + command = parts[0].lstrip('/') + args = parts[1:] if len(parts) > 1 else [] + + if command in self.commands: + try: + self.commands[command](args) + return True + except Exception as e: + print(f"❌ 执行命令 '{command}' 时出错: {str(e)}") + return False + else: + print(f"❌ 未知命令: {command}") + print("💡 输入 'help' 查看可用命令") + return False + + def get_available_commands(self) -> List[str]: + """ + 获取可用命令列表 + + Returns: + List[str]: 可用命令列表 + """ + return list(self.commands.keys()) + + def cmd_add_money(self, args: List[str]): + """添加金币命令: /addmoney QQ号 数量""" + if len(args) != 2: + print("❌ 用法: /addmoney <数量>") + return + + qq_number, amount_str = args + try: + amount = int(amount_str) + except ValueError: + print("❌ 金币数量必须是整数") + return + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 修改金币 + old_money = player_data.get("钱币", 0) + player_data["钱币"] = old_money + amount + + # 保存数据 + self.server.save_player_data(qq_number, player_data) + + print(f"✅ 已为玩家 {qq_number} 添加 {amount} 金币") + print(f" 原金币: {old_money} → 新金币: {player_data['钱币']}") + + def cmd_add_experience(self, args: List[str]): + """添加经验命令: /addxp QQ号 数量""" + if len(args) != 2: + print("❌ 用法: /addxp <数量>") + return + + qq_number, amount_str = args + try: + amount = int(amount_str) + except ValueError: + print("❌ 经验数量必须是整数") + return + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 修改经验 + old_exp = player_data.get("经验值", 0) + player_data["经验值"] = old_exp + amount + + # 检查是否升级 + old_level = player_data.get("等级", 1) + self.server._check_level_up(player_data) + new_level = player_data.get("等级", 1) + + # 保存数据 + self.server.save_player_data(qq_number, player_data) + + print(f"✅ 已为玩家 {qq_number} 添加 {amount} 经验") + print(f" 原经验: {old_exp} → 新经验: {player_data['经验值']}") + if new_level > old_level: + print(f"🎉 玩家升级了! {old_level} → {new_level}") + + def cmd_add_level(self, args: List[str]): + """添加等级命令: /addlevel QQ号 数量""" + if len(args) != 2: + print("❌ 用法: /addlevel <数量>") + return + + qq_number, amount_str = args + try: + amount = int(amount_str) + except ValueError: + print("❌ 等级数量必须是整数") + return + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 修改等级 + old_level = player_data.get("等级", 1) + new_level = max(1, old_level + amount) # 确保等级不小于1 + player_data["等级"] = new_level + + # 保存数据 + self.server.save_player_data(qq_number, player_data) + + print(f"✅ 已为玩家 {qq_number} 添加 {amount} 等级") + print(f" 原等级: {old_level} → 新等级: {new_level}") + + def cmd_add_seed(self, args: List[str]): + """添加种子命令: /addseed QQ号 作物名称 数量""" + if len(args) != 3: + print("❌ 用法: /addseed <作物名称> <数量>") + return + + qq_number, crop_name, amount_str = args + try: + amount = int(amount_str) + except ValueError: + print("❌ 种子数量必须是整数") + return + + # 加载玩家数据 + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 检查作物是否存在 + crop_data = self.server._load_crop_data() + if crop_name not in crop_data: + print(f"❌ 作物 '{crop_name}' 不存在") + print(f"💡 可用作物: {', '.join(list(crop_data.keys())[:10])}...") + return + + # 添加种子到背包 + if "seeds" not in player_data: + player_data["seeds"] = {} + + old_count = player_data["seeds"].get(crop_name, 0) + player_data["seeds"][crop_name] = old_count + amount + + # 保存数据 + self.server.save_player_data(qq_number, player_data) + + print(f"✅ 已为玩家 {qq_number} 添加 {amount} 个 {crop_name} 种子") + print(f" 原数量: {old_count} → 新数量: {player_data['seeds'][crop_name]}") + + def cmd_list_players(self, args: List[str]): + """列出所有玩家命令: /lsplayer""" + saves_dir = "game_saves" + if not os.path.exists(saves_dir): + print("❌ 游戏存档目录不存在") + return + + player_files = [f for f in os.listdir(saves_dir) if f.endswith('.json')] + if not player_files: + print("📭 暂无已注册玩家") + return + + print(f"📋 已注册玩家列表 (共 {len(player_files)} 人):") + print("-" * 80) + print(f"{'QQ号':<12} {'昵称':<15} {'等级':<6} {'金币':<10} {'最后登录':<20}") + print("-" * 80) + + for i, filename in enumerate(sorted(player_files), 1): + qq_number = filename.replace('.json', '') + try: + player_data = self.server._load_player_data_from_file(qq_number) + if player_data: + nickname = player_data.get("玩家昵称", "未设置") + level = player_data.get("等级", 1) + money = player_data.get("钱币", 0) + last_login = player_data.get("最后登录时间", "从未登录") + + print(f"{qq_number:<12} {nickname:<15} {level:<6} {money:<10} {last_login:<20}") + except Exception as e: + print(f"{qq_number:<12} {'数据错误':<15} {'--':<6} {'--':<10} {'无法读取':<20}") + + print("-" * 80) + + def cmd_player_info(self, args: List[str]): + """查看玩家信息命令: /playerinfo QQ号""" + if len(args) != 1: + print("❌ 用法: /playerinfo ") + return + + qq_number = args[0] + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + print(f"👤 玩家信息: {qq_number}") + print("=" * 50) + print(f"昵称: {player_data.get('玩家昵称', '未设置')}") + print(f"农场名: {player_data.get('农场名称', '未设置')}") + print(f"等级: {player_data.get('等级', 1)}") + print(f"经验: {player_data.get('经验值', 0)}") + print(f"金币: {player_data.get('钱币', 0)}") + print(f"体力: {player_data.get('体力值', 20)}") + print(f"注册时间: {player_data.get('注册时间', '未知')}") + print(f"最后登录: {player_data.get('最后登录时间', '从未登录')}") + print(f"总在线时长: {player_data.get('总游玩时间', '0时0分0秒')}") + + # 显示土地信息 + farm_lots = player_data.get("农场土地", []) + planted_count = sum(1 for lot in farm_lots if lot.get("is_planted", False)) + digged_count = sum(1 for lot in farm_lots if lot.get("is_diged", False)) + print(f"土地状态: 总共{len(farm_lots)}块,已开垦{digged_count}块,已种植{planted_count}块") + + # 显示种子信息 + seeds = player_data.get("seeds", {}) + if seeds: + print(f"种子背包: {len(seeds)}种作物,总计{sum(seeds.values())}个种子") + else: + print("种子背包: 空") + + print("=" * 50) + + def cmd_reset_land(self, args: List[str]): + """重置玩家土地命令: /resetland QQ号""" + if len(args) != 1: + print("❌ 用法: /resetland ") + return + + qq_number = args[0] + player_data = self.server.load_player_data(qq_number) + if not player_data: + print(f"❌ 玩家 {qq_number} 不存在") + return + + # 加载初始化模板(优先从MongoDB) + template_data = None + if hasattr(self.server, 'use_mongodb') and self.server.use_mongodb and hasattr(self.server, 'mongo_api') and self.server.mongo_api: + try: + template_data = self.server.mongo_api.get_initial_player_data_template() + if template_data: + print("✅ 成功从MongoDB加载初始玩家数据模板") + else: + print("⚠️ MongoDB中未找到初始玩家数据模板,尝试从JSON文件加载") + except Exception as e: + print(f"⚠️ 从MongoDB加载初始玩家数据模板失败: {str(e)},尝试从JSON文件加载") + + # MongoDB加载失败或不可用,从JSON文件加载 + if not template_data: + try: + with open("config/initial_player_data_template.json", 'r', encoding='utf-8') as f: + template_data = json.load(f) + print("✅ 成功从JSON文件加载初始玩家数据模板") + except Exception as e: + print(f"❌ 无法加载初始化模板: {str(e)}") + return + + # 重置土地状态 + if "农场土地" in template_data: + old_lots_count = len(player_data.get("农场土地", [])) + player_data["农场土地"] = template_data["农场土地"] + new_lots_count = len(player_data["农场土地"]) + + # 保存数据 + self.server.save_player_data(qq_number, player_data) + + print(f"✅ 已重置玩家 {qq_number} 的土地状态") + print(f" 土地数量: {old_lots_count} → {new_lots_count}") + print(f" 所有作物和状态已清除,恢复为初始状态") + else: + print("❌ 初始化模板中没有找到土地数据") + + def cmd_weather(self, args: List[str]): + """天气控制命令: /weather <天气类型>""" + if len(args) != 1: + print("❌ 用法: /weather <天气类型>") + print(" 可用天气: clear, rain, snow, cherry, gardenia, willow") + return + + weather_type = args[0].lower() + + # 定义可用的天气类型映射 + weather_map = { + "clear": "晴天", + "rain": "下雨", + "snow": "下雪", + "cherry": "樱花雨", + "gardenia": "栀子花雨", + "willow": "柳叶雨", + "stop": "停止天气" + } + + if weather_type not in weather_map: + print("❌ 无效的天气类型") + print(" 可用天气: clear, rain, snow, cherry, gardenia, willow, stop") + return + + # 广播天气变更消息给所有在线客户端 + weather_message = { + "type": "weather_change", + "weather_type": weather_type, + "weather_name": weather_map[weather_type] + } + + # 发送给所有连接的客户端 + if hasattr(self.server, 'clients'): + for client_id in list(self.server.clients.keys()): + try: + self.server.send_data(client_id, weather_message) + except Exception as e: + print(f"⚠️ 向客户端 {client_id} 发送天气消息失败: {str(e)}") + + print(f"🌤️ 已将天气切换为: {weather_map[weather_type]}") + if hasattr(self.server, 'clients') and len(self.server.clients) > 0: + print(f" 已通知 {len(self.server.clients)} 个在线客户端") + else: + print(" 当前无在线客户端") + + def cmd_help(self, args: List[str]): + """显示帮助信息""" + print("🌱 萌芽农场服务器控制台命令帮助") + print("=" * 60) + print("玩家管理命令:") + print(" /addmoney <数量> - 为玩家添加金币") + print(" /addxp <数量> - 为玩家添加经验") + print(" /addlevel <数量> - 为玩家添加等级") + print(" /addseed <作物> <数量> - 为玩家添加种子") + print(" /lsplayer - 列出所有已注册玩家") + print(" /playerinfo - 查看玩家详细信息") + print(" /resetland - 重置玩家土地状态") + print("") + print("游戏控制命令:") + print(" /weather <类型> - 控制全服天气") + print(" 可用类型: clear, rain, snow, cherry, gardenia, willow, stop") + print("") + print("服务器管理命令:") + print(" /save - 立即保存所有玩家数据") + print(" /reload - 重新加载配置文件") + print(" /stop - 停止服务器") + print(" /help - 显示此帮助信息") + print("=" * 60) + print("💡 提示: 命令前的斜杠(/)是可选的") + print("") + print("数据库管理命令:") + print(" /dbtest - 测试数据库连接") + print(" /dbconfig <操作> [参数] - 数据库配置管理") + print(" /dbchat <操作> [参数] - 聊天消息管理") + print(" /dbclean <类型> - 数据库清理") + print(" /dbbackup [类型] - 数据库备份") + + def cmd_save_all(self, args: List[str]): + """保存所有数据命令""" + try: + # 保存所有在线玩家数据 + saved_count = 0 + if hasattr(self.server, 'user_data'): + for client_id, user_info in self.server.user_data.items(): + if user_info.get("logged_in", False): + username = user_info.get("username") + if username: + player_data = self.server.load_player_data(username) + if player_data: + self.server.save_player_data(username, player_data) + saved_count += 1 + print(f"✅ 已保存 {saved_count} 个在线玩家的数据") + except Exception as e: + print(f"❌ 保存数据时出错: {str(e)}") + + def cmd_reload_config(self, args: List[str]): + """重新加载配置命令""" + try: + # 重新加载作物数据 + if hasattr(self.server, '_load_crop_data'): + self.server._load_crop_data() + print("✅ 已重新加载配置文件") + except Exception as e: + print(f"❌ 重新加载配置时出错: {str(e)}") + + def cmd_stop(self, args: List[str]): + """停止服务器命令""" + print("⚠️ 正在停止服务器...") + try: + # 保存所有在线玩家数据 + if hasattr(self.server, 'user_data'): + for client_id, user_info in self.server.user_data.items(): + if user_info.get("logged_in", False): + username = user_info.get("username") + if username: + player_data = self.server.load_player_data(username) + if player_data: + self.server.save_player_data(username, player_data) + print("💾 数据保存完成") + except: + pass + + if hasattr(self.server, 'stop'): + self.server.stop() + print("✅ 服务器已停止") + sys.exit(0) + + def _init_mongodb_api(self): + """初始化MongoDB API""" + try: + # 检查服务器是否使用MongoDB + if hasattr(self.server, 'use_mongodb') and self.server.use_mongodb: + environment = "production" if hasattr(self.server, 'environment') and self.server.environment == "production" else "test" + self.mongo_api = SMYMongoDBAPI(environment) + if self.mongo_api.is_connected(): + print(f"✅ MongoDB API 初始化成功 [{environment}]") + else: + print(f"⚠️ MongoDB API 连接失败 [{environment}]") + self.mongo_api = None + else: + print("💡 服务器未启用MongoDB,数据库命令将不可用") + except Exception as e: + print(f"❌ MongoDB API 初始化失败: {str(e)}") + self.mongo_api = None + + # ========================= MongoDB管理命令 ========================= + + def cmd_db_test(self, args): + """测试数据库连接命令: /dbtest""" + if not self.mongo_api: + print("❌ MongoDB API 未初始化或连接失败") + return + + try: + if self.mongo_api.is_connected(): + # 测试基本操作 + config = self.mongo_api.get_daily_checkin_config() + if config: + print("✅ 数据库连接正常,可以正常读取配置") + print(f" 环境: {self.mongo_api.environment}") + print(f" 数据库: {self.mongo_api.config[self.mongo_api.environment]['database']}") + print(f" 主机: {self.mongo_api.config[self.mongo_api.environment]['host']}:{self.mongo_api.config[self.mongo_api.environment]['port']}") + else: + print("⚠️ 数据库连接正常,但无法读取配置数据") + else: + print("❌ 数据库连接失败") + except Exception as e: + print(f"❌ 数据库测试失败: {str(e)}") + + def cmd_db_config(self, args): + """数据库配置管理命令: /dbconfig <操作> [参数]""" + if not self.mongo_api: + print("❌ MongoDB API 未初始化") + return + + if len(args) == 0: + print("❌ 用法: /dbconfig <操作> [参数]") + print(" 可用操作:") + print(" list - 列出所有配置类型") + print(" get <配置类型> - 获取指定配置") + print(" reload <配置类型> - 重新加载指定配置到服务器") + print(" 配置类型: daily_checkin, lucky_draw, new_player, wisdom_tree, online_gift, scare_crow, item, pet, stamina, crop_data, initial_player_data") + return + + operation = args[0].lower() + + if operation == "list": + print("📋 可用的配置类型:") + print("-" * 50) + config_types = [ + ("daily_checkin", "每日签到配置"), + ("lucky_draw", "幸运抽奖配置"), + ("new_player", "新手大礼包配置"), + ("wisdom_tree", "智慧树配置"), + ("online_gift", "在线礼包配置"), + ("scare_crow", "稻草人配置"), + ("item", "道具配置"), + ("pet", "宠物配置"), + ("stamina", "体力系统配置"), + ("crop_data", "作物数据配置"), + ("initial_player_data", "初始玩家数据模板") + ] + for config_type, description in config_types: + print(f" {config_type:<20} - {description}") + print("-" * 50) + + elif operation == "get": + if len(args) < 2: + print("❌ 用法: /dbconfig get <配置类型>") + return + + config_type = args[1] + try: + config_methods = { + "daily_checkin": self.mongo_api.get_daily_checkin_config, + "lucky_draw": self.mongo_api.get_lucky_draw_config, + "new_player": self.mongo_api.get_new_player_config, + "wisdom_tree": self.mongo_api.get_wisdom_tree_config, + "online_gift": self.mongo_api.get_online_gift_config, + "scare_crow": self.mongo_api.get_scare_crow_config, + "item": self.mongo_api.get_item_config, + "pet": self.mongo_api.get_pet_config, + "stamina": self.mongo_api.get_stamina_config, + "crop_data": self.mongo_api.get_crop_data_config, + "initial_player_data": self.mongo_api.get_initial_player_data_template + } + + if config_type not in config_methods: + print(f"❌ 未知的配置类型: {config_type}") + return + + config = config_methods[config_type]() + if config: + print(f"✅ {config_type} 配置:") + print(json.dumps(config, ensure_ascii=False, indent=2)) + else: + print(f"❌ 无法获取 {config_type} 配置") + + except Exception as e: + print(f"❌ 获取配置失败: {str(e)}") + + elif operation == "reload": + if len(args) < 2: + print("❌ 用法: /dbconfig reload <配置类型>") + return + + config_type = args[1] + print(f"🔄 正在重新加载 {config_type} 配置到服务器...") + + try: + # 这里可以添加重新加载配置到服务器的逻辑 + # 例如重新加载作物数据等 + if config_type == "crop_data": + if hasattr(self.server, '_load_crop_data'): + self.server._load_crop_data() + print(f"✅ 已重新加载 {config_type} 配置到服务器") + else: + print(f"⚠️ 服务器不支持重新加载 {config_type} 配置") + else: + print(f"💡 {config_type} 配置重新加载功能暂未实现") + + except Exception as e: + print(f"❌ 重新加载配置失败: {str(e)}") + + else: + print(f"❌ 未知操作: {operation}") + + def cmd_db_chat(self, args): + """聊天消息管理命令: /dbchat <操作> [参数]""" + if not self.mongo_api: + print("❌ MongoDB API 未初始化") + return + + if len(args) == 0: + print("❌ 用法: /dbchat <操作> [参数]") + print(" 可用操作:") + print(" latest - 获取最新聊天消息") + print(" history [天数] [数量] - 获取聊天历史 (默认3天,最多500条)") + print(" clean [保留天数] - 清理旧聊天消息 (默认保留30天)") + return + + operation = args[0].lower() + + if operation == "latest": + try: + message = self.mongo_api.get_latest_chat_message() + if message: + print("💬 最新聊天消息:") + print(f" 玩家: {message.get('player_name', 'N/A')} (QQ: {message.get('username', 'N/A')})") + print(f" 内容: {message.get('content', '')}") + print(f" 时间: {message.get('time_str', 'N/A')}") + else: + print("📭 暂无聊天消息") + except Exception as e: + print(f"❌ 获取最新聊天消息失败: {str(e)}") + + elif operation == "history": + days = 3 + limit = 500 + + if len(args) > 1: + try: + days = int(args[1]) + except ValueError: + print("❌ 天数必须是整数") + return + + if len(args) > 2: + try: + limit = int(args[2]) + except ValueError: + print("❌ 数量必须是整数") + return + + try: + messages = self.mongo_api.get_chat_history(days, limit) + if messages: + print(f"💬 聊天历史 (最近{days}天,共{len(messages)}条):") + print("-" * 80) + for msg in messages[-10:]: # 只显示最后10条 + print(f"[{msg.get('time_str', 'N/A')}] {msg.get('player_name', 'N/A')}: {msg.get('content', '')}") + if len(messages) > 10: + print(f"... 还有 {len(messages) - 10} 条历史消息") + print("-" * 80) + else: + print("📭 暂无聊天历史") + except Exception as e: + print(f"❌ 获取聊天历史失败: {str(e)}") + + elif operation == "clean": + keep_days = 30 + + if len(args) > 1: + try: + keep_days = int(args[1]) + except ValueError: + print("❌ 保留天数必须是整数") + return + + try: + deleted_count = self.mongo_api.clean_old_chat_messages(keep_days) + print(f"🧹 清理完成: 删除了 {deleted_count} 个文档 ({keep_days}天前的消息)") + except Exception as e: + print(f"❌ 清理聊天消息失败: {str(e)}") + + else: + print(f"❌ 未知操作: {operation}") + + def cmd_db_clean(self, args): + """数据库清理命令: /dbclean <类型>""" + if not self.mongo_api: + print("❌ MongoDB API 未初始化") + return + + if len(args) == 0: + print("❌ 用法: /dbclean <类型>") + print(" 可用类型:") + print(" codes - 清理过期验证码") + print(" chat [保留天数] - 清理旧聊天消息 (默认保留30天)") + print(" all - 清理所有过期数据") + return + + clean_type = args[0].lower() + + if clean_type == "codes": + try: + removed_count = self.mongo_api.clean_expired_verification_codes() + print(f"🧹 验证码清理完成: 清理了 {removed_count} 个过期验证码") + except Exception as e: + print(f"❌ 清理验证码失败: {str(e)}") + + elif clean_type == "chat": + keep_days = 30 + if len(args) > 1: + try: + keep_days = int(args[1]) + except ValueError: + print("❌ 保留天数必须是整数") + return + + try: + deleted_count = self.mongo_api.clean_old_chat_messages(keep_days) + print(f"🧹 聊天消息清理完成: 删除了 {deleted_count} 个文档 ({keep_days}天前的消息)") + except Exception as e: + print(f"❌ 清理聊天消息失败: {str(e)}") + + elif clean_type == "all": + print("🧹 开始清理所有过期数据...") + total_cleaned = 0 + + # 清理验证码 + try: + codes_count = self.mongo_api.clean_expired_verification_codes() + print(f" 验证码: 清理了 {codes_count} 个") + total_cleaned += codes_count + except Exception as e: + print(f" 验证码清理失败: {str(e)}") + + # 清理聊天消息 + try: + chat_count = self.mongo_api.clean_old_chat_messages(30) + print(f" 聊天消息: 删除了 {chat_count} 个文档") + total_cleaned += chat_count + except Exception as e: + print(f" 聊天消息清理失败: {str(e)}") + + print(f"✅ 清理完成,总计处理 {total_cleaned} 项") + + else: + print(f"❌ 未知清理类型: {clean_type}") + + def cmd_db_backup(self, args): + """数据库备份命令: /dbbackup [类型]""" + if not self.mongo_api: + print("❌ MongoDB API 未初始化") + return + + backup_type = "config" if len(args) == 0 else args[0].lower() + + if backup_type == "config": + try: + # 备份所有游戏配置 + configs = self.mongo_api.find_documents("gameconfig") + if configs: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_file = f"backup/gameconfig_backup_{timestamp}.json" + + # 确保备份目录存在 + os.makedirs("backup", exist_ok=True) + + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(configs, f, ensure_ascii=False, indent=2) + + print(f"✅ 游戏配置备份完成: {backup_file}") + print(f" 备份了 {len(configs)} 个配置文档") + else: + print("❌ 没有找到配置数据") + except Exception as e: + print(f"❌ 配置备份失败: {str(e)}") + + elif backup_type == "chat": + try: + # 备份聊天消息 + chat_docs = self.mongo_api.find_documents("chat") + if chat_docs: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + backup_file = f"backup/chat_backup_{timestamp}.json" + + # 确保备份目录存在 + os.makedirs("backup", exist_ok=True) + + with open(backup_file, 'w', encoding='utf-8') as f: + json.dump(chat_docs, f, ensure_ascii=False, indent=2) + + print(f"✅ 聊天消息备份完成: {backup_file}") + print(f" 备份了 {len(chat_docs)} 个聊天文档") + else: + print("❌ 没有找到聊天数据") + except Exception as e: + print(f"❌ 聊天备份失败: {str(e)}") + + else: + print("❌ 用法: /dbbackup [类型]") + print(" 可用类型:") + print(" config - 备份游戏配置 (默认)") + print(" chat - 备份聊天消息") + + # ===================== 扩展功能方法 ===================== + + def add_custom_command(self, command_name: str, command_func): + """ + 添加自定义命令 + + Args: + command_name: 命令名称 + command_func: 命令处理函数 + """ + self.commands[command_name] = command_func + print(f"✅ 已添加自定义命令: {command_name}") + + def remove_command(self, command_name: str) -> bool: + """ + 移除命令 + + Args: + command_name: 命令名称 + + Returns: + bool: 是否成功移除 + """ + if command_name in self.commands: + del self.commands[command_name] + print(f"✅ 已移除命令: {command_name}") + return True + else: + print(f"❌ 命令不存在: {command_name}") + return False + + def get_command_info(self, command_name: str) -> Optional[str]: + """ + 获取命令信息 + + Args: + command_name: 命令名称 + + Returns: + Optional[str]: 命令文档字符串 + """ + if command_name in self.commands: + func = self.commands[command_name] + return func.__doc__ if func.__doc__ else "无描述" + return None + + def execute_batch_commands(self, commands: List[str]) -> Dict[str, bool]: + """ + 批量执行命令 + + Args: + commands: 命令列表 + + Returns: + Dict[str, bool]: 每个命令的执行结果 + """ + results = {} + for cmd in commands: + print(f"\n执行命令: {cmd}") + results[cmd] = self.process_command(cmd) + return results \ No newline at end of file diff --git a/Server/ConsoleCommandsAPI_README.md b/Server/ConsoleCommandsAPI_README.md new file mode 100644 index 0000000..80fb8ea --- /dev/null +++ b/Server/ConsoleCommandsAPI_README.md @@ -0,0 +1,90 @@ +# 控制台命令API模块 (ConsoleCommandsAPI) + +## 功能特性 + +### 基础游戏管理命令 +- `/addmoney <数量>` - 为玩家添加金币 +- `/addxp <数量>` - 为玩家添加经验值 +- `/addlevel <数量>` - 为玩家添加等级 +- `/addseed <作物名称> <数量>` - 为玩家添加种子 +- `/lsplayer` - 列出所有在线玩家 +- `/playerinfo ` - 查看玩家详细信息 +- `/resetland ` - 重置玩家土地 +- `/weather <天气类型>` - 设置天气 + +### 系统管理命令 +- `/help` - 显示帮助信息 +- `/save` - 保存所有玩家数据 +- `/reload` - 重新加载配置文件 +- `/stop` - 停止服务器 + +### MongoDB数据库管理命令 +- `/dbtest` - 测试数据库连接 +- `/dbconfig <操作> [参数]` - 数据库配置管理 + - `list` - 列出所有配置类型 + - `get <配置类型>` - 获取指定配置 + - `reload <配置类型>` - 重新加载指定配置到服务器 +- `/dbchat <操作> [参数]` - 聊天消息管理 + - `latest` - 获取最新聊天消息 + - `history [天数] [数量]` - 获取聊天历史 + - `clean [保留天数]` - 清理旧聊天消息 +- `/dbclean <类型>` - 数据库清理 + - `codes` - 清理过期验证码 + - `chat [保留天数]` - 清理旧聊天消息 + - `all` - 清理所有过期数据 +- `/dbbackup [类型]` - 数据库备份 + - `config` - 备份游戏配置 + - `chat` - 备份聊天消息 + +## 使用方法 + +### 1. 导入模块 +```python +from ConsoleCommandsAPI import ConsoleCommandsAPI +``` + +### 2. 初始化 +```python +# 在服务器初始化时创建控制台命令实例 +console = ConsoleCommandsAPI(server) +``` + +### 3. 处理命令 +```python +# 在控制台输入处理函数中 +command_line = input("服务器控制台> ") +console.process_command(command_line) +``` + +## 扩展功能 + +### 添加自定义命令 +```python +# 添加新命令 +console.add_custom_command("mycommand", my_command_function, "我的自定义命令") + +# 移除命令 +console.remove_command("mycommand") + +# 获取命令信息 +info = console.get_command_info("addmoney") + +# 批量执行命令 +commands = ["addmoney 123456 1000", "addxp 123456 500"] +console.execute_batch_commands(commands) +``` + +## 依赖项 + +- `SMYMongoDBAPI` - MongoDB数据库操作模块 +- `json` - JSON数据处理 +- `os` - 操作系统接口 +- `datetime` - 日期时间处理 +- `typing` - 类型提示 + +## 注意事项 + +1. **MongoDB集成**: 数据库相关命令需要服务器启用MongoDB支持 +2. **权限管理**: 所有命令都具有管理员权限,请谨慎使用 +3. **数据备份**: 建议定期使用 `/dbbackup` 命令备份重要数据 +4. **错误处理**: 所有命令都包含完善的错误处理和用户友好的提示信息 diff --git a/Server/TCPGameServer.py b/Server/TCPGameServer.py index db07e93..e48d7ac 100644 --- a/Server/TCPGameServer.py +++ b/Server/TCPGameServer.py @@ -11,39 +11,14 @@ import random #导入服务器外置插件模块 from SMYMongoDBAPI import SMYMongoDBAPI #导入MongoDB数据库模块 from QQEmailSendAPI import EmailVerification#导入QQ邮箱发送模块 +from ConsoleCommandsAPI import ConsoleCommandsAPI #导入控制台命令API模块 """ -萌芽农场TCP游戏服务器 - 代码结构说明 +萌芽农场TCP游戏服务器 ==================================================================== - -📁 代码组织结构: -├── 1. 初始化和生命周期管理 - 服务器启动、停止、客户端管理 -├── 2. 验证和检查方法 - 版本检查、登录状态验证 -├── 3. 数据管理方法 - 玩家数据读写、缓存管理 -├── 4. 作物系统管理 - 作物生长、更新推送 -├── 5. 消息处理路由 - 客户端消息分发处理 -├── 6. 用户认证处理 - 登录、注册、验证码 -├── 7. 游戏操作处理 - 种植、收获、浇水等 -├── 8. 系统功能处理 - 签到、抽奖、排行榜 -└── 9. 性能优化功能 - 缓存优化、批量保存 - -🔧 性能优化特性: -- 内存缓存系统:减少磁盘I/O操作 -- 分层更新策略:在线玩家快速更新,离线玩家慢速更新 -- 批量数据保存:定时批量写入,提升性能 -- 智能缓存管理:LRU策略,自动清理过期数据 - -📊 数据存储: -- 玩家数据:JSON格式存储在game_saves目录 -- 配置文件:作物数据、初始模板等 -- 缓存策略:内存缓存 + 定时持久化 - -🌐 网络通信: - 协议:TCP长连接 - 数据格式:JSON消息 - 消息类型:请求/响应模式 - - ==================================================================== """ @@ -83,8 +58,11 @@ class TCPGameServer(TCPServer): # 初始化MongoDB API(优先使用MongoDB,失败则使用JSON文件) self._init_mongodb_api() - # 性能优化相关配置 - self._init_performance_settings() + # 初始化杂草系统配置 + self._init_weed_settings() + + # 禁用父类的日志输出,避免重复 + self._setup_game_server_logging() # 数据缓存 self.crop_data_cache = None @@ -95,7 +73,6 @@ class TCPGameServer(TCPServer): # 启动定时器 self.start_crop_growth_timer() - self.start_batch_save_timer() self.start_weed_growth_timer() self.start_wisdom_tree_health_decay_timer() self.start_verification_code_cleanup_timer() @@ -122,18 +99,9 @@ class TCPGameServer(TCPServer): self.mongo_api = None self.log('ERROR', f"MongoDB API初始化异常: {e},将使用JSON配置文件", 'SERVER') - #初始化性能操作 - def _init_performance_settings(self): - """初始化性能优化配置""" - self.player_cache = {} # 玩家数据内存缓存 - self.dirty_players = set() # 需要保存到磁盘的玩家列表 - self.last_save_time = time.time() # 上次批量保存时间 - self.save_interval = 30 # 批量保存间隔(秒) - self.update_counter = 0 # 更新计数器 - self.slow_update_interval = 10 # 慢速更新间隔(每10秒进行一次完整更新) - self.active_players_cache = {} # 活跃玩家缓存 - self.cache_expire_time = 300 # 缓存过期时间(5分钟) - + #初始化杂草系统配置 + def _init_weed_settings(self): + """初始化杂草生长配置""" # 杂草生长相关配置 self.weed_check_interval = 86400 # 杂草检查间隔(24小时) self.offline_threshold_days = 3 # 离线多少天后开始长杂草 @@ -141,11 +109,18 @@ class TCPGameServer(TCPServer): self.weed_growth_probability = 0.3 # 每个空地长杂草的概率(30%) self.last_weed_check_time = time.time() # 上次检查杂草的时间 + #设置游戏服务器日志配置 + def _setup_game_server_logging(self): + """设置游戏服务器日志配置,禁用父类重复输出""" + # 禁用父类logger的传播,避免重复输出 + if hasattr(self, 'logger') and self.logger: + self.logger.propagate = False + #启动作物生长计时器 def start_crop_growth_timer(self): """启动作物生长计时器,每秒更新一次""" try: - self.update_crops_growth_optimized() + self.update_crops_growth() except Exception as e: self.log('ERROR', f"作物生长更新时出错: {str(e)}", 'SERVER') @@ -154,19 +129,7 @@ class TCPGameServer(TCPServer): self.crop_timer.daemon = True self.crop_timer.start() - #启动批量报错计时器 - def start_batch_save_timer(self): - """启动批量保存计时器""" - try: - self.batch_save_dirty_players() - self.cleanup_expired_cache() - except Exception as e: - self.log('ERROR', f"批量保存时出错: {str(e)}", 'SERVER') - - # 创建下一个批量保存计时器 - batch_timer = threading.Timer(self.save_interval, self.start_batch_save_timer) - batch_timer.daemon = True - batch_timer.start() + #启动杂草生长计时器 def start_weed_growth_timer(self): @@ -216,7 +179,6 @@ class TCPGameServer(TCPServer): """获取服务器统计信息""" online_players = len([cid for cid in self.user_data if self.user_data[cid].get("logged_in", False)]) return { - "cached_players": len(self.player_cache), "online_players": online_players, "total_connections": len(self.clients) } @@ -250,14 +212,9 @@ class TCPGameServer(TCPServer): self.verification_cleanup_timer = None self.log('INFO', "验证码清理定时器已停止", 'SERVER') - # 强制保存所有缓存数据 - self.log('INFO', "正在保存所有玩家数据...", 'SERVER') - saved_count = self.force_save_all_data() - self.log('INFO', f"已保存 {saved_count} 个玩家的数据", 'SERVER') - # 显示服务器统计信息 stats = self.get_server_stats() - self.log('INFO', f"服务器统计 - 缓存玩家: {stats['cached_players']}, 在线玩家: {stats['online_players']}, 总连接: {stats['total_connections']}", 'SERVER') + self.log('INFO', f"服务器统计 - 在线玩家: {stats['online_players']}, 总连接: {stats['total_connections']}", 'SERVER') # 调用父类方法完成实际停止 super().stop() @@ -275,13 +232,6 @@ class TCPGameServer(TCPServer): # 处理已登录用户的离开 if client_id in self.user_data and self.user_data[client_id].get("logged_in", False): self._update_player_logout_time(client_id, username) - - # 立即保存离线玩家的数据 - if username and username in self.player_cache: - self.save_player_data_immediate(username) - self.dirty_players.discard(username) - self.log('INFO', f"已立即保存离线玩家 {username} 的数据", 'SERVER') - self.log('INFO', f"用户 {username} 登出", 'SERVER') # 广播用户离开消息 @@ -333,24 +283,6 @@ class TCPGameServer(TCPServer): #=================================数据管理方法==================================== #加载玩家数据 def load_player_data(self, account_id): - """从缓存或文件加载玩家数据(优化版本)""" - # 先检查内存缓存 - if account_id in self.player_cache: - self._update_cache_access_time(account_id) - return self.player_cache[account_id] - - # 缓存未命中,从文件读取 - return self._load_player_data_from_file(account_id) - - #更新缓存访问时间 - def _update_cache_access_time(self, account_id): - """更新缓存访问时间""" - if account_id not in self.active_players_cache: - self.active_players_cache[account_id] = {} - self.active_players_cache[account_id]["last_access"] = time.time() - - #从文件里加载玩家数据 - def _load_player_data_from_file(self, account_id): """从文件加载玩家数据""" file_path = os.path.join("game_saves", f"{account_id}.json") @@ -358,45 +290,20 @@ class TCPGameServer(TCPServer): if os.path.exists(file_path): with open(file_path, 'r', encoding='utf-8') as file: player_data = json.load(file) - - # 存入缓存 - self.player_cache[account_id] = player_data - self.active_players_cache[account_id] = { - "last_access": time.time(), - "is_online": account_id in self.user_data and self.user_data[account_id].get("logged_in", False) - } - return player_data return None except Exception as e: self.log('ERROR', f"读取玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') return None - #保存玩家数据到缓存 + #保存玩家数据 def save_player_data(self, account_id, player_data): - """保存玩家数据到缓存""" - # 更新内存缓存 - self.player_cache[account_id] = player_data - - # 标记为脏数据,等待批量保存 - self.dirty_players.add(account_id) - - # 更新活跃缓存 - self._update_cache_access_time(account_id) - - return True - - #保存玩家数据到磁盘 - def save_player_data_immediate(self, account_id): - """立即保存玩家数据到磁盘""" - if account_id not in self.player_cache: - return False - + """保存玩家数据到文件""" file_path = os.path.join("game_saves", f"{account_id}.json") try: with open(file_path, 'w', encoding='utf-8') as file: - json.dump(self.player_cache[account_id], file, indent=2, ensure_ascii=False) + json.dump(player_data, file, indent=2, ensure_ascii=False) return True except Exception as e: self.log('ERROR', f"保存玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER') @@ -536,21 +443,10 @@ class TCPGameServer(TCPServer): #================================作物系统管理========================================= - #优化的作物生长更新系统 - def update_crops_growth_optimized(self): - """优化的作物生长更新系统""" - self.update_counter += 1 - - # 每秒快速更新在线玩家 - self.update_online_players_crops() - - # 每10秒进行一次慢速更新(离线玩家和深度检查) - if self.update_counter % self.slow_update_interval == 0: - self.update_offline_players_crops() - - #快速更新在线玩家的作物 - def update_online_players_crops(self): - """快速更新在线玩家的作物""" + #作物生长更新系统 + def update_crops_growth(self): + """更新所有玩家的作物生长""" + # 更新在线玩家的作物 for client_id, user_info in self.user_data.items(): if not user_info.get("logged_in", False): continue @@ -564,64 +460,16 @@ class TCPGameServer(TCPServer): if not player_data: continue - if self.update_player_crops_fast(player_data, username): + if self.update_player_crops(player_data, username): self.save_player_data(username, player_data) self._push_crop_update_to_player(username, player_data) except Exception as e: - self.log('ERROR', f"快速更新在线玩家 {username} 作物时出错: {str(e)}", 'SERVER') + self.log('ERROR', f"更新在线玩家 {username} 作物时出错: {str(e)}", 'SERVER') - #慢速更新离线玩家的作物 - def update_offline_players_crops(self): - """慢速更新离线玩家的作物(每10秒一次)""" - import glob - - try: - save_files = glob.glob(os.path.join("game_saves", "*.json")) - offline_count = 0 - updated_count = 0 - - for save_file in save_files: - account_id = os.path.basename(save_file).split('.')[0] - - # 跳过在线玩家 - is_online = any( - user_info.get("username") == account_id and user_info.get("logged_in", False) - for user_info in self.user_data.values() - ) - - if is_online: - continue - - offline_count += 1 - - player_data = self.load_player_data(account_id) - if not player_data: - continue - - if self.update_player_crops_slow(player_data, account_id): - self.save_player_data(account_id, player_data) - updated_count += 1 - - if updated_count > 0: - self.log('INFO', f"慢速更新:检查了 {offline_count} 个离线玩家,更新了 {updated_count} 个", 'SERVER') - - except Exception as e: - self.log('ERROR', f"慢速更新离线玩家作物时出错: {str(e)}", 'SERVER') - - #快速更新单个玩家的作物 - def update_player_crops_fast(self, player_data, account_id): - """快速更新单个玩家的作物(在线玩家用)""" - return self.update_player_crops_common(player_data, account_id, 1) - - #慢速更新单个玩家的作物 - def update_player_crops_slow(self, player_data, account_id): - """慢速更新单个玩家的作物(离线玩家用,补偿倍数)""" - return self.update_player_crops_common(player_data, account_id, self.slow_update_interval) - - #通用的作物更新逻辑 - def update_player_crops_common(self, player_data, account_id, time_multiplier): - """通用的作物更新逻辑""" + #更新单个玩家的作物 + def update_player_crops(self, player_data, account_id): + """更新单个玩家的作物""" growth_updated = False for farm_lot in player_data.get("农场土地", []): @@ -671,8 +519,8 @@ class TCPGameServer(TCPServer): if "施肥持续时间" in farm_lot: del farm_lot["施肥持续时间"] - # 应用生长速度倍数和时间补偿 - growth_increase = int(growth_multiplier * time_multiplier) + # 应用生长速度倍数 + growth_increase = int(growth_multiplier) if growth_increase < 1: growth_increase = 1 @@ -1367,12 +1215,8 @@ class TCPGameServer(TCPServer): try: player_data["玩家密码"] = new_password - # 保存到缓存和文件 - self.player_cache[username] = player_data - self.dirty_players.add(username) - - # 立即保存重要的账户信息 - self.save_player_data_immediate(username) + # 保存到文件 + self.save_player_data(username, player_data) self.log('INFO', f"用户 {username} 密码重置成功", 'ACCOUNT') @@ -6098,11 +5942,6 @@ class TCPGameServer(TCPServer): #==========================作物数据处理========================== - - - - - #==========================访问其他玩家农场处理========================== #处理访问其他玩家农场的请求 def _handle_visit_player_request(self, client_id, message): @@ -7403,7 +7242,6 @@ class TCPGameServer(TCPServer): - #==========================幸运抽奖处理========================== #处理幸运抽奖请求 @@ -7892,105 +7730,7 @@ class TCPGameServer(TCPServer): -#==========================缓存数据处理========================== - #清理过期的缓存数据 - def cleanup_expired_cache(self): - """清理过期的缓存数据""" - current_time = time.time() - expired_players = [] - - for account_id, cache_data in self.active_players_cache.items(): - if current_time - cache_data.get("last_access", 0) > self.cache_expire_time: - expired_players.append(account_id) - - for account_id in expired_players: - # 如果是脏数据,先保存 - if account_id in self.dirty_players: - self.save_player_data_immediate(account_id) - self.dirty_players.discard(account_id) - - # 移除过期缓存 - self.player_cache.pop(account_id, None) - self.active_players_cache.pop(account_id, None) - - if expired_players: - self.log('INFO', f"清理了 {len(expired_players)} 个过期缓存", 'SERVER') - - #批量保存脏数据到磁盘 - def batch_save_dirty_players(self): - """批量保存脏数据到磁盘""" - if not self.dirty_players: - return - - saved_count = 0 - for account_id in list(self.dirty_players): - try: - if self.save_player_data_immediate(account_id): - saved_count += 1 - except Exception as e: - self.log('ERROR', f"保存玩家 {account_id} 数据时出错: {str(e)}", 'SERVER') - - self.dirty_players.clear() - self.last_save_time = time.time() - - if saved_count > 0: - self.log('INFO', f"批量保存了 {saved_count} 个玩家的数据", 'SERVER') - - #强制保存所有缓存数据 - def force_save_all_data(self): - """强制保存所有缓存数据(用于服务器关闭时)""" - saved_count = 0 - for account_id in list(self.player_cache.keys()): - try: - if self.save_player_data_immediate(account_id): - saved_count += 1 - except Exception as e: - self.log('ERROR', f"强制保存玩家 {account_id} 数据时出错: {str(e)}", 'SERVER') - - self.dirty_players.clear() - self.log('INFO', f"强制保存完成,保存了 {saved_count} 个玩家的数据", 'SERVER') - return saved_count - - #优化缓存大小,移除不活跃的数据 - def optimize_cache_size(self): - """优化缓存大小,移除不活跃的数据""" - current_time = time.time() - removed_count = 0 - - # 如果缓存过大,移除最不活跃的数据 - if len(self.player_cache) > 1000: # 缓存超过1000个玩家时进行清理 - sorted_players = sorted( - self.active_players_cache.items(), - key=lambda x: x[1].get("last_access", 0) - ) - - # 移除最不活跃的50% - remove_count = len(sorted_players) // 2 - for account_id, _ in sorted_players[:remove_count]: - if account_id in self.dirty_players: - self.save_player_data_immediate(account_id) - self.dirty_players.discard(account_id) - - self.player_cache.pop(account_id, None) - self.active_players_cache.pop(account_id, None) - removed_count += 1 - - if removed_count > 0: - self.log('INFO', f"缓存优化:移除了 {removed_count} 个不活跃的缓存数据", 'SERVER') - - return removed_count - #获取缓存命中信息(用于调试) - def get_cache_hit_info(self, account_id): - """获取缓存命中信息(用于调试)""" - return { - "in_memory_cache": account_id in self.player_cache, - "in_active_cache": account_id in self.active_players_cache, - "is_dirty": account_id in self.dirty_players, - "last_access": self.active_players_cache.get(account_id, {}).get("last_access", 0) - } - -#==========================缓存数据处理========================== # ================================账户设置处理方法================================ @@ -8033,17 +7773,13 @@ class TCPGameServer(TCPServer): try: # 更新玩家数据 - player_data[""] = new_password + player_data["玩家密码"] = new_password player_data["玩家昵称"] = new_player_name player_data["农场名称"] = new_farm_name player_data["个人简介"] = new_personal_profile - # 保存到缓存和文件 - self.player_cache[username] = player_data - self.dirty_players.add(username) - - # 立即保存重要的账户信息 - self.save_player_data_immediate(username) + # 保存到文件 + self.save_player_data(username, player_data) # 发送成功响应 self.send_data(client_id, { @@ -8081,16 +7817,6 @@ class TCPGameServer(TCPServer): os.remove(file_path) self.log('INFO', f"已删除玩家文件: {file_path}", 'ACCOUNT') - # 从缓存中删除 - if username in self.player_cache: - del self.player_cache[username] - - if username in self.dirty_players: - self.dirty_players.discard(username) - - if username in self.active_players_cache: - del self.active_players_cache[username] - # 清理用户数据 if client_id in self.user_data: del self.user_data[client_id] @@ -9073,14 +8799,7 @@ class TCPGameServer(TCPServer): self.save_player_data(username, player_data) processed_count += 1 - # 检查缓存中的离线玩家 - for username in list(self.player_cache.keys()): - if username not in [self.user_data[cid].get("username") for cid in self.user_data if self.user_data[cid].get("logged_in", False)]: - player_data = self.player_cache[username] - if "智慧树配置" in player_data: - self._process_wisdom_tree_decay(player_data["智慧树配置"], username) - self.save_player_data(username, player_data) - processed_count += 1 + # 注释:缓存机制已移除,只处理在线玩家的智慧树衰减 if processed_count > 0: self.log('INFO', f"已处理 {processed_count} 个玩家的智慧树生命值衰减", 'SERVER') @@ -9607,427 +9326,47 @@ class TCPGameServer(TCPServer): #==========================小卖部管理处理========================== -# 控制台命令系统 -class ConsoleCommands: - """控制台命令处理类""" - - def __init__(self, server): - self.server = server - self.commands = { - "addmoney": self.cmd_add_money, - "addxp": self.cmd_add_experience, - "addlevel": self.cmd_add_level, - "addseed": self.cmd_add_seed, - "lsplayer": self.cmd_list_players, - "playerinfo": self.cmd_player_info, - "resetland": self.cmd_reset_land, - "weather": self.cmd_weather, - "help": self.cmd_help, - "stop": self.cmd_stop, - "save": self.cmd_save_all, - "reload": self.cmd_reload_config - } - - def process_command(self, command_line): - """处理控制台命令""" - if not command_line.strip(): - return - - parts = command_line.strip().split() - if not parts: - return - - # 移除命令前的斜杠(如果有) - command = parts[0].lstrip('/') - args = parts[1:] if len(parts) > 1 else [] - - if command in self.commands: - try: - self.commands[command](args) - except Exception as e: - print(f"❌ 执行命令 '{command}' 时出错: {str(e)}") - else: - print(f"❌ 未知命令: {command}") - print("💡 输入 'help' 查看可用命令") - - def cmd_add_money(self, args): - """添加金币命令: /addmoney QQ号 数量""" - if len(args) != 2: - print("❌ 用法: /addmoney <数量>") - return - - qq_number, amount_str = args - try: - amount = int(amount_str) - except ValueError: - print("❌ 金币数量必须是整数") - return - - # 加载玩家数据 - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - # 修改金币 - old_money = player_data.get("钱币", 0) - player_data["钱币"] = old_money + amount - - # 保存数据 - self.server.save_player_data(qq_number, player_data) - self.server.save_player_data_immediate(qq_number) - - print(f"✅ 已为玩家 {qq_number} 添加 {amount} 金币") - print(f" 原金币: {old_money} → 新金币: {player_data['money']}") - - def cmd_add_experience(self, args): - """添加经验命令: /addxp QQ号 数量""" - if len(args) != 2: - print("❌ 用法: /addxp <数量>") - return - - qq_number, amount_str = args - try: - amount = int(amount_str) - except ValueError: - print("❌ 经验数量必须是整数") - return - - # 加载玩家数据 - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - # 修改经验 - old_exp = player_data.get("经验值", 0) - player_data["经验值"] = old_exp + amount - - # 检查是否升级 - old_level = player_data.get("等级", 1) - self.server._check_level_up(player_data) - new_level = player_data.get("等级", 1) - - # 保存数据 - self.server.save_player_data(qq_number, player_data) - self.server.save_player_data_immediate(qq_number) - - print(f"✅ 已为玩家 {qq_number} 添加 {amount} 经验") - print(f" 原经验: {old_exp} → 新经验: {player_data['experience']}") - if new_level > old_level: - print(f"🎉 玩家升级了! {old_level} → {new_level}") - - def cmd_add_level(self, args): - """添加等级命令: /addlevel QQ号 数量""" - if len(args) != 2: - print("❌ 用法: /addlevel <数量>") - return - - qq_number, amount_str = args - try: - amount = int(amount_str) - except ValueError: - print("❌ 等级数量必须是整数") - return - - # 加载玩家数据 - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - # 修改等级 - old_level = player_data.get("等级", 1) - new_level = max(1, old_level + amount) # 确保等级不小于1 - player_data["等级"] = new_level - - # 保存数据 - self.server.save_player_data(qq_number, player_data) - self.server.save_player_data_immediate(qq_number) - - print(f"✅ 已为玩家 {qq_number} 添加 {amount} 等级") - print(f" 原等级: {old_level} → 新等级: {new_level}") - - def cmd_add_seed(self, args): - """添加种子命令: /addseed QQ号 作物名称 数量""" - if len(args) != 3: - print("❌ 用法: /addseed <作物名称> <数量>") - return - - qq_number, crop_name, amount_str = args - try: - amount = int(amount_str) - except ValueError: - print("❌ 种子数量必须是整数") - return - - # 加载玩家数据 - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - # 检查作物是否存在 - crop_data = self.server._load_crop_data() - if crop_name not in crop_data: - print(f"❌ 作物 '{crop_name}' 不存在") - print(f"💡 可用作物: {', '.join(list(crop_data.keys())[:10])}...") - return - - # 添加种子到背包 - if "seeds" not in player_data: - player_data["seeds"] = {} - - old_count = player_data["seeds"].get(crop_name, 0) - player_data["seeds"][crop_name] = old_count + amount - - # 保存数据 - self.server.save_player_data(qq_number, player_data) - self.server.save_player_data_immediate(qq_number) - - print(f"✅ 已为玩家 {qq_number} 添加 {amount} 个 {crop_name} 种子") - print(f" 原数量: {old_count} → 新数量: {player_data['seeds'][crop_name]}") - - def cmd_list_players(self, args): - """列出所有玩家命令: /lsplayer""" - saves_dir = "game_saves" - if not os.path.exists(saves_dir): - print("❌ 游戏存档目录不存在") - return - - player_files = [f for f in os.listdir(saves_dir) if f.endswith('.json')] - if not player_files: - print("📭 暂无已注册玩家") - return - - print(f"📋 已注册玩家列表 (共 {len(player_files)} 人):") - print("-" * 80) - print(f"{'QQ号':<12} {'昵称':<15} {'等级':<6} {'金币':<10} {'最后登录':<20}") - print("-" * 80) - - for i, filename in enumerate(sorted(player_files), 1): - qq_number = filename.replace('.json', '') - try: - player_data = self.server._load_player_data_from_file(qq_number) - if player_data: - nickname = player_data.get("玩家昵称", "未设置") - level = player_data.get("等级", 1) - money = player_data.get("钱币", 0) - last_login = player_data.get("最后登录时间", "从未登录") - - print(f"{qq_number:<12} {nickname:<15} {level:<6} {money:<10} {last_login:<20}") - except Exception as e: - print(f"{qq_number:<12} {'数据错误':<15} {'--':<6} {'--':<10} {'无法读取':<20}") - - print("-" * 80) - - def cmd_player_info(self, args): - """查看玩家信息命令: /playerinfo QQ号""" - if len(args) != 1: - print("❌ 用法: /playerinfo ") - return - - qq_number = args[0] - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - print(f"👤 玩家信息: {qq_number}") - print("=" * 50) - print(f"昵称: {player_data.get('玩家昵称', '未设置')}") - print(f"农场名: {player_data.get('农场名称', '未设置')}") - print(f"等级: {player_data.get('level', 1)}") - print(f"经验: {player_data.get('experience', 0)}") - print(f"金币: {player_data.get('money', 0)}") - print(f"体力: {player_data.get('体力值', 20)}") - print(f"注册时间: {player_data.get('注册时间', '未知')}") - print(f"最后登录: {player_data.get('最后登录时间', '从未登录')}") - print(f"总在线时长: {player_data.get('总游玩时间', '0时0分0秒')}") - - # 显示土地信息 - farm_lots = player_data.get("农场土地", []) - planted_count = sum(1 for lot in farm_lots if lot.get("is_planted", False)) - digged_count = sum(1 for lot in farm_lots if lot.get("is_diged", False)) - print(f"土地状态: 总共{len(farm_lots)}块,已开垦{digged_count}块,已种植{planted_count}块") - - # 显示种子信息 - seeds = player_data.get("seeds", {}) - if seeds: - print(f"种子背包: {len(seeds)}种作物,总计{sum(seeds.values())}个种子") - else: - print("种子背包: 空") - - print("=" * 50) - - def cmd_reset_land(self, args): - """重置玩家土地命令: /resetland QQ号""" - if len(args) != 1: - print("❌ 用法: /resetland ") - return - - qq_number = args[0] - player_data = self.server.load_player_data(qq_number) - if not player_data: - print(f"❌ 玩家 {qq_number} 不存在") - return - - # 加载初始化模板(优先从MongoDB) - template_data = None - if self.server.use_mongodb and self.server.mongo_api: - try: - template_data = self.server.mongo_api.get_initial_player_data_template() - if template_data: - print("✅ 成功从MongoDB加载初始玩家数据模板") - else: - print("⚠️ MongoDB中未找到初始玩家数据模板,尝试从JSON文件加载") - except Exception as e: - print(f"⚠️ 从MongoDB加载初始玩家数据模板失败: {str(e)},尝试从JSON文件加载") - - # MongoDB加载失败或不可用,从JSON文件加载 - if not template_data: - try: - with open("config/initial_player_data_template.json", 'r', encoding='utf-8') as f: - template_data = json.load(f) - print("✅ 成功从JSON文件加载初始玩家数据模板") - except Exception as e: - print(f"❌ 无法加载初始化模板: {str(e)}") - return - - # 重置土地状态 - if "农场土地" in template_data: - old_lots_count = len(player_data.get("农场土地", [])) - player_data["农场土地"] = template_data["农场土地"] - new_lots_count = len(player_data["农场土地"]) - - # 保存数据 - self.server.save_player_data(qq_number, player_data) - self.server.save_player_data_immediate(qq_number) - - print(f"✅ 已重置玩家 {qq_number} 的土地状态") - print(f" 土地数量: {old_lots_count} → {new_lots_count}") - print(f" 所有作物和状态已清除,恢复为初始状态") - else: - print("❌ 初始化模板中没有找到土地数据") - - def cmd_weather(self, args): - """天气控制命令: /weather <天气类型>""" - if len(args) != 1: - print("❌ 用法: /weather <天气类型>") - print(" 可用天气: clear, rain, snow, cherry, gardenia, willow") - return - - weather_type = args[0].lower() - - # 定义可用的天气类型映射 - weather_map = { - "clear": "晴天", - "rain": "下雨", - "snow": "下雪", - "cherry": "樱花雨", - "gardenia": "栀子花雨", - "willow": "柳叶雨", - "stop": "停止天气" - } - - if weather_type not in weather_map: - print("❌ 无效的天气类型") - print(" 可用天气: clear, rain, snow, cherry, gardenia, willow, stop") - return - - # 广播天气变更消息给所有在线客户端 - weather_message = { - "type": "weather_change", - "weather_type": weather_type, - "weather_name": weather_map[weather_type] - } - - # 发送给所有连接的客户端 - for client_id in list(self.server.clients.keys()): - try: - self.server.send_data(client_id, weather_message) - except Exception as e: - print(f"⚠️ 向客户端 {client_id} 发送天气消息失败: {str(e)}") - - print(f"🌤️ 已将天气切换为: {weather_map[weather_type]}") - if len(self.server.clients) > 0: - print(f" 已通知 {len(self.server.clients)} 个在线客户端") - else: - print(" 当前无在线客户端") - - def cmd_help(self, args): - """显示帮助信息""" - print("🌱 萌芽农场服务器控制台命令帮助") - print("=" * 60) - print("玩家管理命令:") - print(" /addmoney <数量> - 为玩家添加金币") - print(" /addxp <数量> - 为玩家添加经验") - print(" /addlevel <数量> - 为玩家添加等级") - print(" /addseed <作物> <数量> - 为玩家添加种子") - print(" /lsplayer - 列出所有已注册玩家") - print(" /playerinfo - 查看玩家详细信息") - print(" /resetland - 重置玩家土地状态") - print("") - print("游戏控制命令:") - print(" /weather <类型> - 控制全服天气") - print(" 可用类型: clear, rain, snow, cherry, gardenia, willow, stop") - print("") - print("服务器管理命令:") - print(" /save - 立即保存所有玩家数据") - print(" /reload - 重新加载配置文件") - print(" /stop - 停止服务器") - print(" /help - 显示此帮助信息") - print("=" * 60) - print("💡 提示: 命令前的斜杠(/)是可选的") - - def cmd_save_all(self, args): - """保存所有数据命令""" - try: - self.server.force_save_all_data() - print("✅ 已强制保存所有玩家数据") - except Exception as e: - print(f"❌ 保存数据时出错: {str(e)}") - - def cmd_reload_config(self, args): - """重新加载配置命令""" - try: - # 重新加载作物数据 - self.server._load_crop_data() - print("✅ 已重新加载配置文件") - except Exception as e: - print(f"❌ 重新加载配置时出错: {str(e)}") - - def cmd_stop(self, args): - """停止服务器命令""" - print("⚠️ 正在停止服务器...") - try: - self.server.force_save_all_data() - print("💾 数据保存完成") - except: - pass - self.server.stop() - print("✅ 服务器已停止") - import sys - sys.exit(0) - - def console_input_thread(server): """控制台输入处理线程""" - console = ConsoleCommands(server) + import threading + import sys - print("💬 控制台已就绪,输入 'help' 查看可用命令") + # 等待服务器完全启动 + time.sleep(0.5) + + console = ConsoleCommandsAPI(server) + + # 创建输入锁,防止日志输出打乱命令输入 + input_lock = threading.Lock() + server._console_input_lock = input_lock + + # 获取服务器IP地址 + server_ip = getattr(server, 'host', 'localhost') + if server_ip == '0.0.0.0': + server_ip = 'localhost' + + # 使用锁保护初始化消息输出 + with input_lock: + print("💬 控制台已就绪,输入 'help' 查看可用命令") while True: try: - command = input("> ").strip() + # 使用自定义提示符格式,不使用锁避免阻塞 + prompt = f"mengyafarm#{server_ip}> " + command = input(prompt).strip() + if command: - console.process_command(command) + # 只在处理命令时使用锁,避免输出被打乱 + with input_lock: + console.process_command(command) except EOFError: break except KeyboardInterrupt: break except Exception as e: - print(f"❌ 处理命令时出错: {str(e)}") + # 使用锁保护错误输出 + with input_lock: + print(f"❌ 处理命令时出错: {str(e)}") # 主程序启动入口 if __name__ == "__main__": @@ -10068,7 +9407,14 @@ if __name__ == "__main__": if 'server' in locals(): try: - server.force_save_all_data() + # 保存所有在线玩家数据 + for client_id, user_info in server.user_data.items(): + if user_info.get("logged_in", False): + username = user_info.get("username") + if username: + player_data = server.load_player_data(username) + if player_data: + server.save_player_data(username, player_data) print("💾 数据保存完成") except: pass diff --git a/Server/TCPServer.py b/Server/TCPServer.py index 1da4818..d663409 100644 --- a/Server/TCPServer.py +++ b/Server/TCPServer.py @@ -116,7 +116,13 @@ class TCPServer: exc_info=None ) record.category = category - self.logger.handle(record) + + # 检查是否存在控制台输入锁,如果存在则使用锁来避免打乱命令输入 + if hasattr(self, '_console_input_lock'): + with self._console_input_lock: + self.logger.handle(record) + else: + self.logger.handle(record) def start(self): """启动服务器""" @@ -352,4 +358,4 @@ if __name__ == "__main__": print("\n程序被用户中断") if 'server' in locals(): server.stop() - sys.exit(0) \ No newline at end of file + sys.exit(0) \ No newline at end of file diff --git a/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc b/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc new file mode 100644 index 0000000..e360271 Binary files /dev/null and b/Server/__pycache__/ConsoleCommandsAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc index 0b3228a..a21b90b 100644 Binary files a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc and b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/TCPServer.cpython-313.pyc b/Server/__pycache__/TCPServer.cpython-313.pyc index c9b7799..d3d7905 100644 Binary files a/Server/__pycache__/TCPServer.cpython-313.pyc and b/Server/__pycache__/TCPServer.cpython-313.pyc differ diff --git a/Server/deploy.sh b/Server/deploy.sh index 7a52586..78b9fca 100644 --- a/Server/deploy.sh +++ b/Server/deploy.sh @@ -41,9 +41,6 @@ check_docker() { start_server() { echo "🚀 启动萌芽农场服务器..." - # 创建必要的目录 - mkdir -p game_saves config chat - # 检查配置文件是否存在 if [ ! -f "config/crop_data.json" ]; then echo "⚠️ 警告: 配置文件不存在,请确保 config 目录包含必要的配置文件" diff --git a/Server/game_saves/2804775686.json b/Server/game_saves/2804775686.json index 020b70b..466aa5b 100644 --- a/Server/game_saves/2804775686.json +++ b/Server/game_saves/2804775686.json @@ -1,232 +1,232 @@ { - "经验值": 346, + "经验值": 396, "等级": 5, - "钱币": 12176, + "钱币": 11476, "农场名称": "123", "玩家昵称": "123", "玩家账号": "2804775686", "玩家密码": "123", - "最后登录时间": "2025年07月21日21时52分01秒", - "总游玩时间": "0时2分25秒", + "最后登录时间": "2025年07月22日08时51分11秒", + "总游玩时间": "0时3分4秒", "注册时间": "2025年07月21日20时35分51秒", "个人简介": "21323123", "农场土地": [ { - "crop_type": "", - "grow_time": 0, + "crop_type": "杂交树2", + "grow_time": 860, "is_dead": false, "is_diged": true, - "is_planted": false, + "is_planted": true, + "max_grow_time": 25200, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "土豆", + "grow_time": 480, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 480, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "胡萝卜", + "grow_time": 240, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 240, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "小麦", + "grow_time": 300, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 300, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "龙果", + "grow_time": 840, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 14400, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "玉米", + "grow_time": 840, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 900, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "土豆", + "grow_time": 480, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 480, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "玉米", + "grow_time": 830, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 900, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "辣椒", + "grow_time": 650, + "is_dead": false, + "is_diged": true, + "is_planted": true, "max_grow_time": 650, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "辣椒", + "grow_time": 650, "is_dead": false, "is_diged": true, - "is_planted": false, + "is_planted": true, "max_grow_time": 650, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "小麦", + "grow_time": 300, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 650, + "is_planted": true, + "max_grow_time": 300, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "胡萝卜", + "grow_time": 240, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 650, + "is_planted": true, + "max_grow_time": 240, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "胡萝卜", + "grow_time": 240, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 650, + "is_planted": true, + "max_grow_time": 240, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "小麦", + "grow_time": 300, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 650, + "is_planted": true, + "max_grow_time": 300, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "玉米", + "grow_time": 810, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 650, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3, + "is_planted": true, + "max_grow_time": 900, "已浇水": false, "已施肥": false, "土地等级": 0 @@ -464,35 +464,20 @@ } ], "种子仓库": [ - { - "name": "龙果", - "quality": "传奇", - "count": 1 - }, { "name": "杂交树1", "quality": "传奇", "count": 1 }, - { - "name": "杂交树2", - "quality": "传奇", - "count": 1 - }, - { - "name": "土豆", - "quality": "普通", - "count": 2 - }, { "name": "小麦", "quality": "普通", - "count": 9 + "count": 6 }, { "name": "辣椒", "quality": "普通", - "count": 3 + "count": 1 }, { "name": "胡萝卜", @@ -500,9 +485,9 @@ "count": 8 }, { - "name": "玉米", + "name": "椰子", "quality": "优良", - "count": 3 + "count": 2 } ], "作物仓库": [ @@ -558,17 +543,16 @@ "2025年07月21日21时13分12秒": "金币276 经验56 土豆x2 小麦x4" }, "在线礼包": { - "当前日期": "2025-07-21", - "今日在线时长": 245.16000938415527, + "当前日期": "2025-07-22", + "今日在线时长": 78.0709433555603, "已领取礼包": [ - "1分钟", - "3分钟" + "1分钟" ], - "登录时间": 1753101378.1634717 + "登录时间": 1753145471.320933 }, "点赞系统": { "今日剩余点赞次数": 10, - "点赞上次刷新时间": "2025-07-21", + "点赞上次刷新时间": "2025-07-22", "总点赞数": 0 }, "新手礼包": { @@ -578,8 +562,8 @@ "体力系统": { "当前体力值": 25, "最大体力值": 25, - "上次刷新时间": "2025-07-21", - "上次恢复时间": 1753101378.1589587 + "上次刷新时间": "2025-07-22", + "上次恢复时间": 1753145471.3187816 }, "小卖部配置": { "商品列表": [], diff --git a/server/game_saves/3205788256.json b/server/game_saves/3205788256.json index ad82613..499f4a8 100644 --- a/server/game_saves/3205788256.json +++ b/server/game_saves/3205788256.json @@ -1,12 +1,12 @@ { "农场土地": [ { - "crop_type": "", - "grow_time": 0, + "crop_type": "南瓜", + "grow_time": 3600, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 3600, "已浇水": false, "已施肥": false, "土地等级": 4 @@ -23,12 +23,12 @@ "土地等级": 4 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "南瓜", + "grow_time": 3600, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 14400, + "is_planted": true, + "max_grow_time": 3600, "已浇水": false, "已施肥": false, "土地等级": 4 @@ -56,12 +56,12 @@ "土地等级": 1 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "人参", + "grow_time": 7200, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 7200, "已浇水": false, "已施肥": false, "土地等级": 2 @@ -78,30 +78,30 @@ "土地等级": 3 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "树莓", + "grow_time": 2113, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 2700, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "树莓", + "grow_time": 2111, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 2700, "已浇水": false, "已施肥": false, "土地等级": 0 }, { "crop_type": "杂交树2", - "grow_time": 12749, + "grow_time": 14927, "is_dead": false, "is_diged": true, "is_planted": true, @@ -111,74 +111,74 @@ "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "甘蔗", + "grow_time": 5400, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 480, + "is_planted": true, + "max_grow_time": 5400, "已浇水": false, "已施肥": false, "土地等级": 4 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "香蕉", + "grow_time": 8400, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 480, + "is_planted": true, + "max_grow_time": 8400, "已浇水": false, "已施肥": false, "土地等级": 4 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "杂交树1", + "grow_time": 21330, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 21600, "已浇水": false, "已施肥": false, "土地等级": 4 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "葫芦", + "grow_time": 1080, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 1080, "已浇水": false, "已施肥": false, "土地等级": 4 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "葫芦", + "grow_time": 1080, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 480, + "is_planted": true, + "max_grow_time": 1080, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "葫芦", + "grow_time": 1080, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 300, + "is_planted": true, + "max_grow_time": 1080, "已浇水": false, "已施肥": false, "土地等级": 0 }, { "crop_type": "杂交树2", - "grow_time": 12747, + "grow_time": 14925, "is_dead": false, "is_diged": true, "is_planted": true, @@ -189,7 +189,7 @@ }, { "crop_type": "杂交树2", - "grow_time": 12747, + "grow_time": 14925, "is_dead": false, "is_diged": true, "is_planted": true, @@ -200,7 +200,7 @@ }, { "crop_type": "杂交树2", - "grow_time": 12747, + "grow_time": 14925, "is_dead": false, "is_diged": true, "is_planted": true, @@ -210,188 +210,188 @@ "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 480, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 12745, + "crop_type": "豌豆", + "grow_time": 720, "is_dead": false, "is_diged": true, "is_planted": true, - "max_grow_time": 25200, + "max_grow_time": 720, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "杂交树2", - "grow_time": 12744, + "crop_type": "胡萝卜", + "grow_time": 240, "is_dead": false, "is_diged": true, "is_planted": true, - "max_grow_time": 25200, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, "max_grow_time": 240, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "土豆", + "grow_time": 480, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 14400, + "is_planted": true, + "max_grow_time": 480, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "椰子", + "grow_time": 1080, "is_dead": false, "is_diged": true, - "is_planted": false, + "is_planted": true, + "max_grow_time": 1080, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "玉米", + "grow_time": 900, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 900, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "番茄", + "grow_time": 720, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 720, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "杂交树2", + "grow_time": 14923, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 25200, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "土豆", + "grow_time": 480, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 480, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "番茄", + "grow_time": 720, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 720, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "生菜", + "grow_time": 650, + "is_dead": false, + "is_diged": true, + "is_planted": true, "max_grow_time": 650, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "杂交树2", + "grow_time": 14922, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 6600, + "is_planted": true, + "max_grow_time": 25200, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "梨子", + "grow_time": 2823, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 4800, + "is_planted": true, + "max_grow_time": 2820, + "已浇水": false, + "已施肥": false, + "土地等级": 4 + }, + { + "crop_type": "香蕉", + "grow_time": 8401, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 8400, + "已浇水": false, + "已施肥": false, + "土地等级": 3 + }, + { + "crop_type": "可可豆", + "grow_time": 2102, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 2500, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "黄瓜", + "grow_time": 1200, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 2400, + "is_planted": true, + "max_grow_time": 1200, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "葡萄", + "grow_time": 2102, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 2700, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "大豆", + "grow_time": 1080, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 1080, "已浇水": false, "已施肥": false, "土地等级": 0 @@ -408,56 +408,56 @@ "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "生菜", + "grow_time": 650, "is_dead": false, "is_diged": true, - "is_planted": false, + "is_planted": true, + "max_grow_time": 650, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "野草1", + "grow_time": 5, + "is_dead": false, + "is_diged": true, + "is_planted": true, + "max_grow_time": 5, + "已浇水": false, + "已施肥": false, + "土地等级": 0 + }, + { + "crop_type": "大豆", + "grow_time": 1080, + "is_dead": false, + "is_diged": true, + "is_planted": true, "max_grow_time": 1080, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "鱼腥草", + "grow_time": 2101, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 10200, + "is_planted": true, + "max_grow_time": 7200, "已浇水": false, "已施肥": false, "土地等级": 0 }, { - "crop_type": "", - "grow_time": 0, + "crop_type": "稻谷", + "grow_time": 600, "is_dead": false, "is_diged": true, - "is_planted": false, - "max_grow_time": 1080, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 300, - "已浇水": false, - "已施肥": false, - "土地等级": 0 - }, - { - "crop_type": "", - "grow_time": 0, - "is_dead": false, - "is_diged": true, - "is_planted": false, - "max_grow_time": 3060, + "is_planted": true, + "max_grow_time": 600, "已浇水": false, "已施肥": false, "土地等级": 0 @@ -555,27 +555,22 @@ { "name": "小麦", "quality": "普通", - "count": 23 + "count": 27 }, { "name": "胡萝卜", "quality": "普通", - "count": 7 + "count": 6 }, { "name": "土豆", "quality": "普通", - "count": 10 + "count": 8 }, { "name": "鱼腥草", "quality": "优良", - "count": 5 - }, - { - "name": "玉米", - "quality": "优良", - "count": 1 + "count": 4 }, { "name": "蓝莓", @@ -595,12 +590,12 @@ { "name": "稻谷", "quality": "普通", - "count": 7 + "count": 10 }, { "name": "大豆", "quality": "普通", - "count": 6 + "count": 4 }, { "name": "咖啡豆", @@ -620,7 +615,7 @@ { "name": "椰子", "quality": "优良", - "count": 8 + "count": 7 }, { "name": "菠萝", @@ -690,7 +685,7 @@ { "name": "番茄", "quality": "普通", - "count": 4 + "count": 2 }, { "name": "大白菜", @@ -705,17 +700,17 @@ { "name": "梨子", "quality": "优良", - "count": 2 + "count": 1 }, { "name": "生菜", "quality": "普通", - "count": 8 + "count": 6 }, { "name": "黄瓜", "quality": "普通", - "count": 6 + "count": 5 }, { "name": "面包树", @@ -730,12 +725,12 @@ { "name": "野草1", "quality": "普通", - "count": 6 + "count": 5 }, { "name": "豌豆", "quality": "普通", - "count": 4 + "count": 3 }, { "name": "柿子", @@ -745,12 +740,12 @@ { "name": "葡萄", "quality": "普通", - "count": 3 + "count": 2 }, { "name": "树莓", "quality": "优良", - "count": 3 + "count": 1 }, { "name": "玫瑰花", @@ -772,11 +767,6 @@ "quality": "稀有", "count": 2 }, - { - "name": "可可豆", - "quality": "稀有", - "count": 1 - }, { "name": "桃子", "quality": "稀有", @@ -785,28 +775,8 @@ { "name": "甘蔗", "quality": "稀有", - "count": 3 - }, - { - "name": "人参", - "quality": "史诗", - "count": 1 - }, - { - "name": "南瓜", - "quality": "普通", "count": 2 }, - { - "name": "葫芦", - "quality": "优良", - "count": 3 - }, - { - "name": "杂交树1", - "quality": "传奇", - "count": 1 - }, { "name": "杂草1", "quality": "普通", @@ -815,16 +785,16 @@ { "name": "香蕉", "quality": "优良", - "count": 3 + "count": 1 } ], - "经验值": 3553, + "经验值": 3611, "农场名称": "树萌芽の狗窝", "玩家昵称": "树萌芽", "等级": 66, - "钱币": 615196902055, - "最后登录时间": "2025年07月21日13时20分46秒", - "总游玩时间": "162时58分35秒", + "钱币": 615196880382, + "最后登录时间": "2025年07月22日12时49分34秒", + "总游玩时间": "163时34分9秒", "玩家账号": "3205788256", "玩家密码": "123456", "个人简介": "人生啊,就这样吧", @@ -1346,20 +1316,19 @@ "体力系统": { "当前体力值": 25, "最大体力值": 25, - "上次刷新时间": "2025-07-21", - "上次恢复时间": 1753061433.2399058 + "上次刷新时间": "2025-07-22", + "上次恢复时间": 1753145617.2709374 }, "点赞系统": { - "今日剩余点赞次数": 0, - "点赞上次刷新时间": "2025-07-21" + "今日剩余点赞次数": 10, + "点赞上次刷新时间": "2025-07-22", + "总点赞数": 0 }, "在线礼包": { - "当前日期": "2025-07-21", - "今日在线时长": 94.40529680252075, - "已领取礼包": [ - "1分钟" - ], - "登录时间": 1753061433.2417476 + "当前日期": "2025-07-22", + "今日在线时长": 45.17346143722534, + "已领取礼包": [], + "登录时间": 1753145617.273117 }, "小卖部配置": { "商品列表": [], @@ -1374,6 +1343,7 @@ "领取时间": "2025-07-21 09:30:48" }, "签到历史": { - "2025年07月21日10时47分55秒": "金币439 经验57 小麦x5 土豆x3" + "2025年07月21日10时47分55秒": "金币439 经验57 小麦x5 土豆x3", + "2025年07月22日10时34分15秒": "金币327 经验58 稻谷x4 小麦x4" } } \ No newline at end of file