Files
Sprout-Farm/SproutFarm-Backend/TCPGameServer.py
2025-09-24 15:06:30 +08:00

9594 lines
417 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from TCPServer import TCPServer
import time
import json
import os
import glob
import threading
import datetime
import re
import random
#导入服务器外置插件模块
from module.SMYMongoDBAPI import SMYMongoDBAPI #导入MongoDB数据库模块
from module.QQEmailSendAPI import EmailVerification#导入QQ邮箱发送模块
from module import ConsoleCommandsAPI #导入控制台命令API模块
from module.SpecialFarm import SpecialFarmManager #导入特殊农场管理系统
from module import WSRemoteCmdApi #导入WebSocket远程命令API
from module.NetworkCore import MessageHandler
from module.WisdomTreeSystem import WisdomTreeMixin #导入网络通信核心模块
"""
萌芽农场TCP游戏服务器
====================================================================
- 协议TCP长连接
- 数据格式JSON消息
- 消息类型:请求/响应模式
====================================================================
"""
server_host: str = "0.0.0.0"
server_port: int = 7070
buffer_size: int = 4096
server_version: str = "2.2.0"
class TCPGameServer(WisdomTreeMixin, TCPServer):
"""
萌芽农场TCP游戏服务器
"""
#==========================初始化和生命周期管理==========================
#初始化操作
def __init__(self, server_host=server_host, server_port=server_port, buffer_size=buffer_size):
super().__init__(server_host, server_port, buffer_size)
# 基础数据存储
self.user_data = {} # 存储用户相关数据
self.crop_timer = None # 作物生长计时器
self.weed_timer = None # 杂草生长计时器
# 配置文件目录
self.config_dir = "config" # 配置文件存储目录
# 初始化MongoDB API优先使用MongoDB失败则使用JSON文件
self._init_mongodb_api()
# 初始化杂草系统配置
self._init_weed_settings()
# 禁用父类的日志输出,避免重复
self._setup_game_server_logging()
# 数据缓存
self.crop_data_cache = None
self.crop_data_cache_time = 0
self.cache_expire_duration = 300 # 缓存过期时间5分钟
# 偷菜免被发现临时计数器 {玩家名: {目标玩家名: 剩余免被发现次数}}
self.steal_immunity_counters = {}
# 初始化消息处理器
self.message_handler = MessageHandler(self)
self.log('INFO', f"萌芽农场TCP游戏服务器初始化完成 - 版本: {server_version}", 'SERVER')
# 清理配置缓存,确保使用最新的配置数据
self._clear_config_cache()
# 初始化特殊农场管理系统
self._init_special_farm_manager()
# 初始化WebSocket远程命令API
self._init_websocket_remote_api()
# 启动定时器
self.start_crop_growth_timer()
self.start_weed_growth_timer()
self.start_wisdom_tree_health_decay_timer()
self.start_verification_code_cleanup_timer()
self.start_offline_crop_update_timer()
#初始化MongoDB API
def _init_mongodb_api(self):
try:
# 根据配置决定使用测试环境还是生产环境
# 检查是否在Docker容器中或生产环境
import os
if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true':
environment = "production"
else:
environment = "test"
# 保存环境信息供其他组件使用
self.environment = environment
self.mongo_api = SMYMongoDBAPI(environment)
if self.mongo_api.is_connected():
self.use_mongodb = True
self.log('INFO', f"MongoDB API初始化成功 [{environment}]", 'SERVER')
else:
self.use_mongodb = False
self.mongo_api = None
self.log('WARNING', "MongoDB连接失败将使用JSON配置文件", 'SERVER')
except Exception as e:
self.use_mongodb = False
self.mongo_api = None
self.log('ERROR', f"MongoDB API初始化异常: {e}将使用JSON配置文件", 'SERVER')
#初始化杂草系统配置
def _init_weed_settings(self):
# 杂草生长相关配置
self.weed_check_interval = 86400 # 杂草检查间隔24小时
self.offline_threshold_days = 3 # 离线多少天后开始长杂草
self.max_weeds_per_check = 3 # 每次检查时最多长多少个杂草
self.weed_growth_probability = 0.3 # 每个空地长杂草的概率30%
self.last_weed_check_time = time.time() # 上次检查杂草的时间
#初始化特殊农场管理系统
def _init_special_farm_manager(self):
try:
# 使用自动环境检测,确保与游戏服务器环境一致
self.special_farm_manager = SpecialFarmManager()
# 启动特殊农场定时任务
self.special_farm_manager.start_scheduler()
self.log('INFO', f"特殊农场管理系统初始化完成 - 环境: {self.special_farm_manager.environment}", 'SERVER')
except Exception as e:
self.log('ERROR', f"特殊农场管理系统初始化失败: {str(e)}", 'SERVER')
self.special_farm_manager = None
#初始化WebSocket远程命令API
def _init_websocket_remote_api(self):
try:
# 创建WebSocket远程命令API实例
ws_host = "0.0.0.0"
ws_port = 7071
auth_key = "mengya2024" # 可以从配置文件读取
self.ws_remote_api = WSRemoteCmdApi(
game_server=self,
host=ws_host,
port=ws_port,
auth_key=auth_key
)
# 启动WebSocket服务器
self.ws_remote_api.start_server()
self.log('INFO', f"WebSocket远程命令API初始化完成 - ws://{ws_host}:{ws_port}", 'SERVER')
except Exception as e:
self.log('ERROR', f"WebSocket远程命令API初始化失败: {str(e)}", 'SERVER')
self.ws_remote_api = None
#设置游戏服务器日志配置
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()
except Exception as e:
self.log('ERROR', f"作物生长更新时出错: {str(e)}", 'SERVER')
# 创建下一个计时器
self.crop_timer = threading.Timer(1.0, self.start_crop_growth_timer)
self.crop_timer.daemon = True
self.crop_timer.start()
#启动杂草生长计时器
def start_weed_growth_timer(self):
try:
current_time = time.time()
# 检查是否到了杂草检查时间
if current_time - self.last_weed_check_time >= self.weed_check_interval:
self.check_and_grow_weeds()
self.last_weed_check_time = current_time
except Exception as e:
self.log('ERROR', f"杂草生长检查时出错: {str(e)}", 'SERVER')
# 创建下一个杂草检查计时器(每小时检查一次是否到时间)
self.weed_timer = threading.Timer(3600, self.start_weed_growth_timer) # 每小时检查一次
self.weed_timer.daemon = True
self.weed_timer.start()
#启动智慧树生命值衰减计时器
def start_wisdom_tree_health_decay_timer(self):
try:
self.check_wisdom_tree_health_decay()
except Exception as e:
self.log('ERROR', f"智慧树生命值衰减检查时出错: {str(e)}", 'SERVER')
# 创建下一个智慧树衰减检查计时器(每天检查一次)
self.wisdom_tree_decay_timer = threading.Timer(86400, self.start_wisdom_tree_health_decay_timer) # 每24小时检查一次
self.wisdom_tree_decay_timer.daemon = True
self.wisdom_tree_decay_timer.start()
#启动验证码清理定时器
def start_verification_code_cleanup_timer(self):
try:
EmailVerification.clean_expired_codes()
self.log('INFO', "验证码清理完成", 'SERVER')
except Exception as e:
self.log('ERROR', f"验证码清理时出错: {str(e)}", 'SERVER')
# 创建下一个验证码清理计时器每30分钟检查一次
self.verification_cleanup_timer = threading.Timer(1800, self.start_verification_code_cleanup_timer) # 每30分钟清理一次
self.verification_cleanup_timer.daemon = True
self.verification_cleanup_timer.start()
#启动离线玩家作物更新定时器
def start_offline_crop_update_timer(self):
try:
self.update_offline_players_crops()
except Exception as e:
self.log('ERROR', f"离线玩家作物更新时出错: {str(e)}", 'SERVER')
# 创建下一个离线作物更新计时器每1分钟检查一次
self.offline_crop_timer = threading.Timer(60, self.start_offline_crop_update_timer) # 每1分钟更新一次
self.offline_crop_timer.daemon = True
self.offline_crop_timer.start()
#获取服务器统计信息
def get_server_stats(self):
online_players = len([cid for cid in self.user_data if self.user_data[cid].get("logged_in", False)])
return {
"online_players": online_players,
"total_connections": len(self.clients)
}
#停止服务器
def stop(self):
self.log('INFO', "正在停止服务器...", 'SERVER')
# 停止作物生长计时器
if self.crop_timer:
self.crop_timer.cancel()
self.crop_timer = None
self.log('INFO', "作物生长计时器已停止", 'SERVER')
# 停止杂草生长计时器
if hasattr(self, 'weed_timer') and self.weed_timer:
self.weed_timer.cancel()
self.weed_timer = None
self.log('INFO', "杂草生长计时器已停止", 'SERVER')
# 停止智慧树生命值衰减计时器
if hasattr(self, 'wisdom_tree_decay_timer') and self.wisdom_tree_decay_timer:
self.wisdom_tree_decay_timer.cancel()
self.wisdom_tree_decay_timer = None
self.log('INFO', "智慧树生命值衰减计时器已停止", 'SERVER')
# 停止验证码清理定时器
if hasattr(self, 'verification_cleanup_timer') and self.verification_cleanup_timer:
self.verification_cleanup_timer.cancel()
self.verification_cleanup_timer = None
self.log('INFO', "验证码清理定时器已停止", 'SERVER')
# 停止离线作物更新定时器
if hasattr(self, 'offline_crop_timer') and self.offline_crop_timer:
self.offline_crop_timer.cancel()
self.offline_crop_timer = None
self.log('INFO', "离线作物更新定时器已停止", 'SERVER')
# 停止特殊农场管理系统
if hasattr(self, 'special_farm_manager') and self.special_farm_manager:
try:
# 停止特殊农场定时任务
self.special_farm_manager.stop_scheduler()
# 清理特殊农场管理器引用
self.special_farm_manager = None
self.log('INFO', "特殊农场管理系统已停止", 'SERVER')
except Exception as e:
self.log('ERROR', f"停止特殊农场管理系统时出错: {str(e)}", 'SERVER')
# 停止WebSocket远程命令API服务器
if hasattr(self, 'ws_remote_api') and self.ws_remote_api:
try:
self.ws_remote_api.stop_server()
self.ws_remote_api = None
self.log('INFO', "WebSocket远程命令API服务器已停止", 'SERVER')
except Exception as e:
self.log('ERROR', f"停止WebSocket远程命令API服务器时出错: {str(e)}", 'SERVER')
# 显示服务器统计信息
stats = self.get_server_stats()
self.log('INFO', f"服务器统计 - 在线玩家: {stats['online_players']}, 总连接: {stats['total_connections']}", 'SERVER')
# 调用父类方法完成实际停止
super().stop()
#==========================初始化和生命周期管理==========================
#==========================客户端连接管理==========================
#移除客户端
def _remove_client(self, client_id):
if client_id in self.clients:
username = self.user_data.get(client_id, {}).get("username", client_id)
# 处理已登录用户的离开
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)
self.log('INFO', f"用户 {username} 登出", 'SERVER')
# 清理用户数据
if client_id in self.user_data:
# 清理偷菜免被发现计数器
self._clear_player_steal_immunity(username)
del self.user_data[client_id]
self.log('INFO', f"用户 {username} 已离开游戏", 'SERVER')
# 先调用父类方法移除客户端,避免递归调用
super()._remove_client(client_id)
# 在客户端已移除后再广播用户离开消息,避免向已断开的客户端发送消息
self.broadcast({
"type": "user_left",
"user_id": client_id,
"timestamp": time.time(),
"remaining_users": len(self.clients)
})
else:
# 如果客户端不在列表中,直接调用父类方法
super()._remove_client(client_id)
#==========================客户端连接管理==========================
#==========================验证和检查方法==========================
#检查用户是否已登录的通用方法
def _check_user_logged_in(self, client_id, action_name, action_type=None):
if client_id not in self.user_data or not self.user_data[client_id].get("logged_in", False):
self.log('WARNING', f"未登录用户 {client_id} 尝试{action_name}", 'SERVER')
response = {
"success": False,
"message": "您需要先登录才能执行此操作"
}
if action_type:
response["type"] = "action_response"
response["action_type"] = action_type
else:
response["type"] = f"{action_name}_response"
return False, response
return True, None
#==========================验证和检查方法==========================
#=================================数据管理方法====================================
#加载玩家数据
def load_player_data(self, account_id):
try:
if not self.use_mongodb or not self.mongo_api:
self.log('ERROR', 'MongoDB未配置或不可用无法加载玩家数据', 'SERVER')
return None
player_data = self.mongo_api.get_player_data(account_id)
if player_data:
return player_data
else:
self.log('DEBUG', f"MongoDB中未找到玩家 {account_id} 的数据", 'SERVER')
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):
try:
if not self.use_mongodb or not self.mongo_api:
self.log('ERROR', 'MongoDB未配置或不可用无法保存玩家数据', 'SERVER')
return False
success = self.mongo_api.save_player_data(account_id, player_data)
if success:
return True
else:
self.log('ERROR', f"MongoDB保存失败: {account_id}", 'SERVER')
return False
except Exception as e:
self.log('ERROR', f"保存玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER')
return False
#加载玩家数据
def _load_player_data_with_check(self, client_id, action_type=None):
username = self.user_data[client_id]["username"]
player_data = self.load_player_data(username)
if not player_data:
self.log('ERROR', f"无法加载玩家 {username} 的数据", 'SERVER')
response = {
"success": False,
"message": "无法加载玩家数据"
}
if action_type:
response["type"] = "action_response"
response["action_type"] = action_type
else:
response["type"] = "data_response"
return None, username, response
return player_data, username, None
#加载作物配置数据(优化版本)
def _clear_config_cache(self):
self.crop_data_cache = None
self.crop_data_cache_time = 0
self.log('INFO', "配置缓存已清理", 'SERVER')
#加载作物配置数据(优化版本)
def _load_crop_data(self):
current_time = time.time()
# 检查缓存是否有效
if (self.crop_data_cache is not None and
current_time - self.crop_data_cache_time < self.cache_expire_duration):
return self.crop_data_cache
# 缓存过期或不存在,重新加载
if not self.use_mongodb or not self.mongo_api:
self.log('ERROR', 'MongoDB未配置或不可用无法加载作物配置数据', 'SERVER')
return {}
try:
crop_data = self.mongo_api.get_crop_data_config()
if crop_data:
self.crop_data_cache = crop_data
self.crop_data_cache_time = current_time
self.log('INFO', "成功从MongoDB加载作物数据配置", 'SERVER')
return self.crop_data_cache
else:
self.log('ERROR', "MongoDB中未找到作物数据配置", 'SERVER')
return {}
except Exception as e:
self.log('ERROR', f"从MongoDB加载作物数据失败: {str(e)}", 'SERVER')
return {}
#更新玩家登录时间
def _update_player_logout_time(self, client_id, username):
login_timestamp = self.user_data[client_id].get("login_timestamp", time.time())
play_time_seconds = int(time.time() - login_timestamp)
# 清除访问状态
self.user_data[client_id]["visiting_mode"] = False
self.user_data[client_id]["visiting_target"] = ""
# 加载和更新玩家数据
player_data = self.load_player_data(username)
if player_data:
# 更新总游玩时间
self._update_total_play_time(player_data, play_time_seconds)
# 注意:在线礼包时间累计现在由新系统管理,此处不再需要更新旧格式
self.save_player_data(username, player_data)
self.log('INFO', f"用户 {username} 本次游玩时间: {play_time_seconds} 秒,总游玩时间: {player_data['总游玩时间']}", 'SERVER')
#更新总游玩时间
def _update_total_play_time(self, player_data, play_time_seconds):
total_time_str = player_data.get("总游玩时间", "0时0分0秒")
time_parts = re.match(r"(?:(\d+)时)?(?:(\d+)分)?(?:(\d+)秒)?", total_time_str)
if time_parts:
hours = int(time_parts.group(1) or 0)
minutes = int(time_parts.group(2) or 0)
seconds = int(time_parts.group(3) or 0)
# 计算新的总游玩时间
total_seconds = hours * 3600 + minutes * 60 + seconds + play_time_seconds
new_hours = total_seconds // 3600
new_minutes = (total_seconds % 3600) // 60
new_seconds = total_seconds % 60
# 更新总游玩时间
player_data["总游玩时间"] = f"{new_hours}{new_minutes}{new_seconds}"
# 检查玩家是否享受新玩家注册奖励
def _is_new_player_bonus_active(self, player_data):
register_time_str = player_data.get("注册时间", "")
# 如果没有注册时间或者是默认的老玩家时间,则不享受奖励
if not register_time_str or register_time_str == "2025年05月21日15时00分00秒":
return False
try:
# 解析注册时间
register_time = datetime.datetime.strptime(register_time_str, "%Y年%m月%d%H时%M分%S秒")
current_time = datetime.datetime.now()
# 计算注册天数
time_diff = current_time - register_time
days_since_register = time_diff.total_seconds() / 86400 # 转换为天数
# 3天内享受新玩家奖励
if days_since_register <= 3:
return True
else:
return False
except ValueError as e:
self.log('WARNING', f"解析注册时间格式错误: {register_time_str}, 错误: {str(e)}", 'SERVER')
return False
#更新离线玩家的作物生长
def update_offline_players_crops(self):
try:
if not self.use_mongodb or not self.mongo_api:
self.log('WARNING', 'MongoDB未配置或不可用无法更新离线玩家作物', 'SERVER')
return
# 获取需要排除的玩家列表(在线玩家 + 被访问的玩家)
exclude_players = []
# 添加在线玩家
for client_id, user_info in self.user_data.items():
if user_info.get("logged_in", False) and user_info.get("username"):
exclude_players.append(user_info["username"])
# 添加被访问的玩家(避免访问模式下的重复更新)
visited_players = set()
for client_id, user_info in self.user_data.items():
if (user_info.get("logged_in", False) and
user_info.get("visiting_mode", False) and
user_info.get("visiting_target")):
visited_players.add(user_info["visiting_target"])
# 将被访问的玩家也加入排除列表
exclude_players.extend(list(visited_players))
# 直接调用优化后的批量更新方法,传入排除玩家列表
# 离线更新间隔为60秒所以每次更新应该增长60秒
updated_count = self.mongo_api.batch_update_offline_players_crops(
growth_multiplier=60.0,
exclude_online_players=exclude_players
)
if updated_count > 0:
self.log('INFO', f"成功更新了 {updated_count} 个离线玩家的作物生长(排除了 {len(exclude_players)} 个在线/被访问玩家)", 'SERVER')
else:
self.log('DEBUG', "没有离线玩家的作物需要更新", 'SERVER')
except Exception as e:
self.log('ERROR', f"更新离线玩家作物时出错: {str(e)}", 'SERVER')
#=================================数据管理方法====================================
#================================作物系统管理=========================================
#作物生长更新系统
def update_crops_growth(self):
# 收集所有需要更新的玩家(在线玩家 + 被访问的玩家)
players_to_update = set()
# 添加在线玩家
for client_id, user_info in self.user_data.items():
if user_info.get("logged_in", False) and user_info.get("username"):
players_to_update.add(user_info.get("username"))
# 添加被访问的玩家(即使他们不在线)
for client_id, user_info in self.user_data.items():
if user_info.get("logged_in", False) and user_info.get("visiting_mode", False):
visiting_target = user_info.get("visiting_target", "")
if visiting_target:
players_to_update.add(visiting_target)
# 更新所有需要更新的玩家的作物
for username in players_to_update:
try:
player_data = self.load_player_data(username)
if not player_data:
continue
if self.update_player_crops(player_data, username):
# 确保数据保存成功后才推送更新
if self.save_player_data(username, player_data):
self._push_crop_update_to_player(username, player_data)
else:
self.log('ERROR', f"保存玩家 {username} 数据失败,跳过推送更新", 'SERVER')
except Exception as e:
self.log('ERROR', f"更新玩家 {username} 作物时出错: {str(e)}", 'SERVER')
#更新单个玩家的作物
def update_player_crops(self, player_data, account_id):
growth_updated = False
for farm_lot in player_data.get("农场土地", []):
if (farm_lot.get("crop_type") and farm_lot.get("is_planted") and
not farm_lot.get("is_dead") and farm_lot["grow_time"] < farm_lot["max_grow_time"]):
# 计算生长速度增量(累加方式)
growth_increase = 1 # 基础生长速度每次更新增长1秒
# 新玩家注册奖励注册后3天内额外增加9秒总共10倍速度
if self._is_new_player_bonus_active(player_data):
growth_increase += 9
# 土地等级影响 - 根据不同等级额外增加生长速度
land_level = farm_lot.get("土地等级", 0)
land_speed_bonus = {
0: 0, # 默认土地:无额外加成
1: 1, # 黄土地:额外+1秒总共2倍速
2: 3, # 红土地:额外+3秒总共4倍速
3: 5, # 紫土地:额外+5秒总共6倍速
4: 9 # 黑土地:额外+9秒总共10倍速
}
growth_increase += land_speed_bonus.get(land_level, 0)
# 施肥影响 - 支持不同类型的道具施肥
if farm_lot.get("已施肥", False) and "施肥时间" in farm_lot:
fertilize_time = farm_lot.get("施肥时间", 0)
current_time = time.time()
# 获取施肥类型和对应的持续时间、倍数
fertilize_type = farm_lot.get("施肥类型", "普通施肥")
fertilize_duration = farm_lot.get("施肥持续时间", 600) # 默认10分钟
fertilize_bonus = farm_lot.get("施肥加成", 1) # 默认额外+1秒
if current_time - fertilize_time <= fertilize_duration:
# 施肥效果仍在有效期内,累加施肥加成
growth_increase += fertilize_bonus
else:
# 施肥效果过期,清除施肥状态
farm_lot["已施肥"] = False
if "施肥时间" in farm_lot:
del farm_lot["施肥时间"]
if "施肥类型" in farm_lot:
del farm_lot["施肥类型"]
if "施肥倍数" in farm_lot:
del farm_lot["施肥倍数"]
if "施肥持续时间" in farm_lot:
del farm_lot["施肥持续时间"]
if "施肥加成" in farm_lot:
del farm_lot["施肥加成"]
# 确保最小增长量为1
if growth_increase < 1:
growth_increase = 1
farm_lot["grow_time"] += growth_increase
growth_updated = True
return growth_updated
#向在线玩家推送作物生长更新
def _push_crop_update_to_player(self, account_id, player_data):
client_id = self._find_client_by_username(account_id)
if client_id:
visiting_mode = self.user_data[client_id].get("visiting_mode", False)
visiting_target = self.user_data[client_id].get("visiting_target", "")
if visiting_mode and visiting_target:
self._send_visiting_update(client_id, visiting_target)
else:
self._send_normal_update(client_id, player_data)
# 检查是否有其他玩家正在访问这个玩家的农场
self._push_update_to_visitors(account_id, player_data)
#根据用户名查找客户端ID
def _find_client_by_username(self, username):
for cid, user_info in self.user_data.items():
if user_info.get("username") == username and user_info.get("logged_in", False):
return cid
return None
#发送访问模式的更新
def _send_visiting_update(self, client_id, visiting_target):
target_player_data = self.load_player_data(visiting_target)
if target_player_data:
target_client_id = self._find_client_by_username(visiting_target)
update_message = {
"type": "crop_update",
"农场土地": target_player_data.get("农场土地", []),
"timestamp": time.time(),
"is_visiting": True,
"visited_player": visiting_target,
"target_online": target_client_id is not None
}
self.send_data(client_id, update_message)
#发送正常模式的更新
def _send_normal_update(self, client_id, player_data):
update_message = {
"type": "crop_update",
"农场土地": player_data.get("农场土地", []),
"timestamp": time.time(),
"is_visiting": False
}
self.send_data(client_id, update_message)
#向正在访问某个玩家农场的其他玩家推送更新
def _push_update_to_visitors(self, target_username, target_player_data):
for visitor_client_id, visitor_info in self.user_data.items():
if not visitor_info.get("logged_in", False):
continue
visiting_mode = visitor_info.get("visiting_mode", False)
visiting_target = visitor_info.get("visiting_target", "")
# 如果这个玩家正在访问目标玩家的农场,发送更新
if visiting_mode and visiting_target == target_username:
target_client_id = self._find_client_by_username(target_username)
update_message = {
"type": "crop_update",
"农场土地": target_player_data.get("农场土地", []),
"timestamp": time.time(),
"is_visiting": True,
"visited_player": target_username,
"target_online": target_client_id is not None
}
self.send_data(visitor_client_id, update_message)
self.log('DEBUG', f"向访问者 {visitor_info.get('username', 'unknown')} 推送 {target_username} 的农场更新", 'SERVER')
#================================作物系统管理=========================================
# =======================服务端与客户端通信注册==========================================
#服务端与客户端通用消息处理-这个是服务端与客户端通信的核心中的核心
def _handle_message(self, client_id, message):
# 使用NetworkCore模块的MessageHandler处理消息
return self.message_handler.handle_message(client_id, message)
# =======================服务端与客户端通信注册==========================================
#==========================用户认证相关==========================
#处理问候消息
def _handle_greeting(self, client_id, message):
content = message.get("content", "")
self.log('INFO', f"收到来自客户端 {client_id} 的问候: {content}", 'CLIENT')
# 保存用户会话信息
self.user_data[client_id] = {
"last_active": time.time(),
"messages_count": 0
}
# 回复欢迎消息
response = {
"type": "greeting_response",
"content": f"欢迎 {client_id}!",
"server_time": time.time(),
"active_users": len(self.clients)
}
# 通知其他用户有新用户加入
self.broadcast(
{
"type": "user_joined",
"user_id": client_id,
"timestamp": time.time(),
"active_users": len(self.clients)
},
exclude=[client_id]
)
self.log('INFO', f"用户 {client_id} 已加入游戏", 'SERVER')
return self.send_data(client_id, response)
#处理玩家登录
def _handle_login(self, client_id, message):
username = message.get("username", "")
password = message.get("password", "")
client_version = message.get("client_version", "")
# 验证客户端版本
version_valid, version_response = self._check_client_version(client_version, f"用户 {username} 登录")
if not version_valid:
version_response["type"] = "login_response"
version_response["status"] = "failed"
return self.send_data(client_id, version_response)
# 读取玩家数据
player_data = self.load_player_data(username)
if player_data and player_data.get("玩家密码") == password:
# 检查禁用系统
ban_system = player_data.get("禁用系统", {})
is_banned = ban_system.get("是否被禁止登录", False)
if is_banned:
# 检查禁止登录是否已过期
ban_end_time = ban_system.get("禁止登录截止", "")
if ban_end_time:
try:
end_datetime = datetime.datetime.strptime(ban_end_time, "%Y-%m-%d %H:%M:%S")
current_datetime = datetime.datetime.now()
if current_datetime >= end_datetime:
# 禁止登录已过期,解除禁止
player_data["禁用系统"] = {
"是否被禁止登录": False,
"禁止登录原因": "",
"禁止登录开始": "",
"禁止登录截止": ""
}
self.save_player_data(username, player_data)
self.log('INFO', f"用户 {username} 禁止登录已过期,自动解除", 'SERVER')
else:
# 仍在禁止期内
ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录")
self.log('WARNING', f"用户 {username} 登录失败: 账号被禁止登录", 'SERVER')
response = {
"type": "login_response",
"status": "banned",
"message": ban_reason,
"ban_end_time": ban_end_time
}
return self.send_data(client_id, response)
except Exception as e:
self.log('ERROR', f"解析禁止登录时间出错: {e}", 'SERVER')
# 如果解析出错,仍然禁止登录
ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录")
response = {
"type": "login_response",
"status": "banned",
"message": ban_reason
}
return self.send_data(client_id, response)
else:
# 永久禁止或没有截止时间
ban_reason = ban_system.get("禁止登录原因", "您已被管理员禁止登录")
self.log('WARNING', f"用户 {username} 登录失败: 账号被永久禁止登录", 'SERVER')
response = {
"type": "login_response",
"status": "banned",
"message": ban_reason
}
return self.send_data(client_id, response)
# 登录成功
self.log('INFO', f"用户 {username} 登录成功", 'SERVER')
# 更新最后登录时间
current_time = datetime.datetime.now()
player_data["最后登录时间"] = current_time.strftime("%Y年%m月%d%H时%M分%S秒")
# 检查并更新体力值
stamina_updated = self._check_and_update_stamina(player_data)
if stamina_updated:
stamina_system = player_data.get("体力系统", {})
current_stamina = stamina_system.get("当前体力值", 20)
self.log('INFO', f"玩家 {username} 体力值已更新:{current_stamina}", 'SERVER')
# 检查并更新每日点赞次数
likes_updated = self._check_and_update_daily_likes(player_data)
if likes_updated:
like_system = player_data.get("点赞系统", {})
remaining_likes = like_system.get("今日剩余点赞次数", 10)
self.log('INFO', f"玩家 {username} 每日点赞次数已重置:{remaining_likes}", 'SERVER')
# 检查并清理在线礼包历史数据
self._cleanup_online_gift_history(player_data )
# 检查并清理新手礼包历史数据
self._cleanup_new_player_gift_history(player_data)
# 检查并清理体力系统历史数据
self._cleanup_stamina_system_history(player_data)
# 检查并更新已存在玩家的注册时间
self._check_and_update_register_time(player_data, username)
# 检查并修复智慧树配置
self._check_and_fix_wisdom_tree_config(player_data, username)
# 注意:在线礼包数据已改为中文系统管理,不再需要初始化英文格式数据
# 保存用户会话信息
self.user_data[client_id] = {
"username": username,
"last_active": time.time(),
"messages_count": 0,
"logged_in": True,
"login_timestamp": time.time()
}
# 保存更新后的玩家数据
self.save_player_data(username, player_data)
# 发送初始数据
self._send_initial_login_data(client_id, player_data)
# 返回登录成功消息,转换巡逻宠物和出战宠物数据
response_player_data = player_data.copy()
response_player_data["巡逻宠物"] = self._convert_patrol_pets_to_full_data(player_data)
response_player_data["出战宠物"] = self._convert_battle_pets_to_full_data(player_data)
# 获取点赞系统信息
like_system = player_data.get("点赞系统", {})
remaining_likes = like_system.get("今日剩余点赞次数", 10)
response = {
"type": "login_response",
"status": "success",
"message": "登录成功",
"player_data": response_player_data,
"remaining_likes": remaining_likes
}
else:
# 登录失败
self.log('WARNING', f"用户 {username} 登录失败: 账号或密码错误", 'SERVER')
response = {
"type": "login_response",
"status": "failed",
"message": "账号或密码错误"
}
return self.send_data(client_id, response)
#辅助函数-发送登录后初始数据
def _send_initial_login_data(self, client_id, player_data ):
# 立即向客户端发送一次作物状态
farm_lots = player_data.get("农场土地", [])
initial_crop_update = {
"type": "crop_update",
"农场土地": farm_lots,
"timestamp": time.time()
}
self.send_data(client_id, initial_crop_update)
# 发送最新的作物数据配置
crop_data = self._load_crop_data()
if crop_data:
crop_data_message = {
"type": "crop_data_response",
"success": True,
"crop_data": crop_data
}
self.send_data(client_id, crop_data_message)
self.log('INFO', f"已向登录用户发送作物数据配置", 'SERVER')
# 发送最新的道具配置数据
item_config = self._load_item_config()
if item_config:
item_config_message = {
"type": "item_config_response",
"success": True,
"item_config": item_config
}
self.send_data(client_id, item_config_message)
self.log('INFO', f"已向登录用户发送道具配置数据,道具种类:{len(item_config)}", 'SERVER')
#处理注册消息
def _handle_register(self, client_id, message):
"""处理注册消息"""
username = message.get("username", "")
password = message.get("password", "")
farm_name = message.get("农场名称", "")
player_name = message.get("玩家昵称", "")
verification_code = message.get("verification_code", "")
client_version = message.get("client_version", "")
# 验证客户端版本
version_valid, version_response = self._check_client_version(client_version, f"用户 {username} 注册")
if not version_valid:
version_response["type"] = "register_response"
version_response["status"] = "failed"
return self.send_data(client_id, version_response)
# 验证必填字段
if not username or not password:
return self._send_register_error(client_id, "用户名或密码不能为空")
# 验证用户名是否是QQ号
if not self._validate_qq_number(username):
return self._send_register_error(client_id, "用户名必须是5-12位的QQ号码")
# 验证验证码
if verification_code:
success, verify_message = EmailVerification.verify_code(username, verification_code, "register")
if not success:
self.log('WARNING', f"QQ号 {username} 注册验证码验证失败: {verify_message}", 'SERVER')
return self._send_register_error(client_id, f"验证码错误: {verify_message}")
else:
self.log('INFO', f"QQ号 {username} 注册验证码验证成功", 'SERVER')
# 检查用户是否已存在
file_path = os.path.join("game_saves", f"{username}.json")
if os.path.exists(file_path):
return self._send_register_error(client_id, "该用户名已被注册")
# 创建新用户
return self._create_new_user(client_id, username, password, farm_name, player_name)
#检查客户端版本是否与服务端匹配
#创建新用户
#辅助函数-发送注册错误处理
def _send_register_error(self, client_id, message):
"""发送注册错误响应"""
self.log('WARNING', f"注册失败: {message}", 'SERVER')
return self.send_data(client_id, {
"type": "register_response",
"status": "failed",
"message": message
})
#辅助函数-创建新用户
def _create_new_user(self, client_id, username, password, farm_name, player_name):
"""创建新用户从MongoDB加载模板"""
try:
# 从MongoDB加载初始玩家数据模板
if not self.use_mongodb or not self.mongo_api:
return self._send_register_error(client_id, "MongoDB未配置或不可用无法注册新用户")
try:
player_data = self.mongo_api.get_initial_player_data_template()
if not player_data:
return self._send_register_error(client_id, "MongoDB中未找到初始玩家数据模板无法注册新用户")
self.log('INFO', "成功从MongoDB加载初始玩家数据模板", 'SERVER')
except Exception as e:
self.log('ERROR', f"从MongoDB加载初始玩家数据模板失败: {str(e)}", 'SERVER')
return self._send_register_error(client_id, f"加载初始玩家数据模板失败: {str(e)}")
# 更新玩家基本信息
current_time = datetime.datetime.now()
time_str = current_time.strftime("%Y年%m月%d%H时%M分%S秒")
player_data.update({
"玩家账号": username,
"玩家密码": password,
"农场名称": farm_name or "我的农场",
"玩家昵称": player_name or username,
"个人简介": "",
"最后登录时间": time_str,
"注册时间": time_str,
"总游玩时间": player_data.get("总游玩时间", "0时0分0秒")
})
# 确保必要字段存在
self._ensure_player_data_fields(player_data)
# 保存新用户数据到MongoDB
if not self.save_player_data(username, player_data):
return self._send_register_error(client_id, "保存用户数据失败,注册失败")
self.log('INFO', f"用户 {username} 注册成功,注册时间: {time_str}享受3天新玩家10倍生长速度奖励", 'SERVER')
return self.send_data(client_id, {
"type": "register_response",
"status": "success",
"message": "注册成功请登录游戏新玩家享受3天10倍作物生长速度奖励"
})
except Exception as e:
self.log('ERROR', f"注册用户 {username} 时出错: {str(e)}", 'SERVER')
return self._send_register_error(client_id, f"注册过程中出现错误: {str(e)}")
#辅助函数-确保玩家数据包含所有必要字段
def _ensure_player_data_fields(self, player_data):
"""确保玩家数据包含所有必要字段"""
# 确保农场地块存在
if "农场土地" not in player_data:
player_data["农场土地"] = []
for i in range(40):
player_data["农场土地"].append({
"crop_type": "",
"grow_time": 0,
"is_dead": False,
"is_diged": i < 20, # 默认开垦前20块地
"is_planted": False,
"max_grow_time": 5 if i >= 20 else 3,
"已浇水": False,
"已施肥": False,
"土地等级": 0
})
# 确保基本仓库存在
for field in ["种子仓库", "作物仓库", "道具背包", "宠物背包", "巡逻宠物", "出战宠物"]:
if field not in player_data:
player_data[field] = []
#辅助函数-客户端版本检查
def _check_client_version(self, client_version, action_name="操作"):
"""检查客户端版本是否与服务端匹配"""
if client_version != server_version:
self.log('WARNING', f"{action_name}失败: 版本不匹配 (客户端: {client_version}, 服务端: {server_version})", 'SERVER')
response = {
"success": False,
"message": f"版本不匹配!客户端版本: {client_version},\n 服务端版本: {server_version},请更新客户端"
}
return False, response
return True, None
#处理验证码请求
def _handle_verification_code_request(self, client_id, message):
"""处理验证码请求"""
qq_number = message.get("qq_number", "")
# 验证QQ号
if not self._validate_qq_number(qq_number):
return self.send_data(client_id, {
"type": "verification_code_response",
"success": False,
"message": "QQ号格式无效请输入5-12位数字"
})
# 生成验证码
verification_code = EmailVerification.generate_verification_code()
# 发送验证码邮件
success, send_message = EmailVerification.send_verification_email(qq_number, verification_code)
if success:
# 保存验证码(注册类型)
EmailVerification.save_verification_code(qq_number, verification_code, 300, "register")
self.log('INFO', f"已向QQ号 {qq_number} 发送注册验证码: {verification_code}", 'SERVER')
return self.send_data(client_id, {
"type": "verification_code_response",
"success": True,
"message": "验证码已发送到您的QQ邮箱请查收"
})
else:
self.log('ERROR', f"发送验证码失败: {send_message}", 'SERVER')
return self.send_data(client_id, {
"type": "verification_code_response",
"success": False,
"message": f"发送验证码失败: {send_message}"
})
#处理验证码验证
def _handle_verify_code(self, client_id, message):
"""处理验证码验证"""
qq_number = message.get("qq_number", "")
input_code = message.get("code", "")
if not input_code:
return self.send_data(client_id, {
"type": "verify_code_response",
"success": False,
"message": "验证码不能为空"
})
# 验证验证码
success, verify_message = EmailVerification.verify_code(qq_number, input_code)
if success:
self.log('INFO', f"QQ号 {qq_number} 的验证码验证成功", 'SERVER')
return self.send_data(client_id, {
"type": "verify_code_response",
"success": True,
"message": "验证成功"
})
else:
self.log('WARNING', f"QQ号 {qq_number} 的验证码验证失败: {verify_message}", 'SERVER')
return self.send_data(client_id, {
"type": "verify_code_response",
"success": False,
"message": verify_message
})
#验证QQ号格式
#处理忘记密码验证码请求
def _handle_forget_password_verification_code_request(self, client_id, message):
"""处理忘记密码验证码请求"""
qq_number = message.get("qq_number", "")
# 验证QQ号
if not self._validate_qq_number(qq_number):
return self.send_data(client_id, {
"type": "forget_password_verification_code_response",
"success": False,
"message": "QQ号格式无效请输入5-12位数字"
})
# 检查账号是否存在
player_data = self.load_player_data(qq_number)
if not player_data:
return self.send_data(client_id, {
"type": "forget_password_verification_code_response",
"success": False,
"message": "该账号不存在请检查QQ号是否正确"
})
# 生成验证码
verification_code = EmailVerification.generate_verification_code()
# 发送验证码邮件(专门用于密码重置)
success, send_message = EmailVerification.send_verification_email(qq_number, verification_code, "reset_password")
if success:
# 保存验证码(密码重置类型)
EmailVerification.save_verification_code(qq_number, verification_code, 300, "reset_password")
self.log('INFO', f"已向QQ号 {qq_number} 发送密码重置验证码: {verification_code}", 'SERVER')
return self.send_data(client_id, {
"type": "forget_password_verification_code_response",
"success": True,
"message": "密码重置验证码已发送到您的QQ邮箱请查收"
})
else:
self.log('ERROR', f"发送密码重置验证码失败: {send_message}", 'SERVER')
return self.send_data(client_id, {
"type": "forget_password_verification_code_response",
"success": False,
"message": f"发送验证码失败: {send_message}"
})
#处理重置密码请求
def _handle_reset_password_request(self, client_id, message):
"""处理重置密码请求"""
username = message.get("username", "")
new_password = message.get("new_password", "")
verification_code = message.get("verification_code", "")
# 验证必填字段
if not username or not new_password or not verification_code:
return self.send_data(client_id, {
"type": "reset_password_response",
"success": False,
"message": "用户名、新密码或验证码不能为空"
})
# 验证QQ号格式
if not self._validate_qq_number(username):
return self.send_data(client_id, {
"type": "reset_password_response",
"success": False,
"message": "用户名必须是5-12位的QQ号码"
})
# 检查账号是否存在
player_data = self.load_player_data(username)
if not player_data:
return self.send_data(client_id, {
"type": "reset_password_response",
"success": False,
"message": "该账号不存在请检查QQ号是否正确"
})
# 验证验证码(密码重置类型)
success, verify_message = EmailVerification.verify_code(username, verification_code, "reset_password")
if not success:
self.log('WARNING', f"QQ号 {username} 密码重置验证码验证失败: {verify_message}", 'SERVER')
return self.send_data(client_id, {
"type": "reset_password_response",
"success": False,
"message": f"验证码错误: {verify_message}"
})
else:
self.log('INFO', f"QQ号 {username} 密码重置验证码验证成功", 'SERVER')
# 更新密码
try:
player_data["玩家密码"] = new_password
# 保存到文件
self.save_player_data(username, player_data)
self.log('INFO', f"用户 {username} 密码重置成功", 'ACCOUNT')
return self.send_data(client_id, {
"type": "reset_password_response",
"success": True,
"message": "密码重置成功,请使用新密码登录"
})
except Exception as e:
self.log('ERROR', f"重置密码时出错: {str(e)}", 'ACCOUNT')
return self.send_data(client_id, {
"type": "reset_password_response",
"success": False,
"message": "密码重置失败,请稍后重试"
})
#辅助函数-验证QQ号格式
def _validate_qq_number(self, qq_number):
"""验证QQ号格式"""
return re.match(r'^\d{5,12}$', qq_number) is not None
#==========================用户认证相关==========================
#==========================收获作物处理==========================
#处理收获作物请求
def _handle_harvest_crop(self, client_id, message):
"""处理收获作物请求(优化版本)"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "收获作物", "harvest_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取当前操作用户的数据
current_player_data, current_username, response = self._load_player_data_with_check(client_id, "harvest_crop")
if not current_player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
target_username = message.get("target_username", "")
# 预加载作物配置数据(只加载一次)
crop_data = self._load_crop_data()
if not crop_data:
return self._send_action_error(client_id, "harvest_crop", "无法加载作物配置数据")
# 确定操作目标如果有target_username就是访问模式偷菜否则是自己的农场
if target_username and target_username != current_username:
# 访问模式:偷菜(收益给自己,清空目标玩家的作物)
target_player_data = self.load_player_data(target_username)
if not target_player_data:
return self._send_action_error(client_id, "harvest_crop", f"无法找到玩家 {target_username} 的数据")
# 验证地块索引
if lot_index < 0 or lot_index >= len(target_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "harvest_crop", "无效的地块索引")
target_lot = target_player_data["农场土地"][lot_index]
# 检查地块状态
if not target_lot.get("is_planted", False) or not target_lot.get("crop_type", ""):
return self._send_action_error(client_id, "harvest_crop", "此地块没有种植作物")
if target_lot.get("is_dead", False):
# 处理已死亡的作物(只清理,不给收益)
target_lot["is_planted"] = False
target_lot["crop_type"] = ""
target_lot["grow_time"] = 0
self.save_player_data(target_username, target_player_data)
self._push_crop_update_to_player(target_username, target_player_data)
return self.send_data(client_id, {
"type": "action_response",
"action_type": "harvest_crop",
"success": True,
"message": f"已帮助 {target_username} 清理死亡的作物",
"updated_data": {
"钱币": current_player_data["钱币"],
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"]
}
})
if target_lot["grow_time"] < target_lot["max_grow_time"]:
return self._send_action_error(client_id, "harvest_crop", "作物尚未成熟,无法偷菜")
# 处理偷菜
return self._process_steal_crop_optimized(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, crop_data)
else:
# 正常模式:收获自己的作物
# 验证地块索引
if lot_index < 0 or lot_index >= len(current_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "harvest_crop", "无效的地块索引")
lot = current_player_data["农场土地"][lot_index]
# 检查地块状态
if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
return self._send_action_error(client_id, "harvest_crop", "此地块没有种植作物")
if lot.get("is_dead", False):
# 处理已死亡的作物
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
self.save_player_data(current_username, current_player_data)
self._push_crop_update_to_player(current_username, current_player_data)
return self.send_data(client_id, {
"type": "action_response",
"action_type": "harvest_crop",
"success": True,
"message": "已铲除死亡的作物",
"updated_data": {
"钱币": current_player_data["钱币"],
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"]
}
})
if lot["grow_time"] < lot["max_grow_time"]:
return self._send_action_error(client_id, "harvest_crop", "作物尚未成熟")
# 处理正常收获
return self._process_harvest_optimized(client_id, current_player_data, current_username, lot, lot_index, crop_data)
#辅助函数-处理作物收获(优化版本)
def _process_harvest_optimized(self, client_id, player_data, username, lot, lot_index, crop_data):
"""处理作物收获逻辑(优化版本)"""
# 获取作物类型和基本信息
crop_type = lot["crop_type"]
crop_info = crop_data.get(crop_type, {})
# 检查是否为杂草类型(杂草不能收获,只能铲除)
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能收获,只能铲除!请使用铲除功能清理杂草。")
# 额外检查:如果作物收益为负数,也视为杂草
crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0)
if crop_income < 0:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能收获,只能铲除!请使用铲除功能清理杂草。")
# 获取作物经验
crop_exp = crop_info.get("经验", 10)
# 生成成熟物收获1-5个
import random
harvest_count = random.randint(1, 5)
# 10%概率获得1-2个该作物的种子
seed_reward = None
if random.random() <= 0.1:
seed_reward = {
"name": crop_type,
"count": random.randint(1, 2)
}
# 更新玩家经验
player_data["经验值"] += crop_exp
# 检查是否会获得成熟物
mature_name = crop_info.get("成熟物名称")
will_get_mature_item = mature_name is not None
mature_item_name = mature_name if mature_name and mature_name.strip() else crop_type
# 添加成熟物到作物仓库(如果允许)
if will_get_mature_item:
self._add_crop_to_warehouse_optimized(player_data, {"name": crop_type, "count": harvest_count}, crop_type, crop_info.get("品质", "普通"))
# 添加种子奖励到背包
if seed_reward:
self._add_seeds_to_bag_optimized(player_data, seed_reward, crop_info.get("品质", "普通"))
# 检查升级
level_up_experience = 100 * player_data["等级"]
if player_data["经验值"] >= level_up_experience:
player_data["等级"] += 1
player_data["经验值"] -= level_up_experience
self.log('INFO', f"玩家 {username} 升级到 {player_data['等级']}", 'SERVER')
# 清理地块(批量更新)
lot.update({
"is_planted": False,
"crop_type": "",
"grow_time": 0,
"已浇水": False,
"已施肥": False
})
# 清除施肥时间戳
if "施肥时间" in lot:
del lot["施肥时间"]
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
# 构建消息
if will_get_mature_item:
message = f"收获成功,获得 {mature_item_name} x{harvest_count}{crop_exp} 经验"
else:
message = f"收获成功,获得 {crop_exp} 经验({crop_type}无成熟物产出)"
if seed_reward:
message += f",额外获得 {seed_reward['name']} 种子 x{seed_reward['count']}"
self.log('INFO', f"玩家 {username} 从地块 {lot_index} 收获了作物,获得 {crop_type} x{harvest_count}{crop_exp} 经验" + (f",额外获得 {seed_reward['name']} 种子 x{seed_reward['count']}" if seed_reward else ""), 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "harvest_crop",
"success": True,
"message": message,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", []),
"作物仓库": player_data.get("作物仓库", [])
}
})
#辅助函数-处理偷菜逻辑(访问模式下收获其他玩家作物的操作)
def _process_steal_crop_optimized(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, crop_data):
"""处理偷菜逻辑(收益给当前玩家,清空目标玩家的作物)"""
# 偷菜体力值消耗
stamina_cost = 1
# 检查并更新当前玩家的体力值
self._check_and_update_stamina(current_player_data)
# 检查体力值是否足够
if not self._check_stamina_sufficient(current_player_data, stamina_cost):
return self._send_action_error(client_id, "harvest_crop", f"体力值不足,偷菜需要 {stamina_cost} 点体力,当前体力:{current_player_data.get('体力值', 0)}")
# 检查是否被巡逻宠物发现30%概率)
patrol_pets = target_player_data.get("巡逻宠物", [])
if patrol_pets and len(patrol_pets) > 0:
# 先检查是否有免被发现次数
immunity_count = self._get_steal_immunity_count(current_username, target_username)
if immunity_count > 0:
# 有免被发现次数,消耗一次
self._consume_steal_immunity(current_username, target_username)
self.log('INFO', f"玩家 {current_username} 使用免被发现次数偷菜 {target_username},剩余次数:{immunity_count - 1}", 'SERVER')
else:
# 30%概率被发现
import random
if random.random() <= 0.3:
# 被巡逻宠物发现了!
return self._handle_steal_caught_by_patrol(
client_id, current_player_data, current_username,
target_player_data, target_username, patrol_pets[0]
)
# 获取作物类型和基本信息
crop_type = target_lot["crop_type"]
crop_info = crop_data.get(crop_type, {})
# 检查是否为杂草类型(杂草不能偷取,只能铲除)
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能偷取,只能铲除!这是杂草,没有收益价值。")
# 额外检查:如果作物收益为负数,也视为杂草
crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0)
if crop_income < 0:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能偷取,只能铲除!这是杂草,没有收益价值。")
# 获取作物经验偷菜获得50%经验)
crop_exp = int(crop_info.get("经验", 10) * 0.5)
# 生成成熟物收获偷菜获得较少1-3个
import random
harvest_count = random.randint(1, 3)
# 10%概率获得1-2个该作物的种子偷菜也有机会获得种子
seed_reward = None
if random.random() <= 0.1:
seed_reward = {
"name": crop_type,
"count": random.randint(1, 2)
}
# 消耗当前玩家的体力值
stamina_success, stamina_message = self._consume_stamina(current_player_data, stamina_cost, "偷菜")
if not stamina_success:
return self._send_action_error(client_id, "harvest_crop", stamina_message)
# 更新当前玩家数据(获得经验)
current_player_data["经验值"] += crop_exp
# 检查是否会获得成熟物
mature_name = crop_info.get("成熟物名称")
will_get_mature_item = mature_name is not None
mature_item_name = mature_name if mature_name and mature_name.strip() else crop_type
# 添加成熟物到作物仓库(如果允许)
if will_get_mature_item:
self._add_crop_to_warehouse_optimized(current_player_data, {"name": crop_type, "count": harvest_count}, crop_type, crop_info.get("品质", "普通"))
# 添加种子奖励到背包
if seed_reward:
self._add_seeds_to_bag_optimized(current_player_data, seed_reward, crop_info.get("品质", "普通"))
# 检查当前玩家升级
level_up_experience = 100 * current_player_data["等级"]
if current_player_data["经验值"] >= level_up_experience:
current_player_data["等级"] += 1
current_player_data["经验值"] -= level_up_experience
self.log('INFO', f"玩家 {current_username} 升级到 {current_player_data['等级']}", 'SERVER')
# 清理目标玩家的地块(批量更新)
target_lot.update({
"is_planted": False,
"crop_type": "",
"grow_time": 0,
"已浇水": False,
"已施肥": False
})
# 清除施肥时间戳
if "施肥时间" in target_lot:
del target_lot["施肥时间"]
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新(如果在线)
self._push_crop_update_to_player(target_username, target_player_data)
# 构建消息
if will_get_mature_item:
message = f"偷菜成功!从 {target_username} 那里获得 {mature_item_name} x{harvest_count}{crop_exp} 经验,{stamina_message}"
else:
message = f"偷菜成功!从 {target_username} 那里获得 {crop_exp} 经验,{stamina_message}{crop_type}无成熟物产出)"
if seed_reward:
message += f",额外获得 {seed_reward['name']} 种子 x{seed_reward['count']}"
self.log('INFO', f"玩家 {current_username} 偷了玩家 {target_username} 地块 {lot_index} 的作物,获得 {crop_type} x{harvest_count}{crop_exp} 经验" + (f",额外获得 {seed_reward['name']} 种子 x{seed_reward['count']}" if seed_reward else ""), 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "harvest_crop",
"success": True,
"message": message,
"updated_data": {
"钱币": current_player_data["钱币"],
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"体力值": current_player_data.get("体力系统", {}).get("当前体力值", 20),
"种子仓库": current_player_data.get("种子仓库", []),
"作物仓库": current_player_data.get("作物仓库", [])
}
})
# 处理偷菜被巡逻宠物发现的情况
def _handle_steal_caught_by_patrol(self, client_id, current_player_data, current_username, target_player_data, target_username, patrol_pet_id):
"""处理偷菜被巡逻宠物发现的情况"""
# 检查当前玩家是否有出战宠物
battle_pets = current_player_data.get("出战宠物", [])
if len(battle_pets) == 0:
# 没有出战宠物只能逃跑支付1000金币
escape_cost = 1000
if current_player_data.get("钱币", 0) < escape_cost:
# 金币不足,偷菜失败
self.log('INFO', f"玩家 {current_username} 偷菜被发现,金币不足逃跑,偷菜失败", 'SERVER')
return self.send_data(client_id, {
"type": "steal_caught",
"success": False,
"message": f"偷菜被 {target_username} 的巡逻宠物发现!金币不足支付逃跑费用(需要{escape_cost}金币),偷菜失败!",
"patrol_pet_data": self._get_patrol_pet_data(target_player_data, patrol_pet_id),
"has_battle_pet": False,
"escape_cost": escape_cost,
"current_money": current_player_data.get("钱币", 0)
})
else:
# 自动逃跑,扣除金币
current_player_data["钱币"] -= escape_cost
target_player_data["钱币"] += escape_cost
# 保存数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
self.log('INFO', f"玩家 {current_username} 偷菜被发现,支付 {escape_cost} 金币逃跑", 'SERVER')
return self.send_data(client_id, {
"type": "steal_caught",
"success": False,
"message": f"偷菜被 {target_username} 的巡逻宠物发现!支付了 {escape_cost} 金币逃跑",
"patrol_pet_data": self._get_patrol_pet_data(target_player_data, patrol_pet_id),
"has_battle_pet": False,
"escape_cost": escape_cost,
"updated_data": {
"钱币": current_player_data["钱币"]
}
})
else:
# 有出战宠物,可以选择战斗或逃跑
battle_pet_id = battle_pets[0] # 取第一个出战宠物
# 检查出战宠物是否与巡逻宠物是同一个(不应该发生,但保险起见)
if battle_pet_id == patrol_pet_id:
self.log('WARNING', f"玩家 {current_username} 的出战宠物与 {target_username} 的巡逻宠物是同一个,这不应该发生", 'SERVER')
return self._send_action_error(client_id, "harvest_crop", "系统错误:宠物冲突")
self.log('INFO', f"玩家 {current_username} 偷菜被发现,可以选择战斗或逃跑", 'SERVER')
return self.send_data(client_id, {
"type": "steal_caught",
"success": False,
"message": f"偷菜被 {target_username} 的巡逻宠物发现!",
"patrol_pet_data": self._get_patrol_pet_data(target_player_data, patrol_pet_id),
"battle_pet_data": self._get_battle_pet_data(current_player_data, battle_pet_id),
"has_battle_pet": True,
"escape_cost": 1000,
"battle_cost": 1300,
"target_username": target_username,
"current_username": current_username
})
# 获取巡逻宠物数据
def _get_patrol_pet_data(self, player_data, patrol_pet_id):
"""根据巡逻宠物ID获取完整宠物数据"""
pet_bag = player_data.get("宠物背包", [])
for pet in pet_bag:
if pet.get("pet_id", "") == patrol_pet_id:
# 添加场景路径
import copy
pet_data = copy.deepcopy(pet)
# 直接从pet_image字段获取场景路径
scene_path = pet.get("pet_image", "res://Scene/Pet/PetBase.tscn")
pet_data["场景路径"] = scene_path
return pet_data
return None
# 获取出战宠物数据
def _get_battle_pet_data(self, player_data, battle_pet_id):
"""根据出战宠物ID获取完整宠物数据"""
pet_bag = player_data.get("宠物背包", [])
for pet in pet_bag:
if pet.get("pet_id", "") == battle_pet_id:
# 添加场景路径
import copy
pet_data = copy.deepcopy(pet)
# 直接从pet_image字段获取场景路径
scene_path = pet.get("pet_image", "res://Scene/Pet/PetBase.tscn")
pet_data["场景路径"] = scene_path
return pet_data
return None
# 生成收获种子奖励10%概率获得1-2个种子
def _generate_harvest_seed_reward(self, crop_type):
"""生成收获作物时的种子奖励"""
# 10%概率获得种子
if random.random() > 0.1:
return None
# 随机获得1-2个种子
seed_count = random.randint(1, 2)
return {
"name": crop_type,
"count": seed_count
}
# 添加种子到玩家背包
def _add_seeds_to_bag(self, player_data, seed_reward):
"""将种子奖励添加到玩家背包"""
if not seed_reward:
return
seed_name = seed_reward["name"]
seed_count = seed_reward["count"]
# 确保背包存在
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
# 查找背包中是否已有该种子
seed_found = False
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += seed_count
seed_found = True
break
# 如果背包中没有该种子,添加新条目
if not seed_found:
# 从作物数据获取品质信息
crop_data = self._load_crop_data()
quality = "普通"
if crop_data and seed_name in crop_data:
quality = crop_data[seed_name].get("品质", "普通")
player_data["种子仓库"].append({
"name": seed_name,
"quality": quality,
"count": seed_count
})
# 添加成熟物到玩家作物仓库
def _add_crop_to_warehouse(self, player_data, crop_harvest):
"""将成熟物添加到玩家作物仓库"""
if not crop_harvest:
return
crop_name = crop_harvest["name"]
crop_count = crop_harvest["count"]
# 从作物数据检查"成熟物名称"字段
crop_data = self._load_crop_data()
if crop_data and crop_name in crop_data:
mature_name = crop_data[crop_name].get("成熟物名称")
# 如果成熟物名称为null则不添加成熟物到仓库
if mature_name is None:
self.log('DEBUG', f"作物 {crop_name} 的成熟物名称为null跳过添加到作物仓库", 'SERVER')
return
# 如果有指定的成熟物名称,使用它作为仓库中的名称
if mature_name and mature_name.strip():
warehouse_item_name = mature_name
else:
warehouse_item_name = crop_name
else:
# 如果作物数据中没有该作物,使用原名称
warehouse_item_name = crop_name
# 确保作物仓库存在
if "作物仓库" not in player_data:
player_data["作物仓库"] = []
# 查找仓库中是否已有该成熟物
crop_found = False
for item in player_data["作物仓库"]:
if item.get("name") == warehouse_item_name:
item["count"] += crop_count
crop_found = True
break
# 如果仓库中没有该成熟物,添加新条目
if not crop_found:
# 从作物数据获取品质信息
quality = "普通"
if crop_data and crop_name in crop_data:
quality = crop_data[crop_name].get("品质", "普通")
player_data["作物仓库"].append({
"name": warehouse_item_name,
"quality": quality,
"count": crop_count
})
# 添加种子到玩家背包(优化版本)
def _add_seeds_to_bag_optimized(self, player_data, seed_reward, quality="普通"):
"""将种子奖励添加到玩家背包(优化版本)"""
if not seed_reward:
return
seed_name = seed_reward["name"]
seed_count = seed_reward["count"]
# 确保背包存在
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
# 查找背包中是否已有该种子
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += seed_count
return
# 如果背包中没有该种子,添加新条目
player_data["种子仓库"].append({
"name": seed_name,
"quality": quality,
"count": seed_count
})
# 添加成熟物到玩家作物仓库(优化版本)
def _add_crop_to_warehouse_optimized(self, player_data, crop_harvest, warehouse_item_name, quality="普通"):
"""将成熟物添加到玩家作物仓库(优化版本)"""
if not crop_harvest:
return
crop_count = crop_harvest["count"]
# 确保作物仓库存在
if "作物仓库" not in player_data:
player_data["作物仓库"] = []
# 查找仓库中是否已有该成熟物
for item in player_data["作物仓库"]:
if item.get("name") == warehouse_item_name:
item["count"] += crop_count
return
# 如果仓库中没有该成熟物,添加新条目
player_data["作物仓库"].append({
"name": warehouse_item_name,
"quality": quality,
"count": crop_count
})
#==========================收获作物处理==========================
#==========================杂草生长处理==========================
# 定期检查并生长杂草
def check_and_grow_weeds(self):
"""检查所有玩家的离线时间,并在长时间离线玩家的空地上随机生长杂草"""
try:
self.log('INFO', "开始检查杂草生长...", 'SERVER')
current_time = time.time()
affected_players = 0
total_weeds_added = 0
# 获取作物数据以验证杂草类型
crop_data = self._load_crop_data()
if not crop_data:
self.log('ERROR', "无法加载作物数据,跳过杂草检查", 'SERVER')
return
# 可用的杂草类型(从作物数据中筛选标记为杂草的作物)
available_weeds = []
for crop_name, crop_info in crop_data.items():
if crop_info.get("是否杂草", False):
available_weeds.append(crop_name)
if not available_weeds:
self.log('WARNING', "没有找到可用的杂草类型,跳过杂草检查", 'SERVER')
return
# 优先使用MongoDB获取离线玩家
if self.use_mongodb and self.mongo_api:
offline_players = self.mongo_api.get_offline_players(self.offline_threshold_days)
for player_data in offline_players:
account_id = player_data.get("玩家账号")
if not account_id:
continue
try:
# 获取完整玩家数据
full_player_data = self.mongo_api.get_player_data(account_id)
if not full_player_data:
continue
# 为该玩家的空地生长杂草
weeds_added = self._grow_weeds_for_player(full_player_data, account_id, available_weeds)
if weeds_added > 0:
affected_players += 1
total_weeds_added += weeds_added
# 保存玩家数据
self.mongo_api.save_player_data(account_id, full_player_data)
except Exception as e:
self.log('ERROR', f"处理玩家 {account_id} 的杂草生长时出错: {str(e)}", 'SERVER')
continue
else:
# 降级到文件系统
game_saves_dir = "game_saves"
if not os.path.exists(game_saves_dir):
return
# 遍历所有玩家文件
for filename in os.listdir(game_saves_dir):
if not filename.endswith('.json'):
continue
account_id = filename[:-5] # 移除.json后缀
try:
# 加载玩家数据
player_data = self.load_player_data(account_id)
if not player_data:
continue
# 检查玩家是否长时间离线
if self._is_player_long_offline(player_data, current_time):
# 为该玩家的空地生长杂草
weeds_added = self._grow_weeds_for_player(player_data, account_id, available_weeds)
if weeds_added > 0:
affected_players += 1
total_weeds_added += weeds_added
# 保存玩家数据
self.save_player_data(account_id, player_data)
except Exception as e:
self.log('ERROR', f"处理玩家 {account_id} 的杂草生长时出错: {str(e)}", 'SERVER')
continue
self.log('INFO', f"杂草检查完成,共为 {affected_players} 个玩家的农场添加了 {total_weeds_added} 个杂草", 'SERVER')
except Exception as e:
self.log('ERROR', f"杂草生长检查过程中出错: {str(e)}", 'SERVER')
#检查玩家是否长时间不在线
def _is_player_long_offline(self, player_data, current_time):
"""检查玩家是否长时间离线"""
# 获取玩家最后登录时间
last_login_time_str = player_data.get("最后登录时间", "")
if not last_login_time_str:
return False
try:
# 解析最后登录时间戳
last_login_timestamp = self._parse_login_time_to_timestamp(last_login_time_str)
if last_login_timestamp is None:
return False
# 计算离线天数
offline_seconds = current_time - last_login_timestamp
offline_days = offline_seconds / 86400 # 转换为天数
return offline_days >= self.offline_threshold_days
except Exception as e:
self.log('ERROR', f"解析玩家登录时间时出错: {str(e)}", 'SERVER')
return False
#为指定玩家的空地生长杂草
def _grow_weeds_for_player(self, player_data, account_id, available_weeds):
"""为指定玩家的空地生长杂草"""
import random
farm_lots = player_data.get("农场土地", [])
if not farm_lots:
return 0
# 找到所有空的已开垦地块
empty_lots = []
for i, lot in enumerate(farm_lots):
if (lot.get("is_diged", False) and
not lot.get("is_planted", False) and
lot.get("crop_type", "") == ""):
empty_lots.append(i)
if not empty_lots:
return 0
# 随机选择要长杂草的地块数量
max_weeds = min(self.max_weeds_per_check, len(empty_lots))
weeds_to_add = random.randint(1, max_weeds)
# 随机选择地块
selected_lots = random.sample(empty_lots, weeds_to_add)
weeds_added = 0
crop_data = self._load_crop_data()
for lot_index in selected_lots:
# 按概率决定是否在这个地块长杂草
if random.random() < self.weed_growth_probability:
# 随机选择杂草类型
weed_type = random.choice(available_weeds)
weed_info = crop_data.get(weed_type, {})
# 在地块上种植杂草
lot = farm_lots[lot_index]
lot["is_planted"] = True
lot["crop_type"] = weed_type
lot["grow_time"] = weed_info.get("生长时间", 5) # 杂草立即成熟
lot["max_grow_time"] = weed_info.get("生长时间", 5)
lot["已浇水"] = False
lot["已施肥"] = False
weeds_added += 1
if weeds_added > 0:
self.log('INFO', f"为玩家 {account_id} 的农场添加了 {weeds_added} 个杂草", 'SERVER')
return weeds_added
#==========================杂草生长处理==========================
#==========================偷菜免被发现计数器管理==========================
# 玩家免被发现计数器结构:{player_name: {target_player_name: count, ...}, ...}
def _get_steal_immunity_count(self, player_name, target_player_name):
"""获取玩家对目标玩家的免被发现次数"""
return self.steal_immunity_counters.get(player_name, {}).get(target_player_name, 0)
#消耗一次免被发现次数
def _consume_steal_immunity(self, player_name, target_player_name):
"""消耗一次免被发现次数"""
if player_name not in self.steal_immunity_counters:
return False
if target_player_name not in self.steal_immunity_counters[player_name]:
return False
if self.steal_immunity_counters[player_name][target_player_name] > 0:
self.steal_immunity_counters[player_name][target_player_name] -= 1
# 如果计数器归零,清理该条目
if self.steal_immunity_counters[player_name][target_player_name] == 0:
del self.steal_immunity_counters[player_name][target_player_name]
# 如果该玩家没有其他计数器,清理玩家条目
if not self.steal_immunity_counters[player_name]:
del self.steal_immunity_counters[player_name]
return True
return False
#设置免被发现次数默认3次
def _set_steal_immunity(self, player_name, target_player_name, count=3):
"""设置玩家对目标玩家的免被发现次数"""
if player_name not in self.steal_immunity_counters:
self.steal_immunity_counters[player_name] = {}
self.steal_immunity_counters[player_name][target_player_name] = count
self.log('INFO', f"为玩家 {player_name} 设置对 {target_player_name} 的免被发现次数: {count}", 'SERVER')
#清理免被发现计数器
def _clear_player_steal_immunity(self, player_name):
"""清理玩家的所有免被发现计数器"""
if player_name in self.steal_immunity_counters:
del self.steal_immunity_counters[player_name]
self.log('INFO', f"清理玩家 {player_name} 的所有免被发现计数器", 'SERVER')
#清理特定目标的免被发现计数器
def _clear_target_steal_immunity(self, player_name, target_player_name):
"""清理玩家对特定目标的免被发现计数器"""
if player_name in self.steal_immunity_counters:
if target_player_name in self.steal_immunity_counters[player_name]:
del self.steal_immunity_counters[player_name][target_player_name]
# 如果该玩家没有其他计数器,清理玩家条目
if not self.steal_immunity_counters[player_name]:
del self.steal_immunity_counters[player_name]
self.log('INFO', f"清理玩家 {player_name}{target_player_name} 的免被发现计数器", 'SERVER')
#==========================偷菜免被发现计数器管理==========================
#==========================种植作物处理==========================
#处理种植作物请求
def _handle_plant_crop(self, client_id, message):
"""处理种植作物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "种植作物", "plant_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "plant_crop")
if not player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
crop_name = message.get("crop_name", "")
# 验证参数
if lot_index < 0 or lot_index >= len(player_data.get("农场土地", [])):
return self._send_action_error(client_id, "plant_crop", "无效的地块索引")
lot = player_data["农场土地"][lot_index]
# 检查地块状态
if not lot.get("is_diged", False):
return self._send_action_error(client_id, "plant_crop", "此地块尚未开垦")
if lot.get("is_planted", False):
return self._send_action_error(client_id, "plant_crop", "此地块已经种植了作物")
# 处理种植
return self._process_planting(client_id, player_data, username, lot, crop_name)
#辅助函数-处理作物种植逻辑
def _process_planting(self, client_id, player_data, username, lot, crop_name):
"""处理作物种植逻辑"""
# 读取作物配置
crop_data = self._load_crop_data()
# 检查玩家背包中是否有此种子
seed_found = False
seed_index = -1
for i, item in enumerate(player_data.get("种子仓库", [])):
if item.get("name") == crop_name:
seed_found = True
seed_index = i
break
if not seed_found:
return self._send_action_error(client_id, "plant_crop", "背包中没有此种子")
# 获取作物生长时间
if crop_name in crop_data:
grow_time = crop_data[crop_name].get("生长时间", 600)
else:
grow_time = 600
# 从背包中减少种子数量
player_data["种子仓库"][seed_index]["count"] -= 1
# 如果种子用完,从背包中移除
if player_data["种子仓库"][seed_index]["count"] <= 0:
player_data["种子仓库"].pop(seed_index)
# 更新地块数据
lot.update({
"is_planted": True,
"crop_type": crop_name,
"grow_time": 0,
"max_grow_time": grow_time,
"is_dead": False,
"已浇水": False,
"已施肥": False
})
# 清除施肥时间戳
if "施肥时间" in lot:
del lot["施肥时间"]
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
self.log('INFO', f"玩家 {username} 种植了 {crop_name}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "plant_crop",
"success": True,
"message": f"成功种植 {crop_name}",
"updated_data": {
"种子仓库": player_data["种子仓库"]
}
})
#==========================种植作物处理==========================
#==========================购买种子处理==========================
#处理购买种子请求
def _handle_buy_seed(self, client_id, message):
"""处理购买种子请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "购买种子", "buy_seed")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "buy_seed")
if not player_data:
return self.send_data(client_id, response)
crop_name = message.get("crop_name", "")
quantity = max(1, int(message.get("quantity", 1))) # 确保购买数量为正整数
# 加载作物配置
crop_data = self._load_crop_data()
if not crop_data:
return self._send_action_error(client_id, "buy_seed", "服务器无法加载作物数据")
# 检查作物是否存在
if crop_name not in crop_data:
return self._send_action_error(client_id, "buy_seed", "该种子不存在")
# 处理批量购买
return self._process_seed_purchase(client_id, player_data, username, crop_name, crop_data[crop_name], quantity)
#处理种子购买逻辑
def _process_seed_purchase(self, client_id, player_data, username, crop_name, crop, quantity=1):
"""处理种子购买逻辑"""
# 检查玩家等级
player_level = int(player_data.get("等级", 1))
required_level = int(crop.get("等级", 1))
if player_level < required_level:
return self._send_action_error(client_id, "buy_seed", "等级不足,无法购买此种子")
# 计算总花费
unit_cost = crop.get("花费", 0)
total_cost = unit_cost * quantity
# 检查玩家金钱
if player_data["钱币"] < total_cost:
return self._send_action_error(client_id, "buy_seed", f"金钱不足,无法购买此种子。需要{total_cost}元,当前只有{player_data['钱币']}")
# 扣除金钱
player_data["钱币"] -= total_cost
# 将种子添加到背包
seed_found = False
for item in player_data.get("种子仓库", []):
if item.get("name") == crop_name:
item["count"] += quantity
seed_found = True
break
if not seed_found:
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
player_data["种子仓库"].append({
"name": crop_name,
"quality": crop.get("品质", "普通"),
"count": quantity
})
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 购买了 {quantity} 个种子 {crop_name},花费 {total_cost}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "buy_seed",
"success": True,
"message": f"成功购买 {quantity}{crop_name} 种子",
"updated_data": {
"钱币": player_data["钱币"],
"种子仓库": player_data["种子仓库"]
}
})
#==========================购买种子处理==========================
#==========================购买宠物处理==========================
#处理购买宠物请求
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)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "buy_pet")
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 {"success": False, "message": "服务器无法加载宠物数据"}
# 检查宠物是否存在
if pet_name not in pet_config:
return {"success": False, "message": "该宠物不存在"}
pet_info = pet_config[pet_name]
# 从配置中获取宠物价格
actual_cost = pet_info.get("cost", 1000) # 默认价格1000
if pet_cost != actual_cost:
return {"success": False, "message": f"宠物价格验证失败,实际价格为{actual_cost}"}
# 检查玩家是否已拥有该宠物
if self._player_has_pet(player_data, pet_name):
return {"success": False, "message": f"你已经拥有 {pet_name} 了!"}
return {"success": True, "pet_info": pet_info}
#处理宠物购买逻辑
def _process_pet_purchase(self, client_id, player_data, username, pet_name, pet_info):
"""处理宠物购买逻辑"""
pet_cost = pet_info.get("cost", 1000) # 从配置中获取价格默认1000
# 检查玩家金钱
if player_data["钱币"] < pet_cost:
return self._send_action_error(client_id, "buy_pet",
f"金钱不足,无法购买此宠物。需要{pet_cost}元,当前只有{player_data['钱币']}")
# 扣除金钱并添加宠物
player_data["钱币"] -= pet_cost
pet_instance = self._create_pet_instance(pet_info, username, pet_name)
# 确保宠物背包存在并添加宠物
if "宠物背包" not in player_data:
player_data["宠物背包"] = []
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, {
"type": "action_response",
"action_type": "buy_pet",
"success": True,
"message": f"成功购买宠物 {pet_name}",
"updated_data": {
"钱币": player_data["钱币"],
"宠物背包": player_data["宠物背包"]
}
})
#创建宠物实例
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:02d}-{now.day:02d}"
# 新格式:直接在根级别设置属性
pet_instance.update({
"pet_id": unique_id,
"pet_name": f"{username}{pet_name}",
"pet_birthday": birthday,
"pet_owner": username
})
# 初始化当前生命值为最大生命值
max_health = pet_instance.get("max_health", 100)
pet_instance["pet_current_health"] = max_health
return pet_instance
#检查玩家是否已拥有某种宠物
def _player_has_pet(self, player_data, pet_name):
"""检查玩家是否已拥有指定类型的宠物"""
pet_bag = player_data.get("宠物背包", [])
for pet in pet_bag:
pet_type = pet.get("pet_type", "")
if pet_type == pet_name:
return True
return False
#加载宠物配置数据
def _load_pet_config(self):
"""从MongoDB加载宠物配置数据"""
try:
if not hasattr(self, 'mongo_api') or not self.mongo_api:
self.log('ERROR', 'MongoDB未配置或不可用无法加载宠物配置数据', 'SERVER')
return {}
config = self.mongo_api.get_pet_config()
if config:
self.log('INFO', "成功从MongoDB加载宠物配置", 'SERVER')
return config
else:
self.log('ERROR', "MongoDB中未找到宠物配置", 'SERVER')
return {}
except Exception as e:
self.log('ERROR', f"从MongoDB加载宠物配置失败: {str(e)}", 'SERVER')
return {}
#加载游戏小提示配置数据
def _load_game_tips_config(self):
"""从MongoDB加载游戏小提示配置数据"""
try:
if not hasattr(self, 'mongo_api') or not self.mongo_api:
self.log('ERROR', 'MongoDB未配置或不可用无法加载游戏小提示配置数据', 'SERVER')
return {}
config = self.mongo_api.get_game_tips_config()
if config:
self.log('INFO', "成功从MongoDB加载游戏小提示配置", 'SERVER')
return config
else:
self.log('ERROR', "MongoDB中未找到游戏小提示配置", 'SERVER')
return {}
except Exception as e:
self.log('ERROR', f"从MongoDB加载游戏小提示配置失败: {str(e)}", 'SERVER')
return {}
# 将巡逻宠物ID转换为完整宠物数据
def _convert_patrol_pets_to_full_data(self, player_data):
"""将存储的巡逻宠物ID转换为完整的宠物数据"""
patrol_pets_data = []
patrol_pets_ids = player_data.get("巡逻宠物", [])
pet_bag = player_data.get("宠物背包", [])
for patrol_pet_id in patrol_pets_ids:
for pet in pet_bag:
if pet.get("pet_id", "") == patrol_pet_id:
# 为巡逻宠物添加场景路径
import copy
patrol_pet_data = copy.deepcopy(pet)
# 直接从pet_image字段获取场景路径
scene_path = pet.get("pet_image", "")
patrol_pet_data["场景路径"] = scene_path
patrol_pets_data.append(patrol_pet_data)
break
return patrol_pets_data
# 将出战宠物ID转换为完整宠物数据
def _convert_battle_pets_to_full_data(self, player_data):
"""将存储的出战宠物ID转换为完整的宠物数据"""
battle_pets_data = []
battle_pets_ids = player_data.get("出战宠物", [])
pet_bag = player_data.get("宠物背包", [])
for battle_pet_id in battle_pets_ids:
for pet in pet_bag:
if pet.get("pet_id", "") == battle_pet_id:
# 为出战宠物添加场景路径
import copy
battle_pet_data = copy.deepcopy(pet)
# 直接从pet_image字段获取场景路径
scene_path = pet.get("pet_image", "")
battle_pet_data["场景路径"] = scene_path
battle_pets_data.append(battle_pet_data)
break
return battle_pets_data
#==========================购买宠物处理==========================
#==========================重命名宠物处理==========================
#处理重命名宠物请求
def _handle_rename_pet(self, client_id, message):
"""处理重命名宠物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "重命名宠物", "rename_pet")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "rename_pet")
if not player_data:
return self.send_data(client_id, response)
pet_id = message.get("pet_id", "")
new_name = message.get("new_name", "")
# 验证参数
if not pet_id:
return self._send_action_error(client_id, "rename_pet", "宠物ID不能为空")
if not new_name:
return self._send_action_error(client_id, "rename_pet", "宠物名字不能为空")
# 验证名字长度
if len(new_name) > 20:
return self._send_action_error(client_id, "rename_pet", "宠物名字太长最多20个字符")
# 检查宠物是否存在
pet_bag = player_data.get("宠物背包", [])
pet_found = False
for pet in pet_bag:
if pet.get("pet_id", "") == pet_id:
# 检查宠物主人是否正确
if pet.get("pet_owner", "") != username:
return self._send_action_error(client_id, "rename_pet", "你不是该宠物的主人")
# 更新宠物名字
pet["pet_name"] = new_name
pet_found = True
break
if not pet_found:
return self._send_action_error(client_id, "rename_pet", "未找到指定ID的宠物")
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 重命名宠物 {pet_id}{new_name}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "rename_pet",
"success": True,
"message": f"宠物名字已成功修改为 {new_name}",
"pet_id": pet_id,
"new_name": new_name,
"updated_data": {
"宠物背包": player_data["宠物背包"]
}
})
#==========================重命名宠物处理==========================
#==========================设置巡逻宠物处理==========================
#处理设置巡逻宠物请求
def _handle_set_patrol_pet(self, client_id, message):
"""处理设置或取消巡逻宠物的请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "设置巡逻宠物", "set_patrol_pet")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "set_patrol_pet")
if not player_data:
return self.send_data(client_id, response)
pet_id = message.get("pet_id", "")
is_patrolling = message.get("is_patrolling", False)
self.log('INFO', f"处理巡逻宠物请求: pet_id={pet_id}, is_patrolling={is_patrolling}", client_id)
# 验证参数
if not pet_id:
return self._send_action_error(client_id, "set_patrol_pet", "宠物ID不能为空")
# 获取宠物背包和巡逻宠物列表
pet_bag = player_data.get("宠物背包", [])
patrol_pets = player_data.get("巡逻宠物", [])
# 查找目标宠物
target_pet = None
for pet in pet_bag:
if pet.get("pet_id", "") == pet_id:
target_pet = pet
break
if not target_pet:
return self._send_action_error(client_id, "set_patrol_pet", "未找到指定的宠物")
# 检查宠物主人是否正确
if target_pet.get("pet_owner", "") != username:
return self._send_action_error(client_id, "set_patrol_pet", "你不是该宠物的主人")
pet_name = target_pet.get("pet_name", target_pet.get("pet_type", "未知宠物"))
if is_patrolling:
# 添加到巡逻列表
# 检查巡逻宠物数量限制最多4个
if len(patrol_pets) >= 4:
return self._send_action_error(client_id, "set_patrol_pet", "最多只能设置4个巡逻宠物")
# 检查是否已在巡逻列表中现在只检查ID
for patrol_pet_id in patrol_pets:
if patrol_pet_id == pet_id:
return self._send_action_error(client_id, "set_patrol_pet", f"{pet_name} 已在巡逻列表中")
# 添加到巡逻列表只保存宠物ID
patrol_pets.append(pet_id)
message_text = f"{pet_name} 已设置为巡逻宠物"
self.log('INFO', f"玩家 {username} 设置宠物 {pet_name} 为巡逻宠物", 'SERVER')
else:
# 从巡逻列表移除
original_count = len(patrol_pets)
patrol_pets = [pid for pid in patrol_pets if pid != pet_id]
if len(patrol_pets) == original_count:
return self._send_action_error(client_id, "set_patrol_pet", f"{pet_name} 不在巡逻列表中")
message_text = f"{pet_name} 已取消巡逻"
self.log('INFO', f"玩家 {username} 取消宠物 {pet_name} 的巡逻", 'SERVER')
# 更新玩家数据
player_data["巡逻宠物"] = patrol_pets
# 保存玩家数据
self.save_player_data(username, player_data)
# 构建返回给客户端的巡逻宠物数据(完整宠物数据)
patrol_pets_data = []
for patrol_pet_id in patrol_pets:
for pet in pet_bag:
if pet.get("pet_id", "") == patrol_pet_id:
# 为巡逻宠物添加场景路径
import copy
patrol_pet_data = copy.deepcopy(pet)
# 新格式中场景路径已经在pet_image字段中
if "pet_image" in pet:
patrol_pet_data["scene_path"] = pet["pet_image"]
patrol_pets_data.append(patrol_pet_data)
break
return self.send_data(client_id, {
"type": "action_response",
"action_type": "set_patrol_pet",
"success": True,
"message": message_text,
"pet_id": pet_id,
"is_patrolling": is_patrolling,
"updated_data": {
"巡逻宠物": patrol_pets_data
}
})
#==========================设置巡逻宠物处理==========================
#==========================设置出战宠物处理==========================
#处理设置出战宠物请求
def _handle_set_battle_pet(self, client_id, message):
"""处理设置出战宠物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "设置出战宠物", "set_battle_pet")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "set_battle_pet")
if not player_data:
return self.send_data(client_id, response)
pet_id = message.get("pet_id", "")
is_battle = message.get("is_battle", True) # 默认为设置出战
if not pet_id:
return self._send_action_error(client_id, "set_battle_pet", "宠物ID不能为空")
# 获取宠物背包和出战宠物列表
pet_bag = player_data.get("宠物背包", [])
battle_pets = player_data.get("出战宠物", [])
patrol_pets = player_data.get("巡逻宠物", [])
# 查找宠物是否在背包中
target_pet = None
for pet in pet_bag:
if pet.get("pet_id", "") == pet_id:
target_pet = pet
break
if not target_pet:
return self._send_action_error(client_id, "set_battle_pet", f"宠物背包中找不到ID为 {pet_id} 的宠物")
pet_name = target_pet.get("pet_name", "未知宠物")
if is_battle:
# 添加到出战列表
# 检查是否已在出战列表中
if pet_id in battle_pets:
return self._send_action_error(client_id, "set_battle_pet", f"{pet_name} 已在出战列表中")
# 检查是否在巡逻列表中(出战宠物不能是巡逻宠物)
if pet_id in patrol_pets:
return self._send_action_error(client_id, "set_battle_pet", f"{pet_name} 正在巡逻,不能同时设置为出战宠物")
# 检查出战宠物数量限制最多4个
if len(battle_pets) >= 4:
return self._send_action_error(client_id, "set_battle_pet", "最多只能设置4个出战宠物")
# 添加到出战列表
battle_pets.append(pet_id)
message_text = f"{pet_name} 已设置为出战宠物"
self.log('INFO', f"玩家 {username} 设置宠物 {pet_name} 为出战宠物", 'SERVER')
else:
# 从出战列表移除
if pet_id not in battle_pets:
return self._send_action_error(client_id, "set_battle_pet", f"{pet_name} 不在出战列表中")
battle_pets.remove(pet_id)
message_text = f"{pet_name} 已移除出战状态"
self.log('INFO', f"玩家 {username} 移除宠物 {pet_name} 的出战状态", 'SERVER')
# 更新玩家数据
player_data["出战宠物"] = battle_pets
# 保存玩家数据
self.save_player_data(username, player_data)
# 构建返回给客户端的出战宠物数据(完整宠物数据)
battle_pets_data = []
for battle_pet_id in battle_pets:
for pet in pet_bag:
if pet.get("pet_id", "") == battle_pet_id:
# 为出战宠物添加场景路径
import copy
battle_pet_data = copy.deepcopy(pet)
# 新格式中场景路径已经在pet_image字段中
if "pet_image" in pet:
battle_pet_data["scene_path"] = pet["pet_image"]
battle_pets_data.append(battle_pet_data)
break
return self.send_data(client_id, {
"type": "action_response",
"action_type": "set_battle_pet",
"success": True,
"message": message_text,
"pet_id": pet_id,
"is_battle": is_battle,
"updated_data": {
"出战宠物": battle_pets_data
}
})
#==========================设置出战宠物处理==========================
#==========================更新宠物对战数据处理==========================
#处理更新宠物对战数据请求
def _handle_update_battle_pet_data(self, client_id, message):
"""处理更新宠物对战数据请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "更新宠物对战数据", "update_battle_pet_data")
if not logged_in:
return self.send_data(client_id, response)
# 获取请求参数
pet_id = message.get("pet_id", "")
attacker_name = message.get("attacker_name", "")
exp_gained = message.get("exp_gained", 0)
intimacy_gained = message.get("intimacy_gained", 0)
new_level = message.get("new_level", 1)
new_experience = message.get("new_experience", 0)
new_max_experience = message.get("new_max_experience", 100)
new_intimacy = message.get("new_intimacy", 0)
level_ups = message.get("level_ups", 0)
level_bonus_multiplier = message.get("level_bonus_multiplier", 1.0)
is_steal_battle = message.get("is_steal_battle", False)
battle_winner = message.get("battle_winner", "")
if not pet_id or not attacker_name:
return self._send_action_error(client_id, "update_battle_pet_data", "无效的宠物ID或进攻者名称")
# 获取进攻者玩家数据
player_data = self.load_player_data(attacker_name)
if not player_data:
return self._send_action_error(client_id, "update_battle_pet_data", "无法找到进攻者数据")
# 更新宠物数据
success = self._update_pet_battle_data(player_data, pet_id, exp_gained, intimacy_gained,
new_level, new_experience, new_max_experience,
new_intimacy, level_ups, level_bonus_multiplier)
if success:
# 检查是否是偷菜对战且玩家获胜,如果是则设置免被发现计数器
if is_steal_battle and battle_winner == "team1":
# 获取当前访问的目标玩家名称(从客户端连接信息中获取)
target_player_name = self.user_data.get(client_id, {}).get("visiting_target", "")
if target_player_name:
self._set_steal_immunity(attacker_name, target_player_name, 3)
self.log('INFO', f"玩家 {attacker_name} 战胜巡逻宠物,获得对 {target_player_name} 的3次免被发现机会", 'SERVER')
# 保存玩家数据
self.save_player_data(attacker_name, player_data)
self.log('INFO', f"成功更新玩家 {attacker_name} 的宠物 {pet_id} 对战数据:经验+{exp_gained},亲密度+{intimacy_gained},升级{level_ups}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "update_battle_pet_data",
"success": True,
"message": f"成功更新宠物对战数据",
"pet_id": pet_id,
"exp_gained": exp_gained,
"intimacy_gained": intimacy_gained,
"level_ups": level_ups
})
else:
return self._send_action_error(client_id, "update_battle_pet_data", f"无法找到宠物ID为 {pet_id} 的宠物")
#辅助函数-更新宠物对战数据
def _update_pet_battle_data(self, player_data, pet_id, exp_gained, intimacy_gained,
new_level, new_experience, new_max_experience,
new_intimacy, level_ups, level_bonus_multiplier):
"""更新宠物对战数据"""
# 确保宠物背包存在
if "宠物背包" not in player_data:
player_data["宠物背包"] = []
# 查找指定宠物
target_pet = None
for pet in player_data["宠物背包"]:
if pet.get("pet_id") == pet_id:
target_pet = pet
break
if not target_pet:
return False
# 更新等级经验数据
target_pet["pet_level"] = new_level
target_pet["pet_experience"] = new_experience
target_pet["pet_max_experience"] = new_max_experience
target_pet["pet_intimacy"] = new_intimacy
# 如果有升级,更新属性
if level_ups > 0:
# 计算升级后的属性每级10%加成)
old_max_health = target_pet.get("pet_max_health", 100.0)
old_max_armor = target_pet.get("pet_max_armor", 100.0)
old_attack_damage = target_pet.get("pet_attack_damage", 20.0)
# 应用升级加成
new_max_health = old_max_health * level_bonus_multiplier
new_max_armor = old_max_armor * level_bonus_multiplier
new_attack_damage = old_attack_damage * level_bonus_multiplier
target_pet["pet_max_health"] = new_max_health
target_pet["pet_current_health"] = new_max_health # 升级回满血
target_pet["pet_max_armor"] = new_max_armor
target_pet["pet_current_armor"] = new_max_armor # 升级回满护甲
target_pet["pet_attack_damage"] = new_attack_damage
return True
#==========================更新宠物对战数据处理==========================
#==========================宠物喂食处理==========================
#处理宠物喂食请求
def _handle_feed_pet(self, client_id, message):
"""处理宠物喂食请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "宠物喂食", "feed_pet")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "feed_pet")
if not player_data:
return self.send_data(client_id, response)
# 获取请求参数
pet_id = message.get("pet_id", "")
crop_name = message.get("crop_name", "")
feed_effects = message.get("feed_effects", {})
# 验证参数
if not pet_id:
return self._send_action_error(client_id, "feed_pet", "宠物ID不能为空")
if not crop_name:
return self._send_action_error(client_id, "feed_pet", "作物名称不能为空")
# 检查玩家是否有该作物
crop_warehouse = player_data.get("作物仓库", [])
crop_found = False
crop_index = -1
for i, crop_item in enumerate(crop_warehouse):
if crop_item.get("name") == crop_name:
if crop_item.get("count", 0) > 0:
crop_found = True
crop_index = i
break
if not crop_found:
return self._send_action_error(client_id, "feed_pet", f"没有足够的{crop_name}用于喂食")
# 检查宠物是否存在
pet_bag = player_data.get("宠物背包", [])
target_pet = None
for pet in pet_bag:
if pet.get("pet_id", "") == pet_id:
# 检查宠物主人是否正确
if pet.get("pet_owner", "") != username:
return self._send_action_error(client_id, "feed_pet", "你不是该宠物的主人")
target_pet = pet
break
if not target_pet:
return self._send_action_error(client_id, "feed_pet", "未找到指定的宠物")
# 验证作物是否有喂养效果
crop_data = self._load_crop_data()
if crop_name not in crop_data or "喂养效果" not in crop_data[crop_name]:
return self._send_action_error(client_id, "feed_pet", f"{crop_name}没有喂养效果")
# 获取作物的喂养效果
crop_feed_effects = crop_data[crop_name]["喂养效果"]
# 执行喂食
success, applied_effects = self._process_pet_feeding(player_data, target_pet, crop_name, crop_index, crop_feed_effects)
if success:
# 保存玩家数据
self.save_player_data(username, player_data)
pet_name = target_pet.get("pet_name", "未知宠物")
# 构建效果描述
effect_descriptions = []
for effect_name, effect_value in applied_effects.items():
if effect_value > 0:
effect_descriptions.append(f"{effect_name}+{effect_value}")
effect_text = "".join(effect_descriptions) if effect_descriptions else "无效果"
self.log('INFO', f"玩家 {username}{crop_name}喂食宠物 {pet_name},获得:{effect_text}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "feed_pet",
"success": True,
"message": f"成功喂食{pet_name}!获得:{effect_text}",
"pet_id": pet_id,
"crop_name": crop_name,
"applied_effects": applied_effects,
"updated_data": {
"宠物背包": player_data["宠物背包"],
"作物仓库": player_data["作物仓库"]
}
})
else:
return self._send_action_error(client_id, "feed_pet", "喂食失败")
#辅助函数-处理宠物喂食逻辑
def _process_pet_feeding(self, player_data, target_pet, crop_name, crop_index, feed_effects):
"""处理宠物喂食逻辑,支持多种属性提升"""
try:
# 消耗作物
crop_warehouse = player_data.get("作物仓库", [])
if crop_index >= 0 and crop_index < len(crop_warehouse):
crop_warehouse[crop_index]["count"] -= 1
# 如果数量为0移除该作物
if crop_warehouse[crop_index]["count"] <= 0:
crop_warehouse.pop(crop_index)
# 记录实际应用的效果
applied_effects = {}
# 处理经验效果
if "经验" in feed_effects:
exp_gain = feed_effects["经验"]
current_exp = target_pet.get("pet_experience", 0)
max_exp = target_pet.get("pet_max_experience", 100)
current_level = target_pet.get("pet_level", 1)
new_exp = current_exp + exp_gain
applied_effects["经验"] = exp_gain
# 检查是否升级
level_ups = 0
while new_exp >= max_exp and current_level < 100: # 假设最大等级为100
level_ups += 1
new_exp -= max_exp
current_level += 1
# 每升一级最大经验增加20%
max_exp = int(max_exp * 1.2)
# 更新经验数据
target_pet["pet_experience"] = new_exp
target_pet["pet_max_experience"] = max_exp
target_pet["pet_level"] = current_level
# 如果升级了,记录升级次数
if level_ups > 0:
applied_effects["升级"] = level_ups
# 升级时应用属性加成
self._apply_level_up_bonus(target_pet, level_ups)
# 处理生命值效果(增加最大生命值)
if "生命值" in feed_effects:
max_hp_gain = feed_effects["生命值"]
# 增加最大生命值
current_max_hp = target_pet.get("max_health", 100)
new_max_hp = current_max_hp + max_hp_gain
target_pet["max_health"] = new_max_hp
# 同时恢复相应的当前生命值
current_hp = target_pet.get("pet_current_health", current_max_hp)
target_pet["pet_current_health"] = current_hp + max_hp_gain
applied_effects["生命值"] = max_hp_gain
# 处理攻击力效果
if "攻击力" in feed_effects:
attack_gain = feed_effects["攻击力"]
current_attack = target_pet.get("base_attack_damage", 20)
new_attack = current_attack + attack_gain
target_pet["base_attack_damage"] = new_attack
applied_effects["攻击力"] = attack_gain
# 处理移动速度效果
if "移动速度" in feed_effects:
speed_gain = feed_effects["移动速度"]
current_speed = target_pet.get("move_speed", 100)
new_speed = current_speed + speed_gain
target_pet["move_speed"] = new_speed
applied_effects["移动速度"] = speed_gain
# 处理亲密度效果
if "亲密度" in feed_effects:
intimacy_gain = feed_effects["亲密度"]
current_intimacy = target_pet.get("pet_intimacy", 0)
max_intimacy = target_pet.get("pet_max_intimacy", 1000)
actual_intimacy_gain = min(intimacy_gain, max_intimacy - current_intimacy)
if actual_intimacy_gain > 0:
target_pet["pet_intimacy"] = current_intimacy + actual_intimacy_gain
applied_effects["亲密度"] = actual_intimacy_gain
# 处理护甲值效果
if "护甲值" in feed_effects:
armor_gain = feed_effects["护甲值"]
current_armor = target_pet.get("current_armor", target_pet.get("max_armor", 10))
max_armor = target_pet.get("max_armor", 10)
actual_armor_gain = min(armor_gain, max_armor - current_armor)
if actual_armor_gain > 0:
target_pet["current_armor"] = current_armor + actual_armor_gain
applied_effects["护甲值"] = actual_armor_gain
# 处理暴击率效果
if "暴击率" in feed_effects:
crit_gain = feed_effects["暴击率"] / 100.0 # 转换为小数
current_crit = target_pet.get("crit_rate", 0.1)
new_crit = min(current_crit + crit_gain, 1.0) # 最大100%
target_pet["crit_rate"] = new_crit
applied_effects["暴击率"] = feed_effects["暴击率"]
# 处理闪避率效果
if "闪避率" in feed_effects:
dodge_gain = feed_effects["闪避率"] / 100.0 # 转换为小数
current_dodge = target_pet.get("dodge_rate", 0.05)
new_dodge = min(current_dodge + dodge_gain, 1.0) # 最大100%
target_pet["dodge_rate"] = new_dodge
applied_effects["闪避率"] = feed_effects["闪避率"]
# 处理护盾值效果
if "护盾值" in feed_effects:
shield_gain = feed_effects["护盾值"]
current_shield = target_pet.get("current_shield", target_pet.get("max_shield", 0))
max_shield = target_pet.get("max_shield", 0)
actual_shield_gain = min(shield_gain, max_shield - current_shield)
if actual_shield_gain > 0:
target_pet["current_shield"] = current_shield + actual_shield_gain
applied_effects["护盾值"] = actual_shield_gain
return True, applied_effects
except Exception as e:
self.log('ERROR', f"宠物喂食处理失败: {str(e)}", 'SERVER')
return False, {}
#辅助函数-应用升级加成
def _apply_level_up_bonus(self, target_pet, level_ups):
"""应用升级时的属性加成"""
# 每升一级属性增加10%
level_bonus_multiplier = 1.1 ** level_ups
# 更新生命和防御属性
old_max_hp = target_pet.get("max_health", 100)
old_max_armor = target_pet.get("max_armor", 10)
old_max_shield = target_pet.get("max_shield", 0)
new_max_hp = int(old_max_hp * level_bonus_multiplier)
new_max_armor = int(old_max_armor * level_bonus_multiplier)
new_max_shield = int(old_max_shield * level_bonus_multiplier)
target_pet["max_health"] = new_max_hp
target_pet["pet_current_health"] = new_max_hp # 升级回满血
target_pet["max_armor"] = new_max_armor
target_pet["current_armor"] = new_max_armor # 升级回满护甲
# 如果有护盾系统,也更新护盾
if old_max_shield > 0:
target_pet["max_shield"] = new_max_shield
target_pet["current_shield"] = new_max_shield # 升级回满护盾
# 更新攻击属性
old_attack = target_pet.get("base_attack_damage", 20)
new_attack = int(old_attack * level_bonus_multiplier)
target_pet["base_attack_damage"] = new_attack
# 更新移动速度
old_speed = target_pet.get("move_speed", 100)
new_speed = int(old_speed * level_bonus_multiplier)
target_pet["move_speed"] = new_speed
#==========================宠物喂食处理==========================
#==========================宠物对战结果处理==========================
def _handle_pet_battle_result(self, client_id, message):
"""处理宠物对战结果"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "提交宠物对战结果", "pet_battle_result")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "pet_battle_result")
if not player_data:
return self.send_data(client_id, response)
# 获取对战结果数据
battle_data = message.get("battle_data", {})
winner = battle_data.get("winner", "")
attacker_name = battle_data.get("attacker_name", "")
defender_name = battle_data.get("defender_name", "")
battle_type = battle_data.get("battle_type", "")
attacker_pets = battle_data.get("attacker_pets", [])
defender_pets = battle_data.get("defender_pets", [])
duration = battle_data.get("duration", 0)
timestamp = battle_data.get("timestamp", time.time())
# 验证必要参数
if not winner or not attacker_name or not defender_name:
return self._send_action_error(client_id, "pet_battle_result", "对战结果数据不完整")
# 记录对战结果到日志
self.log('INFO', f"宠物对战结果 - 获胜方: {winner}, 攻击方: {attacker_name}, 防守方: {defender_name}, 类型: {battle_type}, 持续时间: {duration}", 'BATTLE')
# 初始化对战历史记录
if "对战历史" not in player_data:
player_data["对战历史"] = []
# 添加对战记录
battle_record = {
"获胜方": winner,
"攻击方": attacker_name,
"防守方": defender_name,
"对战类型": battle_type,
"攻击方宠物": attacker_pets,
"防守方宠物": defender_pets,
"持续时间": duration,
"时间戳": timestamp,
"日期": datetime.datetime.fromtimestamp(timestamp).strftime("%Y年%m月%d%H时%M分%S秒")
}
player_data["对战历史"].append(battle_record)
# 限制历史记录数量保留最近100条
if len(player_data["对战历史"]) > 100:
player_data["对战历史"] = player_data["对战历史"][-100:]
# 更新对战统计
if "对战统计" not in player_data:
player_data["对战统计"] = {
"总对战次数": 0,
"胜利次数": 0,
"失败次数": 0,
"胜率": 0.0
}
stats = player_data["对战统计"]
stats["总对战次数"] += 1
if winner == username:
stats["胜利次数"] += 1
else:
stats["失败次数"] += 1
# 计算胜率
if stats["总对战次数"] > 0:
stats["胜率"] = round(stats["胜利次数"] / stats["总对战次数"] * 100, 2)
# 保存玩家数据
self.save_player_data(username, player_data)
# 如果是与其他玩家的对战,也更新对方的记录
if defender_name != username and defender_name != "系统":
defender_data = self.load_player_data(defender_name)
if defender_data:
# 初始化对方的对战历史和统计
if "对战历史" not in defender_data:
defender_data["对战历史"] = []
if "对战统计" not in defender_data:
defender_data["对战统计"] = {
"总对战次数": 0,
"胜利次数": 0,
"失败次数": 0,
"胜率": 0.0
}
# 添加对战记录
defender_data["对战历史"].append(battle_record)
# 限制历史记录数量
if len(defender_data["对战历史"]) > 100:
defender_data["对战历史"] = defender_data["对战历史"][-100:]
# 更新对战统计
defender_stats = defender_data["对战统计"]
defender_stats["总对战次数"] += 1
if winner == defender_name:
defender_stats["胜利次数"] += 1
else:
defender_stats["失败次数"] += 1
# 计算胜率
if defender_stats["总对战次数"] > 0:
defender_stats["胜率"] = round(defender_stats["胜利次数"] / defender_stats["总对战次数"] * 100, 2)
# 保存对方数据
self.save_player_data(defender_name, defender_data)
return self.send_data(client_id, {
"type": "action_response",
"action_type": "pet_battle_result",
"success": True,
"message": "对战结果已记录",
"updated_data": {
"对战统计": player_data["对战统计"]
}
})
#==========================宠物对战结果处理==========================
#==========================开垦土地处理==========================
#处理开垦土地请求
def _handle_dig_ground(self, client_id, message):
"""处理开垦土地请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "开垦土地", "dig_ground")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "dig_ground")
if not player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
# 验证地块索引
if lot_index < 0 or lot_index >= len(player_data.get("农场土地", [])):
return self._send_action_error(client_id, "dig_ground", "无效的地块索引")
lot = player_data["农场土地"][lot_index]
# 检查地块是否已开垦
if lot.get("is_diged", False):
return self._send_action_error(client_id, "dig_ground", "此地块已经开垦过了")
# 处理开垦
return self._process_digging(client_id, player_data, username, lot, lot_index)
#辅助函数-处理土地开垦逻辑
def _process_digging(self, client_id, player_data, username, lot, lot_index):
"""处理土地开垦逻辑"""
# 计算开垦费用 - 基于已开垦地块数量
digged_count = sum(1 for l in player_data.get("农场土地", []) if l.get("is_diged", False))
dig_money = digged_count * 1000
# 检查玩家金钱是否足够
if player_data["钱币"] < dig_money:
return self._send_action_error(client_id, "dig_ground", f"金钱不足,开垦此地块需要 {dig_money} 金钱")
# 执行开垦操作
player_data["钱币"] -= dig_money
lot["is_diged"] = True
# 生成开垦随机奖励
rewards = self._generate_dig_rewards()
# 应用奖励
player_data["钱币"] += rewards["钱币"]
player_data["经验值"] += rewards["经验值"]
# 添加种子到背包
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
for seed_name, quantity in rewards["seeds"].items():
# 查找是否已有该种子
found = False
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += quantity
found = True
break
# 如果没有找到,添加新种子
if not found:
player_data["种子仓库"].append({
"name": seed_name,
"count": quantity
})
# 检查是否升级
self._check_level_up(player_data)
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
# 构建奖励消息
reward_message = f"获得 {rewards['钱币']} 金钱、{rewards['经验值']} 经验"
if rewards["seeds"]:
seed_list = [f"{name} x{qty}" for name, qty in rewards["seeds"].items()]
reward_message += f"、种子:{', '.join(seed_list)}"
self.log('INFO', f"玩家 {username} 成功开垦地块 {lot_index},花费 {dig_money} 金钱,{reward_message}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "dig_ground",
"success": True,
"message": f"成功开垦地块,花费 {dig_money} 金钱!{reward_message}",
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"],
"种子仓库": player_data["种子仓库"]
}
})
#辅助函数-生成开垦土地随机奖励
def _generate_dig_rewards(self):
"""生成开垦土地的随机奖励"""
rewards = {
"钱币": 0,
"经验值": 0,
"seeds": {}
}
# 随机金钱200-500元
rewards["钱币"] = random.randint(200, 500)
# 随机经验300-600经验
rewards["经验值"] = random.randint(300, 600)
# 随机种子0-3种种子
seed_types_count = random.randint(0, 3)
if seed_types_count > 0:
# 获取作物数据
crop_data = self._load_crop_data()
if crop_data:
# 获取所有可购买的种子
all_seeds = []
for crop_name, crop_info in crop_data.items():
if crop_info.get("能否购买", False):
all_seeds.append(crop_name)
if all_seeds:
# 随机选择种子类型
selected_seeds = random.sample(all_seeds, min(seed_types_count, len(all_seeds)))
for seed_name in selected_seeds:
# 每种种子1-3个
quantity = random.randint(1, 3)
rewards["seeds"][seed_name] = quantity
return rewards
#==========================开垦土地处理==========================
#==========================铲除作物处理==========================
#处理铲除作物请求
def _handle_remove_crop(self, client_id, message):
"""处理铲除作物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "铲除作物", "remove_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "remove_crop")
if not player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
# 验证地块索引
if lot_index < 0 or lot_index >= len(player_data.get("农场土地", [])):
return self._send_action_error(client_id, "remove_crop", "无效的地块索引")
lot = player_data["农场土地"][lot_index]
# 检查地块状态
if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
return self._send_action_error(client_id, "remove_crop", "此地块没有种植作物")
# 处理铲除
return self._process_crop_removal(client_id, player_data, username, lot, lot_index)
#辅助函数-处理铲除作物逻辑
def _process_crop_removal(self, client_id, player_data, username, lot, lot_index):
"""处理铲除作物逻辑"""
# 铲除费用
removal_cost = 500
# 检查玩家金钱是否足够
if player_data["钱币"] < removal_cost:
return self._send_action_error(client_id, "remove_crop", f"金钱不足,铲除作物需要 {removal_cost} 金钱")
# 获取作物名称用于日志
crop_type = lot.get("crop_type", "未知作物")
# 执行铲除操作
player_data["钱币"] -= removal_cost
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
lot["is_dead"] = False # 重置死亡状态
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
self.log('INFO', f"玩家 {username} 铲除了地块 {lot_index} 的作物 {crop_type},花费 {removal_cost} 金钱", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "remove_crop",
"success": True,
"message": f"成功铲除作物 {crop_type},花费 {removal_cost} 金钱",
"updated_data": {
"钱币": player_data["钱币"],
"农场土地": player_data["农场土地"]
}
})
#==========================铲除作物处理==========================
#==========================浇水作物处理==========================
#处理浇水请求
def _handle_water_crop(self, client_id, message):
"""处理浇水作物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "浇水作物", "water_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取当前操作用户的数据
current_player_data, current_username, response = self._load_player_data_with_check(client_id, "water_crop")
if not current_player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
target_username = message.get("target_username", "")
# 确定操作目标如果有target_username就是访问模式否则是自己的农场
if target_username and target_username != current_username:
# 访问模式:浇水别人的作物,但花自己的钱
target_player_data = self.load_player_data(target_username)
if not target_player_data:
return self._send_action_error(client_id, "water_crop", f"无法找到玩家 {target_username} 的数据")
# 验证地块索引
if lot_index < 0 or lot_index >= len(target_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "water_crop", "无效的地块索引")
target_lot = target_player_data["农场土地"][lot_index]
# 检查地块状态
if not target_lot.get("is_planted", False) or not target_lot.get("crop_type", ""):
return self._send_action_error(client_id, "water_crop", "此地块没有种植作物")
# 处理访问模式浇水(花自己的钱,效果作用在目标玩家作物上)
return self._process_visiting_watering(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index)
else:
# 正常模式:浇水自己的作物
# 验证地块索引
if lot_index < 0 or lot_index >= len(current_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "water_crop", "无效的地块索引")
lot = current_player_data["农场土地"][lot_index]
# 检查地块状态
if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
return self._send_action_error(client_id, "water_crop", "此地块没有种植作物")
# 处理正常浇水
return self._process_watering(client_id, current_player_data, current_username, lot, lot_index)
#辅助函数-处理浇水逻辑
def _process_watering(self, client_id, player_data, username, lot, lot_index):
"""处理浇水逻辑"""
# 浇水费用
water_cost = 50
# 检查玩家金钱是否足够
if player_data["钱币"] < water_cost:
return self._send_action_error(client_id, "water_crop", f"金钱不足,浇水需要 {water_cost} 金钱")
# 检查作物是否已死亡
if lot.get("is_dead", False):
return self._send_action_error(client_id, "water_crop", "死亡的作物无法浇水")
# 检查是否已经成熟
if lot["grow_time"] >= lot["max_grow_time"]:
return self._send_action_error(client_id, "water_crop", "作物已经成熟,无需浇水")
# 检查是否在1小时内已经浇过水3600秒 = 1小时
current_time = time.time()
last_water_time = lot.get("浇水时间", 0)
water_cooldown = 3600 # 1小时冷却时间
if current_time - last_water_time < water_cooldown:
remaining_time = water_cooldown - (current_time - last_water_time)
remaining_minutes = int(remaining_time // 60)
remaining_seconds = int(remaining_time % 60)
return self._send_action_error(client_id, "water_crop", f"浇水冷却中,还需等待 {remaining_minutes} 分钟 {remaining_seconds}")
# 执行浇水操作
player_data["钱币"] -= water_cost
# 生成随机经验奖励100-300
experience_reward = random.randint(100, 300)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 计算浇水效果增加1%的生长进度
growth_increase = int(lot["max_grow_time"] * 0.01) # 1%的生长时间
if growth_increase < 1:
growth_increase = 1 # 至少增加1秒
lot["grow_time"] += growth_increase
# 确保不超过最大生长时间
if lot["grow_time"] > lot["max_grow_time"]:
lot["grow_time"] = lot["max_grow_time"]
# 记录浇水时间戳
lot["浇水时间"] = current_time
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
crop_type = lot.get("crop_type", "未知作物")
progress = (lot["grow_time"] / lot["max_grow_time"]) * 100
self.log('INFO', f"玩家 {username} 给地块 {lot_index}{crop_type} 浇水,花费 {water_cost} 金钱,获得 {experience_reward} 经验,生长进度: {progress:.1f}%", 'SERVER')
message = f"浇水成功!{crop_type} 生长了 {growth_increase} 秒,当前进度: {progress:.1f}%,获得 {experience_reward} 经验"
if lot["grow_time"] >= lot["max_grow_time"]:
message += ",作物已成熟!"
return self.send_data(client_id, {
"type": "action_response",
"action_type": "water_crop",
"success": True,
"message": message,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"]
}
})
#处理访问模式浇水逻辑
def _process_visiting_watering(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index):
"""处理访问模式浇水逻辑(花自己的钱,效果作用在目标玩家作物上)"""
# 浇水费用
water_cost = 50
# 检查当前玩家金钱是否足够
if current_player_data["钱币"] < water_cost:
return self._send_action_error(client_id, "water_crop", f"金钱不足,帮助浇水需要 {water_cost} 金钱")
# 检查目标作物是否已死亡
if target_lot.get("is_dead", False):
return self._send_action_error(client_id, "water_crop", "死亡的作物无法浇水")
# 检查是否已经成熟
if target_lot["grow_time"] >= target_lot["max_grow_time"]:
return self._send_action_error(client_id, "water_crop", "作物已经成熟,无需浇水")
# 检查是否在1小时内已经浇过水
current_time = time.time()
last_water_time = target_lot.get("浇水时间", 0)
water_cooldown = 3600 # 1小时冷却时间
if current_time - last_water_time < water_cooldown:
remaining_time = water_cooldown - (current_time - last_water_time)
remaining_minutes = int(remaining_time // 60)
remaining_seconds = int(remaining_time % 60)
return self._send_action_error(client_id, "water_crop", f"浇水冷却中,还需等待 {remaining_minutes} 分钟 {remaining_seconds}")
# 执行浇水操作:扣除当前玩家的钱
current_player_data["钱币"] -= water_cost
# 生成随机经验奖励100-300给当前玩家
experience_reward = random.randint(100, 300)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 计算浇水效果:增加目标作物的生长进度
growth_increase = int(target_lot["max_grow_time"] * 0.01) # 1%的生长时间
if growth_increase < 1:
growth_increase = 1 # 至少增加1秒
target_lot["grow_time"] += growth_increase
# 确保不超过最大生长时间
if target_lot["grow_time"] > target_lot["max_grow_time"]:
target_lot["grow_time"] = target_lot["max_grow_time"]
# 记录浇水时间戳
target_lot["浇水时间"] = current_time
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新(如果在线)
self._push_crop_update_to_player(target_username, target_player_data)
crop_type = target_lot.get("crop_type", "未知作物")
progress = (target_lot["grow_time"] / target_lot["max_grow_time"]) * 100
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 给地块 {lot_index}{crop_type} 浇水,花费 {water_cost} 金钱,获得 {experience_reward} 经验,生长进度: {progress:.1f}%", 'SERVER')
message = f"帮助浇水成功!{target_username}{crop_type} 生长了 {growth_increase} 秒,当前进度: {progress:.1f}%,获得 {experience_reward} 经验"
if target_lot["grow_time"] >= target_lot["max_grow_time"]:
message += ",作物已成熟!"
return self.send_data(client_id, {
"type": "action_response",
"action_type": "water_crop",
"success": True,
"message": message,
"updated_data": {
"钱币": current_player_data["钱币"],
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"]
}
})
#==========================浇水作物处理==========================
#==========================施肥作物处理==========================
#处理施肥请求
def _handle_fertilize_crop(self, client_id, message):
"""处理施肥作物请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "施肥作物", "fertilize_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取当前操作用户的数据
current_player_data, current_username, response = self._load_player_data_with_check(client_id, "fertilize_crop")
if not current_player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
target_username = message.get("target_username", "")
# 确定操作目标如果有target_username就是访问模式否则是自己的农场
if target_username and target_username != current_username:
# 访问模式:施肥别人的作物,但花自己的钱
target_player_data = self.load_player_data(target_username)
if not target_player_data:
return self._send_action_error(client_id, "fertilize_crop", f"无法找到玩家 {target_username} 的数据")
# 验证地块索引
if lot_index < 0 or lot_index >= len(target_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "fertilize_crop", "无效的地块索引")
target_lot = target_player_data["农场土地"][lot_index]
# 检查地块状态
if not target_lot.get("is_planted", False) or not target_lot.get("crop_type", ""):
return self._send_action_error(client_id, "fertilize_crop", "此地块没有种植作物")
# 处理访问模式施肥
return self._process_visiting_fertilizing(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index)
else:
# 正常模式:施肥自己的作物
# 验证地块索引
if lot_index < 0 or lot_index >= len(current_player_data.get("农场土地", [])):
return self._send_action_error(client_id, "fertilize_crop", "无效的地块索引")
lot = current_player_data["农场土地"][lot_index]
# 检查地块状态
if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
return self._send_action_error(client_id, "fertilize_crop", "此地块没有种植作物")
# 处理正常施肥
return self._process_fertilizing(client_id, current_player_data, current_username, lot, lot_index)
#辅助函数-处理访问模式施肥逻辑
def _process_visiting_fertilizing(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index):
"""处理访问模式施肥逻辑(花自己的钱,效果作用在目标玩家作物上)"""
# 施肥费用
fertilize_cost = 150
# 检查当前玩家金钱是否足够
if current_player_data["钱币"] < fertilize_cost:
return self._send_action_error(client_id, "fertilize_crop", f"金钱不足,帮助施肥需要 {fertilize_cost} 金钱")
# 检查目标作物是否已死亡
if target_lot.get("is_dead", False):
return self._send_action_error(client_id, "fertilize_crop", "死亡的作物无法施肥")
# 检查是否已经成熟
if target_lot["grow_time"] >= target_lot["max_grow_time"]:
return self._send_action_error(client_id, "fertilize_crop", "作物已经成熟,无需施肥")
# 检查是否已经施过肥
if target_lot.get("已施肥", False):
return self._send_action_error(client_id, "fertilize_crop", "此作物已经施过肥了")
# 执行施肥操作:扣除当前玩家的钱
current_player_data["钱币"] -= fertilize_cost
# 生成随机经验奖励100-300给当前玩家
experience_reward = random.randint(100, 300)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 标记目标作物已施肥,施肥效果会在作物生长更新时生效
target_lot["已施肥"] = True
# 记录施肥时间戳用于计算10分钟的双倍生长效果
target_lot["施肥时间"] = time.time()
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新(如果在线)
self._push_crop_update_to_player(target_username, target_player_data)
crop_type = target_lot.get("crop_type", "未知作物")
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 给地块 {lot_index}{crop_type} 施肥,花费 {fertilize_cost} 金钱,获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "fertilize_crop",
"success": True,
"message": f"帮助施肥成功!{target_username}{crop_type} 将在10分钟内以双倍速度生长获得 {experience_reward} 经验",
"updated_data": {
"钱币": current_player_data["钱币"],
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"]
}
})
#辅助函数-处理施肥逻辑
def _process_fertilizing(self, client_id, player_data, username, lot, lot_index):
"""处理施肥逻辑"""
# 施肥费用
fertilize_cost = 150
# 检查玩家金钱是否足够
if player_data["钱币"] < fertilize_cost:
return self._send_action_error(client_id, "fertilize_crop", f"金钱不足,施肥需要 {fertilize_cost} 金钱")
# 检查作物是否已死亡
if lot.get("is_dead", False):
return self._send_action_error(client_id, "fertilize_crop", "死亡的作物无法施肥")
# 检查是否已经成熟
if lot["grow_time"] >= lot["max_grow_time"]:
return self._send_action_error(client_id, "fertilize_crop", "作物已经成熟,无需施肥")
# 检查是否已经施过肥
if lot.get("已施肥", False):
return self._send_action_error(client_id, "fertilize_crop", "此作物已经施过肥了")
# 执行施肥操作
player_data["钱币"] -= fertilize_cost
# 生成随机经验奖励100-300
experience_reward = random.randint(100, 300)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 标记已施肥,施肥效果会在作物生长更新时生效
lot["已施肥"] = True
# 记录施肥时间戳用于计算10分钟的双倍生长效果
lot["施肥时间"] = time.time()
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
crop_type = lot.get("crop_type", "未知作物")
self.log('INFO', f"玩家 {username} 给地块 {lot_index}{crop_type} 施肥,花费 {fertilize_cost} 金钱,获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "fertilize_crop",
"success": True,
"message": f"施肥成功!{crop_type} 将在10分钟内以双倍速度生长获得 {experience_reward} 经验",
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"]
}
})
#==========================施肥作物处理==========================
#==========================购买道具处理==========================
#处理购买道具请求
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["钱币"] < total_cost:
return self._send_action_error(client_id, "buy_item",
f"金钱不足,需要{total_cost}元,当前只有{player_data['money']}")
# 扣除金钱并添加道具
player_data["钱币"] -= 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": {
"钱币": player_data["钱币"],
"道具背包": 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加载道具配置数据"""
if not self.mongo_api or not self.mongo_api.is_connected():
self.log('ERROR', 'MongoDB未配置或不可用无法加载道具配置数据', 'SERVER')
return {}
try:
config = self.mongo_api.get_item_config()
if config:
self.log('INFO', '成功从MongoDB加载道具配置', 'SERVER')
return config
else:
self.log('ERROR', 'MongoDB中未找到道具配置', 'SERVER')
return {}
except Exception as e:
self.log('ERROR', f'从MongoDB加载道具配置失败: {e}', 'SERVER')
return {}
#==========================购买道具处理==========================
#==========================道具使用处理==========================
#处理使用道具请求
def _handle_use_item(self, client_id, message):
"""处理使用道具请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "使用道具", "use_item")
if not logged_in:
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:
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", "")
# 验证参数
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:
# 访问模式:对别人的作物使用道具
return self._handle_visiting_item_use(client_id, player_data, username, target_username, lot_index, item_name, use_type)
else:
# 正常模式:对自己的作物使用道具
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("农场土地", [])):
return self._send_action_error(client_id, "use_item", "无效的地块索引")
lot = player_data["农场土地"][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("农场土地", [])):
return self._send_action_error(client_id, "use_item", "无效的地块索引")
target_lot = target_player_data["农场土地"][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):
"""检查玩家是否拥有指定道具"""
item_bag = player_data.get("道具背包", [])
for item in item_bag:
if item.get("name", "") == item_name and item.get("count", 0) > 0:
return True
return False
#辅助函数-从玩家背包中移除指定道具
def _remove_item_from_inventory(self, player_data, item_name, count=1):
"""从玩家道具背包中移除指定数量的道具"""
item_bag = player_data.get("道具背包", [])
for i, item in enumerate(item_bag):
if item.get("name", "") == item_name and item.get("count", 0) >= count:
item["count"] -= count
if item["count"] <= 0:
item_bag.pop(i)
return True
return False
#处理正常模式下的道具使用逻辑
def _process_item_use_normal(self, client_id, player_data, username, lot, lot_index, item_name, use_type):
"""处理正常模式下的道具使用"""
# 检查地块状态
if not lot.get("is_planted", False) or not lot.get("crop_type", ""):
return self._send_action_error(client_id, "use_item", "此地块没有种植作物")
# 检查作物是否已死亡
if lot.get("is_dead", False):
return self._send_action_error(client_id, "use_item", "死亡的作物无法使用道具")
# 根据使用类型和道具名称执行不同逻辑
if use_type == "fertilize":
# 检查是否已经成熟(施肥道具需要检查)
if lot.get("grow_time", 0) >= lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物已经成熟,无需施肥")
return self._use_fertilizer_item(client_id, player_data, username, lot, lot_index, item_name)
elif use_type == "water":
# 检查是否已经成熟(浇水道具需要检查)
if lot.get("grow_time", 0) >= lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物已经成熟,无需浇水")
return self._use_watering_item(client_id, player_data, username, lot, lot_index, item_name)
elif use_type == "remove":
# 铲子可以清除任何作物,包括成熟的
return self._use_removal_item(client_id, player_data, username, lot, lot_index, item_name)
elif use_type == "weed_killer":
# 除草剂可以清除任何杂草,包括成熟的
return self._use_weed_killer_item(client_id, player_data, username, lot, lot_index, item_name)
elif use_type == "harvest":
# 采集道具只能对成熟的作物使用
if lot.get("grow_time", 0) < lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物还未成熟,无法使用采集道具")
return self._use_harvest_item(client_id, player_data, username, lot, lot_index, item_name)
else:
return self._send_action_error(client_id, "use_item", f"不支持的使用类型: {use_type}")
#处理访问模式下的道具使用逻辑
def _process_item_use_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name, use_type):
"""处理访问模式下的道具使用"""
# 检查地块状态
if not target_lot.get("is_planted", False) or not target_lot.get("crop_type", ""):
return self._send_action_error(client_id, "use_item", "此地块没有种植作物")
# 检查作物是否已死亡
if target_lot.get("is_dead", False):
return self._send_action_error(client_id, "use_item", "死亡的作物无法使用道具")
# 根据使用类型和道具名称执行不同逻辑
if use_type == "fertilize":
# 检查是否已经成熟(施肥道具需要检查)
if target_lot.get("grow_time", 0) >= target_lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物已经成熟,无需施肥")
return self._use_fertilizer_item_visiting(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name)
elif use_type == "water":
# 检查是否已经成熟(浇水道具需要检查)
if target_lot.get("grow_time", 0) >= target_lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物已经成熟,无需浇水")
return self._use_watering_item_visiting(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name)
elif use_type == "remove":
# 铲子可以清除任何作物,包括成熟的
return self._use_removal_item_visiting(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name)
elif use_type == "weed_killer":
# 除草剂可以清除任何杂草,包括成熟的
return self._use_weed_killer_item_visiting(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name)
elif use_type == "harvest":
# 采集道具只能对成熟的作物使用
if target_lot.get("grow_time", 0) < target_lot.get("max_grow_time", 1):
return self._send_action_error(client_id, "use_item", "作物还未成熟,无法使用采集道具")
return self._use_harvest_item_visiting(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name)
else:
return self._send_action_error(client_id, "use_item", f"不支持的使用类型: {use_type}")
#使用施肥类道具
def _use_fertilizer_item(self, client_id, player_data, username, lot, lot_index, item_name):
"""使用施肥类道具"""
# 检查是否已经施过肥
if lot.get("已施肥", False):
return self._send_action_error(client_id, "use_item", "此作物已经施过肥了")
# 移除道具
if not self._remove_item_from_inventory(player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励
experience_reward = random.randint(50, 150)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 根据道具类型设置不同的施肥效果
current_time = time.time()
if item_name == "农家肥":
# 30分钟内额外+1秒/次生长
lot["已施肥"] = True
lot["施肥时间"] = current_time
lot["施肥类型"] = "农家肥"
lot["施肥加成"] = 1
lot["施肥持续时间"] = 1800 # 30分钟
message = f"使用 {item_name} 成功作物将在30分钟内获得额外生长加成"
elif item_name == "金坷垃":
# 5分钟内额外+4秒/次生长
lot["已施肥"] = True
lot["施肥时间"] = current_time
lot["施肥类型"] = "金坷垃"
lot["施肥加成"] = 4
lot["施肥持续时间"] = 300 # 5分钟
message = f"使用 {item_name} 成功作物将在5分钟内获得强力生长加成"
elif item_name == "生长素":
# 10分钟内额外+2秒/次生长
lot["已施肥"] = True
lot["施肥时间"] = current_time
lot["施肥类型"] = "生长素"
lot["施肥加成"] = 2
lot["施肥持续时间"] = 600 # 10分钟
message = f"使用 {item_name} 成功作物将在10分钟内获得中等生长加成"
else:
return self._send_action_error(client_id, "use_item", f"不支持的施肥道具: {item_name}")
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
crop_type = lot.get("crop_type", "未知作物")
self.log('INFO', f"玩家 {username} 对地块 {lot_index}{crop_type} 使用了 {item_name},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"{message},获得 {experience_reward} 经验",
"updated_data": {
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"],
"道具背包": player_data["道具背包"]
}
})
#使用浇水类道具
def _use_watering_item(self, client_id, player_data, username, lot, lot_index, item_name):
"""使用浇水类道具"""
# 移除道具
if not self._remove_item_from_inventory(player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励
experience_reward = random.randint(30, 100)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 根据道具类型计算浇水效果
if item_name == "水壶":
# 增加1%的生长进度
growth_increase = int(lot["max_grow_time"] * 0.01)
message = f"使用 {item_name} 成功作物生长进度增加了1%"
elif item_name == "水桶":
# 增加2%的生长进度
growth_increase = int(lot["max_grow_time"] * 0.02)
message = f"使用 {item_name} 成功作物生长进度增加了2%"
else:
return self._send_action_error(client_id, "use_item", f"不支持的浇水道具: {item_name}")
if growth_increase < 1:
growth_increase = 1 # 至少增加1秒
lot["grow_time"] += growth_increase
# 确保不超过最大生长时间
if lot["grow_time"] > lot["max_grow_time"]:
lot["grow_time"] = lot["max_grow_time"]
# 记录浇水时间戳
lot["浇水时间"] = time.time()
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
crop_type = lot.get("crop_type", "未知作物")
progress = (lot["grow_time"] / lot["max_grow_time"]) * 100
self.log('INFO', f"玩家 {username} 对地块 {lot_index}{crop_type} 使用了 {item_name},生长进度: {progress:.1f}%,获得 {experience_reward} 经验", 'SERVER')
final_message = f"{message},当前进度: {progress:.1f}%,获得 {experience_reward} 经验"
if lot["grow_time"] >= lot["max_grow_time"]:
final_message += ",作物已成熟!"
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": final_message,
"updated_data": {
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"],
"道具背包": player_data["道具背包"]
}
})
#访问模式下使用施肥类道具
def _use_fertilizer_item_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name):
"""访问模式下使用施肥类道具"""
# 检查是否已经施过肥
if target_lot.get("已施肥", False):
return self._send_action_error(client_id, "use_item", "此作物已经施过肥了")
# 移除当前玩家的道具
if not self._remove_item_from_inventory(current_player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励给当前玩家
experience_reward = random.randint(50, 150)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 根据道具类型设置不同的施肥效果
current_time = time.time()
if item_name == "农家肥":
# 30分钟内额外+1秒/次生长
target_lot["已施肥"] = True
target_lot["施肥时间"] = current_time
target_lot["施肥类型"] = "农家肥"
target_lot["施肥加成"] = 1
target_lot["施肥持续时间"] = 1800 # 30分钟
message = f"帮助施肥成功!{target_username} 的作物将在30分钟内获得额外生长加成"
elif item_name == "金坷垃":
# 5分钟内额外+4秒/次生长
target_lot["已施肥"] = True
target_lot["施肥时间"] = current_time
target_lot["施肥类型"] = "金坷垃"
target_lot["施肥加成"] = 4
target_lot["施肥持续时间"] = 300 # 5分钟
message = f"帮助施肥成功!{target_username} 的作物将在5分钟内获得强力生长加成"
elif item_name == "生长素":
# 10分钟内额外+2秒/次生长
target_lot["已施肥"] = True
target_lot["施肥时间"] = current_time
target_lot["施肥类型"] = "生长素"
target_lot["施肥加成"] = 2
target_lot["施肥持续时间"] = 600 # 10分钟
message = f"帮助施肥成功!{target_username} 的作物将在10分钟内获得中等生长加成"
else:
return self._send_action_error(client_id, "use_item", f"不支持的施肥道具: {item_name}")
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新
self._push_crop_update_to_player(target_username, target_player_data)
crop_type = target_lot.get("crop_type", "未知作物")
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 对地块 {lot_index}{crop_type} 使用了 {item_name},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"{message},获得 {experience_reward} 经验",
"updated_data": {
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"道具背包": current_player_data["道具背包"]
}
})
def _use_watering_item_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name):
"""访问模式下使用浇水类道具"""
# 移除当前玩家的道具
if not self._remove_item_from_inventory(current_player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励给当前玩家
experience_reward = random.randint(30, 100)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 根据道具类型计算浇水效果
if item_name == "水壶":
# 增加1%的生长进度
growth_increase = int(target_lot["max_grow_time"] * 0.01)
message = f"帮助浇水成功!{target_username} 的作物生长进度增加了1%"
elif item_name == "水桶":
# 增加2%的生长进度
growth_increase = int(target_lot["max_grow_time"] * 0.02)
message = f"帮助浇水成功!{target_username} 的作物生长进度增加了2%"
else:
return self._send_action_error(client_id, "use_item", f"不支持的浇水道具: {item_name}")
if growth_increase < 1:
growth_increase = 1 # 至少增加1秒
target_lot["grow_time"] += growth_increase
# 确保不超过最大生长时间
if target_lot["grow_time"] > target_lot["max_grow_time"]:
target_lot["grow_time"] = target_lot["max_grow_time"]
# 记录浇水时间戳
target_lot["浇水时间"] = time.time()
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新
self._push_crop_update_to_player(target_username, target_player_data)
crop_type = target_lot.get("crop_type", "未知作物")
progress = (target_lot["grow_time"] / target_lot["max_grow_time"]) * 100
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 对地块 {lot_index}{crop_type} 使用了 {item_name},生长进度: {progress:.1f}%,获得 {experience_reward} 经验", 'SERVER')
final_message = f"{message},当前进度: {progress:.1f}%,获得 {experience_reward} 经验"
if target_lot["grow_time"] >= target_lot["max_grow_time"]:
final_message += ",作物已成熟!"
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": final_message,
"updated_data": {
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"道具背包": current_player_data["道具背包"]
}
})
def _use_removal_item(self, client_id, player_data, username, lot, lot_index, item_name):
"""使用铲除类道具(铲子)"""
# 检查玩家是否有这个道具
if not self._has_item_in_inventory(player_data, item_name):
return self._send_action_error(client_id, "use_item", f"您没有 {item_name}")
# 移除道具
if not self._remove_item_from_inventory(player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励
experience_reward = random.randint(20, 60)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 获取作物名称用于日志
crop_type = lot.get("crop_type", "未知作物")
# 执行铲除操作
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
lot["is_dead"] = False # 重置死亡状态
lot["已浇水"] = False # 重置浇水状态
lot["已施肥"] = False # 重置施肥状态
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
self.log('INFO', f"玩家 {username} 使用 {item_name} 铲除了地块 {lot_index} 的作物 {crop_type},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"使用 {item_name} 成功铲除作物 {crop_type},获得 {experience_reward} 经验",
"updated_data": {
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"],
"道具背包": player_data["道具背包"]
}
})
def _use_weed_killer_item(self, client_id, player_data, username, lot, lot_index, item_name):
"""使用除草剂"""
# 检查是否为杂草
crop_type = lot.get("crop_type", "")
crop_data = self._load_crop_data()
if not crop_data or crop_type not in crop_data:
return self._send_action_error(client_id, "use_item", f"未知的作物类型: {crop_type}")
is_weed = crop_data[crop_type].get("是否杂草", False)
if not is_weed:
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 not self._remove_item_from_inventory(player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励
experience_reward = random.randint(15, 50)
player_data["经验值"] += experience_reward
# 检查是否升级
self._check_level_up(player_data)
# 执行除草操作
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
lot["is_dead"] = False # 重置死亡状态
lot["已浇水"] = False # 重置浇水状态
lot["已施肥"] = False # 重置施肥状态
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
self.log('INFO', f"玩家 {username} 使用 {item_name} 清除了地块 {lot_index} 的杂草 {crop_type},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"使用 {item_name} 成功清除杂草 {crop_type},获得 {experience_reward} 经验",
"updated_data": {
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"农场土地": player_data["农场土地"],
"道具背包": player_data["道具背包"]
}
})
def _use_removal_item_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name):
"""访问模式下使用铲除道具"""
# 检查当前玩家是否有这个道具
if not self._has_item_in_inventory(current_player_data, item_name):
return self._send_action_error(client_id, "use_item", f"您没有 {item_name}")
# 移除当前玩家的道具
if not self._remove_item_from_inventory(current_player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励给当前玩家
experience_reward = random.randint(20, 60)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 获取作物名称用于日志
crop_type = target_lot.get("crop_type", "未知作物")
# 执行铲除操作
target_lot["is_planted"] = False
target_lot["crop_type"] = ""
target_lot["grow_time"] = 0
target_lot["is_dead"] = False # 重置死亡状态
target_lot["已浇水"] = False # 重置浇水状态
target_lot["已施肥"] = False # 重置施肥状态
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新
self._push_crop_update_to_player(target_username, target_player_data)
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 使用 {item_name} 铲除了地块 {lot_index} 的作物 {crop_type},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"帮助 {target_username} 铲除作物 {crop_type} 成功,获得 {experience_reward} 经验",
"updated_data": {
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"道具背包": current_player_data["道具背包"]
}
})
def _use_weed_killer_item_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name):
"""访问模式下使用除草剂"""
# 检查是否为杂草
crop_type = target_lot.get("crop_type", "")
crop_data = self._load_crop_data()
if not crop_data or crop_type not in crop_data:
return self._send_action_error(client_id, "use_item", f"未知的作物类型: {crop_type}")
is_weed = crop_data[crop_type].get("是否杂草", False)
if not is_weed:
return self._send_action_error(client_id, "use_item", "除草剂只能用于清除杂草,此作物不是杂草")
# 检查当前玩家是否有这个道具
if not self._has_item_in_inventory(current_player_data, item_name):
return self._send_action_error(client_id, "use_item", f"您没有 {item_name}")
# 移除当前玩家的道具
if not self._remove_item_from_inventory(current_player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 生成随机经验奖励给当前玩家
experience_reward = random.randint(15, 50)
current_player_data["经验值"] += experience_reward
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 执行除草操作
target_lot["is_planted"] = False
target_lot["crop_type"] = ""
target_lot["grow_time"] = 0
target_lot["is_dead"] = False # 重置死亡状态
target_lot["已浇水"] = False # 重置浇水状态
target_lot["已施肥"] = False # 重置施肥状态
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新
self._push_crop_update_to_player(target_username, target_player_data)
self.log('INFO', f"玩家 {current_username} 帮助玩家 {target_username} 使用 {item_name} 清除了地块 {lot_index} 的杂草 {crop_type},获得 {experience_reward} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": f"帮助 {target_username} 清除杂草 {crop_type} 成功,获得 {experience_reward} 经验",
"updated_data": {
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"道具背包": current_player_data["道具背包"]
}
})
def _use_harvest_item(self, client_id, player_data, username, lot, lot_index, item_name):
"""使用采集道具(精准采集锄、时运锄)"""
# 移除道具
if not self._remove_item_from_inventory(player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 读取作物配置
crop_data = self._load_crop_data()
# 获取作物类型
crop_type = lot["crop_type"]
# 检查是否为杂草类型(杂草不能用采集道具收获)
if crop_type in crop_data:
crop_info = crop_data[crop_type]
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "use_item", f"{crop_type}不能使用采集道具收获,只能铲除!")
crop_exp = crop_info.get("经验", 10)
# 额外检查:如果作物收益为负数,也视为杂草
crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0)
if crop_income < 0:
return self._send_action_error(client_id, "use_item", f"{crop_type}不能使用采集道具收获,只能铲除!")
else:
# 默认经验
crop_exp = 10
# 道具特殊效果
import random
if item_name == "精准采集锄":
# 精准采集锄收获数量正常1-5个但必定掉落种子
harvest_count = random.randint(1, 5)
# 100%概率获得2-4个该作物的种子
seed_reward = {
"name": crop_type + "种子",
"count": random.randint(2, 4)
}
message_suffix = ",精准采集锄确保了种子的获得"
elif item_name == "时运锄":
# 时运锄收获数量更多3-8个种子掉落率正常
harvest_count = random.randint(3, 8)
# 15%概率获得1-3个该作物的种子稍微提高
seed_reward = None
if random.random() < 0.15:
seed_reward = {
"name": crop_type + "种子",
"count": random.randint(1, 3)
}
message_suffix = ",时运锄增加了收获数量"
else:
return self._send_action_error(client_id, "use_item", f"不支持的采集道具: {item_name}")
# 生成采集奖励经验
experience_reward = random.randint(30, 80)
crop_exp += experience_reward
# 创建收获物
crop_harvest = {
"name": crop_type,
"count": harvest_count
}
# 更新玩家经验
player_data["经验值"] += crop_exp
# 检查是否升级
self._check_level_up(player_data)
# 检查是否会获得成熟物
crop_data = self._load_crop_data()
will_get_mature_item = True
mature_item_name = crop_type
if crop_data and crop_type in crop_data:
mature_name = crop_data[crop_type].get("成熟物名称")
if mature_name is None:
will_get_mature_item = False
elif mature_name and mature_name.strip():
mature_item_name = mature_name
# 添加成熟物到作物仓库(如果允许)
if will_get_mature_item:
self._add_crop_to_warehouse(player_data, crop_harvest)
# 添加种子奖励到背包
if seed_reward:
self._add_seeds_to_bag(player_data, seed_reward)
# 清理地块
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
lot["已浇水"] = False
lot["已施肥"] = False
# 清除施肥时间戳
if "施肥时间" in lot:
del lot["施肥时间"]
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
# 构建消息
if will_get_mature_item:
message = f"使用 {item_name} 收获成功,获得 {mature_item_name} x{harvest_count}{crop_exp} 经验{message_suffix}"
else:
message = f"使用 {item_name} 收获成功,获得 {crop_exp} 经验{message_suffix}{crop_type}无成熟物产出)"
if seed_reward:
message += f",额外获得 {seed_reward['name']} x{seed_reward['count']}"
self.log('INFO', f"玩家 {username} 使用 {item_name} 从地块 {lot_index} 收获了作物,获得 {crop_type} x{harvest_count}{crop_exp} 经验" + (f",额外获得 {seed_reward['name']} x{seed_reward['count']}" if seed_reward else ""), 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": message,
"updated_data": {
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", []),
"作物仓库": player_data.get("作物仓库", []),
"道具背包": player_data.get("道具背包", [])
}
})
def _use_harvest_item_visiting(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index, item_name):
"""访问模式下使用采集道具"""
# 移除当前玩家的道具
if not self._remove_item_from_inventory(current_player_data, item_name, 1):
return self._send_action_error(client_id, "use_item", f"移除道具 {item_name} 失败")
# 读取作物配置
crop_data = self._load_crop_data()
# 获取作物类型
crop_type = target_lot["crop_type"]
# 检查是否为杂草类型(杂草不能用采集道具收获)
if crop_type in crop_data:
crop_info = crop_data[crop_type]
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "use_item", f"{crop_type}不能使用采集道具收获,只能铲除!")
crop_exp = int(crop_info.get("经验", 10) * 0.7) # 访问模式获得70%经验
# 额外检查:如果作物收益为负数,也视为杂草
crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0)
if crop_income < 0:
return self._send_action_error(client_id, "use_item", f"{crop_type}不能使用采集道具收获,只能铲除!")
else:
# 默认经验
crop_exp = 7
# 道具特殊效果(访问模式稍微降低效果)
import random
if item_name == "精准采集锄":
# 精准采集锄收获数量稍少1-4个但必定掉落种子
harvest_count = random.randint(1, 4)
# 100%概率获得1-3个该作物的种子
seed_reward = {
"name": crop_type + "种子",
"count": random.randint(1, 3)
}
message_suffix = ",精准采集锄确保了种子的获得"
elif item_name == "时运锄":
# 时运锄收获数量较多2-6个种子掉落率正常
harvest_count = random.randint(2, 6)
# 10%概率获得1-2个该作物的种子
seed_reward = None
if random.random() < 0.10:
seed_reward = {
"name": crop_type + "种子",
"count": random.randint(1, 2)
}
message_suffix = ",时运锄增加了收获数量"
else:
return self._send_action_error(client_id, "use_item", f"不支持的采集道具: {item_name}")
# 生成帮助采集奖励经验
experience_reward = random.randint(20, 60)
crop_exp += experience_reward
# 创建收获物
crop_harvest = {
"name": crop_type,
"count": harvest_count
}
# 更新当前玩家经验
current_player_data["经验值"] += crop_exp
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 检查是否会获得成熟物
crop_data = self._load_crop_data()
will_get_mature_item = True
mature_item_name = crop_type
if crop_data and crop_type in crop_data:
mature_name = crop_data[crop_type].get("成熟物名称")
if mature_name is None:
will_get_mature_item = False
elif mature_name and mature_name.strip():
mature_item_name = mature_name
# 收获物给当前玩家(如果允许)
if will_get_mature_item:
self._add_crop_to_warehouse(current_player_data, crop_harvest)
# 种子奖励给当前玩家
if seed_reward:
self._add_seeds_to_bag(current_player_data, seed_reward)
# 清理目标玩家的地块
target_lot["is_planted"] = False
target_lot["crop_type"] = ""
target_lot["grow_time"] = 0
target_lot["已浇水"] = False
target_lot["已施肥"] = False
# 清除施肥时间戳
if "施肥时间" in target_lot:
del target_lot["施肥时间"]
# 保存两个玩家的数据
self.save_player_data(current_username, current_player_data)
self.save_player_data(target_username, target_player_data)
# 向目标玩家推送作物更新(如果在线)
self._push_crop_update_to_player(target_username, target_player_data)
# 构建消息
if will_get_mature_item:
message = f"使用 {item_name} 帮助收获成功!从 {target_username} 那里获得 {mature_item_name} x{harvest_count}{crop_exp} 经验{message_suffix}"
else:
message = f"使用 {item_name} 帮助收获成功!从 {target_username} 那里获得 {crop_exp} 经验{message_suffix}{crop_type}无成熟物产出)"
if seed_reward:
message += f",额外获得 {seed_reward['name']} x{seed_reward['count']}"
self.log('INFO', f"玩家 {current_username} 使用 {item_name} 帮助玩家 {target_username} 收获地块 {lot_index} 的作物,获得 {crop_type} x{harvest_count}{crop_exp} 经验" + (f",额外获得 {seed_reward['name']} x{seed_reward['count']}" if seed_reward else ""), 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "use_item",
"success": True,
"message": message,
"updated_data": {
"经验值": current_player_data["经验值"],
"等级": current_player_data["等级"],
"种子仓库": current_player_data.get("种子仓库", []),
"作物仓库": current_player_data.get("作物仓库", []),
"道具背包": current_player_data.get("道具背包", [])
}
})
#==========================道具使用处理==========================
#==========================宠物使用道具处理==========================
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_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_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_index = -1
for i, item in enumerate(item_bag):
if item.get("name") == item_name and item.get("count", 0) > 0:
item_index = i
break
if item_index == -1:
return {"success": False, "message": f"道具 {item_name} 不足"}
# 检查宠物
pet_bag = player_data.get("宠物背包", [])
pet_index = -1
for i, pet in enumerate(pet_bag):
if pet.get("pet_id") == pet_id:
pet_index = i
break
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:
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')
return self.send_data(client_id, {
"type": "use_pet_item_response",
"success": True,
"message": result_message,
"updated_data": {"宠物背包": pet_bag, "道具背包": item_bag}
})
else:
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_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):
"""处理具体的宠物道具使用逻辑"""
try:
# 根据道具类型应用不同的效果
if item_name == "不死图腾":
# 启用死亡重生技能
if "enable_death_respawn_skill" not in pet_data:
pet_data["enable_death_respawn_skill"] = True
else:
pet_data["enable_death_respawn_skill"] = True
if "respawn_health_percentage" not in pet_data:
pet_data["respawn_health_percentage"] = 0.5 # 重生时50%血量
return True, f"宠物 {pet_data['pet_name']} 获得了死亡重生能力!", pet_data
elif item_name == "荆棘护甲":
# 启用反伤机制
if "enable_damage_reflection_skill" not in pet_data:
pet_data["enable_damage_reflection_skill"] = True
else:
pet_data["enable_damage_reflection_skill"] = True
return True, f"宠物 {pet_data['pet_name']} 获得了荆棘护甲!", pet_data
elif item_name == "狂暴药水":
# 启用狂暴技能
if "enable_berserker_skill" not in pet_data:
pet_data["enable_berserker_skill"] = True
else:
pet_data["enable_berserker_skill"] = True
return True, f"宠物 {pet_data['pet_name']} 获得了狂暴能力!", pet_data
elif item_name == "援军令牌":
# 启用召唤宠物技能
if "enable_summon_pet_skill" not in pet_data:
pet_data["enable_summon_pet_skill"] = True
else:
pet_data["enable_summon_pet_skill"] = True
return True, f"宠物 {pet_data['pet_name']} 获得了援军召唤能力!", pet_data
elif item_name in ["金刚图腾", "灵木图腾", "潮汐图腾", "烈焰图腾", "敦岩图腾"]:
# 改变宠物元素
element_map = {
"金刚图腾": "METAL",
"灵木图腾": "WOOD",
"潮汐图腾": "WATER",
"烈焰图腾": "FIRE",
"敦岩图腾": "EARTH"
}
element_name_map = {
"金刚图腾": "",
"灵木图腾": "",
"潮汐图腾": "",
"烈焰图腾": "",
"敦岩图腾": ""
}
new_element = element_map[item_name]
element_name = element_name_map[item_name]
# 根据实际宠物数据结构更新元素类型
pet_data["element_type"] = new_element
# 如果没有元素伤害加成字段,则添加
if "element_damage_bonus" not in pet_data:
pet_data["element_damage_bonus"] = 100.0
return True, f"宠物 {pet_data['pet_name']} 的元素属性已改变为{element_name}元素!", pet_data
else:
return False, f"未知的宠物道具: {item_name}", None
except Exception as e:
self.log('ERROR', f"处理宠物道具效果失败: {str(e)}", 'PET_ITEM')
return False, "道具效果处理失败", None
#==========================宠物使用道具处理==========================
#==========================农场道具使用处理==========================
def _handle_use_farm_item(self, client_id, message):
"""处理农场道具使用请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "农场道具使用", "use_farm_item")
if not logged_in:
return self.send_data(client_id, response)
# 获取请求参数
item_name = message.get("item_name", "")
if not item_name:
return self.send_data(client_id, {
"type": "use_farm_item_response",
"success": False,
"message": "缺少必要参数"
})
# 获取玩家数据
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_farm_item_response",
"success": False,
"message": "玩家数据加载失败"
})
# 检查道具是否存在
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 not item_found:
return self.send_data(client_id, {
"type": "use_farm_item_response",
"success": False,
"message": f"道具 {item_name} 不足"
})
# 处理道具使用
try:
success, result_message, rewards = self._process_farm_item_use(item_name, player_data)
if success:
# 减少道具数量
item_bag[item_index]["count"] -= 1
if item_bag[item_index]["count"] <= 0:
item_bag.pop(item_index)
# 应用奖励
if "钱币" in rewards:
player_data["钱币"] += rewards["钱币"]
if "经验值" in rewards:
player_data["经验值"] += rewards["经验值"]
# 检查是否升级
self._check_level_up(player_data)
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送成功响应
response = {
"type": "use_farm_item_response",
"success": True,
"message": result_message,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"道具背包": item_bag
}
}
self.log('INFO', f"用户 {username} 使用农场道具 {item_name} 成功", 'FARM_ITEM')
else:
# 发送失败响应
response = {
"type": "use_farm_item_response",
"success": False,
"message": result_message
}
return self.send_data(client_id, response)
except Exception as e:
self.log('ERROR', f"农场道具使用处理失败: {str(e)}", 'FARM_ITEM')
return self.send_data(client_id, {
"type": "use_farm_item_response",
"success": False,
"message": "道具使用处理失败"
})
def _process_farm_item_use(self, item_name, player_data):
"""处理具体的农场道具使用逻辑"""
try:
rewards = {}
if item_name == "小额经验卡":
# 给玩家增加500经验
rewards["经验值"] = 500
return True, f"使用 {item_name} 成功获得了500经验值", rewards
elif item_name == "小额金币卡":
# 给玩家增加500金币
rewards["钱币"] = 500
return True, f"使用 {item_name} 成功获得了500金币", rewards
else:
return False, f"未知的农场道具: {item_name}", {}
except Exception as e:
self.log('ERROR', f"处理农场道具效果失败: {str(e)}", 'FARM_ITEM')
return False, "道具效果处理失败", {}
#==========================农场道具使用处理==========================
#==========================道具配置数据处理==========================
#处理客户端请求道具配置数据
def _handle_item_config_request(self, client_id):
"""处理客户端请求道具配置数据"""
item_config = self._load_item_config()
if item_config:
self.log('INFO', f"向客户端 {client_id} 发送道具配置数据,道具种类:{len(item_config)}", 'SERVER')
return self.send_data(client_id, {
"type": "item_config_response",
"success": True,
"item_config": item_config
})
else:
return self.send_data(client_id, {
"type": "item_config_response",
"success": False,
"message": "无法读取道具配置数据"
})
def _handle_pet_config_request(self, client_id):
"""处理客户端请求宠物配置数据"""
pet_config = self._load_pet_config()
if pet_config:
self.log('INFO', f"向客户端 {client_id} 发送宠物配置数据,宠物种类:{len(pet_config)}", 'SERVER')
return self.send_data(client_id, {
"type": "pet_config_response",
"success": True,
"pet_config": pet_config
})
else:
return self.send_data(client_id, {
"type": "pet_config_response",
"success": False,
"message": "无法读取宠物配置数据"
})
#==========================道具配置数据处理==========================
#处理客户端请求游戏小提示配置数据
def _handle_game_tips_config_request(self, client_id):
"""处理客户端请求游戏小提示配置数据"""
game_tips_config = self._load_game_tips_config()
if game_tips_config:
self.log('INFO', f"向客户端 {client_id} 发送游戏小提示配置数据", 'SERVER')
return self.send_data(client_id, {
"type": "game_tips_config_response",
"success": True,
"game_tips_config": game_tips_config
})
else:
return self.send_data(client_id, {
"type": "game_tips_config_response",
"success": False,
"message": "无法读取游戏小提示配置数据"
})
#==========================升级土地处理==========================
#处理升级土地请求
def _handle_upgrade_land(self, client_id, message):
"""处理升级土地请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "升级土地", "upgrade_land")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "upgrade_land")
if not player_data:
return self.send_data(client_id, response)
lot_index = message.get("lot_index", -1)
# 验证地块索引
if lot_index < 0 or lot_index >= len(player_data.get("农场土地", [])):
return self._send_action_error(client_id, "upgrade_land", "无效的地块索引")
lot = player_data["农场土地"][lot_index]
# 检查地块是否已开垦
if not lot.get("is_diged", False):
return self._send_action_error(client_id, "upgrade_land", "此地块尚未开垦")
# 处理升级
return self._process_land_upgrade(client_id, player_data, username, lot, lot_index)
#辅助函数-处理土地升级逻辑
def _process_land_upgrade(self, client_id, player_data, username, lot, lot_index):
"""处理土地升级逻辑"""
# 土地升级配置
upgrade_config = {
0: {"cost": 1000, "name": "黄土地", "speed": 2.0}, # 0级->1级1000元2倍速
1: {"cost": 2000, "name": "红土地", "speed": 4.0}, # 1级->2级2000元4倍速
2: {"cost": 4000, "name": "紫土地", "speed": 6.0}, # 2级->3级4000元6倍速
3: {"cost": 8000, "name": "黑土地", "speed": 10.0} # 3级->4级8000元10倍速
}
# 获取当前土地等级
current_level = lot.get("土地等级", 0)
# 检查是否已达到最高等级
if current_level >= 4:
return self._send_action_error(client_id, "upgrade_land", "此土地已达到最高等级(黑土地)")
# 检查升级配置是否存在
if current_level not in upgrade_config:
return self._send_action_error(client_id, "upgrade_land", f"土地等级数据异常,当前等级: {current_level}")
# 获取升级配置
config = upgrade_config[current_level]
upgrade_cost = config["cost"]
next_name = config["name"]
next_level = current_level + 1
speed_multiplier = config["speed"]
# 检查玩家金钱是否足够
if player_data["钱币"] < upgrade_cost:
return self._send_action_error(client_id, "upgrade_land", f"金钱不足,升级到{next_name}需要 {upgrade_cost} 金钱")
# 执行升级操作
player_data["钱币"] -= upgrade_cost
lot["土地等级"] = next_level
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
self.log('INFO', f"玩家 {username} 将地块 {lot_index} 升级到{next_level}{next_name},花费 {upgrade_cost} 金钱", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "upgrade_land",
"success": True,
"message": f"土地升级成功!升级到{next_level}{next_name},作物将以{speed_multiplier}倍速度生长",
"updated_data": {
"钱币": player_data["钱币"],
"农场土地": player_data["农场土地"]
}
})
#==========================升级土地处理==========================
#==========================购买新地块处理==========================
#处理购买新地块请求
def _handle_buy_new_ground(self, client_id, message):
"""处理购买新地块请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "购买新地块", "buy_new_ground")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "buy_new_ground")
if not player_data:
return self.send_data(client_id, response)
# 处理购买新地块
return self._process_buy_new_ground(client_id, player_data, username)
#辅助函数-处理购买新地块逻辑
def _process_buy_new_ground(self, client_id, player_data, username):
"""处理购买新地块逻辑"""
# 购买新地块费用
new_ground_cost = 2000
# 检查玩家金钱是否足够
if player_data["钱币"] < new_ground_cost:
return self._send_action_error(client_id, "buy_new_ground", f"金钱不足,购买新地块需要 {new_ground_cost} 金钱")
# 检查地块数量限制
max_lots = 1000 # 最大地块数量限制
current_lots = len(player_data.get("农场土地", []))
if current_lots >= max_lots:
return self._send_action_error(client_id, "buy_new_ground", f"已达到最大地块数量限制({max_lots}个)")
# 执行购买操作
player_data["钱币"] -= new_ground_cost
# 创建新的未开垦地块
new_lot = {
"crop_type": "",
"grow_time": 0,
"is_dead": False,
"is_diged": False, # 新购买的地块默认未开垦
"is_planted": False,
"max_grow_time": 5,
"已浇水": False,
"已施肥": False,
"土地等级": 0
}
# 添加到农场地块数组
if "农场土地" not in player_data:
player_data["农场土地"] = []
player_data["农场土地"].append(new_lot)
# 保存玩家数据
self.save_player_data(username, player_data)
# 发送作物更新
self._push_crop_update_to_player(username, player_data)
new_lot_index = len(player_data["农场土地"])
self.log('INFO', f"玩家 {username} 成功购买新地块,花费 {new_ground_cost} 金钱,新地块位置:{new_lot_index}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "buy_new_ground",
"success": True,
"message": f"购买新地块成功!花费 {new_ground_cost} 元,新地块位置:{new_lot_index}",
"updated_data": {
"钱币": player_data["钱币"],
"农场土地": player_data["农场土地"]
}
})
#==========================购买新地块处理==========================
#==========================点赞玩家处理==========================
#处理玩家点赞请求
def _handle_like_player(self, client_id, message):
"""处理点赞请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "点赞玩家", "like_player")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "like_player")
if not player_data:
return self.send_data(client_id, response)
target_username = message.get("target_username", "")
if not target_username:
return self.send_data(client_id, {
"type": "like_player_response",
"success": False,
"message": "缺少目标用户名"
})
# 不能给自己点赞
if target_username == username:
return self.send_data(client_id, {
"type": "like_player_response",
"success": False,
"message": "不能给自己点赞"
})
# 检查并更新每日点赞次数
self._check_and_update_daily_likes(player_data)
# 检查今日剩余点赞次数
like_system = player_data.get("点赞系统", {})
remaining_likes = like_system.get("今日剩余点赞次数", 10)
if remaining_likes <= 0:
return self.send_data(client_id, {
"type": "like_player_response",
"success": False,
"message": "今日点赞次数已用完,明天再来吧!"
})
# 加载目标玩家数据
target_player_data = self.load_player_data(target_username)
if not target_player_data:
return self.send_data(client_id, {
"type": "like_player_response",
"success": False,
"message": f"无法找到玩家 {target_username} 的数据"
})
# 扣除点赞次数
player_data["点赞系统"]["今日剩余点赞次数"] = remaining_likes - 1
# 初始化目标玩家的点赞系统(如果不存在)
if "点赞系统" not in target_player_data:
target_player_data["点赞系统"] = {
"今日剩余点赞次数": 10,
"点赞上次刷新时间": datetime.datetime.now().strftime("%Y-%m-%d"),
"总点赞数": 0
}
# 更新目标玩家的点赞数量
target_player_data["点赞系统"]["总点赞数"] = target_player_data["点赞系统"].get("总点赞数", 0) + 1
# 保存两个玩家的数据
self.save_player_data(username, player_data)
self.save_player_data(target_username, target_player_data)
self.log('INFO', f"玩家 {username} 点赞了玩家 {target_username},目标玩家点赞数:{target_player_data['点赞系统']['总点赞数']},剩余点赞次数:{player_data['点赞系统']['今日剩余点赞次数']}", 'SERVER')
return self.send_data(client_id, {
"type": "like_player_response",
"success": True,
"message": f"成功点赞玩家 {target_username}!剩余点赞次数:{player_data['点赞系统']['今日剩余点赞次数']}",
"target_likes": target_player_data["点赞系统"]["总点赞数"],
"remaining_likes": player_data["点赞系统"]["今日剩余点赞次数"]
})
#检查并更新每日点赞次数
def _check_and_update_daily_likes(self, player_data):
"""检查并更新每日点赞次数每天重置为10次"""
import datetime
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
# 初始化点赞系统
if "点赞系统" not in player_data:
player_data["点赞系统"] = {
"今日剩余点赞次数": 10,
"点赞上次刷新时间": current_date,
"总点赞数": 0
}
return True # 发生了初始化
like_system = player_data["点赞系统"]
# 确保必要字段存在
if "今日剩余点赞次数" not in like_system:
like_system["今日剩余点赞次数"] = 10
if "点赞上次刷新时间" not in like_system:
like_system["点赞上次刷新时间"] = current_date
if "总点赞数" not in like_system:
like_system["总点赞数"] = 0
# 检查是否需要每日重置
last_refresh_date = like_system.get("点赞上次刷新时间", "")
if last_refresh_date != current_date:
# 新的一天,重置点赞次数
like_system["今日剩余点赞次数"] = 10
like_system["点赞上次刷新时间"] = current_date
return True # 发生了重置
return False # 没有重置
#清理在线礼包历史数据
def _cleanup_online_gift_history(self, player_data):
"""清理过期的在线礼包数据(只保留当天的数据)并删除旧的英文格式"""
import datetime
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
# 清理旧的英文格式数据
if "online_gift" in player_data:
del player_data["online_gift"]
self.log('INFO', f"已清理玩家数据中的旧英文在线礼包格式", 'SERVER')
# 初始化在线礼包数据
if "在线礼包" not in player_data:
player_data["在线礼包"] = {
"当前日期": current_date,
"今日在线时长": 0.0,
"已领取礼包": [],
"登录时间": time.time()
}
return
online_gift_data = player_data["在线礼包"]
# 检查是否是新的一天
last_date = online_gift_data.get("当前日期", "")
if last_date != current_date:
# 新的一天,重置所有数据
player_data["在线礼包"] = {
"当前日期": current_date,
"今日在线时长": 0.0,
"已领取礼包": [],
"登录时间": time.time()
}
self.log('INFO', f"在线礼包数据已重置到新日期:{current_date}", 'SERVER')
#清理新手礼包历史数据
def _cleanup_new_player_gift_history(self, player_data):
"""清理旧的英文新手礼包数据并转换为中文格式"""
import datetime
# 检查是否有旧的英文数据
old_claimed = player_data.get("new_player_gift_claimed", False)
old_time = player_data.get("new_player_gift_time", "")
if old_claimed or old_time:
# 转换为中文格式
if "新手礼包" not in player_data:
player_data["新手礼包"] = {}
if old_claimed:
player_data["新手礼包"]["已领取"] = True
player_data["新手礼包"]["领取时间"] = old_time if old_time else datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
# 删除旧的英文字段
if "new_player_gift_claimed" in player_data:
del player_data["new_player_gift_claimed"]
if "new_player_gift_time" in player_data:
del player_data["new_player_gift_time"]
self.log('INFO', f"已清理玩家数据中的旧英文新手礼包格式", 'SERVER')
def _cleanup_stamina_system_history(self, player_data):
"""清理旧的体力系统数据,迁移到新的"体力系统"对象"""
if "体力值" in player_data or "体力上次刷新时间" in player_data or "体力上次恢复时间" in player_data:
# 加载体力系统配置
stamina_config = self._load_stamina_config()
max_stamina = stamina_config.get("最大体力值", 20)
# 保存旧的体力数据
old_stamina = player_data.get("体力值", 20)
old_refresh_time = player_data.get("体力上次刷新时间", "")
old_recovery_time = player_data.get("体力上次恢复时间", 0)
# 创建新的体力系统对象
if "体力系统" not in player_data:
player_data["体力系统"] = {}
stamina_system = player_data["体力系统"]
# 迁移数据到新格式
stamina_system["当前体力值"] = old_stamina
stamina_system["最大体力值"] = max_stamina
stamina_system["上次刷新时间"] = old_refresh_time
stamina_system["上次恢复时间"] = old_recovery_time
# 移除旧的体力数据
if "体力值" in player_data:
del player_data["体力值"]
if "体力上次刷新时间" in player_data:
del player_data["体力上次刷新时间"]
if "体力上次恢复时间" in player_data:
del player_data["体力上次恢复时间"]
self.log('INFO', f"已清理玩家数据中的旧体力系统格式,迁移到新的体力系统对象", 'SERVER')
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')
return {
"最大体力值": 20,
"每小时恢复体力": 1,
"恢复间隔秒数": 3600,
"新玩家初始体力": 20
}
except json.JSONDecodeError as e:
self.log('ERROR', f"体力系统配置文件格式错误: {e}", 'SERVER')
return {
"最大体力值": 20,
"每小时恢复体力": 1,
"恢复间隔秒数": 3600,
"新玩家初始体力": 20
}
except Exception as e:
self.log('ERROR', f"加载体力系统配置时发生错误: {e}", 'SERVER')
return {
"最大体力值": 20,
"每小时恢复体力": 1,
"恢复间隔秒数": 3600,
"新玩家初始体力": 20
}
#==========================点赞玩家处理==========================
#==========================在线玩家处理==========================
#处理请求在线玩家请求
def _handle_online_players_request(self, client_id, message):
"""处理获取在线玩家数量的请求"""
online_players = len([cid for cid in self.user_data if self.user_data[cid].get("logged_in", False)])
return self.send_data(client_id, {
"type": "online_players_response",
"success": True,
"online_players": online_players
})
#==========================在线玩家处理==========================
#==========================玩家体力值处理==========================
#检查并更新体力值
def _check_and_update_stamina(self, player_data):
"""检查并更新体力值每小时恢复1点每天重置"""
import datetime
current_time = time.time()
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
# 加载体力系统配置
stamina_config = self._load_stamina_config()
max_stamina = stamina_config.get("最大体力值", 20)
recovery_amount = stamina_config.get("每小时恢复体力", 1)
recovery_interval = stamina_config.get("恢复间隔秒数", 3600)
initial_stamina = stamina_config.get("新玩家初始体力", 20)
# 获取或创建体力系统对象
if "体力系统" not in player_data:
player_data["体力系统"] = {
"当前体力值": initial_stamina,
"最大体力值": max_stamina,
"上次刷新时间": current_date,
"上次恢复时间": current_time
}
stamina_system = player_data["体力系统"]
# 确保最大体力值与配置同步
stamina_system["最大体力值"] = max_stamina
# 检查是否需要每日重置
last_refresh_date = stamina_system.get("上次刷新时间", "")
if last_refresh_date != current_date:
# 新的一天,重置体力值
stamina_system["当前体力值"] = max_stamina
stamina_system["上次刷新时间"] = current_date
stamina_system["上次恢复时间"] = current_time
return True # 发生了重置
# 检查每小时恢复
last_recovery_time = stamina_system.get("上次恢复时间", current_time)
time_diff = current_time - last_recovery_time
# 如果超过恢复间隔时间,恢复体力值
if time_diff >= recovery_interval:
recovery_cycles = int(time_diff // recovery_interval)
current_stamina = stamina_system.get("当前体力值", 0)
# 体力值恢复,但不能超过最大值
new_stamina = min(max_stamina, current_stamina + (recovery_cycles * recovery_amount))
if new_stamina > current_stamina:
stamina_system["当前体力值"] = new_stamina
stamina_system["上次恢复时间"] = current_time
return True # 发生了恢复
return False # 没有变化
#消耗体力值
def _consume_stamina(self, player_data, amount, action_name):
"""消耗体力值"""
stamina_system = player_data.get("体力系统", {})
current_stamina = stamina_system.get("当前体力值", 20)
if current_stamina < amount:
return False, f"体力值不足!{action_name}需要 {amount} 点体力,当前体力:{current_stamina}"
stamina_system["当前体力值"] = current_stamina - amount
return True, f"消耗 {amount} 点体力,剩余体力:{stamina_system['当前体力值']}"
#检查体力值是否足够
def _check_stamina_sufficient(self, player_data, amount):
"""检查体力值是否足够"""
stamina_system = player_data.get("体力系统", {})
current_stamina = stamina_system.get("当前体力值", 20)
return current_stamina >= amount
def _check_and_update_register_time(self, player_data, username):
"""检查并更新已存在玩家的注册时间"""
default_register_time = "2025年05月21日15时00分00秒"
# 如果玩家没有注册时间字段,设为默认值(老玩家)
if "注册时间" not in player_data:
player_data["注册时间"] = default_register_time
self.save_player_data(username, player_data)
self.log('INFO', f"为已存在玩家 {username} 设置默认注册时间", 'SERVER')
def _check_and_fix_wisdom_tree_config(self, player_data, username):
"""检查并修复智慧树配置"""
import time
current_time = int(time.time())
# 初始化智慧树配置(如果不存在)
if "智慧树配置" not in player_data:
player_data["智慧树配置"] = {
"距离上一次杀虫时间": current_time,
"距离上一次除草时间": current_time,
"智慧树显示的话": "",
"等级": 1,
"当前经验值": 0,
"最大经验值": 100,
"最大生命值": 100,
"当前生命值": 100,
"高度": 20
}
self.log('INFO', f"为玩家 {username} 初始化智慧树配置", 'SERVER')
else:
# 检查并修复已存在的智慧树配置
wisdom_tree_config = player_data["智慧树配置"]
config_fixed = False
# 修复空字符串或无效的时间戳
if "距离上一次除草时间" not in wisdom_tree_config or not wisdom_tree_config["距离上一次除草时间"] or wisdom_tree_config["距离上一次除草时间"] == "":
wisdom_tree_config["距离上一次除草时间"] = current_time
config_fixed = True
if "距离上一次杀虫时间" not in wisdom_tree_config or not wisdom_tree_config["距离上一次杀虫时间"] or wisdom_tree_config["距离上一次杀虫时间"] == "":
wisdom_tree_config["距离上一次杀虫时间"] = current_time
config_fixed = True
if "上次护理时间" not in wisdom_tree_config or not wisdom_tree_config["上次护理时间"]:
wisdom_tree_config["上次护理时间"] = current_time
config_fixed = True
# 确保其他必需字段存在并转换旧格式
if "等级" not in wisdom_tree_config:
wisdom_tree_config["等级"] = 1
config_fixed = True
if "当前经验值" not in wisdom_tree_config:
# 兼容旧的"经验"字段
old_exp = wisdom_tree_config.get("经验", 0)
wisdom_tree_config["当前经验值"] = old_exp
if "经验" in wisdom_tree_config:
del wisdom_tree_config["经验"]
config_fixed = True
if "最大经验值" not in wisdom_tree_config:
wisdom_tree_config["最大经验值"] = self._calculate_wisdom_tree_max_exp(wisdom_tree_config.get("等级", 1))
config_fixed = True
if "当前生命值" not in wisdom_tree_config:
# 兼容旧的"生命值"字段
old_health = wisdom_tree_config.get("生命值", 100)
wisdom_tree_config["当前生命值"] = old_health
if "生命值" in wisdom_tree_config:
del wisdom_tree_config["生命值"] # 删除旧字段
config_fixed = True
if "最大生命值" not in wisdom_tree_config:
wisdom_tree_config["最大生命值"] = 100
config_fixed = True
if "高度" not in wisdom_tree_config:
wisdom_tree_config["高度"] = 20
config_fixed = True
if "智慧树显示的话" not in wisdom_tree_config:
wisdom_tree_config["智慧树显示的话"] = ""
config_fixed = True
if config_fixed:
self.log('INFO', f"为玩家 {username} 修复智慧树配置", 'SERVER')
def _calculate_wisdom_tree_max_exp(self, level):
"""计算智慧树指定等级的最大经验值
使用前期升级快,后期愈来愈慢的公式
"""
if level <= 1:
return 100
# 使用指数增长公式:基础经验 * (等级^1.5) * 1.2
base_exp = 50
exp_multiplier = 1.2
level_factor = pow(level, 1.5)
max_exp = int(base_exp * level_factor * exp_multiplier)
return max_exp
#==========================玩家体力值处理==========================
#==========================游戏设置处理==========================
def _handle_save_game_settings(self, client_id, message):
"""处理保存游戏设置请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "保存游戏设置", "save_game_settings")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "save_game_settings")
if not player_data:
return self.send_data(client_id, response)
# 获取设置数据
settings = message.get("settings", {})
if not settings:
return self.send_data(client_id, {
"type": "save_game_settings_response",
"success": False,
"message": "设置数据为空"
})
# 验证设置数据格式
valid_settings = {}
# 验证背景音乐音量 (0.0-1.0)
if "背景音乐音量" in settings:
volume = settings["背景音乐音量"]
if isinstance(volume, (int, float)) and 0.0 <= volume <= 1.0:
valid_settings["背景音乐音量"] = float(volume)
else:
return self.send_data(client_id, {
"type": "save_game_settings_response",
"success": False,
"message": "背景音乐音量值无效应在0.0-1.0之间"
})
# 验证天气显示设置
if "天气显示" in settings:
weather_display = settings["天气显示"]
if isinstance(weather_display, bool):
valid_settings["天气显示"] = weather_display
else:
return self.send_data(client_id, {
"type": "save_game_settings_response",
"success": False,
"message": "天气显示设置值无效,应为布尔值"
})
# 保存设置到玩家数据
if "游戏设置" not in player_data:
player_data["游戏设置"] = {}
player_data["游戏设置"].update(valid_settings)
# 保存到数据库
if self.save_player_data(username, player_data):
self.log('INFO', f"用户 {username} 保存游戏设置: {valid_settings}", 'SERVER')
return self.send_data(client_id, {
"type": "save_game_settings_response",
"success": True,
"message": "游戏设置保存成功",
"settings": valid_settings
})
else:
return self.send_data(client_id, {
"type": "save_game_settings_response",
"success": False,
"message": "保存游戏设置失败"
})
#==========================游戏设置处理==========================
#==========================玩家游玩时间处理==========================
#处理获取玩家游玩时间请求
def _handle_get_play_time(self, client_id):
"""处理获取游玩时间请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取游玩时间")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "play_time")
if not player_data:
return self.send_data(client_id, response)
# 计算当前会话的游玩时间
login_timestamp = self.user_data[client_id].get("login_timestamp", time.time())
current_session_seconds = int(time.time() - login_timestamp)
# 格式化当前会话时间
current_hours = current_session_seconds // 3600
current_minutes = (current_session_seconds % 3600) // 60
current_seconds = current_session_seconds % 60
current_session_time = f"{current_hours}{current_minutes}{current_seconds}"
# 获取最后登录时间和总游玩时间
last_login_time = player_data.get("最后登录时间", "未知")
total_login_time = player_data.get("总游玩时间", "0时0分0秒")
self.log('INFO', f"玩家 {username} 请求游玩时间统计", 'SERVER')
return self.send_data(client_id, {
"type": "play_time_response",
"success": True,
"最后登录时间": last_login_time,
"总游玩时间": total_login_time,
"current_session_time": current_session_time
})
#处理更新游玩时间请求
def _handle_update_play_time(self, client_id):
"""处理更新游玩时间请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "更新游玩时间", "update_time")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "update_time")
if not player_data:
return self.send_data(client_id, response)
# 计算当前会话的游玩时间
login_timestamp = self.user_data[client_id].get("login_timestamp", time.time())
play_time_seconds = int(time.time() - login_timestamp)
# 解析现有的总游玩时间
total_time_str = player_data.get("总游玩时间", "0时0分0秒")
time_parts = re.match(r"(?:(\d+)时)?(?:(\d+)分)?(?:(\d+)秒)?", total_time_str)
if time_parts:
hours = int(time_parts.group(1) or 0)
minutes = int(time_parts.group(2) or 0)
seconds = int(time_parts.group(3) or 0)
# 计算新的总游玩时间
total_seconds = hours * 3600 + minutes * 60 + seconds + play_time_seconds
new_hours = total_seconds // 3600
new_minutes = (total_seconds % 3600) // 60
new_seconds = total_seconds % 60
# 更新总游玩时间
player_data["总游玩时间"] = f"{new_hours}{new_minutes}{new_seconds}"
# 保存玩家数据
self.save_player_data(username, player_data)
# 重置登录时间戳,以便下次计算
self.user_data[client_id]["login_timestamp"] = time.time()
self.log('INFO', f"已更新玩家 {username} 的游玩时间,当前游玩时间: {play_time_seconds} 秒,总游玩时间: {player_data['总游玩时间']}", 'SERVER')
return self.send_data(client_id, {
"type": "update_time_response",
"success": True,
"message": "游玩时间已更新",
"总游玩时间": player_data["总游玩时间"]
})
else:
self.log('ERROR', f"解析玩家 {username} 的游玩时间失败", 'SERVER')
return self.send_data(client_id, {
"type": "update_time_response",
"success": False,
"message": "更新游玩时间失败,格式错误"
})
#==========================玩家游玩时间处理==========================
#==========================玩家排行榜处理==========================
#处理获取玩家排行榜请求
def _handle_player_rankings_request(self, client_id, message):
"""处理获取玩家排行榜的请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取玩家排行榜", "player_rankings")
if not logged_in:
return self.send_data(client_id, response)
# 获取排序和筛选参数
sort_by = message.get("sort_by", "等级") # 排序字段
sort_order = message.get("sort_order", "desc") # 排序顺序
filter_online = message.get("filter_online", False) # 是否只显示在线玩家
search_qq = message.get("search_qq", "") # 搜索的QQ号
try:
players_data = []
total_registered_players = 0
# 优先使用MongoDB
if self.use_mongodb and self.mongo_api:
# 获取所有玩家基本信息
all_players = self.mongo_api.get_all_players_basic_info()
total_registered_players = len(all_players)
for player_data in all_players:
account_id = player_data.get("玩家账号", "")
# 如果有搜索条件,先检查是否匹配
if search_qq and search_qq not in account_id:
continue
# 统计背包中的种子数量
seed_count = sum(item.get("count", 0) for item in player_data.get("种子仓库", []))
# 检查玩家是否在线
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 filter_online and not is_online:
continue
# 解析总游玩时间为秒数(用于排序)
total_time_str = player_data.get("总游玩时间", "0时0分0秒")
total_time_seconds = self._parse_time_to_seconds(total_time_str)
# 解析最后登录时间为时间戳(用于排序)
last_login_str = player_data.get("最后登录时间", "未知")
last_login_timestamp = self._parse_login_time_to_timestamp(last_login_str)
# 获取体力值
stamina_system = player_data.get("体力系统", {})
current_stamina = stamina_system.get("当前体力值", 20)
player_info = {
"玩家账号": account_id,
"玩家昵称": player_data.get("玩家昵称", account_id),
"农场名称": player_data.get("农场名称", ""),
"等级": player_data.get("等级", 1),
"钱币": player_data.get("钱币", 0),
"经验值": player_data.get("经验值", 0),
"体力值": current_stamina,
"seed_count": seed_count,
"最后登录时间": last_login_str,
"last_login_timestamp": last_login_timestamp,
"总游玩时间": total_time_str,
"total_time_seconds": total_time_seconds,
"like_num": player_data.get("点赞系统", {}).get("总点赞数", 0),
"is_online": is_online
}
players_data.append(player_info)
else:
# 降级到文件系统
save_files = glob.glob(os.path.join("game_saves", "*.json"))
total_registered_players = len(save_files)
for save_file in save_files:
try:
# 从文件名提取账号ID
account_id = os.path.basename(save_file).split('.')[0]
# 如果有搜索条件,先检查是否匹配
if search_qq and search_qq not in account_id:
continue
# 加载玩家数据
with open(save_file, 'r', encoding='utf-8') as file:
player_data = json.load(file)
if player_data:
# 统计背包中的种子数量
seed_count = sum(item.get("count", 0) for item in player_data.get("种子仓库", []))
# 检查玩家是否在线
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 filter_online and not is_online:
continue
# 解析总游玩时间为秒数(用于排序)
total_time_str = player_data.get("总游玩时间", "0时0分0秒")
total_time_seconds = self._parse_time_to_seconds(total_time_str)
# 解析最后登录时间为时间戳(用于排序)
last_login_str = player_data.get("最后登录时间", "未知")
last_login_timestamp = self._parse_login_time_to_timestamp(last_login_str)
# 获取所需的玩家信息
stamina_system = player_data.get("体力系统", {})
current_stamina = stamina_system.get("当前体力值", 20)
player_info = {
"玩家账号": player_data.get("玩家账号", account_id),
"玩家昵称": player_data.get("玩家昵称", player_data.get("玩家账号", account_id)),
"农场名称": player_data.get("农场名称", ""),
"等级": player_data.get("等级", 1),
"钱币": player_data.get("钱币", 0),
"经验值": player_data.get("经验值", 0),
"体力值": current_stamina,
"seed_count": seed_count,
"最后登录时间": last_login_str,
"last_login_timestamp": last_login_timestamp,
"总游玩时间": total_time_str,
"total_time_seconds": total_time_seconds,
"like_num": player_data.get("点赞系统", {}).get("总点赞数", 0),
"is_online": is_online
}
players_data.append(player_info)
except Exception as e:
self.log('ERROR', f"读取玩家 {account_id} 的数据时出错: {str(e)}", 'SERVER')
# 根据排序参数进行排序
reverse_order = (sort_order == "desc")
if sort_by == "seed_count":
players_data.sort(key=lambda x: x["seed_count"], reverse=reverse_order)
elif sort_by == "等级":
players_data.sort(key=lambda x: x["等级"], reverse=reverse_order)
elif sort_by == "online_time":
players_data.sort(key=lambda x: x["total_time_seconds"], reverse=reverse_order)
elif sort_by == "login_time":
players_data.sort(key=lambda x: x["last_login_timestamp"], reverse=reverse_order)
elif sort_by == "like_num":
players_data.sort(key=lambda x: x["like_num"], reverse=reverse_order)
elif sort_by == "钱币":
players_data.sort(key=lambda x: x["钱币"], reverse=reverse_order)
else:
# 默认按等级排序
players_data.sort(key=lambda x: x["等级"], reverse=True)
# 统计在线玩家数量
online_count = sum(1 for player in players_data if player.get("is_online", False))
# 记录日志
search_info = f"搜索QQ{search_qq}" if search_qq else ""
filter_info = ",仅在线玩家" if filter_online else ""
sort_info = f",按{sort_by}{'降序' if reverse_order else '升序'}排序"
self.log('INFO', f"玩家 {self.user_data[client_id].get('username')} 请求玩家排行榜{search_info}{filter_info}{sort_info},返回 {len(players_data)} 个玩家数据,注册总人数:{total_registered_players},在线人数:{online_count}", 'SERVER')
# 返回排行榜数据(包含注册总人数)
return self.send_data(client_id, {
"type": "player_rankings_response",
"success": True,
"players": players_data,
"total_registered_players": total_registered_players,
"sort_by": sort_by,
"sort_order": sort_order,
"filter_online": filter_online,
"search_qq": search_qq
})
except Exception as e:
self.log('ERROR', f"处理玩家排行榜请求时出错: {str(e)}", 'SERVER')
return self.send_data(client_id, {
"type": "player_rankings_response",
"success": False,
"message": "获取排行榜数据失败"
})
# 辅助函数:将时间字符串转换为秒数
def _parse_time_to_seconds(self, time_str):
"""将时间字符串(如'1时30分45秒')转换为总秒数"""
try:
import re
# 使用正则表达式提取时、分、秒
pattern = r'(\d+)时(\d+)分(\d+)秒'
match = re.match(pattern, time_str)
if match:
hours = int(match.group(1))
minutes = int(match.group(2))
seconds = int(match.group(3))
return hours * 3600 + minutes * 60 + seconds
return 0
except:
return 0
# 辅助函数:将登录时间字符串转换为时间戳
def _parse_login_time_to_timestamp(self, login_time_str):
"""将登录时间字符串转换为时间戳用于排序"""
try:
if login_time_str == "未知":
return 0
# 解析格式2024年01月01日12时30分45秒
import datetime
dt = datetime.datetime.strptime(login_time_str, "%Y年%m月%d%H时%M分%S秒")
return dt.timestamp()
except:
return 0
#==========================玩家排行榜处理==========================
#==========================作物数据处理==========================
#处理客户端请求作物数据
def _handle_crop_data_request(self, client_id):
"""处理客户端请求作物数据"""
crop_data = self._load_crop_data()
if crop_data:
self.log('INFO', f"向客户端 {client_id} 发送作物数据", 'SERVER')
return self.send_data(client_id, {
"type": "crop_data_response",
"success": True,
"crop_data": crop_data
})
else:
return self.send_data(client_id, {
"type": "crop_data_response",
"success": False,
"message": "无法读取作物数据"
})
#==========================作物数据处理==========================
#==========================访问其他玩家农场处理==========================
#处理访问其他玩家农场的请求
def _handle_visit_player_request(self, client_id, message):
"""处理访问其他玩家农场的请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "访问玩家农场", "visit_player")
if not logged_in:
return self.send_data(client_id, response)
target_username = message.get("target_username", "")
if not target_username:
return self.send_data(client_id, {
"type": "visit_player_response",
"success": False,
"message": "缺少目标用户名"
})
# 加载目标玩家数据
target_player_data = self.load_player_data(target_username)
if not target_player_data:
return self.send_data(client_id, {
"type": "visit_player_response",
"success": False,
"message": f"无法找到玩家 {target_username} 的数据"
})
# 检查并修复目标玩家的智慧树配置格式
self._check_and_fix_wisdom_tree_config(target_player_data, target_username)
# 返回目标玩家的农场数据(只返回可见的数据,不包含敏感信息如密码)
target_stamina_system = target_player_data.get("体力系统", {})
target_current_stamina = target_stamina_system.get("当前体力值", 20)
safe_player_data = {
"玩家账号": target_player_data.get("玩家账号", target_username),
"username": target_username, # 添加username字段用于购买商品时标识卖家
"玩家昵称": target_player_data.get("玩家昵称", target_username),
"农场名称": target_player_data.get("农场名称", ""),
"等级": target_player_data.get("等级", 1),
"钱币": target_player_data.get("钱币", 0),
"经验值": target_player_data.get("经验值", 0),
"体力值": target_current_stamina,
"农场土地": target_player_data.get("农场土地", []),
"种子仓库": target_player_data.get("种子仓库", []),
"作物仓库": target_player_data.get("作物仓库", []),
"道具背包": target_player_data.get("道具背包", []),
"宠物背包": target_player_data.get("宠物背包", []),
"巡逻宠物": self._convert_patrol_pets_to_full_data(target_player_data),
"出战宠物": self._convert_battle_pets_to_full_data(target_player_data),
"稻草人配置": target_player_data.get("稻草人配置", {}),
"智慧树配置": target_player_data.get("智慧树配置", {}),
"小卖部配置": target_player_data.get("小卖部配置", {"商品列表": [], "格子数": 10}), # 添加小卖部配置
"点赞数": target_player_data.get("点赞系统", {}).get("总点赞数", 0), # 添加点赞数
"最后登录时间": target_player_data.get("最后登录时间", "未知"),
"总游玩时间": target_player_data.get("总游玩时间", "0时0分0秒"),
"total_likes": target_player_data.get("total_likes", 0),
"访问系统": target_player_data.get("访问系统", {
"总访问人数": 0,
"今日访问人数": 0,
"访问记录": {}
}) # 添加访问系统数据
}
current_username = self.user_data[client_id]["username"]
# 更新被访问玩家的访问系统数据
self._update_visit_system(target_username, current_username)
self.log('INFO', f"玩家 {current_username} 访问了玩家 {target_username} 的农场", 'SERVER')
# 记录玩家的访问状态
self.user_data[client_id]["visiting_mode"] = True
self.user_data[client_id]["visiting_target"] = target_username
return self.send_data(client_id, {
"type": "visit_player_response",
"success": True,
"message": f"成功获取玩家 {target_username} 的农场数据",
"player_data": safe_player_data,
"is_visiting": True
})
#==========================访问其他玩家农场处理==========================
# ==========================返回自己农场处理==========================
def _update_visit_system(self, target_username, visitor_username):
"""更新被访问玩家的访问系统数据"""
try:
# 加载被访问玩家的数据
target_player_data = self.load_player_data(target_username)
if not target_player_data:
self.log('ERROR', f"无法加载被访问玩家 {target_username} 的数据", 'SERVER')
return
# 获取访问者的昵称
visitor_player_data = self.load_player_data(visitor_username)
visitor_nickname = visitor_player_data.get("玩家昵称", visitor_username) if visitor_player_data else visitor_username
# 初始化访问系统(如果不存在)
if "访问系统" not in target_player_data:
target_player_data["访问系统"] = {
"总访问人数": 0,
"今日访问人数": 0,
"访问记录": {}
}
visit_system = target_player_data["访问系统"]
# 获取今日日期
from datetime import datetime
today = datetime.now().strftime("%Y-%m-%d")
# 更新总访问人数
visit_system["总访问人数"] = visit_system.get("总访问人数", 0) + 1
# 检查是否需要重置今日访问人数(新的一天)
last_visit_date = visit_system.get("最后访问日期", "")
if last_visit_date != today:
visit_system["今日访问人数"] = 0
visit_system["最后访问日期"] = today
# 更新今日访问人数
visit_system["今日访问人数"] = visit_system.get("今日访问人数", 0) + 1
# 更新访问记录
if "访问记录" not in visit_system:
visit_system["访问记录"] = {}
if today not in visit_system["访问记录"]:
visit_system["访问记录"][today] = []
# 添加访问者昵称到今日访问记录(避免重复)
if visitor_nickname not in visit_system["访问记录"][today]:
visit_system["访问记录"][today].append(visitor_nickname)
# 保存更新后的数据
if self.save_player_data(target_username, target_player_data):
self.log('INFO', f"成功更新玩家 {target_username} 的访问系统数据,访问者: {visitor_nickname}", 'SERVER')
else:
self.log('ERROR', f"保存玩家 {target_username} 的访问系统数据失败", 'SERVER')
except Exception as e:
self.log('ERROR', f"更新访问系统数据时出错: {e}", 'SERVER')
def _reset_daily_visit_count(self):
"""重置所有玩家的今日访问人数(凌晨调用)"""
try:
# 获取所有玩家的基本信息
if hasattr(self, 'mongo_api') and self.mongo_api:
players_info = self.mongo_api.get_all_players_basic_info()
from datetime import datetime
today = datetime.now().strftime("%Y-%m-%d")
reset_count = 0
for player_info in players_info:
username = player_info.get("玩家账号")
if username:
player_data = self.load_player_data(username)
if player_data and "访问系统" in player_data:
visit_system = player_data["访问系统"]
last_visit_date = visit_system.get("最后访问日期", "")
# 如果不是今天,重置今日访问人数
if last_visit_date != today:
visit_system["今日访问人数"] = 0
visit_system["最后访问日期"] = today
if self.save_player_data(username, player_data):
reset_count += 1
self.log('INFO', f"成功重置了 {reset_count} 个玩家的今日访问人数", 'SERVER')
except Exception as e:
self.log('ERROR', f"重置今日访问人数时出错: {e}", 'SERVER')
def _handle_give_money_request(self, client_id, message):
"""处理送金币请求"""
try:
# 获取发送者信息
sender_info = self.user_data.get(client_id)
if not sender_info or not sender_info.get("logged_in", False):
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "请先登录"
})
return
sender_username = sender_info.get("username")
target_username = message.get("target_username", "")
amount = message.get("amount", 0)
# 验证参数
if not target_username:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "目标玩家用户名不能为空"
})
return
if amount != 500:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "每次只能送500金币"
})
return
if sender_username == target_username:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "不能给自己送金币"
})
return
# 加载发送者数据
sender_data = self.load_player_data(sender_username)
if not sender_data:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "无法加载发送者数据"
})
return
# 检查发送者金币是否足够
sender_money = sender_data.get("钱币", 0)
if sender_money < amount:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": f"您的金币不足,当前拥有{sender_money}金币"
})
return
# 加载接收者数据
target_data = self.load_player_data(target_username)
if not target_data:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "目标玩家不存在"
})
return
# 执行金币转移
sender_data["钱币"] = sender_money - amount
target_data["钱币"] = target_data.get("钱币", 0) + amount
# 记录送金币日志
from datetime import datetime
current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_message = f"[{current_time}] {sender_username} 送给 {target_username} {amount}金币"
self.log('INFO', log_message, 'GIVE_MONEY')
# 保存数据
if self.save_player_data(sender_username, sender_data) and self.save_player_data(target_username, target_data):
# 获取目标玩家昵称
target_nickname = target_data.get("玩家昵称", target_username)
# 发送成功响应
self.send_data(client_id, {
"type": "give_money_response",
"success": True,
"message": f"成功送给 {target_nickname} {amount}金币!",
"updated_data": {
"钱币": sender_data["钱币"]
},
"target_updated_data": {
"钱币": target_data["钱币"]
}
})
# 如果目标玩家在线,通知他们收到金币
target_client_id = None
for cid, user_info in self.user_data.items():
if user_info.get("username") == target_username and user_info.get("logged_in", False):
target_client_id = cid
break
if target_client_id:
sender_nickname = sender_data.get("玩家昵称", sender_username)
self.send_data(target_client_id, {
"type": "money_received_notification",
"sender_nickname": sender_nickname,
"amount": amount,
"new_money": target_data["钱币"]
})
else:
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "数据保存失败,请重试"
})
except Exception as e:
self.log('ERROR', f"处理送金币请求失败: {str(e)}", 'GIVE_MONEY')
self.send_data(client_id, {
"type": "give_money_response",
"success": False,
"message": "服务器内部错误"
})
#==========================访问系统处理==========================
#==========================返回自己农场处理==========================
#处理玩家返回自己农场的请求
def _handle_return_my_farm_request(self, client_id, message):
"""处理玩家返回自己农场的请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "返回自己农场", "return_my_farm")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "return_my_farm")
if not player_data:
return self.send_data(client_id, response)
# 清除访问状态
self.user_data[client_id]["visiting_mode"] = False
self.user_data[client_id]["visiting_target"] = ""
# 清理偷菜免被发现计数器
self._clear_player_steal_immunity(username)
self.log('INFO', f"玩家 {username} 返回了自己的农场", 'SERVER')
# 返回玩家自己的农场数据
my_stamina_system = player_data.get("体力系统", {})
my_current_stamina = my_stamina_system.get("当前体力值", 20)
return self.send_data(client_id, {
"type": "return_my_farm_response",
"success": True,
"message": "已返回自己的农场",
"player_data": {
"玩家账号": player_data.get("玩家账号", username),
"玩家昵称": player_data.get("玩家昵称", username),
"农场名称": player_data.get("农场名称", ""),
"等级": player_data.get("等级", 1),
"钱币": player_data.get("钱币", 0),
"经验值": player_data.get("经验值", 0),
"体力值": my_current_stamina,
"农场土地": player_data.get("农场土地", []),
"种子仓库": player_data.get("种子仓库", []),
"宠物背包": player_data.get("宠物背包", []),
"巡逻宠物": self._convert_patrol_pets_to_full_data(player_data),
"出战宠物": self._convert_battle_pets_to_full_data(player_data),
"稻草人配置": player_data.get("稻草人配置", {}),
"total_likes": player_data.get("total_likes", 0)
},
"is_visiting": False
})
#==========================返回自己农场处理==========================
#==========================在线礼包处理==========================
#处理获取在线礼包数据请求
def _handle_get_online_gift_data_request(self, client_id, message):
"""处理获取在线礼包数据请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取在线礼包数据", "get_online_gift_data")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "get_online_gift_data")
if not player_data:
return self.send_data(client_id, response)
# 确保在线礼包数据已初始化
self._cleanup_online_gift_history(player_data)
online_gift_data = player_data["在线礼包"]
# 更新在线时间
current_time = time.time()
login_time = online_gift_data.get("登录时间", current_time)
# 计算本次登录的在线时间并累加
if client_id in self.user_data and self.user_data[client_id].get("logged_in", False):
session_online_time = current_time - self.user_data[client_id].get("login_timestamp", current_time)
online_gift_data["今日在线时长"] += session_online_time
# 重置登录时间戳
self.user_data[client_id]["login_timestamp"] = current_time
# 保存数据
self.save_player_data(username, player_data)
return self.send_data(client_id, {
"type": "online_gift_data_response",
"success": True,
"current_online_duration": online_gift_data["今日在线时长"],
"claimed_gifts": {gift: True for gift in online_gift_data["已领取礼包"]}
})
#处理领取在线礼包请求
def _handle_claim_online_gift_request(self, client_id, message):
"""处理领取在线礼包请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "领取在线礼包", "claim_online_gift")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "claim_online_gift")
if not player_data:
return self.send_data(client_id, response)
gift_name = message.get("gift_name", "")
if not gift_name:
return self.send_data(client_id, {
"type": "claim_online_gift_response",
"success": False,
"message": "礼包名称不能为空"
})
# 加载在线礼包配置
config = self._load_online_gift_config()
gift_config = config.get("在线礼包配置", {})
if gift_name not in gift_config:
return self.send_data(client_id, {
"type": "claim_online_gift_response",
"success": False,
"message": "无效的礼包名称"
})
# 确保在线礼包数据已初始化
self._cleanup_online_gift_history(player_data)
online_gift_data = player_data["在线礼包"]
# 检查是否已领取
if gift_name in online_gift_data["已领取礼包"]:
return self.send_data(client_id, {
"type": "claim_online_gift_response",
"success": False,
"message": "该礼包今日已领取"
})
# 更新在线时间
current_time = time.time()
if client_id in self.user_data and self.user_data[client_id].get("logged_in", False):
session_online_time = current_time - self.user_data[client_id].get("login_timestamp", current_time)
online_gift_data["今日在线时长"] += session_online_time
# 重置登录时间戳
self.user_data[client_id]["login_timestamp"] = current_time
# 检查在线时长是否满足条件
gift_info = gift_config[gift_name]
required_time = gift_info["时长秒数"]
current_duration = online_gift_data["今日在线时长"]
if current_duration < required_time:
return self.send_data(client_id, {
"type": "claim_online_gift_response",
"success": False,
"message": f"在线时间不足,还需要 {self._format_time(required_time - current_duration)}"
})
# 发放奖励
rewards = gift_info["奖励"]
self._apply_online_gift_rewards_new(player_data, rewards)
# 记录领取状态
online_gift_data["已领取礼包"].append(gift_name)
# 保存数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 领取在线礼包 {gift_name},获得奖励: {rewards}", 'SERVER')
return self.send_data(client_id, {
"type": "claim_online_gift_response",
"success": True,
"message": f"成功领取 {gift_name} 礼包!",
"gift_name": gift_name,
"rewards": rewards,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", [])
}
})
#发放在线礼包奖励(新版本 - 支持中文配置)
def _apply_online_gift_rewards_new(self, player_data, rewards):
"""发放在线礼包奖励(中文配置格式)"""
# 发放金币
if "金币" in rewards:
player_data["钱币"] = player_data.get("钱币", 0) + rewards["金币"]
# 发放经验
if "经验" in rewards:
old_experience = player_data.get("经验值", 0)
player_data["经验值"] = old_experience + rewards["经验"]
# 检查是否升级
self._check_level_up(player_data)
# 发放种子
if "种子" in rewards:
player_bag = player_data.get("种子仓库", [])
crop_data = self._load_crop_data()
for seed_info in rewards["种子"]:
seed_name = seed_info["名称"]
seed_count = seed_info["数量"]
# 从作物数据中获取品质信息
quality = "普通" # 默认品质
if crop_data and seed_name in crop_data:
quality = crop_data[seed_name].get("品质", "普通")
# 查找是否已有该种子
found = False
for item in player_bag:
if item.get("name") == seed_name:
item["count"] += seed_count
found = True
break
# 如果没有找到,添加新种子
if not found:
player_bag.append({
"name": seed_name,
"quality": quality,
"count": seed_count
})
player_data["种子仓库"] = player_bag
#发放在线礼包奖励(旧版本)
def _apply_online_gift_rewards(self, player_data, rewards):
"""发放在线礼包奖励"""
# 发放金币
if "钱币" in rewards:
player_data["钱币"] = player_data.get("钱币", 0) + rewards["钱币"]
# 发放经验
if "经验值" in rewards:
old_experience = player_data.get("经验值", 0)
player_data["经验值"] = old_experience + rewards["经验值"]
# 检查是否升级
self._check_level_up(player_data)
# 发放种子
if "seeds" in rewards:
player_bag = player_data.get("种子仓库", [])
crop_data = self._load_crop_data()
for seed_info in rewards["seeds"]:
seed_name = seed_info["name"]
seed_count = seed_info["count"]
# 从作物数据中获取品质信息
quality = "普通" # 默认品质
if crop_data and seed_name in crop_data:
quality = crop_data[seed_name].get("品质", "普通")
# 查找是否已有该种子
found = False
for item in player_bag:
if item["name"] == seed_name:
item["count"] += seed_count
found = True
break
# 如果没有找到,添加新物品
if not found:
player_bag.append({
"name": seed_name,
"count": seed_count,
"type": "seed",
"quality": quality
})
player_data["种子仓库"] = player_bag
#检查玩家是否升级
def _check_level_up(self, player_data):
"""检查玩家是否升级"""
current_level = player_data.get("等级", 1)
current_experience = player_data.get("经验值", 0)
# 计算升级所需经验 (每级需要的经验递增)
experience_needed = current_level * 100
# 检查是否可以升级
while current_experience >= experience_needed:
current_level += 1
current_experience -= experience_needed
experience_needed = current_level * 100
player_data["等级"] = current_level
player_data["经验值"] = current_experience
#更新玩家今日在线时间
def _update_daily_online_time(self, client_id, player_data):
"""更新玩家今日在线时间(现在由中文在线礼包系统管理)"""
if client_id not in self.user_data or not self.user_data[client_id].get("logged_in", False):
return 0
# 使用新的中文在线礼包系统
current_time = time.time()
login_time = self.user_data[client_id].get("login_timestamp", current_time)
session_online_time = current_time - login_time
# 重置用户登录时间戳
self.user_data[client_id]["login_timestamp"] = current_time
# 确保在线礼包数据存在
self._cleanup_online_gift_history(player_data)
online_gift_data = player_data.get("在线礼包", {})
if online_gift_data:
# 更新中文在线礼包系统的在线时长
online_gift_data["今日在线时长"] = online_gift_data.get("今日在线时长", 0.0) + session_online_time
return online_gift_data["今日在线时长"]
return session_online_time
#格式化时间显示
def _format_time(self, seconds):
"""格式化时间显示"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = int(seconds % 60)
if hours > 0:
return f"{hours}小时{minutes}分钟{secs}"
elif minutes > 0:
return f"{minutes}分钟{secs}"
else:
return f"{secs}"
#==========================在线礼包处理==========================
#==========================PING延迟检测处理==========================
#处理ping请求
def _handle_ping_request(self, client_id, message):
"""处理客户端ping请求立即返回pong响应"""
timestamp = message.get("timestamp", time.time())
# 立即返回pong响应
pong_response = {
"type": "pong",
"timestamp": timestamp,
"server_time": time.time()
}
return self.send_data(client_id, pong_response)
#==========================PING延迟检测处理==========================
#==========================全服大喇叭消息处理==========================
#处理全服大喇叭消息
def _handle_global_broadcast_message(self, client_id, message):
"""处理全服大喇叭消息"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "发送全服大喇叭消息", "global_broadcast")
if not logged_in:
return self.send_data(client_id, response)
# 获取消息内容
content = message.get("content", "").strip()
if not content:
return self.send_data(client_id, {
"type": "global_broadcast_response",
"success": False,
"message": "消息内容不能为空"
})
# 检查消息长度
if len(content) > 200:
return self.send_data(client_id, {
"type": "global_broadcast_response",
"success": False,
"message": "消息长度不能超过200字符"
})
# 获取发送者信息
username = self.user_data[client_id]["username"]
# 获取玩家数据以获取昵称
player_data = self.load_player_data(username)
player_name = ""
if player_data:
player_name = player_data.get("玩家昵称", "")
# 获取当前时间戳
current_timestamp = time.time()
# 创建广播消息
broadcast_message = {
"type": "global_broadcast_message",
"username": username,
"玩家昵称": player_name,
"content": content,
"timestamp": current_timestamp
}
# 广播给所有在线用户
self.broadcast(broadcast_message)
# 保存消息到MongoDB
if self.mongo_api and self.mongo_api.is_connected():
success = self.mongo_api.save_chat_message(username, player_name, content)
if not success:
self.log('WARNING', f"保存聊天消息到MongoDB失败尝试保存到本地文件", 'BROADCAST')
self._save_broadcast_message_to_log(username, player_name, content)
else:
# 如果MongoDB不可用保存到本地文件作为备份
self.log('WARNING', f"MongoDB不可用保存聊天消息到本地文件", 'BROADCAST')
self._save_broadcast_message_to_log(username, player_name, content)
# 发送成功响应给发送者
self.send_data(client_id, {
"type": "global_broadcast_response",
"success": True,
"message": "大喇叭消息发送成功"
})
self.log('INFO', f"用户 {username}({player_name}) 发送全服大喇叭消息: {content}", 'BROADCAST')
return True
#保存大喇叭消息到日志文件
def _save_broadcast_message_to_log(self, username, player_name, content):
"""保存大喇叭消息到日志文件"""
try:
# 创建chat文件夹如果不存在
import os
chat_dir = os.path.join(os.path.dirname(__file__), "chat")
if not os.path.exists(chat_dir):
os.makedirs(chat_dir)
# 获取当前日期作为文件名
current_date = datetime.datetime.now().strftime("%Y-%m-%d")
log_file_path = os.path.join(chat_dir, f"{current_date}.log")
# 格式化时间戳
timestamp = datetime.datetime.now().strftime("%Y年%m月%d%H:%M:%S")
# 创建日志条目
display_name = player_name if player_name else username
log_entry = f"[{timestamp}] {display_name}({username}): {content}\n"
# 追加到日志文件
with open(log_file_path, 'a', encoding='utf-8') as f:
f.write(log_entry)
except Exception as e:
self.log('ERROR', f"保存大喇叭消息到日志文件时出错: {str(e)}", 'BROADCAST')
def _handle_request_broadcast_history(self, client_id, message):
"""处理请求全服大喇叭历史消息"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "请求全服大喇叭历史消息", "request_broadcast_history")
if not logged_in:
return self.send_data(client_id, response)
try:
days = message.get("days", 3) # 默认加载3天
limit = message.get("limit", 500) # 默认限制500条
if days > 30: # 限制最多30天
days = 30
# 优先从MongoDB获取历史消息
messages = []
if self.mongo_api and self.mongo_api.is_connected():
try:
messages = self.mongo_api.get_chat_history(days, limit)
# 转换数据格式以兼容客户端
for msg in messages:
msg["玩家昵称"] = msg.get("player_name", "")
msg["display_name"] = msg.get("player_name", "") if msg.get("player_name") else msg.get("username", "匿名")
self.log('INFO', f"从MongoDB获取了 {len(messages)} 条历史消息", 'SERVER')
except Exception as e:
self.log('ERROR', f"从MongoDB获取历史消息失败: {str(e)}", 'SERVER')
messages = []
# 如果MongoDB获取失败或没有数据尝试从本地文件获取
if not messages:
self.log('INFO', f"尝试从本地文件获取历史消息", 'SERVER')
messages = self._load_broadcast_history(days)
# 发送历史消息响应
response = {
"type": "broadcast_history_response",
"success": True,
"messages": messages,
"days": days
}
self.log('INFO', f"向客户端 {client_id} 发送了 {len(messages)} 条历史消息(最近{days}天)", 'SERVER')
return self.send_data(client_id, response)
except Exception as e:
self.log('ERROR', f"处理全服大喇叭历史消息请求失败: {str(e)}", 'SERVER')
error_response = {
"type": "broadcast_history_response",
"success": False,
"message": "加载历史消息失败"
}
return self.send_data(client_id, error_response)
def _load_broadcast_history(self, days):
"""从日志文件加载历史消息"""
messages = []
chat_dir = os.path.join(os.path.dirname(__file__), "chat")
if not os.path.exists(chat_dir):
return messages
try:
# 获取需要加载的日期范围
end_date = datetime.datetime.now()
start_date = end_date - datetime.timedelta(days=days-1)
self.log('INFO', f"查找历史消息,日期范围: {start_date.strftime('%Y-%m-%d')}{end_date.strftime('%Y-%m-%d')}", 'SERVER')
# 遍历日期范围内的所有日志文件
current_date = start_date
while current_date <= end_date:
date_str = current_date.strftime("%Y-%m-%d")
log_file = os.path.join(chat_dir, f"{date_str}.log")
self.log('INFO', f"检查日志文件: {log_file}", 'SERVER')
if os.path.exists(log_file):
self.log('INFO', f"找到日志文件: {log_file}", 'SERVER')
with open(log_file, "r", encoding="utf-8") as f:
lines = f.readlines()
self.log('INFO', f"日志文件 {date_str}.log 包含 {len(lines)}", 'SERVER')
# 解析每一行消息
for line in lines:
line = line.strip()
if line:
parsed_message = self._parse_log_message(line)
if parsed_message:
messages.append(parsed_message)
self.log('INFO', f"解析消息成功: {parsed_message['content'][:20]}...", 'SERVER')
else:
self.log('WARNING', f"解析消息失败: {line[:50]}...", 'SERVER')
else:
self.log('INFO', f"日志文件不存在: {log_file}", 'SERVER')
current_date += datetime.timedelta(days=1)
# 按时间戳排序
messages.sort(key=lambda x: x.get("timestamp", 0))
# 限制消息数量最多返回500条
if len(messages) > 500:
messages = messages[-500:]
return messages
except Exception as e:
self.log('ERROR', f"加载全服大喇叭历史消息失败: {str(e)}", 'SERVER')
return []
def _parse_log_message(self, line):
"""解析日志消息行"""
try:
# 消息格式: [时间] 昵称(QQ号): 消息内容
import re
# 匹配时间部分
time_match = re.match(r'\[([^\]]+)\]', line)
if not time_match:
return None
time_str = time_match.group(1)
# 匹配用户名和消息内容
# 格式: 昵称(QQ号): 消息内容
content_part = line[len(time_match.group(0)):].strip()
# 查找用户名和消息内容
user_match = re.match(r'([^(]+)\(([^)]+)\):\s*(.+)', content_part)
if not user_match:
return None
player_name = user_match.group(1).strip()
username = user_match.group(2).strip()
content = user_match.group(3).strip()
# 解析时间戳
try:
# 时间格式: 2024年01月01日 12:00:00
time_obj = datetime.datetime.strptime(time_str, "%Y年%m月%d%H:%M:%S")
timestamp = time_obj.timestamp()
except:
timestamp = time.time()
return {
"username": username,
"玩家昵称": player_name,
"display_name": player_name if player_name else username,
"content": content,
"timestamp": timestamp,
"time_str": time_str
}
except Exception as e:
self.log('ERROR', f"解析日志消息失败: {line}, 错误: {str(e)}", 'SERVER')
return None
#==========================全服大喇叭消息处理==========================
# #==========================聊天消息处理==========================
# #处理聊天消息(暂未完成)
# def _handle_chat_message(self, client_id, message):
# """处理聊天消息"""
# # 检查用户是否已登录
# logged_in, response = self._check_user_logged_in(client_id, "发送聊天消息")
# if not logged_in:
# return self.send_data(client_id, response)
# content = message.get("content", "")
# if not content.strip():
# return self.send_data(client_id, {
# "type": "chat_response",
# "success": False,
# "message": "消息内容不能为空"
# })
# username = self.user_data[client_id]["username"]
# # 广播聊天消息给所有在线用户
# chat_message = {
# "type": "chat_message",
# "username": username,
# "content": content,
# "timestamp": time.time()
# }
# self.broadcast(chat_message)
# self.log('INFO', f"用户 {username} 发送聊天消息: {content}", 'SERVER')
# return True
# #==========================聊天消息处理==========================
#==========================每日签到处理==========================
#加载每日签到配置
def _load_daily_check_in_config(self):
"""加载每日签到配置 - 优先使用MongoDB失败则回退到JSON文件"""
# 优先尝试从MongoDB获取配置
if hasattr(self, 'use_mongodb') and self.use_mongodb and self.mongo_api:
try:
config = self.mongo_api.get_daily_checkin_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加载每日签到配置失败: {e}回退到JSON文件", 'SERVER')
# 回退到JSON文件
try:
config_path = os.path.join(self.config_dir, "daily_checkin_config.json")
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
self.log('INFO', "从JSON文件成功加载每日签到配置", 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"从JSON文件加载每日签到配置失败: {e}", 'SERVER')
# 默认配置
self.log('WARNING', "使用默认每日签到配置", 'SERVER')
return {
"基础奖励": {
"金币": {"最小值": 200, "最大值": 500, "图标": "💰", "颜色": "#FFD700"},
"经验": {"最小值": 50, "最大值": 120, "图标": "", "颜色": "#00BFFF"}
},
"种子奖励": {
"普通": {"概率": 0.6, "数量范围": [2, 5], "种子池": ["小麦", "胡萝卜", "土豆", "稻谷"]},
"优良": {"概率": 0.25, "数量范围": [2, 4], "种子池": ["玉米", "番茄", "洋葱", "大豆", "豌豆", "黄瓜", "大白菜"]},
"稀有": {"概率": 0.12, "数量范围": [1, 3], "种子池": ["草莓", "花椰菜", "柿子", "蓝莓", "树莓"]},
"史诗": {"概率": 0.025, "数量范围": [1, 2], "种子池": ["葡萄", "南瓜", "芦笋", "茄子", "向日葵", "蕨菜"]},
"传奇": {"概率": 0.005, "数量范围": [1, 1], "种子池": ["西瓜", "甘蔗", "香草", "甜菜", "人参", "富贵竹", "芦荟", "哈密瓜"]}
},
"连续签到奖励": {
"第3天": {"额外金币": 100, "额外经验": 50, "描述": "连续签到奖励"},
"第7天": {"额外金币": 200, "额外经验": 100, "描述": "一周连击奖励"},
"第14天": {"额外金币": 500, "额外经验": 200, "描述": "半月连击奖励"},
"第21天": {"额外金币": 800, "额外经验": 300, "描述": "三周连击奖励"},
"第30天": {"额外金币": 1500, "额外经验": 500, "描述": "满月连击奖励"}
}
}
#更新每日签到配置到MongoDB
def _update_daily_checkin_config_to_mongodb(self, config_data):
"""更新每日签到配置到MongoDB"""
if hasattr(self, 'use_mongodb') and self.use_mongodb and self.mongo_api:
try:
success = self.mongo_api.update_daily_checkin_config(config_data)
if success:
self.log('INFO', "成功更新每日签到配置到MongoDB", 'SERVER')
return True
else:
self.log('ERROR', "更新每日签到配置到MongoDB失败", 'SERVER')
return False
except Exception as e:
self.log('ERROR', f"更新每日签到配置到MongoDB异常: {e}", 'SERVER')
return False
else:
self.log('WARNING', "MongoDB未连接无法更新配置", 'SERVER')
return False
#处理每日签到请求
def _handle_daily_check_in_request(self, client_id, message):
"""处理每日签到请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "每日签到", "daily_check_in")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "daily_check_in")
if not player_data:
return self.send_data(client_id, response)
# 清理过期签到记录并使用新格式
self._cleanup_check_in_history(player_data)
# 检查今日是否已签到
current_time = datetime.datetime.now()
today_key = current_time.strftime("%Y年%m月%d")
check_in_history = player_data.get("签到历史", {})
# 检查今日是否已签到
for time_key in check_in_history.keys():
if time_key.startswith(today_key):
return self.send_data(client_id, {
"type": "daily_check_in_response",
"success": False,
"message": "今日已签到,请明日再来",
"has_checked_in": True
})
# 计算连续签到天数
consecutive_days = self._calculate_consecutive_check_in_days_new(check_in_history)
# 生成签到奖励
config = self._load_daily_check_in_config()
rewards = self._generate_check_in_rewards_new(consecutive_days, config)
# 发放奖励
self._apply_check_in_rewards(player_data, rewards)
# 保存签到记录 - 使用新格式
if "签到历史" not in player_data:
player_data["签到历史"] = {}
time_key = current_time.strftime("%Y年%m月%d%H时%M分%S秒")
reward_text = self._format_reward_text_simple(rewards)
player_data["签到历史"][time_key] = reward_text
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 完成每日签到,连续 {consecutive_days} 天,获得奖励: {rewards}", 'SERVER')
return self.send_data(client_id, {
"type": "daily_check_in_response",
"success": True,
"message": f"签到成功!连续签到 {consecutive_days}",
"rewards": rewards,
"consecutive_days": consecutive_days,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", [])
}
})
#处理客户端获取签到数据请求
def _handle_get_check_in_data_request(self, client_id, message):
"""处理获取签到数据请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取签到数据", "get_check_in_data")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "get_check_in_data")
if not player_data:
return self.send_data(client_id, response)
# 清理过期签到记录
self._cleanup_check_in_history(player_data)
check_in_history = player_data.get("签到历史", {})
# 计算连续签到天数
consecutive_days = self._calculate_consecutive_check_in_days_new(check_in_history)
# 检查今日是否已签到
current_time = datetime.datetime.now()
today_key = current_time.strftime("%Y年%m月%d")
has_checked_in_today = any(time_key.startswith(today_key) for time_key in check_in_history.keys())
return self.send_data(client_id, {
"type": "check_in_data_response",
"success": True,
"check_in_data": check_in_history,
"consecutive_days": consecutive_days,
"has_checked_in_today": has_checked_in_today,
"current_date": current_time.strftime("%Y年%m月%d")
})
#清理过期签到记录
def _cleanup_check_in_history(self, player_data):
"""清理过期签到记录只保留最近30天的记录"""
if "签到历史" not in player_data:
return
current_time = datetime.datetime.now()
cutoff_time = current_time - datetime.timedelta(days=30)
# 清理过期记录
history = player_data["签到历史"]
keys_to_remove = []
for time_key in history.keys():
try:
# 解析时间字符串
check_time = datetime.datetime.strptime(time_key, "%Y年%m月%d%H时%M分%S秒")
if check_time < cutoff_time:
keys_to_remove.append(time_key)
except:
# 如果解析失败,删除这条记录
keys_to_remove.append(time_key)
for key in keys_to_remove:
del history[key]
#计算连续签到天数(新版本)
def _calculate_consecutive_check_in_days_new(self, check_in_history):
"""计算连续签到天数(新版本)"""
if not check_in_history:
return 0
# 提取所有签到日期(只取日期部分)
check_dates = set()
for time_key in check_in_history.keys():
try:
check_time = datetime.datetime.strptime(time_key, "%Y年%m月%d%H时%M分%S秒")
date_str = check_time.strftime("%Y-%m-%d")
check_dates.add(date_str)
except:
continue
if not check_dates:
return 0
# 从今天开始向前计算连续天数
consecutive_days = 0
current_date = datetime.datetime.now()
# 检查今天是否已签到
today_str = current_date.strftime("%Y-%m-%d")
if today_str in check_dates:
consecutive_days += 1
check_date = current_date - datetime.timedelta(days=1)
else:
check_date = current_date - datetime.timedelta(days=1)
# 向前查找连续签到天数
while True:
date_str = check_date.strftime("%Y-%m-%d")
if date_str in check_dates:
consecutive_days += 1
check_date -= datetime.timedelta(days=1)
else:
break
# 限制最大连续天数为30天
if consecutive_days >= 30:
break
return consecutive_days
#生成签到奖励(新版本)
def _generate_check_in_rewards_new(self, consecutive_days, config):
"""生成签到奖励(新版本)"""
import random
rewards = {}
# 基础奖励倍数
base_multiplier = 1.0 + (consecutive_days - 1) * 0.1
max_multiplier = 3.0
multiplier = min(base_multiplier, max_multiplier)
# 基础金币奖励
coin_config = config.get("基础奖励", {}).get("金币", {})
base_coins = random.randint(coin_config.get("最小值", 200), coin_config.get("最大值", 500))
rewards["coins"] = int(base_coins * multiplier)
# 基础经验奖励
exp_config = config.get("基础奖励", {}).get("经验", {})
base_exp = random.randint(exp_config.get("最小值", 50), exp_config.get("最大值", 120))
rewards["exp"] = int(base_exp * multiplier)
# 种子奖励
seeds = self._generate_check_in_seeds_new(consecutive_days, config)
if seeds:
rewards["seeds"] = seeds
# 连续签到特殊奖励
consecutive_rewards = config.get("连续签到奖励", {})
for milestone, bonus in consecutive_rewards.items():
milestone_days = int(milestone.replace("", "").replace("", ""))
if consecutive_days >= milestone_days:
if "额外金币" in bonus:
rewards["bonus_coins"] = bonus["额外金币"]
if "额外经验" in bonus:
rewards["bonus_exp"] = bonus["额外经验"]
return rewards
#生成签到种子奖励(新版本)
def _generate_check_in_seeds_new(self, consecutive_days, config):
"""生成签到种子奖励(新版本)"""
import random
seeds = []
seed_configs = config.get("种子奖励", {})
# 根据连续签到天数确定种子稀有度
if consecutive_days <= 2:
rarity = "普通"
elif consecutive_days <= 5:
rarity = "优良"
elif consecutive_days <= 10:
rarity = "稀有"
elif consecutive_days <= 15:
rarity = "史诗"
else:
rarity = "传奇"
# 获取对应稀有度的种子池
rarity_config = seed_configs.get(rarity, {})
seed_pool = rarity_config.get("种子池", [])
quantity_range = rarity_config.get("数量范围", [1, 2])
if not seed_pool:
return seeds
# 生成1-3个种子
seed_count = random.randint(1, min(3, len(seed_pool)))
selected_seeds = random.sample(seed_pool, seed_count)
for seed_name in selected_seeds:
quantity = random.randint(quantity_range[0], quantity_range[1])
seeds.append({
"name": seed_name,
"quantity": quantity,
"quality": rarity
})
return seeds
#格式化奖励文本(简单版本)
def _format_reward_text_simple(self, rewards):
"""格式化奖励文本(简单版本)"""
parts = []
if "coins" in rewards:
parts.append(f"金币{rewards['coins']}")
if "exp" in rewards:
parts.append(f"经验{rewards['exp']}")
if "bonus_coins" in rewards:
parts.append(f"额外金币{rewards['bonus_coins']}")
if "bonus_exp" in rewards:
parts.append(f"额外经验{rewards['bonus_exp']}")
if "seeds" in rewards:
for seed in rewards["seeds"]:
parts.append(f"{seed['name']}x{seed['quantity']}")
return " ".join(parts)
#应用签到奖励到玩家数据
def _apply_check_in_rewards(self, player_data, rewards):
"""应用签到奖励到玩家数据"""
# 应用钱币奖励
if "coins" in rewards:
player_data["钱币"] = player_data.get("钱币", 0) + rewards["coins"]
if "bonus_coins" in rewards:
player_data["钱币"] = player_data.get("钱币", 0) + rewards["bonus_coins"]
# 应用经验奖励
if "exp" in rewards:
player_data["经验值"] = player_data.get("经验值", 0) + rewards["exp"]
if "bonus_exp" in rewards:
player_data["经验值"] = player_data.get("经验值", 0) + rewards["bonus_exp"]
# 检查升级
level_up_experience = 100 * player_data.get("等级", 1)
while player_data.get("经验值", 0) >= level_up_experience:
player_data["等级"] = player_data.get("等级", 1) + 1
player_data["经验值"] -= level_up_experience
level_up_experience = 100 * player_data["等级"]
# 应用种子奖励
if "seeds" in rewards:
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
for seed_reward in rewards["seeds"]:
seed_name = seed_reward["name"]
quantity = seed_reward["quantity"]
quality = seed_reward["quality"]
# 查找背包中是否已有该种子
found = False
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += quantity
found = True
break
# 如果背包中没有,添加新条目
if not found:
player_data["种子仓库"].append({
"name": seed_name,
"quality": quality,
"count": quantity
})
#==========================每日签到处理==========================
#==========================新手大礼包处理==========================
#处理新手大礼包请求
def _handle_new_player_gift_request(self, client_id, message):
"""处理新手大礼包请求"""
try:
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "领取新手大礼包", "new_player_gift")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "new_player_gift")
if not player_data:
return self.send_data(client_id, response)
# 加载新手礼包配置
config = self._load_new_player_config()
gift_config = config.get("新手礼包配置", {})
# 检查是否已经领取过新手大礼包
new_player_gift_data = player_data.get("新手礼包", {})
if new_player_gift_data.get("已领取", False):
return self.send_data(client_id, {
"type": "new_player_gift_response",
"success": False,
"message": gift_config.get("提示消息", {}).get("已领取", "新手大礼包已经领取过了")
})
# 获取新手大礼包内容
reward_content = gift_config.get("奖励内容", {})
# 应用奖励
self._apply_new_player_gift_rewards_new(player_data, reward_content)
# 标记已领取
player_data["新手礼包"] = {
"已领取": True,
"领取时间": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 成功领取新手大礼包", 'SERVER')
return self.send_data(client_id, {
"type": "new_player_gift_response",
"success": True,
"message": gift_config.get("提示消息", {}).get("成功", "新手大礼包领取成功!"),
"gift_contents": reward_content,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", []),
"宠物背包": player_data.get("宠物背包", []),
"新手礼包": player_data["新手礼包"]
}
})
except Exception as e:
# 捕获所有异常,防止服务器崩溃
self.log('ERROR', f"处理新手大礼包请求时出错: {str(e)}", 'SERVER')
# 发送错误响应
return self.send_data(client_id, {
"type": "new_player_gift_response",
"success": False,
"message": "服务器处理新手大礼包时出现错误,请稍后重试"
})
#应用新手大礼包奖励到玩家数据
def _apply_new_player_gift_rewards(self, player_data, gift_contents):
"""应用新手大礼包奖励到玩家数据(旧格式,保留兼容性)"""
# 应用金币奖励
if "coins" in gift_contents:
player_data["钱币"] = player_data.get("钱币", 0) + gift_contents["coins"]
# 应用经验奖励
if "经验值" in gift_contents:
player_data["经验值"] = player_data.get("经验值", 0) + gift_contents["经验值"]
# 检查升级
level_up_experience = 100 * player_data.get("等级", 1)
while player_data.get("经验值", 0) >= level_up_experience:
player_data["等级"] = player_data.get("等级", 1) + 1
player_data["经验值"] -= level_up_experience
level_up_experience = 100 * player_data["等级"]
# 应用种子奖励
if "seeds" in gift_contents:
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
for seed_reward in gift_contents["seeds"]:
seed_name = seed_reward["name"]
quantity = seed_reward["count"]
quality = seed_reward["quality"]
# 查找背包中是否已有该种子
found = False
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += quantity
found = True
break
# 如果背包中没有,添加新条目
if not found:
player_data["种子仓库"].append({
"name": seed_name,
"quality": quality,
"count": quantity
})
#应用新手大礼包奖励到玩家数据(新中文格式)
def _apply_new_player_gift_rewards_new(self, player_data, reward_content):
"""应用新手大礼包奖励到玩家数据(新中文格式)"""
# 应用金币奖励
if "金币" in reward_content:
player_data["钱币"] = player_data.get("钱币", 0) + reward_content["金币"]
# 应用经验奖励
if "经验" in reward_content:
player_data["经验值"] = player_data.get("经验值", 0) + reward_content["经验"]
# 检查升级
while True:
level_up_experience = 100 * player_data.get("等级", 1)
if player_data.get("经验值", 0) >= level_up_experience:
player_data["等级"] = player_data.get("等级", 1) + 1
player_data["经验值"] -= level_up_experience
else:
break
# 应用种子奖励
if "种子" in reward_content:
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
for seed_reward in reward_content["种子"]:
seed_name = seed_reward["名称"]
quantity = seed_reward["数量"]
quality = seed_reward["品质"]
# 查找背包中是否已有该种子
found = False
for item in player_data["种子仓库"]:
if item.get("name") == seed_name:
item["count"] += quantity
found = True
break
# 如果背包中没有,添加新条目
if not found:
player_data["种子仓库"].append({
"name": seed_name,
"quality": quality,
"count": quantity
})
#==========================新手大礼包处理==========================
#==========================幸运抽奖处理==========================
#处理幸运抽奖请求
def _handle_lucky_draw_request(self, client_id, message):
"""处理幸运抽奖请求"""
try:
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "幸运抽奖", "lucky_draw")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "lucky_draw")
if not player_data:
return self.send_data(client_id, response)
# 删除历史记录(如果存在)
if "lucky_draw_history" in player_data:
del player_data["lucky_draw_history"]
draw_type = message.get("draw_type", "single") # single, five, ten
# 从配置文件获取费用
config = self._load_lucky_draw_config()
costs = config.get("抽奖费用", {"单抽": 800, "五连抽": 3600, "十连抽": 6400})
# 计算抽奖费用和数量
if draw_type == "single":
draw_count = 1
total_cost = costs.get("单抽", 800)
elif draw_type == "five":
draw_count = 5
total_cost = costs.get("五连抽", 3600)
elif draw_type == "ten":
draw_count = 10
total_cost = costs.get("十连抽", 6400)
else:
return self.send_data(client_id, {
"type": "lucky_draw_response",
"success": False,
"message": "无效的抽奖类型"
})
# 检查玩家金钱是否足够
if player_data.get("钱币", 0) < total_cost:
return self.send_data(client_id, {
"type": "lucky_draw_response",
"success": False,
"message": f"金钱不足,需要 {total_cost} 金币"
})
# 扣除金钱
player_data["钱币"] -= total_cost
# 生成奖励
rewards = self._generate_lucky_draw_rewards(draw_count, draw_type, config)
# 应用奖励到玩家数据
self._apply_lucky_draw_rewards(player_data, rewards)
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 进行{draw_type}抽奖,花费 {total_cost} 金币,获得 {len(rewards)} 个奖励", 'SERVER')
return self.send_data(client_id, {
"type": "lucky_draw_response",
"success": True,
"message": f"{draw_type}抽奖成功!",
"draw_type": draw_type,
"cost": total_cost,
"rewards": rewards,
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"种子仓库": player_data.get("种子仓库", [])
}
})
except Exception as e:
self.log('ERROR', f"处理玩家抽奖请求时出错: {str(e)}", 'SERVER')
return self.send_data(client_id, {
"type": "lucky_draw_response",
"success": False,
"message": "服务器处理抽奖时出现错误,请稍后重试"
})
#加载抽奖配置
def _load_lucky_draw_config(self):
"""加载抽奖配置优先从MongoDB读取"""
# 优先尝试从MongoDB读取
if self.use_mongodb and self.mongo_api:
try:
config = self.mongo_api.get_lucky_draw_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读取幸运抽奖配置失败: {e}尝试从JSON文件读取", 'SERVER')
# 回退到JSON文件
try:
config_path = os.path.join(self.config_dir, "lucky_draw_config.json")
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
self.log('INFO', "成功从JSON文件加载幸运抽奖配置", 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"从JSON文件读取幸运抽奖配置失败: {e},使用默认配置", 'SERVER')
# 默认配置
self.log('WARNING', "使用默认幸运抽奖配置", 'SERVER')
return {
"抽奖费用": {"单抽": 800, "五连抽": 3600, "十连抽": 6400},
"概率配置": {
"普通": {"概率": 0.45, "金币范围": [100, 300], "经验范围": [50, 150], "种子数量": [2, 4]},
"优良": {"概率": 0.25, "金币范围": [300, 600], "经验范围": [150, 300], "种子数量": [1, 3]},
"稀有": {"概率": 0.12, "金币范围": [600, 1000], "经验范围": [300, 500], "种子数量": [1, 2]},
"史诗": {"概率": 0.025, "金币范围": [1000, 1500], "经验范围": [500, 800], "种子数量": [1, 1]},
"传奇": {"概率": 0.005, "金币范围": [1500, 2500], "经验范围": [800, 1200], "种子数量": [1, 1]},
"空奖": {"概率": 0.15, "提示语": ["谢谢惠顾", "下次再来", "再试一次", "继续努力"]}
}
}
#加载在线礼包配置
def _load_online_gift_config(self):
"""加载在线礼包配置"""
# 优先从MongoDB读取配置
if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected():
try:
config = self.mongo_api.get_online_gift_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:
config_path = os.path.join(self.config_dir, "online_gift_config.json")
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
self.log('INFO', '成功从JSON文件加载在线礼包配置', 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"从JSON文件加载在线礼包配置失败: {str(e)}", 'SERVER')
# 默认配置
self.log('WARNING', '使用默认在线礼包配置', 'SERVER')
return {
"在线礼包配置": {
"1分钟": {"时长秒数": 60, "奖励": {"金币": 100, "经验": 50, "种子": [{"名称": "小麦", "数量": 5}]}},
"5分钟": {"时长秒数": 300, "奖励": {"金币": 500, "经验": 250, "种子": [{"名称": "玉米", "数量": 3}]}},
"30分钟": {"时长秒数": 1800, "奖励": {"金币": 1500, "经验": 750, "种子": [{"名称": "草莓", "数量": 2}]}},
"1小时": {"时长秒数": 3600, "奖励": {"金币": 3000, "经验": 1500, "种子": [{"名称": "葡萄", "数量": 2}]}}
},
"每日重置": True
}
#加载新手礼包配置
def _load_new_player_config(self):
"""加载新手礼包配置"""
# 优先从MongoDB读取配置
if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected():
try:
config = self.mongo_api.get_new_player_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:
config_path = os.path.join(self.config_dir, "new_player_config.json")
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
config = json.load(f)
self.log('INFO', '成功从JSON文件加载新手大礼包配置', 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"从JSON文件加载新手礼包配置失败: {str(e)}", 'SERVER')
# 默认配置
self.log('WARNING', '使用默认新手大礼包配置', 'SERVER')
return {
"新手礼包配置": {
"奖励内容": {
"金币": 6000,
"经验": 1000,
"种子": [
{"名称": "龙果", "品质": "传奇", "数量": 1},
{"名称": "杂交树1", "品质": "传奇", "数量": 1},
{"名称": "杂交树2", "品质": "传奇", "数量": 1}
]
},
"提示消息": {
"成功": "新手大礼包领取成功获得6000金币、1000经验和3个传奇种子",
"已领取": "新手大礼包已经领取过了"
}
}
}
#生成幸运抽奖奖励
def _generate_lucky_draw_rewards(self, count: int, draw_type: str, config: dict):
"""生成幸运抽奖奖励"""
# 加载作物配置
crop_data = self._load_crop_data()
rewards = []
# 根据 crop_data.json 构建奖励池
seed_pools = {"普通": [], "优良": [], "稀有": [], "史诗": [], "传奇": []}
for crop_name, crop_info in crop_data.items():
if not crop_info.get("能否购买", True):
continue
quality = crop_info.get("品质", "普通")
if quality in seed_pools:
seed_pools[quality].append(crop_name)
# 十连抽保底机制
guaranteed_rare = (draw_type == "ten")
rare_given = False
for i in range(count):
# 生成单个奖励
reward = self._generate_single_lucky_reward(
seed_pools, config, guaranteed_rare and i == count - 1 and not rare_given
)
# 检查是否给出了稀有奖励
if reward.get("rarity", "普通") in ["稀有", "史诗", "传奇"]:
rare_given = True
rewards.append(reward)
return rewards
#生成单个抽奖奖励
def _generate_single_lucky_reward(self, seed_pools: dict, config: dict, force_rare=False):
"""生成单个幸运抽奖奖励"""
import random
prob_config = config.get("概率配置", {})
# 决定稀有度
if force_rare:
# 强制稀有33%稀有33%史诗34%传奇
rand = random.random()
if rand < 0.33:
rarity = "稀有"
elif rand < 0.66:
rarity = "史诗"
else:
rarity = "传奇"
else:
# 正常概率
rand = random.random()
cumulative = 0
rarity = "普通"
for r, config_data in prob_config.items():
prob = config_data.get("概率", 0)
cumulative += prob
if rand < cumulative:
rarity = r
break
# 根据稀有度生成奖励
if rarity == "空奖":
empty_messages = prob_config.get("空奖", {}).get("提示语", ["谢谢惠顾"])
return {
"type": "empty",
"name": random.choice(empty_messages),
"rarity": "空奖",
"amount": 0
}
# 获取稀有度配置
rarity_config = prob_config.get(rarity, {})
# 根据奖励类型权重选择奖励类型
type_weights = config.get("奖励类型权重", {}).get(rarity, {"金币": 0.5, "经验": 0.3, "种子": 0.2})
# 随机选择奖励类型
rand = random.random()
cumulative = 0
reward_type = "金币"
for r_type, weight in type_weights.items():
cumulative += weight
if rand < cumulative:
reward_type = r_type
break
# 生成具体奖励
if reward_type == "金币":
coin_range = rarity_config.get("金币范围", [100, 300])
return {
"type": "coins",
"name": "金币",
"rarity": rarity,
"amount": random.randint(coin_range[0], coin_range[1])
}
elif reward_type == "经验":
exp_range = rarity_config.get("经验范围", [50, 150])
return {
"type": "exp",
"name": "经验",
"rarity": rarity,
"amount": random.randint(exp_range[0], exp_range[1])
}
elif reward_type == "种子":
seeds = seed_pools.get(rarity, [])
if not seeds:
# 如果没有对应稀有度的种子,给金币
coin_range = rarity_config.get("金币范围", [100, 300])
return {
"type": "coins",
"name": "金币",
"rarity": rarity,
"amount": random.randint(coin_range[0], coin_range[1])
}
seed_count_range = rarity_config.get("种子数量", [1, 2])
return {
"type": "seed",
"name": random.choice(seeds),
"rarity": rarity,
"amount": random.randint(seed_count_range[0], seed_count_range[1])
}
elif reward_type == "礼包":
package_config = config.get("礼包配置", {})
package_names = [name for name, info in package_config.items() if info.get("稀有度") == rarity]
if not package_names:
# 如果没有对应稀有度的礼包,给金币
coin_range = rarity_config.get("金币范围", [100, 300])
return {
"type": "coins",
"name": "金币",
"rarity": rarity,
"amount": random.randint(coin_range[0], coin_range[1])
}
package_name = random.choice(package_names)
package_info = package_config[package_name]
contents = []
# 生成礼包内容
content_config = package_info.get("内容", {})
if "金币" in content_config:
coin_range = content_config["金币"]
contents.append({
"type": "coins",
"amount": random.randint(coin_range[0], coin_range[1]),
"rarity": rarity
})
if "经验" in content_config:
exp_range = content_config["经验"]
contents.append({
"type": "exp",
"amount": random.randint(exp_range[0], exp_range[1]),
"rarity": rarity
})
if "种子数量" in content_config:
seed_count_range = content_config["种子数量"]
lower_rarity = {"优良": "普通", "稀有": "优良", "史诗": "稀有", "传奇": "史诗"}.get(rarity, "普通")
seeds = seed_pools.get(lower_rarity, [])
if seeds:
contents.append({
"type": "seed",
"name": random.choice(seeds),
"amount": random.randint(seed_count_range[0], seed_count_range[1]),
"rarity": rarity
})
return {
"type": "package",
"name": package_name,
"rarity": rarity,
"amount": 1,
"contents": contents
}
# 默认给金币
coin_range = rarity_config.get("金币范围", [100, 300])
return {
"type": "coins",
"name": "金币",
"rarity": rarity,
"amount": random.randint(coin_range[0], coin_range[1])
}
#应用幸运抽奖奖励到玩家数据
def _apply_lucky_draw_rewards(self, player_data, rewards):
"""应用幸运抽奖奖励到玩家数据"""
for reward in rewards:
reward_type = reward.get("type", "empty")
if reward_type == "empty":
continue # 空奖励不处理
elif reward_type == "coins":
player_data["钱币"] = player_data.get("钱币", 0) + reward.get("amount", 0)
elif reward_type == "exp":
player_data["经验值"] = player_data.get("经验值", 0) + reward.get("amount", 0)
# 检查升级
level_up_experience = 100 * player_data.get("等级", 1)
while player_data.get("经验值", 0) >= level_up_experience:
player_data["等级"] = player_data.get("等级", 1) + 1
player_data["经验值"] -= level_up_experience
level_up_experience = 100 * player_data["等级"]
elif reward_type == "seed":
if "种子仓库" not in player_data:
player_data["种子仓库"] = []
# 查找背包中是否已有该种子
found = False
for item in player_data["种子仓库"]:
if item.get("name") == reward.get("name", ""):
item["count"] += reward.get("amount", 0)
found = True
break
# 如果背包中没有,添加新条目
if not found:
player_data["种子仓库"].append({
"name": reward.get("name", "未知种子"),
"quality": reward.get("rarity", "普通"),
"count": reward.get("amount", 0)
})
elif reward_type == "package":
# 递归处理礼包内容
contents = reward.get("contents", [])
if contents:
# 为礼包内容添加默认的rarity字段
for content in contents:
if not content.get("rarity"):
content["rarity"] = reward.get("rarity", "普通")
# 递归处理礼包内容
self._apply_lucky_draw_rewards(player_data, contents)
#==========================幸运抽奖处理==========================
#==========================发送游戏操作错误处理==========================
#发送游戏操作错误
def _send_action_error(self, client_id, action_type, message):
"""发送游戏操作错误响应"""
return self.send_data(client_id, {
"type": "action_response",
"action_type": action_type,
"success": False,
"message": message
})
#==========================发送游戏操作错误处理==========================
# ================================账户设置处理方法================================
def _handle_modify_account_info_request(self, client_id, message):
"""处理修改账号信息请求"""
# 检查用户是否已登录
is_logged_in, error_response = self._check_user_logged_in(client_id, "修改账号信息")
if not is_logged_in:
return self._send_modify_account_error(client_id, error_response["message"])
# 加载玩家数据
player_data, username, error_response = self._load_player_data_with_check(client_id, "modify_account_info")
if not player_data:
return self.send_data(client_id, error_response)
# 获取新的信息
new_password = message.get("new_password", "").strip()
new_player_name = message.get("new_player_name", "").strip()
new_farm_name = message.get("new_farm_name", "").strip()
new_personal_profile = message.get("new_personal_profile", "").strip()
# 验证输入
if not new_password:
return self._send_modify_account_error(client_id, "密码不能为空")
if not new_player_name:
return self._send_modify_account_error(client_id, "玩家昵称不能为空")
if not new_farm_name:
return self._send_modify_account_error(client_id, "农场名称不能为空")
if len(new_player_name) > 20:
return self._send_modify_account_error(client_id, "玩家昵称不能超过20个字符")
if len(new_farm_name) > 20:
return self._send_modify_account_error(client_id, "农场名称不能超过20个字符")
if len(new_personal_profile) > 100:
return self._send_modify_account_error(client_id, "个人简介不能超过100个字符")
try:
# 更新玩家数据
player_data["玩家密码"] = new_password
player_data["玩家昵称"] = new_player_name
player_data["农场名称"] = new_farm_name
player_data["个人简介"] = new_personal_profile
# 保存到文件
self.save_player_data(username, player_data)
# 发送成功响应
self.send_data(client_id, {
"type": "modify_account_info_response",
"success": True,
"message": "账号信息修改成功",
"updated_data": {
"玩家密码": new_password,
"玩家昵称": new_player_name,
"农场名称": new_farm_name,
"个人简介": new_personal_profile
}
})
self.log('INFO', f"用户 {username} 修改账号信息成功", 'ACCOUNT')
except Exception as e:
self.log('ERROR', f"修改账号信息时出错: {str(e)}", 'ACCOUNT')
return self._send_modify_account_error(client_id, "修改账号信息失败,请稍后重试")
def _handle_delete_account_request(self, client_id, message):
"""处理删除账号请求"""
# 检查用户是否已登录
is_logged_in, error_response = self._check_user_logged_in(client_id, "删除账号")
if not is_logged_in:
return self._send_delete_account_error(client_id, error_response["message"])
# 获取用户名
username = self.user_data[client_id]["username"]
try:
# 优先从MongoDB删除
if self.use_mongodb and self.mongo_api:
success = self.mongo_api.delete_player_data(username)
if not success:
self.log('WARNING', f"MongoDB删除失败尝试删除文件: {username}", 'ACCOUNT')
# 清理用户数据
if client_id in self.user_data:
del self.user_data[client_id]
# 发送成功响应
self.send_data(client_id, {
"type": "delete_account_response",
"success": True,
"message": "账号删除成功,即将返回主菜单"
})
self.log('INFO', f"用户 {username} 账号删除成功", 'ACCOUNT')
# 稍后断开连接
import threading
def delayed_disconnect():
import time
time.sleep(2)
self._remove_client(client_id)
disconnect_thread = threading.Thread(target=delayed_disconnect)
disconnect_thread.daemon = True
disconnect_thread.start()
except Exception as e:
self.log('ERROR', f"删除账号时出错: {str(e)}", 'ACCOUNT')
return self._send_delete_account_error(client_id, "删除账号失败,请稍后重试")
def _handle_refresh_player_info_request(self, client_id, message):
"""处理刷新玩家信息请求"""
# 检查用户是否已登录
is_logged_in, error_response = self._check_user_logged_in(client_id, "刷新玩家信息")
if not is_logged_in:
return self._send_refresh_info_error(client_id, error_response["message"])
# 获取用户名
username = self.user_data[client_id]["username"]
try:
# 强制从数据库重新加载最新数据
player_data = self.load_player_data(username)
if not player_data:
return self._send_refresh_info_error(client_id, "无法加载玩家数据")
# 只发送账户相关信息,不发送农场数据等
account_info = {
"玩家账号": player_data.get("玩家账号", ""),
"玩家密码": player_data.get("玩家密码", ""),
"玩家昵称": player_data.get("玩家昵称", ""),
"农场名称": player_data.get("农场名称", ""),
"个人简介": player_data.get("个人简介", ""),
"等级": player_data.get("等级", 1),
"经验值": player_data.get("经验值", 0),
"钱币": player_data.get("钱币", 0)
}
# 发送刷新后的账户信息
self.send_data(client_id, {
"type": "refresh_player_info_response",
"success": True,
"message": "玩家信息已刷新",
"account_info": account_info
})
self.log('INFO', f"用户 {username} 刷新玩家信息成功", 'ACCOUNT')
except Exception as e:
self.log('ERROR', f"刷新玩家信息时出错: {str(e)}", 'ACCOUNT')
return self._send_refresh_info_error(client_id, "刷新玩家信息失败,请稍后重试")
def _send_modify_account_error(self, client_id, message):
"""发送修改账号信息错误响应"""
self.send_data(client_id, {
"type": "modify_account_info_response",
"success": False,
"message": message
})
def _send_delete_account_error(self, client_id, message):
"""发送删除账号错误响应"""
self.send_data(client_id, {
"type": "delete_account_response",
"success": False,
"message": message
})
def _send_refresh_info_error(self, client_id, message):
"""发送刷新信息错误响应"""
self.send_data(client_id, {
"type": "refresh_player_info_response",
"success": False,
"message": message
})
#处理背包数据同步消息
def _handle_sync_bag_data(self, client_id, message):
"""处理背包数据同步请求"""
username = self.user_data.get(client_id, {}).get("username")
if not username:
return self.send_data(client_id, {
"type": "sync_bag_data_response",
"success": False,
"message": "用户未登录"
})
# 从数据库加载最新的玩家数据
player_data = self.load_player_data(username)
if not player_data:
return self.send_data(client_id, {
"type": "sync_bag_data_response",
"success": False,
"message": "玩家数据加载失败"
})
# 提取所有背包数据
bag_data = {
"道具背包": player_data.get("道具背包", []),
"宠物背包": player_data.get("宠物背包", []),
"种子仓库": player_data.get("种子仓库", []),
"作物仓库": player_data.get("作物仓库", [])
}
self.log('INFO', f"用户 {username} 请求同步背包数据", 'SERVER')
return self.send_data(client_id, {
"type": "sync_bag_data_response",
"success": True,
"message": "背包数据同步成功",
"bag_data": bag_data
})
def _handle_kick_player(self, client_id, message):
"""处理踢出玩家消息(服务器内部使用)"""
# 这个函数主要用于接收来自控制台命令的踢出消息
# 实际的踢出逻辑在 ConsoleCommandsAPI 中处理
reason = message.get("reason", "您已被管理员踢出服务器")
duration = message.get("duration", 0)
# 发送踢出通知给客户端
response = {
"type": "kick_notification",
"reason": reason,
"duration": duration,
"message": reason
}
self.log('INFO', f"向客户端 {client_id} 发送踢出通知: {reason}", 'SERVER')
return self.send_data(client_id, response)
# ================================账户设置处理方法================================
#==========================稻草人系统处理==========================
def _handle_buy_scare_crow(self, client_id, message):
"""处理购买稻草人请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "购买稻草人", "buy_scare_crow")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "buy_scare_crow")
if not player_data:
return self.send_data(client_id, response)
scare_crow_type = message.get("scare_crow_type", "")
price = message.get("price", 0)
# 加载稻草人配置
scare_crow_config = self._load_scare_crow_config()
if not scare_crow_config:
return self._send_buy_scare_crow_error(client_id, "服务器无法加载稻草人配置")
# 检查稻草人类型是否存在
if scare_crow_type not in scare_crow_config.get("稻草人类型", {}):
return self._send_buy_scare_crow_error(client_id, "该稻草人类型不存在")
# 验证价格是否正确
actual_price = scare_crow_config["稻草人类型"][scare_crow_type]["价格"]
if price != actual_price:
return self._send_buy_scare_crow_error(client_id, f"稻草人价格验证失败,实际价格为{actual_price}金币")
# 检查玩家金钱
if player_data["钱币"] < price:
return self._send_buy_scare_crow_error(client_id, f"金币不足,需要{price}金币,当前只有{player_data['money']}金币")
# 确保稻草人配置存在
if "稻草人配置" not in player_data:
player_data["稻草人配置"] = {
"已拥有稻草人类型": ["稻草人1"],
"稻草人展示类型": "",
"稻草人昵称": "我的稻草人",
"稻草人昵称颜色": "#ffffff",
"稻草人说的话": {
"第一句话": {"内容": "", "颜色": "#000000"},
"第二句话": {"内容": "", "颜色": "#000000"},
"第三句话": {"内容": "", "颜色": "#000000"},
"第四句话": {"内容": "", "颜色": "#000000"}
}
}
# 检查是否已拥有该稻草人
if scare_crow_type in player_data["稻草人配置"]["已拥有稻草人类型"]:
return self._send_buy_scare_crow_error(client_id, f"你已经拥有{scare_crow_type}")
# 扣除金钱
player_data["钱币"] -= price
# 添加稻草人到已拥有列表
player_data["稻草人配置"]["已拥有稻草人类型"].append(scare_crow_type)
# 如果是第一个稻草人,设置为展示类型
if player_data["稻草人配置"]["稻草人展示类型"] == "":
player_data["稻草人配置"]["稻草人展示类型"] = scare_crow_type
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 购买了稻草人 {scare_crow_type},花费 {price} 金币", 'SERVER')
return self.send_data(client_id, {
"type": "buy_scare_crow_response",
"success": True,
"message": f"成功购买{scare_crow_type}",
"updated_data": {
"钱币": player_data["钱币"],
"稻草人配置": player_data["稻草人配置"]
}
})
def _handle_modify_scare_crow_config(self, client_id, message):
"""处理修改稻草人配置请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "修改稻草人配置", "modify_scare_crow_config")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "modify_scare_crow_config")
if not player_data:
return self.send_data(client_id, response)
config_data = message.get("config_data", {})
modify_cost = message.get("modify_cost", 300)
# 加载稻草人配置
scare_crow_config = self._load_scare_crow_config()
if not scare_crow_config:
return self._send_modify_scare_crow_config_error(client_id, "服务器无法加载稻草人配置")
# 检查是否只是切换展示类型(不收费)
is_only_changing_display = (
len(config_data) == 1 and
"稻草人展示类型" in config_data and
modify_cost == 0
)
if not is_only_changing_display:
# 验证修改费用
actual_cost = scare_crow_config.get("修改稻草人配置花费", 300)
if modify_cost != actual_cost:
return self._send_modify_scare_crow_config_error(client_id, f"修改费用验证失败,实际费用为{actual_cost}金币")
# 检查玩家金钱
if player_data["钱币"] < modify_cost:
return self._send_modify_scare_crow_config_error(client_id, f"金币不足,需要{modify_cost}金币,当前只有{player_data['money']}金币")
# 确保稻草人配置存在
if "稻草人配置" not in player_data:
return self._send_modify_scare_crow_config_error(client_id, "你还没有稻草人,请先购买稻草人")
# 只在非切换展示类型时扣除金钱
if not is_only_changing_display:
player_data["钱币"] -= modify_cost
# 更新稻草人配置
if "稻草人展示类型" in config_data:
# 检查展示类型是否已拥有
owned_types = player_data["稻草人配置"].get("已拥有稻草人类型", [])
if config_data["稻草人展示类型"] in owned_types:
player_data["稻草人配置"]["稻草人展示类型"] = config_data["稻草人展示类型"]
else:
return self._send_modify_scare_crow_config_error(client_id, "你没有拥有该稻草人类型")
if "稻草人昵称" in config_data:
player_data["稻草人配置"]["稻草人昵称"] = config_data["稻草人昵称"]
if "稻草人昵称颜色" in config_data:
player_data["稻草人配置"]["稻草人昵称颜色"] = config_data["稻草人昵称颜色"]
if "稻草人说的话" in config_data:
player_data["稻草人配置"]["稻草人说的话"] = config_data["稻草人说的话"]
# 保存玩家数据
self.save_player_data(username, player_data)
if is_only_changing_display:
self.log('INFO', f"玩家 {username} 切换了稻草人展示类型到 {config_data['稻草人展示类型']}", 'SERVER')
message = f"成功切换到{config_data['稻草人展示类型']}"
else:
self.log('INFO', f"玩家 {username} 修改了稻草人配置,花费 {modify_cost} 金币", 'SERVER')
message = f"稻草人配置修改成功!花费{modify_cost}金币"
return self.send_data(client_id, {
"type": "modify_scare_crow_config_response",
"success": True,
"message": message,
"updated_data": {
"钱币": player_data["钱币"],
"稻草人配置": player_data["稻草人配置"]
}
})
def _handle_get_scare_crow_config(self, client_id, message):
"""处理获取稻草人配置请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取稻草人配置", "get_scare_crow_config")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "get_scare_crow_config")
if not player_data:
return self.send_data(client_id, response)
# 确保稻草人配置存在
if "稻草人配置" not in player_data:
player_data["稻草人配置"] = {
"已拥有稻草人类型": [],
"稻草人展示类型": "",
"稻草人昵称": "我的稻草人",
"稻草人昵称颜色": "#ffffff",
"稻草人说的话": {
"第一句话": {"内容": "", "颜色": "#000000"},
"第二句话": {"内容": "", "颜色": "#000000"},
"第三句话": {"内容": "", "颜色": "#000000"},
"第四句话": {"内容": "", "颜色": "#000000"}
}
}
# 保存默认配置
self.save_player_data(username, player_data)
return self.send_data(client_id, {
"type": "get_scare_crow_config_response",
"success": True,
"message": "获取稻草人配置成功",
"scare_crow_config": player_data["稻草人配置"]
})
def _load_scare_crow_config(self):
"""加载稻草人配置"""
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中未找到稻草人配置使用默认配置", 'SERVER')
else:
self.log('WARNING', "MongoDB未连接使用默认稻草人配置", 'SERVER')
except Exception as e:
self.log('ERROR', f"从MongoDB加载稻草人配置失败: {str(e)},使用默认配置", 'SERVER')
# 返回默认配置
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):
"""发送购买稻草人错误响应"""
return self.send_data(client_id, {
"type": "buy_scare_crow_response",
"success": False,
"message": message
})
def _send_modify_scare_crow_config_error(self, client_id, message):
"""发送修改稻草人配置错误响应"""
return self.send_data(client_id, {
"type": "modify_scare_crow_config_response",
"success": False,
"message": message
})
#==========================稻草人系统处理==========================
#==========================作物出售处理==========================
def _handle_sell_crop(self, client_id, message):
"""处理作物出售请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "出售作物", "sell_crop")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "sell_crop")
if not player_data:
return self.send_data(client_id, response)
crop_name = message.get("crop_name", "")
sell_count = message.get("sell_count", 1)
unit_price = message.get("unit_price", 0)
# 验证参数
if not crop_name:
return self._send_action_error(client_id, "sell_crop", "作物名称不能为空")
if sell_count <= 0:
return self._send_action_error(client_id, "sell_crop", "出售数量必须大于0")
if unit_price <= 0:
return self._send_action_error(client_id, "sell_crop", "单价必须大于0")
# 检查作物仓库中是否有足够的作物
crop_warehouse = player_data.get("作物仓库", [])
crop_found = False
crop_index = -1
available_count = 0
for i, crop_item in enumerate(crop_warehouse):
if crop_item.get("name") == crop_name:
crop_found = True
crop_index = i
available_count = crop_item.get("count", 0)
break
if not crop_found:
return self._send_action_error(client_id, "sell_crop", f"作物仓库中没有 {crop_name}")
if available_count < sell_count:
return self._send_action_error(client_id, "sell_crop", f"作物数量不足,仓库中只有 {available_count}{crop_name}")
# 验证价格(防止客户端篡改价格)
crop_data = self._load_crop_data()
if crop_name in crop_data:
expected_price = crop_data[crop_name].get("收益", 0)
if unit_price != expected_price:
return self._send_action_error(client_id, "sell_crop", f"价格验证失败,{crop_name} 的正确价格应为 {expected_price} 元/个")
else:
return self._send_action_error(client_id, "sell_crop", f"未知的作物类型:{crop_name}")
# 计算总收入
total_income = sell_count * unit_price
# 执行出售操作
player_data["钱币"] += total_income
# 从作物仓库中减少数量
crop_warehouse[crop_index]["count"] -= sell_count
# 如果数量为0从仓库中移除该作物
if crop_warehouse[crop_index]["count"] <= 0:
crop_warehouse.pop(crop_index)
# 给予少量出售经验
sell_experience = max(1, sell_count // 5) # 每5个作物给1点经验
player_data["经验值"] += sell_experience
# 检查是否升级
self._check_level_up(player_data)
# 保存玩家数据
self.save_player_data(username, player_data)
# 获取显示名称
display_name = crop_name
if crop_name in crop_data:
mature_name = crop_data[crop_name].get("成熟物名称")
if mature_name:
display_name = mature_name
else:
display_name = crop_data[crop_name].get("作物名称", crop_name)
self.log('INFO', f"玩家 {username} 出售了 {sell_count}{crop_name},获得 {total_income} 金币和 {sell_experience} 经验", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "sell_crop",
"success": True,
"message": f"成功出售 {sell_count}{display_name},获得 {total_income} 金币和 {sell_experience} 经验",
"updated_data": {
"钱币": player_data["钱币"],
"经验值": player_data["经验值"],
"等级": player_data["等级"],
"作物仓库": player_data["作物仓库"]
}
})
#==========================作物出售处理==========================
#==========================小卖部管理处理==========================
def _handle_add_product_to_store(self, client_id, message):
"""处理添加商品到小卖部请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "添加商品到小卖部", "add_product_to_store")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "add_product_to_store")
if not player_data:
return self.send_data(client_id, response)
product_type = message.get("product_type", "")
product_name = message.get("product_name", "")
product_count = message.get("product_count", 1)
product_price = message.get("product_price", 0)
# 验证参数
if not product_type or not product_name:
return self._send_action_error(client_id, "add_product_to_store", "商品类型或名称不能为空")
if product_count <= 0:
return self._send_action_error(client_id, "add_product_to_store", "商品数量必须大于0")
if product_price <= 0:
return self._send_action_error(client_id, "add_product_to_store", "商品价格必须大于0")
# 初始化小卖部数据
if "小卖部配置" not in player_data:
player_data["小卖部配置"] = {
"商品列表": [],
"格子数": 10
}
store_config = player_data["小卖部配置"]
player_store = store_config["商品列表"]
max_slots = store_config["格子数"]
# 检查小卖部格子是否已满
if len(player_store) >= max_slots:
return self._send_action_error(client_id, "add_product_to_store", f"小卖部格子已满({len(player_store)}/{max_slots})")
# 检查作物仓库中是否有足够的商品
if product_type == "作物":
crop_warehouse = player_data.get("作物仓库", [])
crop_found = False
crop_index = -1
available_count = 0
for i, crop_item in enumerate(crop_warehouse):
if crop_item.get("name") == product_name:
crop_found = True
crop_index = i
available_count = crop_item.get("count", 0)
break
if not crop_found:
return self._send_action_error(client_id, "add_product_to_store", f"作物仓库中没有 {product_name}")
if available_count < product_count:
return self._send_action_error(client_id, "add_product_to_store", f"作物数量不足,仓库中只有 {available_count}{product_name}")
# 从作物仓库中扣除商品
crop_warehouse[crop_index]["count"] -= product_count
if crop_warehouse[crop_index]["count"] <= 0:
crop_warehouse.pop(crop_index)
# 添加商品到小卖部
new_product = {
"商品类型": product_type,
"商品名称": product_name,
"商品价格": product_price,
"商品数量": product_count
}
player_store.append(new_product)
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 添加商品到小卖部: {product_name} x{product_count}, 价格 {product_price}元/个", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "add_product_to_store",
"success": True,
"message": f"成功添加 {product_count}{product_name} 到小卖部",
"updated_data": {
"小卖部配置": player_data["小卖部配置"],
"作物仓库": player_data.get("作物仓库", [])
}
})
def _handle_remove_store_product(self, client_id, message):
"""处理下架小卖部商品请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "下架小卖部商品", "remove_store_product")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "remove_store_product")
if not player_data:
return self.send_data(client_id, response)
slot_index = message.get("slot_index", -1)
# 验证参数
if slot_index < 0:
return self._send_action_error(client_id, "remove_store_product", "无效的商品槽位")
# 检查小卖部数据
store_config = player_data.get("小卖部配置", {"商品列表": [], "格子数": 10})
player_store = store_config.get("商品列表", [])
if slot_index >= len(player_store):
return self._send_action_error(client_id, "remove_store_product", "商品槽位不存在")
# 获取要下架的商品信息
product_data = player_store[slot_index]
product_type = product_data.get("商品类型", "")
product_name = product_data.get("商品名称", "")
product_count = product_data.get("商品数量", 0)
# 将商品返回到对应仓库
if product_type == "作物":
# 返回到作物仓库
if "作物仓库" not in player_data:
player_data["作物仓库"] = []
crop_warehouse = player_data["作物仓库"]
# 查找是否已有该作物
crop_found = False
for crop_item in crop_warehouse:
if crop_item.get("name") == product_name:
crop_item["count"] += product_count
crop_found = True
break
if not crop_found:
# 添加新的作物条目
crop_data = self._load_crop_data()
quality = "普通"
if crop_data and product_name in crop_data:
quality = crop_data[product_name].get("品质", "普通")
crop_warehouse.append({
"name": product_name,
"quality": quality,
"count": product_count
})
# 从小卖部移除商品
player_store.pop(slot_index)
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 下架小卖部商品: {product_name} x{product_count}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "remove_store_product",
"success": True,
"message": f"成功下架 {product_count}{product_name},已返回仓库",
"updated_data": {
"小卖部配置": player_data["小卖部配置"],
"作物仓库": player_data.get("作物仓库", [])
}
})
def _handle_buy_store_product(self, client_id, message):
"""处理购买小卖部商品请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "购买小卖部商品", "buy_store_product")
if not logged_in:
return self.send_data(client_id, response)
# 获取买家数据
buyer_data, buyer_username, response = self._load_player_data_with_check(client_id, "buy_store_product")
if not buyer_data:
return self.send_data(client_id, response)
seller_username = message.get("seller_username", "")
slot_index = message.get("slot_index", -1)
product_name = message.get("product_name", "")
unit_price = message.get("unit_price", 0)
quantity = message.get("quantity", 1)
# 验证参数
if not seller_username:
return self._send_action_error(client_id, "buy_store_product", "卖家用户名不能为空")
if slot_index < 0:
return self._send_action_error(client_id, "buy_store_product", "无效的商品槽位")
if quantity <= 0:
return self._send_action_error(client_id, "buy_store_product", "购买数量必须大于0")
# 检查是否是自己购买自己的商品
if buyer_username == seller_username:
return self._send_action_error(client_id, "buy_store_product", "不能购买自己的商品")
# 加载卖家数据
seller_data = self.load_player_data(seller_username)
if not seller_data:
return self._send_action_error(client_id, "buy_store_product", f"卖家 {seller_username} 不存在")
# 检查卖家小卖部
seller_store_config = seller_data.get("小卖部配置", {"商品列表": [], "格子数": 10})
seller_store = seller_store_config.get("商品列表", [])
if slot_index >= len(seller_store):
return self._send_action_error(client_id, "buy_store_product", "商品不存在")
product_data = seller_store[slot_index]
product_type = product_data.get("商品类型", "")
store_product_name = product_data.get("商品名称", "")
store_unit_price = product_data.get("商品价格", 0)
available_count = product_data.get("商品数量", 0)
# 验证商品信息
if store_product_name != product_name:
return self._send_action_error(client_id, "buy_store_product", "商品名称不匹配")
if store_unit_price != unit_price:
return self._send_action_error(client_id, "buy_store_product", "商品价格已变更,请刷新重试")
if available_count < quantity:
return self._send_action_error(client_id, "buy_store_product", f"商品库存不足,仅剩 {available_count}")
# 计算总价
total_cost = quantity * unit_price
# 检查买家金钱是否足够
if buyer_data["钱币"] < total_cost:
return self._send_action_error(client_id, "buy_store_product", f"金钱不足,需要 {total_cost}")
# 执行交易
buyer_data["钱币"] -= total_cost
seller_data["钱币"] += total_cost
# 扣除卖家商品
seller_store[slot_index]["商品数量"] -= quantity
if seller_store[slot_index]["商品数量"] <= 0:
seller_store.pop(slot_index)
# 给买家添加商品
if product_type == "作物":
if "作物仓库" not in buyer_data:
buyer_data["作物仓库"] = []
buyer_warehouse = buyer_data["作物仓库"]
# 查找是否已有该作物
crop_found = False
for crop_item in buyer_warehouse:
if crop_item.get("name") == product_name:
crop_item["count"] += quantity
crop_found = True
break
if not crop_found:
# 添加新的作物条目
crop_data = self._load_crop_data()
quality = "普通"
if crop_data and product_name in crop_data:
quality = crop_data[product_name].get("品质", "普通")
buyer_warehouse.append({
"name": product_name,
"quality": quality,
"count": quantity
})
# 保存两个玩家的数据
self.save_player_data(buyer_username, buyer_data)
self.save_player_data(seller_username, seller_data)
self.log('INFO', f"玩家 {buyer_username}{seller_username} 的小卖部购买了 {quantity}{product_name},花费 {total_cost}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "buy_store_product",
"success": True,
"message": f"成功购买 {quantity}{product_name},花费 {total_cost}",
"updated_data": {
"钱币": buyer_data["钱币"],
"作物仓库": buyer_data.get("作物仓库", [])
}
})
def _handle_buy_store_booth(self, client_id, message):
"""处理购买小卖部格子请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "购买小卖部格子", "buy_store_booth")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "buy_store_booth")
if not player_data:
return self.send_data(client_id, response)
cost = message.get("cost", 0)
# 验证参数
if cost <= 0:
return self._send_action_error(client_id, "buy_store_booth", "无效的购买费用")
# 初始化小卖部数据
if "小卖部配置" not in player_data:
player_data["小卖部配置"] = {
"商品列表": [],
"格子数": 10
}
store_config = player_data["小卖部配置"]
current_slots = store_config["格子数"]
# 检查是否已达上限
if current_slots >= 40:
return self._send_action_error(client_id, "buy_store_booth", "小卖部格子数已达上限(40)")
# 验证费用
expected_cost = 1000 + (current_slots - 10) * 500
if cost != expected_cost:
return self._send_action_error(client_id, "buy_store_booth", f"费用不正确,应为 {expected_cost}")
# 检查玩家金钱是否足够
if player_data["钱币"] < cost:
return self._send_action_error(client_id, "buy_store_booth", f"金钱不足,需要 {cost}")
# 执行购买
player_data["钱币"] -= cost
store_config["格子数"] += 1
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 购买小卖部格子,花费 {cost} 元,格子数:{current_slots} -> {store_config['格子数']}", 'SERVER')
return self.send_data(client_id, {
"type": "action_response",
"action_type": "buy_store_booth",
"success": True,
"message": f"成功购买格子,花费 {cost} 元,当前格子数:{store_config['格子数']}",
"updated_data": {
"钱币": player_data["钱币"],
"小卖部配置": player_data["小卖部配置"]
}
})
#==========================小卖部管理处理==========================
#==========================今日占卜处理==========================
def _handle_today_divination(self, client_id, message):
"""处理今日占卜请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "今日占卜", "today_divination")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "today_divination")
if not player_data:
return self.send_data(client_id, response)
# 获取今日日期
today = datetime.datetime.now().strftime("%Y-%m-%d")
# 检查今日占卜对象是否存在
if "今日占卜对象" not in player_data:
player_data["今日占卜对象"] = {}
divination_data = player_data["今日占卜对象"]
# 检查今日是否已经占卜过
if "占卜日期" in divination_data and divination_data["占卜日期"] == today:
return self.send_data(client_id, {
"type": "today_divination_response",
"success": False,
"message": "今日已经占卜过了,明天再来吧!",
"divination_data": player_data
})
# 生成占卜结果
divination_result = self._generate_divination_result()
# 更新玩家占卜数据
divination_data["占卜日期"] = today
divination_data["占卜结果"] = divination_result["result"]
divination_data["占卜等级"] = divination_result["level"]
divination_data["卦象"] = divination_result["hexagram"]
divination_data["建议"] = divination_result["advice"]
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 进行今日占卜,等级:{divination_result['level']}", 'SERVER')
return self.send_data(client_id, {
"type": "today_divination_response",
"success": True,
"message": "占卜完成!",
"divination_data": player_data
})
def _generate_divination_result(self):
"""生成占卜结果"""
import random
# 占卜等级配置(权重越高,出现概率越大)
levels = [
{"name": "大吉", "weight": 5, "color": "#FFD700"},
{"name": "中吉", "weight": 15, "color": "#FFA500"},
{"name": "小吉", "weight": 25, "color": "#90EE90"},
{"name": "", "weight": 30, "color": "#87CEEB"},
{"name": "小凶", "weight": 20, "color": "#DDA0DD"},
{"name": "", "weight": 5, "color": "#FF6347"}
]
# 易经八卦
hexagrams = [
{"name": "乾卦", "symbol": "", "meaning": "天行健,君子以自强不息"},
{"name": "坤卦", "symbol": "", "meaning": "地势坤,君子以厚德载物"},
{"name": "震卦", "symbol": "", "meaning": "雷声隆隆,万物复苏"},
{"name": "巽卦", "symbol": "", "meaning": "风行天下,顺势而为"},
{"name": "坎卦", "symbol": "", "meaning": "水流不息,智慧如泉"},
{"name": "离卦", "symbol": "", "meaning": "火光明亮,照耀前程"},
{"name": "艮卦", "symbol": "", "meaning": "山高水长,稳如磐石"},
{"name": "兑卦", "symbol": "", "meaning": "泽润万物,和谐共生"}
]
# 占卜结果文案
results = {
"大吉": [
"今日运势如虹,万事皆宜,财运亨通,贵人相助!",
"紫气东来,福星高照,今日必有喜事临门!",
"天时地利人和,今日是您大展宏图的好日子!"
],
"中吉": [
"今日运势不错,做事顺利,宜把握机会!",
"春风得意,今日适合开展新计划!",
"运势上升,今日努力必有收获!"
],
"小吉": [
"今日运势平稳向好,小有收获!",
"和风细雨,今日宜静心修身!",
"运势渐佳,今日适合稳步前进!"
],
"": [
"今日运势平稳,宜守不宜攻!",
"平平淡淡才是真,今日适合休养生息!",
"运势平和,今日宜保持现状!"
],
"小凶": [
"今日运势略有波折,宜谨慎行事!",
"小心驶得万年船,今日宜低调处事!",
"运势稍逊,今日宜多思而后行!"
],
"": [
"今日运势欠佳,宜静待时机!",
"山雨欲来风满楼,今日宜避其锋芒!",
"运势低迷,今日宜韬光养晦!"
]
}
# 建议文案
advice_list = {
"大吉": [
"今日宜:投资理财、开展新业务、拜访贵人",
"今日宜:签订合同、举办庆典、求婚表白",
"今日宜:出行旅游、购买重要物品、做重大决定"
],
"中吉": [
"今日宜:学习进修、拓展人脉、适度投资",
"今日宜:整理规划、健身运动、与朋友聚会",
"今日宜:处理积压事务、改善居住环境"
],
"小吉": [
"今日宜:读书思考、轻松娱乐、关爱家人",
"今日宜:整理物品、制定计划、适度休息",
"今日宜:培养兴趣、与人为善、保持乐观"
],
"": [
"今日宜:维持现状、按部就班、稳中求进",
"今日宜:反思总结、调整心态、积蓄力量",
"今日宜:关注健康、陪伴家人、平和处事"
],
"小凶": [
"今日忌:冲动决定、大额消费、与人争执",
"今日忌:签重要合同、做重大变动、外出远行",
"今日宜:谨言慎行、低调做人、耐心等待"
],
"": [
"今日忌:投资冒险、开展新项目、做重要决定",
"今日忌:与人冲突、外出办事、签署文件",
"今日宜:静心修养、反省自身、等待转机"
]
}
# 按权重随机选择等级
total_weight = sum(level["weight"] for level in levels)
rand_num = random.randint(1, total_weight)
current_weight = 0
selected_level = None
for level in levels:
current_weight += level["weight"]
if rand_num <= current_weight:
selected_level = level
break
# 随机选择卦象
selected_hexagram = random.choice(hexagrams)
# 随机选择对应等级的结果和建议
level_name = selected_level["name"]
selected_result = random.choice(results[level_name])
selected_advice = random.choice(advice_list[level_name])
return {
"level": level_name,
"level_color": selected_level["color"],
"result": selected_result,
"hexagram": f"{selected_hexagram['symbol']} {selected_hexagram['name']}",
"hexagram_meaning": selected_hexagram["meaning"],
"advice": selected_advice
}
#==========================今日占卜处理==========================
def console_input_thread(server):
"""控制台输入处理线程"""
# 等待服务器完全启动
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:
# 使用自定义提示符格式,不使用锁避免阻塞
prompt = f"mengyafarm#{server_ip}> "
command = input(prompt).strip()
if command:
# 只在处理命令时使用锁,避免输出被打乱
with input_lock:
console.process_command(command)
except EOFError:
break
except KeyboardInterrupt:
break
except Exception as e:
# 使用锁保护错误输出
with input_lock:
print(f"❌ 处理命令时出错: {str(e)}")
# 主程序启动入口
if __name__ == "__main__":
import sys
try:
print("=" * 60)
print(f"🌱 萌芽农场游戏服务器 v{server_version} 🌱")
print("=" * 60)
print(f"📡 服务器地址: {server_host}:{server_port}")
print(f"📦 缓冲区大小: {buffer_size} bytes")
print("=" * 60)
# 创建并启动游戏服务器
server = TCPGameServer()
# 在后台线程中启动服务器
server_thread = threading.Thread(target=server.start)
server_thread.daemon = True
server_thread.start()
print("✅ 服务器启动成功!")
# 启动控制台输入线程
console_thread = threading.Thread(target=console_input_thread, args=(server,))
console_thread.daemon = True
console_thread.start()
# 主循环:保持服务器运行
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n" + "=" * 60)
print("⚠️ 程序被用户中断")
print("💾 正在保存数据并关闭服务器...")
if 'server' in locals():
try:
# 保存所有在线玩家数据
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
server.stop()
print("✅ 服务器已安全关闭")
print("👋 感谢使用萌芽农场服务器!")
print("=" * 60)
sys.exit(0)
except Exception as e:
print(f"\n❌ 服务器启动失败: {str(e)}")
print("🔧 请检查配置并重试")
sys.exit(1)