From e3741ed9d489ead0eac17252aaa6f849e8f171b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=A0=91=E8=90=8C=E8=8A=BD?= <3205788256@qq.com> Date: Sun, 20 Jul 2025 22:16:57 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BB=A7=E7=BB=AD=E8=BF=81=E7=A7=BB=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E9=85=8D=E7=BD=AE=E5=88=B0=E6=95=B0=E6=8D=AE=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Server/SMYMongoDBAPI.py | 384 ++++++++++-- Server/TCPGameServer.py | 567 ++++++++++-------- .../__pycache__/SMYMongoDBAPI.cpython-313.pyc | Bin 24880 -> 33024 bytes .../__pycache__/TCPGameServer.cpython-313.pyc | Bin 350744 -> 356177 bytes .../config/initial_player_data_template.json | 1 + Server/game_saves/2143323382.json | 230 +++++-- 6 files changed, 850 insertions(+), 332 deletions(-) diff --git a/Server/SMYMongoDBAPI.py b/Server/SMYMongoDBAPI.py index 5e1c7c6..016baab 100644 --- a/Server/SMYMongoDBAPI.py +++ b/Server/SMYMongoDBAPI.py @@ -140,36 +140,6 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"获取游戏配置失败 [{config_type}]: {e}") return None - - def get_daily_checkin_config(self) -> Optional[Dict[str, Any]]: - """ - 获取每日签到配置 - - Returns: - Dict: 每日签到配置数据 - """ - try: - collection = self.get_collection("gameconfig") - - # 使用已知的文档ID查找 - object_id = ObjectId("687cce278e77ba00a7414ba2") - result = collection.find_one({"_id": object_id}) - - if result: - # 移除MongoDB的_id字段 - if "_id" in result: - del result["_id"] - - self.logger.info("成功获取每日签到配置") - return result - else: - self.logger.warning("未找到每日签到配置") - return None - - except Exception as e: - self.logger.error(f"获取每日签到配置失败: {e}") - return None - def set_game_config(self, config_type: str, config_data: Dict[str, Any]) -> bool: """ 设置游戏配置 @@ -205,6 +175,37 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"设置游戏配置异常 [{config_type}]: {e}") return False + + + #=====================每日签到系统====================== + def get_daily_checkin_config(self) -> Optional[Dict[str, Any]]: + """ + 获取每日签到配置 + + Returns: + Dict: 每日签到配置数据 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID查找 + object_id = ObjectId("687cce278e77ba00a7414ba2") + result = collection.find_one({"_id": object_id}) + + if result: + # 移除MongoDB的_id字段 + if "_id" in result: + del result["_id"] + + self.logger.info("成功获取每日签到配置") + return result + else: + self.logger.warning("未找到每日签到配置") + return None + + except Exception as e: + self.logger.error(f"获取每日签到配置失败: {e}") + return None def update_daily_checkin_config(self, config_data: Dict[str, Any]) -> bool: """ @@ -240,7 +241,10 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新每日签到配置异常: {e}") return False - + #=====================每日签到系统====================== + + + #=====================幸运抽奖系统====================== def get_lucky_draw_config(self) -> Optional[Dict[str, Any]]: """ 获取幸运抽奖配置 @@ -306,7 +310,10 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新幸运抽奖配置异常: {e}") return False - + #=====================幸运抽奖系统====================== + + + #=====================新手大礼包系统====================== def get_new_player_config(self) -> Optional[Dict[str, Any]]: """ 获取新手大礼包配置 @@ -372,7 +379,10 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新新手大礼包配置异常: {e}") return False - + #=====================新手大礼包系统====================== + + + #=====================智慧树系统====================== def get_wisdom_tree_config(self) -> Optional[Dict[str, Any]]: """ 获取智慧树配置 @@ -438,7 +448,79 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新智慧树配置异常: {e}") return False + #=====================智慧树系统====================== + + + #=====================稻草人系统====================== + def get_scare_crow_config(self) -> Optional[Dict[str, Any]]: + """ + 获取稻草人配置 + + Returns: + Dict: 稻草人配置数据 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID查找 + object_id = ObjectId("687cea258e77ba00a7414ba8") + result = collection.find_one({"_id": object_id}) + + if result: + # 移除MongoDB的_id字段和updated_at字段 + if "_id" in result: + del result["_id"] + if "updated_at" in result: + del result["updated_at"] + + self.logger.info("成功获取稻草人配置") + return result + else: + self.logger.warning("未找到稻草人配置") + return None + + except Exception as e: + self.logger.error(f"获取稻草人配置失败: {e}") + return None + def update_scare_crow_config(self, config_data: Dict[str, Any]) -> bool: + """ + 更新稻草人配置 + + Args: + config_data: 配置数据 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID更新 + object_id = ObjectId("687cea258e77ba00a7414ba8") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **config_data + } + + result = collection.replace_one({"_id": object_id}, update_data) + + if result.acknowledged and result.matched_count > 0: + self.logger.info("成功更新稻草人配置") + return True + else: + self.logger.error("更新稻草人配置失败") + return False + + except Exception as e: + self.logger.error(f"更新稻草人配置异常: {e}") + return False + #=====================稻草人系统====================== + + + #=====================在线礼包系统====================== def get_online_gift_config(self) -> Optional[Dict[str, Any]]: """ 获取在线礼包配置 @@ -504,7 +586,216 @@ class SMYMongoDBAPI: except Exception as e: self.logger.error(f"更新在线礼包配置异常: {e}") return False + #=====================在线礼包系统====================== + + + #=====================道具配置系统====================== + def get_item_config(self) -> Optional[Dict[str, Any]]: + """ + 获取道具配置 + + Returns: + Dict: 道具配置数据 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID查找 + object_id = ObjectId("687cf17c8e77ba00a7414baa") + result = collection.find_one({"_id": object_id}) + + if result: + # 移除MongoDB的_id字段和updated_at字段 + if "_id" in result: + del result["_id"] + if "updated_at" in result: + del result["updated_at"] + + self.logger.info("成功获取道具配置") + return result + else: + self.logger.warning("未找到道具配置") + return None + + except Exception as e: + self.logger.error(f"获取道具配置失败: {e}") + return None + def update_item_config(self, config_data: Dict[str, Any]) -> bool: + """ + 更新道具配置 + + Args: + config_data: 配置数据 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID更新 + object_id = ObjectId("687cf17c8e77ba00a7414baa") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **config_data + } + + result = collection.replace_one({"_id": object_id}, update_data) + + if result.acknowledged and result.matched_count > 0: + self.logger.info("成功更新道具配置") + return True + else: + self.logger.error("更新道具配置失败") + return False + + except Exception as e: + self.logger.error(f"更新道具配置异常: {e}") + return False + #=====================道具配置系统====================== + + + #=====================宠物配置系统====================== + def get_pet_config(self) -> Optional[Dict[str, Any]]: + """ + 获取宠物配置 + + Returns: + Dict: 宠物配置数据 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID查找 + object_id = ObjectId("687cf59b8e77ba00a7414bab") + result = collection.find_one({"_id": object_id}) + + if result: + # 移除MongoDB的_id字段和updated_at字段 + if "_id" in result: + del result["_id"] + if "updated_at" in result: + del result["updated_at"] + + self.logger.info("成功获取宠物配置") + return result + else: + self.logger.warning("未找到宠物配置") + return None + + except Exception as e: + self.logger.error(f"获取宠物配置失败: {e}") + return None + + def update_pet_config(self, config_data: Dict[str, Any]) -> bool: + """ + 更新宠物配置 + + Args: + config_data: 配置数据 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID更新 + object_id = ObjectId("687cf59b8e77ba00a7414bab") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **config_data + } + + result = collection.replace_one({"_id": object_id}, update_data) + + if result.acknowledged and result.matched_count > 0: + self.logger.info("成功更新宠物配置") + return True + else: + self.logger.error("更新宠物配置失败") + return False + + except Exception as e: + self.logger.error(f"更新宠物配置异常: {e}") + return False + #=====================宠物配置系统====================== + + + #=====================体力系统====================== + def get_stamina_config(self) -> Optional[Dict[str, Any]]: + """ + 获取体力系统配置 + + Returns: + Dict: 体力系统配置数据 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID查找 + object_id = ObjectId("687cefba8e77ba00a7414ba9") + result = collection.find_one({"_id": object_id}) + + if result: + # 移除MongoDB的_id字段和updated_at字段 + if "_id" in result: + del result["_id"] + if "updated_at" in result: + del result["updated_at"] + + self.logger.info("成功获取体力系统配置") + return result + else: + self.logger.warning("未找到体力系统配置") + return None + + except Exception as e: + self.logger.error(f"获取体力系统配置失败: {e}") + return None + + def update_stamina_config(self, config_data: Dict[str, Any]) -> bool: + """ + 更新体力系统配置 + + Args: + config_data: 配置数据 + + Returns: + bool: 是否成功 + """ + try: + collection = self.get_collection("gameconfig") + + # 使用已知的文档ID更新 + object_id = ObjectId("687cefba8e77ba00a7414ba9") + + # 添加更新时间 + update_data = { + "updated_at": datetime.now(), + **config_data + } + + result = collection.replace_one({"_id": object_id}, update_data) + + if result.acknowledged and result.matched_count > 0: + self.logger.info("成功更新体力系统配置") + return True + else: + self.logger.error("更新体力系统配置失败") + return False + + except Exception as e: + self.logger.error(f"更新体力系统配置异常: {e}") + return False + #=====================体力系统====================== + + # ========================= 通用数据库操作 ========================= def insert_document(self, collection_name: str, document: Dict[str, Any]) -> Optional[str]: @@ -682,8 +973,33 @@ def test_api(): else: print("✗ 获取在线礼包配置失败") + # 测试获取稻草人配置 + print("\n6. 测试获取稻草人配置:") + scare_crow_config = api.get_scare_crow_config() + if scare_crow_config: + print("✓ 成功获取稻草人配置") + scare_crow_types = scare_crow_config.get("稻草人类型", {}) + modify_cost = scare_crow_config.get("修改稻草人配置花费", "N/A") + print(f"稻草人类型数量: {len(scare_crow_types)}") + print(f"修改费用: {modify_cost}金币") + else: + print("✗ 获取稻草人配置失败") + + # 测试获取宠物配置 + print("\n7. 测试获取宠物配置:") + pet_config = api.get_pet_config() + if pet_config: + print("✓ 成功获取宠物配置") + pets = pet_config.get("宠物", {}) + print(f"宠物类型数量: {len(pets)}") + if pets: + first_pet = list(pets.values())[0] + print(f"示例宠物属性: {list(first_pet.keys())[:3]}...") + else: + print("✗ 获取宠物配置失败") + # 测试查找所有游戏配置 - print("\n6. 测试查找游戏配置集合:") + print("\n8. 测试查找游戏配置集合:") try: configs = api.find_documents("gameconfig") print(f"找到 {len(configs)} 个配置文档") diff --git a/Server/TCPGameServer.py b/Server/TCPGameServer.py index 614c02d..468dce6 100644 --- a/Server/TCPGameServer.py +++ b/Server/TCPGameServer.py @@ -2300,112 +2300,11 @@ class TCPGameServer(TCPServer): - -#==========================购买道具处理========================== - #处理购买道具请求 - def _handle_buy_item(self, client_id, message): - """处理购买道具请求""" - # 检查用户是否已登录 - logged_in, response = self._check_user_logged_in(client_id, "购买道具", "buy_item") - if not logged_in: - return self.send_data(client_id, response) - - # 获取玩家数据 - player_data, username, response = self._load_player_data_with_check(client_id, "buy_item") - if not player_data: - return self.send_data(client_id, response) - - item_name = message.get("item_name", "") - item_cost = message.get("item_cost", 0) - quantity = message.get("quantity", 1) # 获取购买数量,默认为1 - - # 确保购买数量为正整数 - if not isinstance(quantity, int) or quantity <= 0: - quantity = 1 - - # 加载道具配置 - item_config = self._load_item_config() - if not item_config: - return self._send_action_error(client_id, "buy_item", "服务器无法加载道具数据") - - # 检查道具是否存在 - if item_name not in item_config: - return self._send_action_error(client_id, "buy_item", "该道具不存在") - - # 验证价格是否正确 - actual_cost = item_config[item_name].get("花费", 0) - if item_cost != actual_cost: - return self._send_action_error(client_id, "buy_item", f"道具价格验证失败,实际价格为{actual_cost}元") - - # 处理批量购买 - return self._process_item_purchase(client_id, player_data, username, item_name, item_config[item_name], quantity) - - #处理道具购买逻辑 - def _process_item_purchase(self, client_id, player_data, username, item_name, item_info, quantity=1): - """处理道具购买逻辑""" - unit_cost = item_info.get("花费", 0) - total_cost = unit_cost * quantity - - # 检查玩家金钱 - if player_data["money"] < total_cost: - return self._send_action_error(client_id, "buy_item", f"金钱不足,无法购买此道具。需要{total_cost}元,当前只有{player_data['money']}元") - - # 扣除金钱 - player_data["money"] -= total_cost - - # 将道具添加到道具背包 - item_found = False - - # 确保道具背包存在 - if "道具背包" not in player_data: - player_data["道具背包"] = [] - - for item in player_data["道具背包"]: - if item.get("name") == item_name: - item["count"] += quantity - item_found = True - break - - if not item_found: - player_data["道具背包"].append({ - "name": item_name, - "count": quantity - }) - - # 保存玩家数据 - self.save_player_data(username, player_data) - - self.log('INFO', f"玩家 {username} 购买了 {quantity} 个道具 {item_name},花费 {total_cost} 元", 'SERVER') - - return self.send_data(client_id, { - "type": "action_response", - "action_type": "buy_item", - "success": True, - "message": f"成功购买 {quantity} 个 {item_name}", - "updated_data": { - "money": player_data["money"], - "道具背包": player_data["道具背包"] - } - }) - - #加载道具配置数据 - def _load_item_config(self): - """从item_config.json加载道具配置数据""" - try: - with open("config/item_config.json", 'r', encoding='utf-8') as file: - return json.load(file) - except Exception as e: - self.log('ERROR', f"无法加载道具数据: {str(e)}", 'SERVER') - return {} -#==========================购买道具处理========================== - - - #==========================购买宠物处理========================== #处理购买宠物请求 def _handle_buy_pet(self, client_id, message): """处理购买宠物请求""" - # 检查用户是否已登录 + # 检查用户登录状态 logged_in, response = self._check_user_logged_in(client_id, "购买宠物", "buy_pet") if not logged_in: return self.send_data(client_id, response) @@ -2415,35 +2314,46 @@ class TCPGameServer(TCPServer): if not player_data: return self.send_data(client_id, response) + # 获取请求参数 pet_name = message.get("pet_name", "") pet_cost = message.get("pet_cost", 0) + # 验证宠物购买条件 + validation_result = self._validate_pet_purchase(pet_name, pet_cost, player_data) + if not validation_result["success"]: + return self._send_action_error(client_id, "buy_pet", validation_result["message"]) + + # 处理宠物购买 + return self._process_pet_purchase(client_id, player_data, username, pet_name, validation_result["pet_info"]) + + def _validate_pet_purchase(self, pet_name, pet_cost, player_data): + """验证宠物购买条件""" # 加载宠物配置 pet_config = self._load_pet_config() if not pet_config: - return self._send_action_error(client_id, "buy_pet", "服务器无法加载宠物数据") + return {"success": False, "message": "服务器无法加载宠物数据"} # 检查宠物是否存在 if pet_name not in pet_config: - return self._send_action_error(client_id, "buy_pet", "该宠物不存在") + return {"success": False, "message": "该宠物不存在"} - # 检查宠物是否可购买 pet_info = pet_config[pet_name] purchase_info = pet_info.get("购买信息", {}) - if not purchase_info.get("能否购买", False): - return self._send_action_error(client_id, "buy_pet", "该宠物不可购买") - # 验证价格是否正确 + # 检查宠物是否可购买 + if not purchase_info.get("能否购买", False): + return {"success": False, "message": "该宠物不可购买"} + + # 验证价格 actual_cost = purchase_info.get("购买价格", 0) if pet_cost != actual_cost: - return self._send_action_error(client_id, "buy_pet", f"宠物价格验证失败,实际价格为{actual_cost}元") + return {"success": False, "message": f"宠物价格验证失败,实际价格为{actual_cost}元"} # 检查玩家是否已拥有该宠物 if self._player_has_pet(player_data, pet_name): - return self._send_action_error(client_id, "buy_pet", f"你已经拥有 {pet_name} 了!") + return {"success": False, "message": f"你已经拥有 {pet_name} 了!"} - # 处理宠物购买 - return self._process_pet_purchase(client_id, player_data, username, pet_name, pet_info) + return {"success": True, "pet_info": pet_info} #处理宠物购买逻辑 def _process_pet_purchase(self, client_id, player_data, username, pet_name, pet_info): @@ -2453,43 +2363,20 @@ class TCPGameServer(TCPServer): # 检查玩家金钱 if player_data["money"] < pet_cost: - return self._send_action_error(client_id, "buy_pet", f"金钱不足,无法购买此宠物。需要{pet_cost}元,当前只有{player_data['money']}元") + return self._send_action_error(client_id, "buy_pet", + f"金钱不足,无法购买此宠物。需要{pet_cost}元,当前只有{player_data['money']}元") - # 扣除金钱 + # 扣除金钱并添加宠物 player_data["money"] -= pet_cost + pet_instance = self._create_pet_instance(pet_info, username, pet_name) - # 确保宠物背包存在 + # 确保宠物背包存在并添加宠物 if "宠物背包" not in player_data: player_data["宠物背包"] = [] - - # 创建宠物实例数据 - 复制宠物配置的完整JSON数据 - import copy - pet_instance = copy.deepcopy(pet_info) - - # 为购买的宠物设置独特的ID和主人信息 - import time - current_time = time.time() - unique_id = str(int(current_time * 1000)) # 使用时间戳作为唯一ID - - # 更新基本信息 - if "基本信息" in pet_instance: - pet_instance["基本信息"]["宠物主人"] = username - pet_instance["基本信息"]["宠物ID"] = unique_id - pet_instance["基本信息"]["宠物名称"] = f"{username}的{pet_name}" - - # 设置宠物生日(详细时间) - import datetime - now = datetime.datetime.now() - birthday = f"{now.year}年{now.month}月{now.day}日{now.hour}时{now.minute}分{now.second}秒" - pet_instance["基本信息"]["生日"] = birthday - pet_instance["基本信息"]["年龄"] = 0 # 刚出生年龄为0 - - # 将宠物添加到宠物背包 player_data["宠物背包"].append(pet_instance) - # 保存玩家数据 + # 保存数据并返回响应 self.save_player_data(username, player_data) - self.log('INFO', f"玩家 {username} 购买了宠物 {pet_name},花费 {pet_cost} 元", 'SERVER') return self.send_data(client_id, { @@ -2503,6 +2390,31 @@ class TCPGameServer(TCPServer): } }) + def _create_pet_instance(self, pet_info, username, pet_name): + """创建宠物实例""" + import copy + import time + import datetime + + # 复制宠物配置数据 + pet_instance = copy.deepcopy(pet_info) + + # 生成唯一ID和设置基本信息 + unique_id = str(int(time.time() * 1000)) + now = datetime.datetime.now() + birthday = f"{now.year}年{now.month}月{now.day}日{now.hour}时{now.minute}分{now.second}秒" + + if "基本信息" in pet_instance: + pet_instance["基本信息"].update({ + "宠物主人": username, + "宠物ID": unique_id, + "宠物名称": f"{username}的{pet_name}", + "生日": birthday, + "年龄": 0 + }) + + return pet_instance + #检查玩家是否已拥有某种宠物 def _player_has_pet(self, player_data, pet_name): """检查玩家是否已拥有指定类型的宠物""" @@ -2516,12 +2428,28 @@ class TCPGameServer(TCPServer): #加载宠物配置数据 def _load_pet_config(self): - """从pet_data.json加载宠物配置数据""" + """优先从MongoDB加载宠物配置数据,失败时回退到JSON文件""" try: + # 优先从MongoDB加载 + if hasattr(self, 'mongo_api') and self.mongo_api: + config = self.mongo_api.get_pet_config() + if config: + self.log('INFO', "成功从MongoDB加载宠物配置", 'SERVER') + return config + else: + self.log('WARNING', "MongoDB中未找到宠物配置,回退到JSON文件", 'SERVER') + + # 回退到JSON文件 with open("config/pet_data.json", 'r', encoding='utf-8') as file: - return json.load(file) + config = json.load(file) + self.log('INFO', "从JSON文件加载宠物配置", 'SERVER') + return config + + except json.JSONDecodeError as e: + self.log('ERROR', f"宠物配置JSON解析错误: {str(e)}", 'SERVER') + return {} except Exception as e: - self.log('ERROR', f"无法加载宠物数据: {str(e)}", 'SERVER') + self.log('ERROR', f"加载宠物配置失败: {str(e)}", 'SERVER') return {} # 将巡逻宠物ID转换为完整宠物数据 @@ -3847,69 +3775,173 @@ class TCPGameServer(TCPServer): +#==========================购买道具处理========================== + #处理购买道具请求 + def _handle_buy_item(self, client_id, message): + """处理购买道具请求""" + # 检查用户是否已登录 + logged_in, response = self._check_user_logged_in(client_id, "购买道具", "buy_item") + if not logged_in: + return self.send_data(client_id, response) + + # 获取玩家数据 + player_data, username, response = self._load_player_data_with_check(client_id, "buy_item") + if not player_data: + return self.send_data(client_id, response) + + # 解析请求参数 + item_name = message.get("item_name", "") + item_cost = message.get("item_cost", 0) + quantity = max(1, int(message.get("quantity", 1))) # 确保购买数量为正整数 + + # 验证道具配置 + item_config = self._load_item_config() + if not item_config: + return self._send_action_error(client_id, "buy_item", "服务器无法加载道具数据") + + if item_name not in item_config: + return self._send_action_error(client_id, "buy_item", "该道具不存在") + + # 验证价格 + actual_cost = item_config[item_name].get("花费", 0) + if item_cost != actual_cost: + return self._send_action_error(client_id, "buy_item", f"道具价格验证失败,实际价格为{actual_cost}元") + + # 处理购买 + return self._process_item_purchase(client_id, player_data, username, item_name, item_config[item_name], quantity) + + #处理道具购买逻辑 + def _process_item_purchase(self, client_id, player_data, username, item_name, item_info, quantity=1): + """处理道具购买逻辑""" + unit_cost = item_info.get("花费", 0) + total_cost = unit_cost * quantity + + # 检查金钱是否足够 + if player_data["money"] < total_cost: + return self._send_action_error(client_id, "buy_item", + f"金钱不足,需要{total_cost}元,当前只有{player_data['money']}元") + + # 扣除金钱并添加道具 + player_data["money"] -= total_cost + self._add_item_to_inventory(player_data, item_name, quantity) + + # 保存数据并记录日志 + self.save_player_data(username, player_data) + self.log('INFO', f"玩家 {username} 购买了 {quantity} 个道具 {item_name},花费 {total_cost} 元", 'SERVER') + + return self.send_data(client_id, { + "type": "action_response", + "action_type": "buy_item", + "success": True, + "message": f"成功购买 {quantity} 个 {item_name}", + "updated_data": { + "money": player_data["money"], + "道具背包": player_data["道具背包"] + } + }) + + def _add_item_to_inventory(self, player_data, item_name, quantity): + """将道具添加到玩家背包""" + if "道具背包" not in player_data: + player_data["道具背包"] = [] + + # 查找是否已有该道具 + for item in player_data["道具背包"]: + if item.get("name") == item_name: + item["count"] += quantity + return + + # 添加新道具 + player_data["道具背包"].append({ + "name": item_name, + "count": quantity + }) + + #加载道具配置数据 + def _load_item_config(self): + """优先从MongoDB加载道具配置数据,失败时回退到JSON文件""" + # 首先尝试从MongoDB加载 + if self.mongo_api and self.mongo_api.is_connected(): + try: + config = self.mongo_api.get_item_config() + if config: + self.log('INFO', '成功从MongoDB加载道具配置', 'SERVER') + return config + else: + self.log('WARNING', 'MongoDB中未找到道具配置,回退到JSON文件', 'SERVER') + except Exception as e: + self.log('WARNING', f'从MongoDB加载道具配置失败: {e},回退到JSON文件', 'SERVER') + + # 回退到JSON文件 + try: + with open("config/item_config.json", 'r', encoding='utf-8') as file: + config = json.load(file) + self.log('INFO', '从JSON文件加载道具配置', 'SERVER') + return config + except json.JSONDecodeError as e: + self.log('ERROR', f'JSON文件格式错误: {e}', 'SERVER') + return {} + except Exception as e: + self.log('ERROR', f'无法加载道具数据: {e}', 'SERVER') + return {} +#==========================购买道具处理========================== + + #==========================道具使用处理========================== def _handle_use_item(self, client_id, message): """处理使用道具请求""" - print(f"调试:服务器收到道具使用请求") - print(f" - client_id: {client_id}") - print(f" - message: {message}") - # 检查用户是否已登录 logged_in, response = self._check_user_logged_in(client_id, "使用道具", "use_item") if not logged_in: - print(f"错误:用户未登录") return self.send_data(client_id, response) # 获取玩家数据 player_data, username, response = self._load_player_data_with_check(client_id, "use_item") if not player_data: - print(f"错误:无法加载玩家数据") return self.send_data(client_id, response) + # 解析请求参数 lot_index = message.get("lot_index", -1) item_name = message.get("item_name", "") use_type = message.get("use_type", "") target_username = message.get("target_username", "") - print(f"调试:解析参数") - print(f" - username: {username}") - print(f" - lot_index: {lot_index}") - print(f" - item_name: {item_name}") - print(f" - use_type: {use_type}") - print(f" - target_username: {target_username}") - # 验证参数 - if not item_name: - return self._send_action_error(client_id, "use_item", "道具名称不能为空") - - if not use_type: - return self._send_action_error(client_id, "use_item", "使用类型不能为空") + if not item_name or not use_type: + return self._send_action_error(client_id, "use_item", "道具名称和使用类型不能为空") # 检查玩家是否拥有该道具 if not self._has_item_in_inventory(player_data, item_name): return self._send_action_error(client_id, "use_item", f"您没有 {item_name}") - # 确定操作目标 + # 确定操作目标并处理 if target_username and target_username != username: # 访问模式:对别人的作物使用道具 - target_player_data = self.load_player_data(target_username) - if not target_player_data: - return self._send_action_error(client_id, "use_item", f"无法找到玩家 {target_username} 的数据") - - # 验证地块索引 - if lot_index < 0 or lot_index >= len(target_player_data.get("farm_lots", [])): - return self._send_action_error(client_id, "use_item", "无效的地块索引") - - target_lot = target_player_data["farm_lots"][lot_index] - return self._process_item_use_visiting(client_id, player_data, username, target_player_data, target_username, target_lot, lot_index, item_name, use_type) + return self._handle_visiting_item_use(client_id, player_data, username, target_username, lot_index, item_name, use_type) else: # 正常模式:对自己的作物使用道具 - if lot_index < 0 or lot_index >= len(player_data.get("farm_lots", [])): - return self._send_action_error(client_id, "use_item", "无效的地块索引") - - lot = player_data["farm_lots"][lot_index] - return self._process_item_use_normal(client_id, player_data, username, lot, lot_index, item_name, use_type) + return self._handle_normal_item_use(client_id, player_data, username, lot_index, item_name, use_type) + + def _handle_normal_item_use(self, client_id, player_data, username, lot_index, item_name, use_type): + """处理正常模式下的道具使用""" + if lot_index < 0 or lot_index >= len(player_data.get("farm_lots", [])): + return self._send_action_error(client_id, "use_item", "无效的地块索引") + + lot = player_data["farm_lots"][lot_index] + return self._process_item_use_normal(client_id, player_data, username, lot, lot_index, item_name, use_type) + + def _handle_visiting_item_use(self, client_id, player_data, username, target_username, lot_index, item_name, use_type): + """处理访问模式下的道具使用""" + target_player_data = self.load_player_data(target_username) + if not target_player_data: + return self._send_action_error(client_id, "use_item", f"无法找到玩家 {target_username} 的数据") + + if lot_index < 0 or lot_index >= len(target_player_data.get("farm_lots", [])): + return self._send_action_error(client_id, "use_item", "无效的地块索引") + + target_lot = target_player_data["farm_lots"][lot_index] + return self._process_item_use_visiting(client_id, player_data, username, target_player_data, target_username, target_lot, lot_index, item_name, use_type) def _has_item_in_inventory(self, player_data, item_name): """检查玩家是否拥有指定道具""" @@ -4759,118 +4791,100 @@ class TCPGameServer(TCPServer): #==========================宠物使用道具处理========================== def _handle_use_pet_item(self, client_id, message): """处理宠物使用道具请求""" - # 检查用户是否已登录 + # 检查用户登录状态 logged_in, response = self._check_user_logged_in(client_id, "宠物使用道具", "use_pet_item") if not logged_in: return self.send_data(client_id, response) - # 获取请求参数 + # 验证请求参数 item_name = message.get("item_name", "") pet_id = message.get("pet_id", "") - if not item_name or not pet_id: - return self.send_data(client_id, { - "type": "use_pet_item_response", - "success": False, - "message": "缺少必要参数" - }) + return self._send_pet_item_error(client_id, "缺少必要参数") # 获取玩家数据 username = self.user_data[client_id]["username"] player_data = self.load_player_data(username) - if not player_data: - return self.send_data(client_id, { - "type": "use_pet_item_response", - "success": False, - "message": "玩家数据加载失败" - }) + return self._send_pet_item_error(client_id, "玩家数据加载失败") - # 检查道具是否存在 + # 验证道具和宠物 + validation_result = self._validate_pet_item_use(player_data, item_name, pet_id) + if not validation_result["success"]: + return self._send_pet_item_error(client_id, validation_result["message"]) + + # 处理道具使用 + return self._execute_pet_item_use(client_id, player_data, username, + validation_result["item_index"], + validation_result["pet_index"], + item_name, pet_id) + + def _validate_pet_item_use(self, player_data, item_name, pet_id): + """验证宠物道具使用条件""" + # 检查道具 item_bag = player_data.get("道具背包", []) - item_found = False item_index = -1 - for i, item in enumerate(item_bag): - if item.get("name") == item_name: - if item.get("count", 0) > 0: - item_found = True - item_index = i - break + if item.get("name") == item_name and item.get("count", 0) > 0: + item_index = i + break - if not item_found: - return self.send_data(client_id, { - "type": "use_pet_item_response", - "success": False, - "message": f"道具 {item_name} 不足" - }) + if item_index == -1: + return {"success": False, "message": f"道具 {item_name} 不足"} - # 检查宠物是否存在 + # 检查宠物 pet_bag = player_data.get("宠物背包", []) - pet_found = False pet_index = -1 - for i, pet in enumerate(pet_bag): if pet.get("基本信息", {}).get("宠物ID") == pet_id: - pet_found = True pet_index = i break - if not pet_found: - return self.send_data(client_id, { - "type": "use_pet_item_response", - "success": False, - "message": "找不到指定的宠物" - }) + if pet_index == -1: + return {"success": False, "message": "找不到指定的宠物"} - # 处理道具使用 + return {"success": True, "item_index": item_index, "pet_index": pet_index} + + def _execute_pet_item_use(self, client_id, player_data, username, item_index, pet_index, item_name, pet_id): + """执行宠物道具使用""" try: - success, result_message, updated_pet = self._process_pet_item_use( - item_name, pet_bag[pet_index] - ) + item_bag = player_data["道具背包"] + pet_bag = player_data["宠物背包"] + + # 处理道具效果 + success, result_message, updated_pet = self._process_pet_item_use(item_name, pet_bag[pet_index]) if success: - # 更新宠物数据 + # 更新数据 pet_bag[pet_index] = updated_pet - - # 减少道具数量 item_bag[item_index]["count"] -= 1 if item_bag[item_index]["count"] <= 0: item_bag.pop(item_index) - # 保存玩家数据 + # 保存并记录 self.save_player_data(username, player_data) + self.log('INFO', f"用户 {username} 对宠物 {pet_id} 使用道具 {item_name} 成功", 'PET_ITEM') - # 发送成功响应 - response = { + return self.send_data(client_id, { "type": "use_pet_item_response", "success": True, "message": result_message, - "updated_data": { - "宠物背包": pet_bag, - "道具背包": item_bag - } - } - - self.log('INFO', f"用户 {username} 对宠物 {pet_id} 使用道具 {item_name} 成功", 'PET_ITEM') - + "updated_data": {"宠物背包": pet_bag, "道具背包": item_bag} + }) else: - # 发送失败响应 - response = { - "type": "use_pet_item_response", - "success": False, - "message": result_message - } - - return self.send_data(client_id, response) - + return self._send_pet_item_error(client_id, result_message) + except Exception as e: self.log('ERROR', f"宠物使用道具处理失败: {str(e)}", 'PET_ITEM') - return self.send_data(client_id, { - "type": "use_pet_item_response", - "success": False, - "message": "道具使用处理失败" - }) + return self._send_pet_item_error(client_id, "道具使用处理失败") + + def _send_pet_item_error(self, client_id, message): + """发送宠物道具使用错误响应""" + return self.send_data(client_id, { + "type": "use_pet_item_response", + "success": False, + "message": message + }) def _process_pet_item_use(self, item_name, pet_data): """处理具体的宠物道具使用逻辑""" @@ -5069,7 +5083,6 @@ class TCPGameServer(TCPServer): - #==========================道具配置数据处理========================== #处理客户端请求道具配置数据 def _handle_item_config_request(self, client_id): @@ -5093,7 +5106,6 @@ class TCPGameServer(TCPServer): - #==========================升级土地处理========================== #处理升级土地请求 def _handle_upgrade_land(self, client_id, message): @@ -5464,10 +5476,24 @@ class TCPGameServer(TCPServer): def _load_stamina_config(self): """加载体力系统配置""" + # 优先从MongoDB加载配置 + if self.use_mongodb and self.mongo_api and self.mongo_api.is_connected(): + try: + config_data = self.mongo_api.get_stamina_config() + if config_data: + self.log('INFO', '成功从MongoDB加载体力系统配置', 'SERVER') + return config_data.get("体力系统配置", {}) + else: + self.log('WARNING', '从MongoDB获取体力系统配置失败,回退到JSON文件', 'SERVER') + except Exception as e: + self.log('ERROR', f'从MongoDB加载体力系统配置时发生错误: {e},回退到JSON文件', 'SERVER') + + # 回退到JSON文件 try: config_path = os.path.join(os.path.dirname(__file__), "config", "stamina_config.json") with open(config_path, 'r', encoding='utf-8') as file: config_data = json.load(file) + self.log('INFO', '从JSON文件加载体力系统配置', 'SERVER') return config_data.get("体力系统配置", {}) except FileNotFoundError: self.log('WARNING', f"体力系统配置文件未找到,使用默认配置", 'SERVER') @@ -5479,10 +5505,20 @@ class TCPGameServer(TCPServer): } except json.JSONDecodeError as e: self.log('ERROR', f"体力系统配置文件格式错误: {e}", 'SERVER') - return {} + return { + "最大体力值": 20, + "每小时恢复体力": 1, + "恢复间隔秒数": 3600, + "新玩家初始体力": 20 + } except Exception as e: self.log('ERROR', f"加载体力系统配置时发生错误: {e}", 'SERVER') - return {} + return { + "最大体力值": 20, + "每小时恢复体力": 1, + "恢复间隔秒数": 3600, + "新玩家初始体力": 20 + } #==========================点赞玩家处理========================== @@ -8293,12 +8329,35 @@ class TCPGameServer(TCPServer): def _load_scare_crow_config(self): """加载稻草人配置""" + # 优先从MongoDB加载配置 + try: + if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected(): + config = self.mongo_api.get_scare_crow_config() + if config: + self.log('INFO', "成功从MongoDB加载稻草人配置", 'SERVER') + return config + else: + self.log('WARNING', "MongoDB中未找到稻草人配置,回退到JSON文件", 'SERVER') + except Exception as e: + self.log('ERROR', f"从MongoDB加载稻草人配置失败: {str(e)},回退到JSON文件", 'SERVER') + + # 回退到从JSON文件加载 try: with open("config/scare_crow_config.json", 'r', encoding='utf-8') as file: - return json.load(file) + config = json.load(file) + self.log('INFO', "成功从JSON文件加载稻草人配置", 'SERVER') + return config except Exception as e: self.log('ERROR', f"无法加载稻草人配置: {str(e)}", 'SERVER') - return {} + # 返回默认配置 + return { + "稻草人类型": { + "稻草人1": {"图片": "res://assets/道具图片/稻草人1.webp", "价格": 1000}, + "稻草人2": {"图片": "res://assets/道具图片/稻草人2.webp", "价格": 1000}, + "稻草人3": {"图片": "res://assets/道具图片/稻草人3.webp", "价格": 1000} + }, + "修改稻草人配置花费": 300 + } def _send_buy_scare_crow_error(self, client_id, message): """发送购买稻草人错误响应""" diff --git a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc index 8eac0053ca1544a38e04e6c108ad71fb7231ed50..6c728ebd0396955c07664367f02fc6abb80dd141 100644 GIT binary patch delta 4492 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

Bxs>BEAhKLaw$QI0^3I&aD`)O{3#~aaDj-oIN zsnpSw?gb|h?8xYqx1b44qiAUCn5C{qO+Q--l*dxpv`>S~gk3uW!+*^c{jra`ss~Zh3{D&wzAKw(PR35PS}vbR5#N-No_C{utSk^Kr6DegFz=;brx$|S-77;0Dg2v$ua^*!??WS3KQH0 zd)&v>0&5-wSDg{Qa95It2o87$LA33cKhN)FBxb#L>rJ&0Q)0h7@$w{wO9dmeV=Np= zl)qnCiT(23!W547ekuM52jqp4MP_loA`5II=`gpwHc^o2t->eZZsCypnfDyyJ$l{_ zgMJym3kxN0$um2Cz|dVinAS!|e8o-yejBmFYHq|0r~pQ@7F)ftGj~g zH`u<`r?1O?d!F;e*y3+8c$>jl3aeayWLQ$O^a6v646eXmJ!PbxfUj4y?!i`- zX?p7o-yAn5%%#r0uw1a~A)>Xlop8Q4kJMvW>b2lHaD;sS;%y~n*Z}AI?vV#6^6=44 zHMYZYB*yyk2f2bJC6x)w`$?Cn$XZdS0cQ za2a$SH$i+zi_KsUTj0|H(^wzxLhacb#wn=VM8(zpq7S8%9BSRiCtG_eVzNl2r z_#aV->M20#DVy~c4f^TD7`@CP6hlArX?HaF-|}15_ycmysW<`K*uslV2z|1zjN)QR zZAPONeN}CxN!+Swtm5MNhQ@L(?p3i$xr$Y)I2tb@O_QHn(&ROja!aMECO;I!TD5FB ziTyp&G@Ol@@H||Nl@tcju=ze>_&~}GU~FHD5X9@=8U2PcukgC@hBXfH7Ac?V{sA=* Bb2R_} diff --git a/Server/__pycache__/TCPGameServer.cpython-313.pyc b/Server/__pycache__/TCPGameServer.cpython-313.pyc index 41499a15f249ce1efb2361f1c186677ef81cafd5..7f7ee6e1b22952987d8d7d924e0afb3c6c24ba87 100644 GIT binary patch delta 25651 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 delta 21358 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�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 d@O7b@>~9x`+q;+?$rPQ diff --git a/Server/config/initial_player_data_template.json b/Server/config/initial_player_data_template.json index bb664df..adc919d 100644 --- a/Server/config/initial_player_data_template.json +++ b/Server/config/initial_player_data_template.json @@ -19,6 +19,7 @@ "个人简介": "个人简介", "稻草人配置": { "已拥有稻草人类型": [ + "稻草人1" ], "稻草人展示类型": "", "稻草人昵称": "稻草人", diff --git a/Server/game_saves/2143323382.json b/Server/game_saves/2143323382.json index 66320b0..13a83bb 100644 --- a/Server/game_saves/2143323382.json +++ b/Server/game_saves/2143323382.json @@ -1,13 +1,13 @@ { - "experience": 90, + "experience": 455, "level": 36, - "money": 200802715, + "money": 200797815, "farm_name": "柚大青の小农场", "player_name": "柚大青", "user_name": "2143323382", "user_password": "tyh@19900420", - "last_login_time": "2025年07月20日21时00分40秒", - "total_login_time": "6时45分52秒", + "last_login_time": "2025年07月20日22时15分13秒", + "total_login_time": "6时47分9秒", "farm_lots": [ { "crop_type": "", @@ -132,26 +132,35 @@ }, { "crop_type": "杂交树1", - "grow_time": 14976, + "grow_time": 21042, "is_dead": false, "is_diged": true, "is_planted": true, "max_grow_time": 21600, "已浇水": false, - "已施肥": false, - "土地等级": 3 + "已施肥": true, + "土地等级": 3, + "施肥时间": 1753019866.8176558, + "施肥类型": "农家肥", + "施肥倍数": 2.0, + "施肥持续时间": 1800, + "浇水时间": 1753019871.817958 }, { "crop_type": "杂交树2", - "grow_time": 15468, + "grow_time": 21636, "is_dead": false, "is_diged": true, "is_planted": true, "max_grow_time": 25200, "已浇水": false, - "已施肥": false, + "已施肥": true, "土地等级": 3, - "浇水时间": 1753003230.8845751 + "浇水时间": 1753019874.5774846, + "施肥时间": 1753019862.2419734, + "施肥类型": "农家肥", + "施肥倍数": 2.0, + "施肥持续时间": 1800 }, { "crop_type": "", @@ -868,6 +877,111 @@ "品质系统": { "宠物品质": "COMMON" } + }, + { + "场景路径": "res://Scene/Pet/BigBeetle.tscn", + "基本信息": { + "宠物主人": "2143323382", + "宠物名称": "2143323382的大甲虫", + "队伍标识": "team1", + "宠物ID": "1753020931947", + "宠物类型": "大甲虫", + "生日": "2025年7月20日22时15分31秒", + "年龄": 0, + "性格": "活泼", + "简介": "", + "爱好": "" + }, + "等级经验": { + "宠物等级": 1, + "当前经验": 0, + "最大经验": 100, + "亲密度": 0, + "最大亲密度": 1000 + }, + "购买信息": { + "能否购买": true, + "购买价格": 1000, + "出售价格": 500 + }, + "生命与防御": { + "最大生命值": 200, + "当前生命值": 200, + "生命恢复速度": 1, + "最大护盾值": 0, + "当前护盾值": 0, + "护盾恢复速度": 0, + "最大护甲值": 100, + "当前护甲值": 100 + }, + "基础攻击属性": { + "攻击类型": "MELEE", + "基础攻击伤害": 25, + "攻击距离": 100, + "暴击率": 0.1, + "暴击伤害倍数": 1.5, + "生命汲取": 0.1, + "护甲穿透": 0 + }, + "近战攻击": { + "近战额外伤害": 0, + "近战攻击速度": 1 + }, + "远程攻击": { + "远程额外伤害": 0, + "远程攻击速度": 1, + "远程攻击模式": "SINGLE", + "子弹速度": 300 + }, + "散弹攻击": { + "散弹数量": 5, + "散弹扩散角度": 45 + }, + "多发射击": { + "多发射击行数": 2, + "多发射击列数": 3, + "多发射击间距": 30 + }, + "加特林属性": { + "加特林子弹数量": 8, + "加特林射击间隔": 0.1, + "加特林冷却时间": 2 + }, + "穿透属性": { + "穿透数量": 3 + }, + "移动与闪避": { + "移动速度": 100, + "闪避率": 0.05, + "击退力度": 300, + "击退抗性": 0 + }, + "元素属性": { + "元素类型": "NONE", + "元素克制额外伤害": 50 + }, + "特殊属性": { + "控制抗性": 0, + "伤害反弹": 0, + "死亡免疫": false, + "狂暴阈值": 0.3, + "狂暴状态伤害倍数": 1.5 + }, + "特殊机制开关": { + "启用伤害反弹机制": false, + "启用狂暴模式机制": false, + "启用死亡免疫机制": false, + "启用援助召唤机制": false, + "启用死亡重生机制": false + }, + "援助系统": { + "援助触发阈值": 0.2, + "援助召唤数量": 2, + "援助召唤间隔": 5 + }, + "品质系统": { + "宠物品质": "COMMON" + } } ], "巡逻宠物": [ @@ -876,41 +990,13 @@ "出战宠物": [ "1751716398095" ], - "稻草人配置": { - "已拥有稻草人类型": [ - "稻草人1", - "稻草人2", - "稻草人3" - ], - "稻草人展示类型": "稻草人3", - "稻草人昵称": "柚大青的稻草人", - "稻草人说的话": { - "第一句话": { - "内容": "233", - "颜色": "52dceeff" - }, - "第三句话": { - "内容": "888", - "颜色": "ac52ffff" - }, - "第二句话": { - "内容": "666", - "颜色": "80d5ffff" - }, - "第四句话": { - "内容": "哈哈哈", - "颜色": "f881ffff" - } - }, - "稻草人昵称颜色": "b38282ff" - }, "签到历史": { "2025年07月12日21时05分47秒": "金币249 经验75 土豆x3", "2025年07月13日07时26分04秒": "金币302 经验63 土豆x5 小麦x3" }, "在线礼包": { "当前日期": "2025-07-20", - "今日在线时长": 999999.271807432174683, + "今日在线时长": 999999.2718074322, "已领取礼包": [], "登录时间": 1753003043.7163484 }, @@ -923,10 +1009,10 @@ "领取时间": "2025-07-20 20:21:04" }, "体力系统": { - "当前体力值": 20, - "最大体力值": 20, + "当前体力值": 24, + "最大体力值": 25, "上次刷新时间": "2025-07-20", - "上次恢复时间": 1753003043.7066433 + "上次恢复时间": 1753018615.8492067 }, "道具背包": [ { @@ -935,11 +1021,39 @@ }, { "name": "农家肥", - "count": 5 + "count": 4 }, { "name": "水桶", - "count": 4 + "count": 3 + }, + { + "name": "荆棘护甲", + "count": 1 + }, + { + "name": "时运-镰刀", + "count": 1 + }, + { + "name": "精准采集-镰刀", + "count": 1 + }, + { + "name": "金坷垃", + "count": 1 + }, + { + "name": "水壶", + "count": 1 + }, + { + "name": "杀虫剂", + "count": 1 + }, + { + "name": "除草剂", + "count": 1 } ], "玩家小卖部": [], @@ -959,5 +1073,33 @@ "当前生命值": 102, "高度": 20, "上次护理时间": 1753014929 + }, + "稻草人配置": { + "已拥有稻草人类型": [ + "稻草人1", + "稻草人2", + "稻草人3" + ], + "稻草人展示类型": "稻草人2", + "稻草人昵称": "稻草人", + "稻草人说的话": { + "第一句话": { + "内容": "第一句话", + "颜色": "52dceeff" + }, + "第二句话": { + "内容": "第二句话", + "颜色": "80d5ffff" + }, + "第三句话": { + "内容": "第三句话", + "颜色": "ac52ffff" + }, + "第四句话": { + "内容": "第四句话", + "颜色": "f881ffff" + } + }, + "稻草人昵称颜色": "b38282ff" } } \ No newline at end of file