继续更新UI

This commit is contained in:
2025-09-24 15:06:30 +08:00
parent 26b856d74e
commit df57f3c5ec
59 changed files with 1415 additions and 2898 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,183 @@
"""
网络通信核心模块
====================================================================
- 负责处理客户端与服务端之间的消息路由
- 将消息类型映射到对应的处理函数
- 提供统一的消息处理接口
====================================================================
"""
class MessageHandler:
"""
消息处理器类
负责将客户端消息路由到对应的处理函数
"""
def __init__(self, server_instance):
"""
初始化消息处理器
Args:
server_instance: 服务器实例,用于调用具体的处理方法
"""
self.server = server_instance
def handle_message(self, client_id, message):
"""
接收客户端消息并路由到对应处理函数
这是服务端与客户端通信的核心中的核心
Args:
client_id: 客户端ID
message: 消息内容(字典格式)
Returns:
处理结果
"""
message_type = message.get("type", "")
# 用户认证相关
if message_type == "greeting": # 默认欢迎
return self.server._handle_greeting(client_id, message)
elif message_type == "login": # 玩家登录
return self.server._handle_login(client_id, message)
elif message_type == "register": # 玩家注册
return self.server._handle_register(client_id, message)
elif message_type == "request_verification_code": # 验证码请求
return self.server._handle_verification_code_request(client_id, message)
elif message_type == "request_forget_password_verification_code": # 忘记密码验证码请求
return self.server._handle_forget_password_verification_code_request(client_id, message)
elif message_type == "reset_password": # 重置密码
return self.server._handle_reset_password_request(client_id, message)
elif message_type == "verify_code": # 验证码
return self.server._handle_verify_code(client_id, message)
# ---------------------------------------------------------------------------
# 游戏操作相关
elif message_type == "harvest_crop": # 收获作物
return self.server._handle_harvest_crop(client_id, message)
elif message_type == "plant_crop": # 种植作物
return self.server._handle_plant_crop(client_id, message)
elif message_type == "buy_seed": # 购买种子
return self.server._handle_buy_seed(client_id, message)
elif message_type == "buy_item": # 购买道具
return self.server._handle_buy_item(client_id, message)
elif message_type == "buy_pet": # 购买宠物
return self.server._handle_buy_pet(client_id, message)
elif message_type == "rename_pet": # 重命名宠物
return self.server._handle_rename_pet(client_id, message)
elif message_type == "set_patrol_pet": # 设置巡逻宠物
return self.server._handle_set_patrol_pet(client_id, message)
elif message_type == "set_battle_pet": # 设置出战宠物
return self.server._handle_set_battle_pet(client_id, message)
elif message_type == "update_battle_pet_data": # 更新宠物对战数据
return self.server._handle_update_battle_pet_data(client_id, message)
elif message_type == "feed_pet": # 喂食宠物
return self.server._handle_feed_pet(client_id, message)
elif message_type == "dig_ground": # 开垦土地
return self.server._handle_dig_ground(client_id, message)
elif message_type == "remove_crop": # 铲除作物
return self.server._handle_remove_crop(client_id, message)
elif message_type == "water_crop": # 浇水
return self.server._handle_water_crop(client_id, message)
elif message_type == "fertilize_crop": # 施肥
return self.server._handle_fertilize_crop(client_id, message)
elif message_type == "use_item": # 使用道具
return self.server._handle_use_item(client_id, message)
elif message_type == "upgrade_land": # 升级土地
return self.server._handle_upgrade_land(client_id, message)
elif message_type == "buy_new_ground": # 添加新的土地
return self.server._handle_buy_new_ground(client_id, message)
elif message_type == "like_player": # 点赞玩家
return self.server._handle_like_player(client_id, message)
elif message_type == "request_online_players": # 请求在线玩家
return self.server._handle_online_players_request(client_id, message)
elif message_type == "get_play_time": # 获取游玩时间
return self.server._handle_get_play_time(client_id)
elif message_type == "update_play_time": # 更新游玩时间
return self.server._handle_update_play_time(client_id)
elif message_type == "request_player_rankings": # 请求玩家排行榜
return self.server._handle_player_rankings_request(client_id, message)
elif message_type == "request_crop_data": # 请求作物数据
return self.server._handle_crop_data_request(client_id)
elif message_type == "request_item_config": # 请求道具配置数据
return self.server._handle_item_config_request(client_id)
elif message_type == "request_pet_config": # 请求宠物配置数据
return self.server._handle_pet_config_request(client_id)
elif message_type == "request_game_tips_config": # 请求游戏小提示配置数据
return self.server._handle_game_tips_config_request(client_id)
elif message_type == "visit_player": # 拜访其他玩家农场
return self.server._handle_visit_player_request(client_id, message)
elif message_type == "return_my_farm": # 返回我的农场
return self.server._handle_return_my_farm_request(client_id, message)
elif message_type == "daily_check_in": # 每日签到
return self.server._handle_daily_check_in_request(client_id, message)
elif message_type == "get_check_in_data": # 获取签到数据
return self.server._handle_get_check_in_data_request(client_id, message)
elif message_type == "lucky_draw": # 幸运抽奖
return self.server._handle_lucky_draw_request(client_id, message)
elif message_type == "claim_new_player_gift": # 领取新手大礼包
return self.server._handle_new_player_gift_request(client_id, message)
elif message_type == "get_online_gift_data": # 获取在线礼包数据
return self.server._handle_get_online_gift_data_request(client_id, message)
elif message_type == "claim_online_gift": # 领取在线礼包
return self.server._handle_claim_online_gift_request(client_id, message)
elif message_type == "ping": # 客户端ping请求
return self.server._handle_ping_request(client_id, message)
elif message_type == "modify_account_info": # 修改账号信息
return self.server._handle_modify_account_info_request(client_id, message)
elif message_type == "delete_account": # 删除账号
return self.server._handle_delete_account_request(client_id, message)
elif message_type == "refresh_player_info": # 刷新玩家信息
return self.server._handle_refresh_player_info_request(client_id, message)
elif message_type == "global_broadcast": # 全服大喇叭消息
return self.server._handle_global_broadcast_message(client_id, message)
elif message_type == "request_broadcast_history": # 请求全服大喇叭历史消息
return self.server._handle_request_broadcast_history(client_id, message)
elif message_type == "use_pet_item": # 宠物使用道具
return self.server._handle_use_pet_item(client_id, message)
elif message_type == "use_farm_item": # 农场道具使用
return self.server._handle_use_farm_item(client_id, message)
elif message_type == "buy_scare_crow": # 购买稻草人
return self.server._handle_buy_scare_crow(client_id, message)
elif message_type == "modify_scare_crow_config": # 修改稻草人配置
return self.server._handle_modify_scare_crow_config(client_id, message)
elif message_type == "get_scare_crow_config": # 获取稻草人配置
return self.server._handle_get_scare_crow_config(client_id, message)
elif message_type == "wisdom_tree_operation": # 智慧树操作
return self.server._handle_wisdom_tree_operation(client_id, message)
elif message_type == "wisdom_tree_message": # 智慧树消息
return self.server._handle_wisdom_tree_message(client_id, message)
elif message_type == "get_wisdom_tree_config": # 获取智慧树配置
return self.server._handle_get_wisdom_tree_config(client_id, message)
elif message_type == "sell_crop": # 出售作物
return self.server._handle_sell_crop(client_id, message)
elif message_type == "add_product_to_store": # 添加商品到小卖部
return self.server._handle_add_product_to_store(client_id, message)
elif message_type == "remove_store_product": # 下架小卖部商品
return self.server._handle_remove_store_product(client_id, message)
elif message_type == "buy_store_product": # 购买小卖部商品
return self.server._handle_buy_store_product(client_id, message)
elif message_type == "buy_store_booth": # 购买小卖部格子
return self.server._handle_buy_store_booth(client_id, message)
elif message_type == "save_game_settings": # 保存游戏设置
return self.server._handle_save_game_settings(client_id, message)
elif message_type == "pet_battle_result": # 宠物对战结果
return self.server._handle_pet_battle_result(client_id, message)
elif message_type == "today_divination": # 今日占卜
return self.server._handle_today_divination(client_id, message)
elif message_type == "give_money": # 送金币
return self.server._handle_give_money_request(client_id, message)
elif message_type == "sync_bag_data": # 同步背包数据
return self.server._handle_sync_bag_data(client_id, message)
# ---------------------------------------------------------------------------
# 管理员操作相关
elif message_type == "kick_player": # 踢出玩家
return self.server._handle_kick_player(client_id, message)
# elif message_type == "message": # 处理聊天消息(暂未实现)
# return self.server._handle_chat_message(client_id, message)
else:
# 调用父类的默认处理方法
return super(type(self.server), self.server)._handle_message(client_id, message)

View File

View File

@@ -0,0 +1,543 @@
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.header import Header
import random
import string
import json
import os
# 邮件发送配置
SENDER_EMAIL = '3205788256@qq.com' # 发件人邮箱
SENDER_AUTH_CODE = 'szcaxvbftusqddhi' # 授权码
SMTP_SERVER = 'smtp.qq.com' # QQ邮箱SMTP服务器
SMTP_PORT = 465 # QQ邮箱SSL端口
# 验证码缓存文件
VERIFICATION_CACHE_FILE = os.path.join("config", "verification_codes.json")
class QQMailAPI:
"""QQ邮箱发送邮件API类"""
def __init__(self, sender_email, authorization_code):
"""
初始化邮箱配置
:param sender_email: 发送方QQ邮箱地址
:param authorization_code: QQ邮箱授权码
"""
self.sender_email = sender_email
self.authorization_code = authorization_code
self.smtp_server = 'smtp.qq.com'
self.smtp_port = 465 # SSL端口
# 发送纯文本邮件
def send_text_email(self, receiver_email, subject, content, cc_emails=None):
"""
发送纯文本邮件
:param receiver_email: 接收方邮箱地址(单个)
:param subject: 邮件主题
:param content: 邮件正文内容
:param cc_emails: 抄送邮箱列表
:return: 发送成功返回True失败返回False
"""
try:
# 创建邮件对象
message = MIMEText(content, 'plain', 'utf-8')
message['From'] = Header(self.sender_email, 'utf-8')
message['To'] = Header(receiver_email, 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
# 添加抄送
if cc_emails:
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
all_receivers = [receiver_email] + cc_emails
else:
all_receivers = [receiver_email]
# 连接SMTP服务器并发送邮件
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.authorization_code)
server.sendmail(self.sender_email, all_receivers, message.as_string())
print(f"邮件发送成功:主题='{subject}', 收件人='{receiver_email}'")
return True
except Exception as e:
print(f"邮件发送失败:{str(e)}")
return False
# 发送HTML格式邮件可带附件
def send_html_email(self, receiver_email, subject, html_content, cc_emails=None, attachments=None):
"""
发送HTML格式邮件可带附件
:param receiver_email: 接收方邮箱地址(单个)
:param subject: 邮件主题
:param html_content: HTML格式的邮件正文
:param cc_emails: 抄送邮箱列表
:param attachments: 附件文件路径列表
:return: 发送成功返回True失败返回False
"""
try:
# 创建带附件的邮件对象
message = MIMEMultipart()
message['From'] = Header(self.sender_email, 'utf-8')
message['To'] = Header(receiver_email, 'utf-8')
message['Subject'] = Header(subject, 'utf-8')
# 添加抄送
if cc_emails:
message['Cc'] = Header(",".join(cc_emails), 'utf-8')
all_receivers = [receiver_email] + cc_emails
else:
all_receivers = [receiver_email]
# 添加HTML正文
message.attach(MIMEText(html_content, 'html', 'utf-8'))
# 添加附件
if attachments:
for file_path in attachments:
try:
with open(file_path, 'rb') as file:
attachment = MIMEApplication(file.read(), _subtype="octet-stream")
attachment.add_header('Content-Disposition', 'attachment', filename=file_path.split("/")[-1])
message.attach(attachment)
except Exception as e:
print(f"添加附件失败 {file_path}: {str(e)}")
# 连接SMTP服务器并发送邮件
with smtplib.SMTP_SSL(self.smtp_server, self.smtp_port) as server:
server.login(self.sender_email, self.authorization_code)
server.sendmail(self.sender_email, all_receivers, message.as_string())
print(f"HTML邮件发送成功主题='{subject}', 收件人='{receiver_email}'")
return True
except Exception as e:
print(f"HTML邮件发送失败{str(e)}")
return False
class EmailVerification:
#生成指定长度的随机验证码
@staticmethod
def generate_verification_code(length=6):
"""
生成指定长度的随机验证码
参数:
length (int): 验证码长度默认6位
返回:
str: 生成的验证码
"""
# 生成包含大写字母和数字的验证码
chars = string.ascii_uppercase + string.digits
return ''.join(random.choice(chars) for _ in range(length))
#发送验证码邮件到QQ邮箱
@staticmethod
def send_verification_email(qq_number, verification_code, email_type="register"):
"""
发送验证码邮件到QQ邮箱
参数:
qq_number (str): 接收者QQ号
verification_code (str): 验证码
email_type (str): 邮件类型,"register""reset_password"
返回:
bool: 发送成功返回True否则返回False
str: 成功或错误信息
"""
receiver_email = f"{qq_number}@qq.com"
# 根据邮件类型设置不同的内容
if email_type == "reset_password":
email_title = "【萌芽农场】密码重置验证码"
email_purpose = "重置萌芽农场游戏账号密码"
email_color = "#FF6B35" # 橙红色,表示警告性操作
else:
email_title = "【萌芽农场】注册验证码"
email_purpose = "注册萌芽农场游戏账号"
email_color = "#4CAF50" # 绿色,表示正常操作
# 创建邮件内容
message = MIMEText(f'''
<html>
<body>
<div style="font-family: Arial, sans-serif; color: #333;">
<h2 style="color: {email_color};">萌芽农场 - 邮箱验证码</h2>
<p>亲爱的玩家,您好!</p>
<p>您正在{email_purpose},您的验证码是:</p>
<div style="background-color: #f2f2f2; padding: 10px; font-size: 24px; font-weight: bold; color: {email_color}; text-align: center; margin: 20px 0;">
{verification_code}
</div>
<p>该验证码有效期为5分钟请勿泄露给他人。</p>
<p>如果这不是您本人的操作,请忽略此邮件。</p>
<p style="margin-top: 30px; font-size: 12px; color: #999;">
本邮件由系统自动发送,请勿直接回复。
</p>
</div>
</body>
</html>
''', 'html', 'utf-8')
# 修正From头格式符合QQ邮箱的要求
message['From'] = SENDER_EMAIL
message['To'] = receiver_email
message['Subject'] = Header(email_title, 'utf-8')
try:
# 使用SSL/TLS连接而不是STARTTLS
smtp_obj = smtplib.SMTP_SSL(SMTP_SERVER, 465)
smtp_obj.login(SENDER_EMAIL, SENDER_AUTH_CODE)
smtp_obj.sendmail(SENDER_EMAIL, [receiver_email], message.as_string())
smtp_obj.quit()
return True, "验证码发送成功"
except Exception as e:
return False, f"发送验证码失败: {str(e)}"
#保存验证码到MongoDB优先或缓存文件备用
@staticmethod
def save_verification_code(qq_number, verification_code, expiry_time=300, code_type="register"):
"""
保存验证码到MongoDB优先或缓存文件备用
参数:
qq_number (str): QQ号
verification_code (str): 验证码
expiry_time (int): 过期时间默认5分钟
code_type (str): 验证码类型,"register""reset_password"
返回:
bool: 保存成功返回True否则返回False
"""
import time
# 优先尝试使用MongoDB
try:
from .SMYMongoDBAPI import SMYMongoDBAPI
import os
# 根据环境动态选择MongoDB配置
if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true':
environment = "production"
else:
environment = "test"
mongo_api = SMYMongoDBAPI(environment)
if mongo_api.is_connected():
success = mongo_api.save_verification_code(qq_number, verification_code, expiry_time, code_type)
if success:
print(f"[验证码系统-MongoDB] 为QQ {qq_number} 保存{code_type}验证码: {verification_code}")
return True
else:
print(f"[验证码系统-MongoDB] 保存失败尝试使用JSON文件")
except Exception as e:
print(f"[验证码系统-MongoDB] MongoDB保存失败: {str(e)}尝试使用JSON文件")
# MongoDB失败使用JSON文件备用
# 创建目录(如果不存在)
os.makedirs(os.path.dirname(VERIFICATION_CACHE_FILE), exist_ok=True)
# 读取现有的验证码数据
verification_data = {}
if os.path.exists(VERIFICATION_CACHE_FILE):
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
except Exception as e:
print(f"读取验证码文件失败: {str(e)}")
verification_data = {}
# 添加新的验证码
expire_at = time.time() + expiry_time
current_time = time.time()
# 创建验证码记录,包含更多信息用于调试
verification_data[qq_number] = {
"code": verification_code,
"expire_at": expire_at,
"code_type": code_type,
"created_at": current_time,
"used": False # 新增:标记验证码是否已使用
}
# 保存到文件
try:
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
print(f"[验证码系统-JSON] 为QQ {qq_number} 保存{code_type}验证码: {verification_code}, 过期时间: {expire_at}")
return True
except Exception as e:
print(f"保存验证码失败: {str(e)}")
return False
#验证用户输入的验证码优先使用MongoDB
@staticmethod
def verify_code(qq_number, input_code, code_type="register"):
"""
验证用户输入的验证码优先使用MongoDB
参数:
qq_number (str): QQ号
input_code (str): 用户输入的验证码
code_type (str): 验证码类型,"register""reset_password"
返回:
bool: 验证成功返回True否则返回False
str: 成功或错误信息
"""
import time
# 优先尝试使用MongoDB
try:
from .SMYMongoDBAPI import SMYMongoDBAPI
import os
# 根据环境动态选择MongoDB配置
if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true':
environment = "production"
else:
environment = "test"
mongo_api = SMYMongoDBAPI(environment)
if mongo_api.is_connected():
success, message = mongo_api.verify_verification_code(qq_number, input_code, code_type)
print(f"[验证码系统-MongoDB] QQ {qq_number} 验证结果: {success}, 消息: {message}")
return success, message
except Exception as e:
print(f"[验证码系统-MongoDB] MongoDB验证失败: {str(e)}尝试使用JSON文件")
# MongoDB失败使用JSON文件备用
# 检查缓存文件是否存在
if not os.path.exists(VERIFICATION_CACHE_FILE):
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 缓存文件不存在")
return False, "验证码不存在或已过期"
# 读取验证码数据
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
except Exception as e:
print(f"[验证码系统-JSON] 读取验证码文件失败: {str(e)}")
return False, "验证码数据损坏"
# 检查该QQ号是否有验证码
if qq_number not in verification_data:
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 没有找到验证码记录")
return False, "验证码不存在,请重新获取"
# 获取存储的验证码信息
code_info = verification_data[qq_number]
stored_code = code_info.get("code", "")
expire_at = code_info.get("expire_at", 0)
stored_code_type = code_info.get("code_type", "register")
is_used = code_info.get("used", False)
created_at = code_info.get("created_at", 0)
print(f"[验证码系统-JSON] QQ {qq_number} 验证码详情: 存储码={stored_code}, 输入码={input_code}, 类型={stored_code_type}, 已使用={is_used}, 创建时间={created_at}")
# 检查验证码类型是否匹配
if stored_code_type != code_type:
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 验证码类型不匹配,存储类型={stored_code_type}, 请求类型={code_type}")
return False, f"验证码类型不匹配,请重新获取{code_type}验证码"
# 检查验证码是否已被使用
if is_used:
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 验证码已被使用")
return False, "验证码已被使用,请重新获取"
# 检查验证码是否过期
current_time = time.time()
if current_time > expire_at:
# 移除过期的验证码
del verification_data[qq_number]
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 验证码已过期")
return False, "验证码已过期,请重新获取"
# 验证码比较(不区分大小写)
if input_code.upper() == stored_code.upper():
# 验证成功,标记为已使用而不是删除
verification_data[qq_number]["used"] = True
verification_data[qq_number]["used_at"] = current_time
try:
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
print(f"[验证码系统-JSON] QQ {qq_number} 验证成功: 验证码已标记为已使用")
return True, "验证码正确"
except Exception as e:
print(f"[验证码系统-JSON] 标记验证码已使用时失败: {str(e)}")
return True, "验证码正确" # 即使标记失败,验证还是成功的
else:
print(f"[验证码系统-JSON] QQ {qq_number} 验证失败: 验证码不匹配")
return False, "验证码错误"
#清理过期的验证码和已使用的验证码优先使用MongoDB
@staticmethod
def clean_expired_codes():
"""
清理过期的验证码和已使用的验证码优先使用MongoDB
"""
import time
# 优先尝试使用MongoDB
try:
from .SMYMongoDBAPI import SMYMongoDBAPI
import os
# 根据环境动态选择MongoDB配置
if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true':
environment = "production"
else:
environment = "test"
mongo_api = SMYMongoDBAPI(environment)
if mongo_api.is_connected():
expired_count = mongo_api.clean_expired_verification_codes()
print(f"[验证码系统-MongoDB] 清理完成,删除了 {expired_count} 个过期验证码")
return expired_count
except Exception as e:
print(f"[验证码系统-MongoDB] MongoDB清理失败: {str(e)}尝试使用JSON文件")
# MongoDB失败使用JSON文件备用
if not os.path.exists(VERIFICATION_CACHE_FILE):
return
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
current_time = time.time()
removed_keys = []
# 找出过期的验证码和已使用的验证码超过1小时
for qq_number, code_info in verification_data.items():
expire_at = code_info.get("expire_at", 0)
is_used = code_info.get("used", False)
used_at = code_info.get("used_at", 0)
should_remove = False
# 过期的验证码
if current_time > expire_at:
should_remove = True
print(f"[验证码清理-JSON] 移除过期验证码: QQ {qq_number}")
# 已使用超过1小时的验证码
elif is_used and used_at > 0 and (current_time - used_at) > 3600:
should_remove = True
print(f"[验证码清理-JSON] 移除已使用的验证码: QQ {qq_number}")
if should_remove:
removed_keys.append(qq_number)
# 移除标记的验证码
for key in removed_keys:
del verification_data[key]
# 保存更新后的数据
if removed_keys:
with open(VERIFICATION_CACHE_FILE, 'w', encoding='utf-8') as file:
json.dump(verification_data, file, indent=2, ensure_ascii=False)
print(f"[验证码清理-JSON] 共清理了 {len(removed_keys)} 个验证码")
except Exception as e:
print(f"清理验证码失败: {str(e)}")
#获取验证码状态优先使用MongoDB
@staticmethod
def get_verification_status(qq_number):
"""
获取验证码状态优先使用MongoDB
参数:
qq_number (str): QQ号
返回:
dict: 验证码状态信息
"""
import time
# 优先尝试使用MongoDB
try:
from .SMYMongoDBAPI import SMYMongoDBAPI
import os
# 根据环境动态选择MongoDB配置
if os.path.exists('/.dockerenv') or os.environ.get('PRODUCTION', '').lower() == 'true':
environment = "production"
else:
environment = "test"
mongo_api = SMYMongoDBAPI(environment)
if mongo_api.is_connected():
verification_codes = mongo_api.get_verification_codes()
if verification_codes and qq_number in verification_codes:
code_info = verification_codes[qq_number]
current_time = time.time()
return {
"status": "found",
"code": code_info.get("code", ""),
"code_type": code_info.get("code_type", "unknown"),
"used": code_info.get("used", False),
"expired": current_time > code_info.get("expire_at", 0),
"created_at": code_info.get("created_at", 0),
"expire_at": code_info.get("expire_at", 0),
"used_at": code_info.get("used_at", 0),
"source": "mongodb"
}
else:
return {"status": "no_code", "source": "mongodb"}
except Exception as e:
print(f"[验证码系统-MongoDB] MongoDB状态查询失败: {str(e)}尝试使用JSON文件")
# MongoDB失败使用JSON文件备用
if not os.path.exists(VERIFICATION_CACHE_FILE):
return {"status": "no_cache_file"}
try:
with open(VERIFICATION_CACHE_FILE, 'r', encoding='utf-8') as file:
verification_data = json.load(file)
if qq_number not in verification_data:
return {"status": "no_code"}
code_info = verification_data[qq_number]
current_time = time.time()
return {
"status": "found",
"code": code_info.get("code", ""),
"code_type": code_info.get("code_type", "unknown"),
"used": code_info.get("used", False),
"expired": current_time > code_info.get("expire_at", 0),
"created_at": code_info.get("created_at", 0),
"expire_at": code_info.get("expire_at", 0),
"used_at": code_info.get("used_at", 0),
"source": "json"
}
except Exception as e:
return {"status": "error", "message": str(e)}
# 测试邮件发送
if __name__ == "__main__":
# 清理过期验证码
EmailVerification.clean_expired_codes()
# 生成验证码
test_qq = input("请输入测试QQ号: ")
verification_code = EmailVerification.generate_verification_code()
print(f"生成的验证码: {verification_code}")
# 发送测试邮件
success, message = EmailVerification.send_verification_email(test_qq, verification_code)
print(f"发送结果: {success}, 消息: {message}")
if success:
# 保存验证码
EmailVerification.save_verification_code(test_qq, verification_code)
# 测试验证
test_input = input("请输入收到的验证码: ")
verify_success, verify_message = EmailVerification.verify_code(test_qq, test_input)
print(f"验证结果: {verify_success}, 消息: {verify_message}")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,508 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
特殊农场管理系统
作者: AI Assistant
功能: 管理特殊农场的自动种植和维护
"""
import time
import random
import logging
from datetime import datetime
from bson import ObjectId
#自定义包
from .SMYMongoDBAPI import SMYMongoDBAPI
#杂交农场666-种植杂交树1杂交树2-每天0点种植
#花卉农场520-随机种植各种花卉-星期一,星期三,星期五,星期日零点种植
#瓜果农场333-随机种植各种瓜果-星期二,星期四,星期六零点种植
#小麦谷222-全屏种植小麦-每天0点种植
#稻香111-全屏种植稻谷-每天0点种植
#幸运农场888-随机种植1-80个幸运草和幸运花-星期一零点种植
class SpecialFarmManager:
#初始化特殊农场管理器
def __init__(self, environment=None):
"""
初始化特殊农场管理器
Args:
environment: 环境类型,"test""production"如果为None则自动检测
"""
# 如果没有指定环境使用与TCPGameServer相同的环境检测逻辑
if environment is None:
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)
# 设置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('special_farm.log', encoding='utf-8'),
logging.StreamHandler()
]
)
self.logger = logging.getLogger(__name__)
# 特殊农场配置
self.special_farms = {
"杂交农场": {
"object_id": "689b4b9286cf953f2f4e56ee",
"crops": ["杂交树1", "杂交树2"],
"description": "专门种植杂交树的特殊农场"
},
"花卉农场": {
"object_id": "689bec6286cf953f2f4e56f1",
"crops": ["郁金香", "牵牛花", "百合花", "栀子花", "玫瑰花", "向日葵", "藏红花", "幸运花"],
"description": "盛产各种美丽花卉的特殊农场",
"plant_type": "random_flowers" # 标记为随机花卉种植类型
},
"瓜果农场": {
"object_id": "689bf73886cf953f2f4e56fa",
"crops": ["西瓜", "南瓜", "哈密瓜", "葫芦", "黄瓜", "龙果", "菠萝", "芒果"],
"description": "盛产各种瓜果的农场",
"plant_type": "random_fruits" # 标记为随机瓜果种植类型
},
"小麦谷": {
"object_id": "689bf9a886cf953f2f4e56fd",
"crops": ["小麦"],
"description": "盛产小麦的农场",
"plant_type": "single_wheat" # 标记为单一小麦种植类型
},
"稻香": {
"object_id": "689bf9ac86cf953f2f4e56fe",
"crops": ["稻谷"],
"description": "盛产稻谷的农场",
"plant_type": "single_rice" # 标记为单一稻谷种植类型
},
"幸运农场": {
"object_id": "689c027886cf953f2f4e56ff",
"crops": ["幸运草", "幸运花"],
"description": "盛产幸运草和幸运花的农场",
"plant_type": "random_lucky" # 标记为随机幸运植物种植类型
}
}
self.logger.info(f"特殊农场管理器初始化完成 - 环境: {environment}")
#获取作物系统数据
def get_crop_data(self):
"""
获取作物配置数据
Returns:
dict: 作物配置数据
"""
try:
crop_config = self.mongo_api.get_crop_data_config()
if crop_config:
# 移除MongoDB相关字段
if "_id" in crop_config:
del crop_config["_id"]
if "config_type" in crop_config:
del crop_config["config_type"]
if "updated_at" in crop_config:
del crop_config["updated_at"]
return crop_config
else:
self.logger.error("无法获取作物配置数据")
return {}
except Exception as e:
self.logger.error(f"获取作物配置数据时出错: {str(e)}")
return {}
#通过文档ID获取农场数据
def get_player_data_by_object_id(self, object_id):
"""
通过ObjectId获取玩家数据
Args:
object_id: MongoDB文档ID
Returns:
dict: 玩家数据如果未找到返回None
"""
try:
collection = self.mongo_api.get_collection("playerdata")
oid = ObjectId(object_id)
player_data = collection.find_one({"_id": oid})
if player_data:
self.logger.info(f"成功获取玩家数据: {player_data.get('玩家昵称', 'Unknown')}")
return player_data
else:
self.logger.warning(f"未找到ObjectId为 {object_id} 的玩家数据")
return None
except Exception as e:
self.logger.error(f"获取玩家数据时出错: {str(e)}")
return None
#通过文档ID保存农场数据
def save_player_data_by_object_id(self, object_id, player_data):
"""
通过ObjectId保存玩家数据
Args:
object_id: MongoDB文档ID
player_data: 玩家数据
Returns:
bool: 是否保存成功
"""
try:
collection = self.mongo_api.get_collection("playerdata")
oid = ObjectId(object_id)
# 更新最后登录时间
player_data["最后登录时间"] = datetime.now().strftime("%Y年%m月%d%H时%M分%S秒")
result = collection.replace_one({"_id": oid}, player_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info(f"成功保存玩家数据: {player_data.get('玩家昵称', 'Unknown')}")
return True
else:
self.logger.error(f"保存玩家数据失败: ObjectId {object_id}")
return False
except Exception as e:
self.logger.error(f"保存玩家数据时出错: {str(e)}")
return False
#在指定农场种植作物
def plant_crops_in_farm(self, farm_name):
"""
为指定特殊农场种植作物
Args:
farm_name: 农场名称
Returns:
bool: 是否种植成功
"""
if farm_name not in self.special_farms:
self.logger.error(f"未知的特殊农场: {farm_name}")
return False
farm_config = self.special_farms[farm_name]
object_id = farm_config["object_id"]
available_crops = farm_config["crops"]
# 获取玩家数据
player_data = self.get_player_data_by_object_id(object_id)
if not player_data:
return False
# 获取作物配置
crop_data = self.get_crop_data()
if not crop_data:
self.logger.error("无法获取作物配置,跳过种植")
return False
# 检查作物是否存在
for crop_name in available_crops:
if crop_name not in crop_data:
self.logger.error(f"作物 {crop_name} 不存在于作物配置中")
return False
# 获取农场土地
farm_lands = player_data.get("农场土地", [])
if not farm_lands:
self.logger.error(f"农场 {farm_name} 没有土地数据")
return False
planted_count = 0
plant_type = farm_config.get("plant_type", "normal")
# 遍历所有土地,先开垦再种植作物
for i, land in enumerate(farm_lands):
# 根据农场类型选择作物
if plant_type == "random_flowers":
# 花卉农场:随机种植各种花卉,种满所有土地
crop_name = random.choice(available_crops)
should_plant = True # 100%种植率,种满所有土地
elif plant_type == "random_fruits":
# 瓜果农场:随机种植各种瓜果,种满所有土地
crop_name = random.choice(available_crops)
should_plant = True # 100%种植率,种满所有土地
elif plant_type == "single_wheat":
# 小麦谷:全屏种植小麦,种满所有土地
crop_name = "小麦"
should_plant = True # 100%种植率,种满所有土地
elif plant_type == "single_rice":
# 稻香:全屏种植稻谷,种满所有土地
crop_name = "稻谷"
should_plant = True # 100%种植率,种满所有土地
elif plant_type == "random_lucky":
# 幸运农场随机种植1-80个幸运草和幸运花
crop_name = random.choice(available_crops)
# 随机决定是否种植确保总数在1-80之间
target_plants = random.randint(1, min(80, len(farm_lands)))
should_plant = planted_count < target_plants
else:
# 普通农场:按原逻辑随机选择
crop_name = random.choice(available_crops)
should_plant = True
if should_plant:
crop_info = crop_data[crop_name]
# 更新土地数据(先开垦,再种植)
land.update({
"is_diged": True, # 确保土地已开垦
"is_planted": True,
"crop_type": crop_name,
"grow_time": 0, # 立即成熟
"max_grow_time": crop_info.get("生长时间", 21600),
"is_dead": False,
"已浇水": True,
"已施肥": True,
"土地等级": 0
})
# 清除施肥时间戳
if "施肥时间" in land:
del land["施肥时间"]
planted_count += 1
else:
# 留空的土地:只开垦不种植
land.update({
"is_diged": True,
"is_planted": False,
"crop_type": "",
"grow_time": 0,
"max_grow_time": 3,
"is_dead": False,
"已浇水": False,
"已施肥": False,
"土地等级": 0
})
# 清除施肥时间戳
if "施肥时间" in land:
del land["施肥时间"]
# 保存玩家数据
if self.save_player_data_by_object_id(object_id, player_data):
self.logger.info(f"成功为 {farm_name} 种植了 {planted_count} 块土地的作物")
return True
else:
self.logger.error(f"保存 {farm_name} 数据失败")
return False
#每日维护任务
def daily_maintenance(self):
"""
每日维护任务
"""
from datetime import datetime
self.logger.info("开始执行特殊农场维护任务...")
success_count = 0
total_farms = 0
current_time = datetime.now()
current_weekday = current_time.weekday() # 0=Monday, 1=Tuesday, ..., 6=Sunday
current_time_str = current_time.strftime("%Y-%m-%d %H:%M:%S")
for farm_name in self.special_farms.keys():
# 检查农场是否需要在今天维护
should_maintain = True
if farm_name == "瓜果农场":
# 瓜果农场只在星期二(1)、四(3)、六(5)维护
if current_weekday not in [1, 3, 5]:
should_maintain = False
self.logger.info(f"瓜果农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护")
elif farm_name == "幸运农场":
# 幸运农场只在星期一(0)维护
if current_weekday != 0:
should_maintain = False
self.logger.info(f"幸运农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护")
if should_maintain:
total_farms += 1
try:
if self.plant_crops_in_farm(farm_name):
success_count += 1
self.logger.info(f"农场 {farm_name} 维护完成")
# 更新维护时间记录
try:
farm_config = self.special_farms[farm_name]
object_id = farm_config["object_id"]
player_data = self.get_player_data_by_object_id(object_id)
if player_data:
player_data["特殊农场最后维护时间"] = current_time_str
self.save_player_data_by_object_id(object_id, player_data)
except Exception as record_error:
self.logger.error(f"更新 {farm_name} 维护时间记录失败: {str(record_error)}")
else:
self.logger.error(f"农场 {farm_name} 维护失败")
except Exception as e:
self.logger.error(f"维护农场 {farm_name} 时出错: {str(e)}")
self.logger.info(f"维护任务完成: {success_count}/{total_farms} 个农场维护成功")
#启动定时任务调度器(后台线程模式)
def start_scheduler(self):
"""
启动定时任务调度器(后台线程模式)
"""
import threading
self.logger.info("特殊农场定时任务调度器已启动")
self.logger.info("维护任务将在每天凌晨0点执行")
# 检查是否需要立即执行维护任务
self._check_and_run_initial_maintenance()
# 在后台线程中运行调度循环
def scheduler_loop():
last_maintenance_date = None
while True:
try:
now = datetime.now()
current_date = now.strftime("%Y-%m-%d")
# 检查是否到了零点且今天还没有执行过维护
if (now.hour == 0 and now.minute == 0 and
last_maintenance_date != current_date):
self.logger.info("零点维护时间到,开始执行维护任务...")
self.daily_maintenance()
last_maintenance_date = current_date
time.sleep(60) # 每分钟检查一次
except Exception as e:
self.logger.error(f"调度器运行时出错: {str(e)}")
time.sleep(60)
# 启动后台线程
scheduler_thread = threading.Thread(target=scheduler_loop, daemon=True)
scheduler_thread.start()
self.logger.info("特殊农场调度器已在后台线程启动")
#检查是否需要执行初始维护任务
def _check_and_run_initial_maintenance(self):
"""
检查是否需要执行初始维护任务
避免服务器重启时重复执行
"""
try:
from datetime import datetime, timedelta
# 检查今天是否已经执行过维护任务
current_time = datetime.now()
today = current_time.strftime("%Y-%m-%d")
current_weekday = current_time.weekday() # 0=Monday, 1=Tuesday, ..., 6=Sunday
# 获取特殊农场数据,检查最后维护时间
for farm_name, farm_config in self.special_farms.items():
object_id = farm_config["object_id"]
player_data = self.get_player_data_by_object_id(object_id)
# 检查农场是否需要在今天维护
should_maintain = True
if farm_name == "瓜果农场":
# 瓜果农场只在星期二(1)、四(3)、六(5)维护
if current_weekday not in [1, 3, 5]:
should_maintain = False
self.logger.info(f"瓜果农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护")
elif farm_name == "幸运农场":
# 幸运农场只在星期一(0)维护
if current_weekday != 0:
should_maintain = False
self.logger.info(f"幸运农场今日({['周一','周二','周三','周四','周五','周六','周日'][current_weekday]})不需要维护")
if should_maintain and player_data:
last_maintenance = player_data.get("特殊农场最后维护时间", "")
# 如果今天还没有维护过,则执行维护
if not last_maintenance or not last_maintenance.startswith(today):
self.logger.info(f"检测到 {farm_name} 今日尚未维护,执行维护任务...")
if self.plant_crops_in_farm(farm_name):
# 更新维护时间记录
player_data["特殊农场最后维护时间"] = current_time.strftime("%Y-%m-%d %H:%M:%S")
self.save_player_data_by_object_id(object_id, player_data)
else:
self.logger.info(f"{farm_name} 今日已维护过,跳过初始维护")
except Exception as e:
self.logger.error(f"检查初始维护任务时出错: {str(e)}")
# 如果检查失败,执行一次维护作为备用
self.logger.info("执行备用维护任务...")
self.daily_maintenance()
#停止定时任务调度器
def stop_scheduler(self):
"""
停止定时任务调度器
"""
try:
# 设置停止标志(如果需要的话)
self.logger.info("特殊农场定时任务调度器已停止")
except Exception as e:
self.logger.error(f"停止定时任务调度器时出错: {str(e)}")
#手动执行维护任务
def manual_maintenance(self, farm_name=None):
"""
手动执行维护任务
Args:
farm_name: 指定农场名称如果为None则维护所有农场
"""
if farm_name:
if farm_name in self.special_farms:
self.logger.info(f"手动维护农场: {farm_name}")
return self.plant_crops_in_farm(farm_name)
else:
self.logger.error(f"未知的农场名称: {farm_name}")
return False
else:
self.logger.info("手动维护所有特殊农场")
self.daily_maintenance()
return True
def main():
"""
主函数
"""
import sys
# 检查命令行参数
environment = "production"
if len(sys.argv) > 1:
if sys.argv[1] in ["test", "production"]:
environment = sys.argv[1]
else:
print("使用方法: python SpecialFarm.py [test|production]")
sys.exit(1)
# 创建特殊农场管理器
manager = SpecialFarmManager(environment)
# 检查是否为手动模式
if len(sys.argv) > 2 and sys.argv[2] == "manual":
# 手动执行维护
farm_name = sys.argv[3] if len(sys.argv) > 3 else None
manager.manual_maintenance(farm_name)
else:
# 启动定时任务
manager.start_scheduler()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,255 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
WebSocket协议的服务器远程命令API
作者: AI Assistant
功能: 提供基于WebSocket的远程控制台命令执行功能
"""
import asyncio
import websockets
import json
import threading
import time
from typing import Dict, Any, Optional
#自定义包
from .ConsoleCommandsAPI import ConsoleCommandsAPI
class WSRemoteCmdApi:
"""WebSocket远程命令API服务器"""
def __init__(self, game_server, host="0.0.0.0", port=7071, auth_key="mengya2024"):
"""
初始化WebSocket远程命令API服务器
Args:
game_server: 游戏服务器实例
host: WebSocket服务器监听地址
port: WebSocket服务器监听端口
auth_key: 认证密钥
"""
self.game_server = game_server
self.host = host
self.port = port
self.auth_key = auth_key
self.server = None
self.clients = {} # 存储已连接的客户端
self.console_api = ConsoleCommandsAPI(game_server)
self.running = False
async def register_client(self, websocket, path=None):
"""注册新的客户端连接"""
client_id = f"{websocket.remote_address[0]}:{websocket.remote_address[1]}_{int(time.time())}"
self.clients[client_id] = {
"websocket": websocket,
"authenticated": False,
"connect_time": time.time()
}
try:
# 发送欢迎消息
await self.send_message(websocket, {
"type": "welcome",
"message": "欢迎连接到萌芽农场远程控制台",
"server_version": getattr(self.game_server, 'server_version', '2.2.0'),
"require_auth": True
})
# 处理客户端消息
async for message in websocket:
await self.handle_message(client_id, message)
except websockets.exceptions.ConnectionClosed:
pass
except Exception as e:
print(f"❌ 客户端 {client_id} 连接处理出错: {str(e)}")
finally:
# 清理客户端连接
if client_id in self.clients:
del self.clients[client_id]
print(f"🔌 客户端 {client_id} 已断开连接")
async def handle_message(self, client_id: str, message: str):
"""处理客户端消息"""
try:
data = json.loads(message)
message_type = data.get("type", "")
if message_type == "auth":
await self.handle_auth(client_id, data)
elif message_type == "command":
await self.handle_command(client_id, data)
elif message_type == "ping":
await self.handle_ping(client_id, data)
else:
await self.send_error(client_id, f"未知消息类型: {message_type}")
except json.JSONDecodeError:
await self.send_error(client_id, "无效的JSON格式")
except Exception as e:
await self.send_error(client_id, f"处理消息时出错: {str(e)}")
async def handle_auth(self, client_id: str, data: Dict[str, Any]):
"""处理客户端认证"""
if client_id not in self.clients:
return
provided_key = data.get("auth_key", "")
if provided_key == self.auth_key:
self.clients[client_id]["authenticated"] = True
await self.send_message(self.clients[client_id]["websocket"], {
"type": "auth_result",
"success": True,
"message": "认证成功,欢迎使用远程控制台"
})
print(f"✅ 客户端 {client_id} 认证成功")
else:
await self.send_message(self.clients[client_id]["websocket"], {
"type": "auth_result",
"success": False,
"message": "认证失败,密钥错误"
})
print(f"❌ 客户端 {client_id} 认证失败")
async def handle_command(self, client_id: str, data: Dict[str, Any]):
"""处理控制台命令"""
if client_id not in self.clients:
return
# 检查是否已认证
if not self.clients[client_id]["authenticated"]:
await self.send_error(client_id, "请先进行认证")
return
command = data.get("command", "").strip()
if not command:
await self.send_error(client_id, "命令不能为空")
return
# 执行命令并捕获输出
try:
# 重定向标准输出来捕获命令执行结果
import io
import sys
old_stdout = sys.stdout
sys.stdout = captured_output = io.StringIO()
# 执行命令
success = self.console_api.process_command(command)
# 恢复标准输出
sys.stdout = old_stdout
output = captured_output.getvalue()
# 发送执行结果
await self.send_message(self.clients[client_id]["websocket"], {
"type": "command_result",
"command": command,
"success": success,
"output": output if output else ("命令执行成功" if success else "命令执行失败")
})
print(f"📝 客户端 {client_id} 执行命令: {command} - {'成功' if success else '失败'}")
except Exception as e:
await self.send_error(client_id, f"执行命令时出错: {str(e)}")
async def handle_ping(self, client_id: str, data: Dict[str, Any]):
"""处理ping请求"""
if client_id not in self.clients:
return
await self.send_message(self.clients[client_id]["websocket"], {
"type": "pong",
"timestamp": time.time()
})
async def send_message(self, websocket, data: Dict[str, Any]):
"""发送消息到客户端"""
try:
message = json.dumps(data, ensure_ascii=False)
await websocket.send(message)
except Exception as e:
print(f"❌ 发送消息失败: {str(e)}")
async def send_error(self, client_id: str, error_message: str):
"""发送错误消息到客户端"""
if client_id in self.clients:
await self.send_message(self.clients[client_id]["websocket"], {
"type": "error",
"message": error_message
})
def start_server(self):
"""启动WebSocket服务器"""
if self.running:
return
async def run_server_async():
try:
self.server = await websockets.serve(
self.register_client,
self.host,
self.port
)
self.running = True
print(f"🌐 WebSocket远程控制台服务器已启动: ws://{self.host}:{self.port}")
print(f"🔑 认证密钥: {self.auth_key}")
# 保持服务器运行
await self.server.wait_closed()
except Exception as e:
print(f"❌ WebSocket服务器启动失败: {str(e)}")
self.running = False
def run_server():
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(run_server_async())
except Exception as e:
print(f"❌ WebSocket服务器线程异常: {str(e)}")
self.running = False
# 在新线程中运行WebSocket服务器
server_thread = threading.Thread(target=run_server, daemon=True)
server_thread.start()
def stop_server(self):
"""停止WebSocket服务器"""
if not self.running:
return
self.running = False
# 关闭所有客户端连接
for client_id, client_info in list(self.clients.items()):
try:
asyncio.create_task(client_info["websocket"].close())
except:
pass
self.clients.clear()
if self.server:
try:
self.server.close()
except:
pass
print("🌐 WebSocket远程控制台服务器已停止")
def get_status(self) -> Dict[str, Any]:
"""获取服务器状态"""
return {
"running": self.running,
"host": self.host,
"port": self.port,
"connected_clients": len(self.clients),
"authenticated_clients": len([c for c in self.clients.values() if c["authenticated"]])
}

View File

@@ -0,0 +1,708 @@
"""智慧树系统相关逻辑模块。"""
import time
class WisdomTreeMixin:
"""智慧树系统逻辑混入类。"""
def _handle_wisdom_tree_operation(self, client_id, message):
"""处理智慧树操作请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "智慧树操作", "wisdom_tree_operation")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "wisdom_tree_operation")
if not player_data:
return self.send_data(client_id, response)
operation_type = message.get("operation_type", "")
# 检查并修复智慧树配置格式
self._check_and_fix_wisdom_tree_config(player_data, username)
# 获取修复后的智慧树配置
wisdom_tree_config = player_data["智慧树配置"]
# 处理不同的操作类型
if operation_type == "water":
return self._process_wisdom_tree_water(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "fertilize":
return self._process_wisdom_tree_fertilize(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "kill_grass":
return self._process_wisdom_tree_kill_grass(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "kill_bug":
return self._process_wisdom_tree_kill_bug(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "play_music":
return self._process_wisdom_tree_play_music(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "revive":
return self._process_wisdom_tree_revive(client_id, player_data, username, wisdom_tree_config)
elif operation_type == "get_random_message":
return self._process_wisdom_tree_get_random_message(client_id, player_data, username, wisdom_tree_config)
else:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "未知的智慧树操作类型",
"operation_type": operation_type
})
def _process_wisdom_tree_water(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树浇水"""
# 检查智慧树是否死亡
if wisdom_tree_config["当前生命值"] <= 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树已死亡,请先复活!",
"operation_type": "water"
})
# 浇水费用
water_cost = 100
# 检查金钱是否足够
if player_data["钱币"] < water_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,浇水需要 {water_cost} 金币",
"operation_type": "water"
})
# 执行浇水
player_data["钱币"] -= water_cost
# 浇水经验50-150随机
import random
exp_gained = random.randint(50, 150)
wisdom_tree_config["当前经验值"] += exp_gained
# 浇水高度40%概率增加1-2高度
height_gained = 0
if random.random() < 0.4: # 40%概率
height_gained = random.randint(1, 2)
wisdom_tree_config["高度"] = min(100, wisdom_tree_config["高度"] + height_gained)
# 检查等级提升
level_up_occurred = self._check_wisdom_tree_level_up(wisdom_tree_config)
# 保存数据
self.save_player_data(username, player_data)
height_msg = f",高度+{height_gained}" if height_gained > 0 else ""
self.log('INFO', f"玩家 {username} 给智慧树浇水,花费 {water_cost} 金币,经验+{exp_gained}{height_msg}", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": f"浇水成功!经验+{exp_gained}{height_msg}",
"operation_type": "water",
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_fertilize(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树施肥"""
# 检查智慧树是否死亡
if wisdom_tree_config["当前生命值"] <= 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树已死亡,请先复活!",
"operation_type": "fertilize"
})
# 施肥费用
fertilize_cost = 200
# 检查金钱是否足够
if player_data["钱币"] < fertilize_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,施肥需要 {fertilize_cost} 金币",
"operation_type": "fertilize"
})
# 执行施肥
player_data["钱币"] -= fertilize_cost
# 施肥经验10-40随机
import random
exp_gained = random.randint(10, 40)
wisdom_tree_config["当前经验值"] += exp_gained
# 施肥高度80%概率增加1-7高度
height_gained = 0
if random.random() < 0.8: # 80%概率
height_gained = random.randint(1, 7)
wisdom_tree_config["高度"] = min(100, wisdom_tree_config["高度"] + height_gained)
# 检查等级提升
level_up_occurred = self._check_wisdom_tree_level_up(wisdom_tree_config)
# 保存数据
self.save_player_data(username, player_data)
height_msg = f",高度+{height_gained}" if height_gained > 0 else ""
self.log('INFO', f"玩家 {username} 给智慧树施肥,花费 {fertilize_cost} 金币,经验+{exp_gained}{height_msg}", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": f"施肥成功!经验+{exp_gained}{height_msg}",
"operation_type": "fertilize",
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_kill_grass(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树除草"""
# 检查智慧树是否死亡
if wisdom_tree_config["当前生命值"] <= 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树已死亡,请先复活!",
"operation_type": "kill_grass"
})
# 除草费用
kill_grass_cost = 150
# 检查金钱是否足够
if player_data["钱币"] < kill_grass_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,除草需要 {kill_grass_cost} 金币",
"operation_type": "kill_grass"
})
# 执行除草
import time
player_data["钱币"] -= kill_grass_cost
max_health = wisdom_tree_config["最大生命值"]
wisdom_tree_config["当前生命值"] = min(max_health, wisdom_tree_config["当前生命值"] + 10)
wisdom_tree_config["距离上一次除草时间"] = int(time.time())
# 保存数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 给智慧树除草,花费 {kill_grass_cost} 金币,生命值+10", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": "除草成功!生命值+10",
"operation_type": "kill_grass",
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_kill_bug(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树杀虫"""
# 检查智慧树是否死亡
if wisdom_tree_config["当前生命值"] <= 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树已死亡,请先复活!",
"operation_type": "kill_bug"
})
# 杀虫费用
kill_bug_cost = 150
# 检查金钱是否足够
if player_data["钱币"] < kill_bug_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,杀虫需要 {kill_bug_cost} 金币",
"operation_type": "kill_bug"
})
# 执行杀虫
player_data["钱币"] -= kill_bug_cost
max_health = wisdom_tree_config["最大生命值"]
wisdom_tree_config["当前生命值"] = min(max_health, wisdom_tree_config["当前生命值"] + 15)
wisdom_tree_config["距离上一次杀虫时间"] = int(time.time())
# 保存数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 给智慧树杀虫,花费 {kill_bug_cost} 金币,生命值+15", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": "杀虫成功!生命值+15",
"operation_type": "kill_bug",
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_play_music(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树放音乐"""
# 检查智慧树是否死亡
if wisdom_tree_config["当前生命值"] <= 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树已死亡,请先复活!",
"operation_type": "play_music"
})
# 放音乐费用
play_music_cost = 100
# 检查金钱是否足够
if player_data["钱币"] < play_music_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,放音乐需要 {play_music_cost} 金币",
"operation_type": "play_music"
})
# 执行放音乐
player_data["钱币"] -= play_music_cost
# 从智慧树消息库中随机获取一条消息
random_message = self._get_random_wisdom_tree_message()
if random_message:
wisdom_tree_config["智慧树显示的话"] = random_message.get("content", "")
# 保存数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 给智慧树放音乐,花费 {play_music_cost} 金币,获得随机消息", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": "放音乐成功!获得了一条神秘消息",
"operation_type": "play_music",
"random_message": random_message,
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_revive(self, client_id, player_data, username, wisdom_tree_config):
"""处理智慧树复活"""
# 检查智慧树是否真的死亡
if wisdom_tree_config["当前生命值"] > 0:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "智慧树还活着,不需要复活!",
"operation_type": "revive"
})
# 复活费用
revive_cost = 1000
# 检查金钱是否足够
if player_data["钱币"] < revive_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": f"金钱不足,复活智慧树需要 {revive_cost} 金币",
"operation_type": "revive"
})
# 执行复活
player_data["钱币"] -= revive_cost
wisdom_tree_config["当前生命值"] = wisdom_tree_config["最大生命值"]
# 保存数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 复活了智慧树,花费 {revive_cost} 金币", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": "智慧树复活成功!",
"operation_type": "revive",
"updated_data": {
"钱币": player_data["钱币"],
"智慧树配置": wisdom_tree_config
}
})
def _process_wisdom_tree_get_random_message(self, client_id, player_data, username, wisdom_tree_config):
"""处理获取随机智慧树消息"""
# 从智慧树消息库中随机获取一条消息
random_message = self._get_random_wisdom_tree_message()
if random_message:
wisdom_tree_config["智慧树显示的话"] = random_message.get("content", "")
# 保存数据
self.save_player_data(username, player_data)
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": True,
"message": "获得了一条神秘消息",
"operation_type": "get_random_message",
"random_message": random_message,
"updated_data": {
"智慧树配置": wisdom_tree_config
}
})
else:
return self.send_data(client_id, {
"type": "wisdom_tree_operation_response",
"success": False,
"message": "暂时没有新消息",
"operation_type": "get_random_message"
})
def _handle_wisdom_tree_message(self, client_id, message):
"""处理智慧树消息发送请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "发送智慧树消息", "wisdom_tree_message")
if not logged_in:
return self.send_data(client_id, response)
# 获取玩家数据
player_data, username, response = self._load_player_data_with_check(client_id, "wisdom_tree_message")
if not player_data:
return self.send_data(client_id, response)
message_content = message.get("message", "").strip()
# 验证消息内容
if not message_content:
return self.send_data(client_id, {
"type": "wisdom_tree_message_response",
"success": False,
"message": "消息内容不能为空"
})
if len(message_content) > 100:
return self.send_data(client_id, {
"type": "wisdom_tree_message_response",
"success": False,
"message": "消息长度不能超过100个字符"
})
# 发送消息费用
send_cost = 50
# 检查金钱是否足够
if player_data["钱币"] < send_cost:
return self.send_data(client_id, {
"type": "wisdom_tree_message_response",
"success": False,
"message": f"金钱不足,发送消息需要 {send_cost} 金币"
})
# 扣除费用
player_data["钱币"] -= send_cost
# 保存消息到智慧树消息库
success = self._save_wisdom_tree_message(username, message_content)
if success:
# 保存玩家数据
self.save_player_data(username, player_data)
self.log('INFO', f"玩家 {username} 发送智慧树消息,花费 {send_cost} 金币:{message_content}", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_message_response",
"success": True,
"message": "消息发送成功!",
"updated_data": {
"钱币": player_data["钱币"]
}
})
else:
return self.send_data(client_id, {
"type": "wisdom_tree_message_response",
"success": False,
"message": "消息发送失败,请重试"
})
def _handle_get_wisdom_tree_config(self, client_id, message):
"""处理获取智慧树配置请求"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "获取智慧树配置", "get_wisdom_tree_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_wisdom_tree_config")
if not player_data:
return self.send_data(client_id, response)
# 检查并修复智慧树配置
self._check_and_fix_wisdom_tree_config(player_data, username)
# 保存修复后的数据
self.save_player_data(username, player_data)
# 返回智慧树配置
wisdom_tree_config = player_data.get("智慧树配置", {})
self.log('INFO', f"玩家 {username} 请求智慧树配置", 'SERVER')
return self.send_data(client_id, {
"type": "wisdom_tree_config_response",
"success": True,
"config": wisdom_tree_config
})
def _check_wisdom_tree_level_up(self, wisdom_tree_config):
"""检查智慧树等级提升"""
current_level = wisdom_tree_config["等级"]
current_experience = wisdom_tree_config["当前经验值"]
max_experience = wisdom_tree_config["最大经验值"]
level_ups = 0
# 检查是否可以升级最高等级20
while current_level < 20 and current_experience >= max_experience:
# 升级
current_level += 1
current_experience -= max_experience # 扣除升级所需经验
level_ups += 1
# 计算新等级的最大经验值
max_experience = self._calculate_wisdom_tree_max_exp(current_level)
self.log('INFO', f"智慧树等级提升到 {current_level} 级,新的最大经验值: {max_experience}", 'SERVER')
# 每升一级,最大生命值+2当前生命值也+2
if level_ups > 0:
health_bonus = level_ups * 2
wisdom_tree_config["最大生命值"] = min(200, wisdom_tree_config["最大生命值"] + health_bonus)
wisdom_tree_config["当前生命值"] = min(wisdom_tree_config["最大生命值"], wisdom_tree_config["当前生命值"] + health_bonus)
self.log('INFO', f"智慧树升级 {level_ups} 级,最大生命值+{health_bonus}", 'SERVER')
# 更新配置
wisdom_tree_config["等级"] = current_level
wisdom_tree_config["当前经验值"] = current_experience
wisdom_tree_config["最大经验值"] = max_experience
return level_ups > 0
def _get_random_wisdom_tree_message(self):
"""从智慧树消息库中随机获取一条消息"""
import os
import json
import random
# 优先从MongoDB读取
if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected():
try:
wisdom_tree_data = self.mongo_api.get_wisdom_tree_config()
if wisdom_tree_data:
messages = wisdom_tree_data.get("messages", [])
if messages:
selected_message = random.choice(messages)
self.log('INFO', f"成功从MongoDB获取智慧树消息", 'SERVER')
return selected_message
else:
return None
except Exception as e:
self.log('ERROR', f"从MongoDB读取智慧树消息失败: {e}", 'SERVER')
# 回退到JSON文件
wisdom_tree_data_path = os.path.join(os.path.dirname(__file__), "config", "wisdom_tree_data.json")
try:
with open(wisdom_tree_data_path, 'r', encoding='utf-8') as f:
wisdom_tree_data = json.load(f)
messages = wisdom_tree_data.get("messages", [])
if messages:
selected_message = random.choice(messages)
self.log('INFO', f"成功从JSON文件获取智慧树消息", 'SERVER')
return selected_message
else:
return None
except Exception as e:
self.log('ERROR', f"从JSON文件读取智慧树消息失败: {e}", 'SERVER')
return None
def _save_wisdom_tree_message(self, username, message_content):
"""保存智慧树消息到消息库"""
import os
import json
import time
import uuid
# 创建新消息
new_message = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
"sender": username,
"content": message_content,
"id": str(uuid.uuid4())
}
# 优先保存到MongoDB
if hasattr(self, 'mongo_api') and self.mongo_api and self.mongo_api.is_connected():
try:
# 获取现有数据
wisdom_tree_data = self.mongo_api.get_wisdom_tree_config()
if not wisdom_tree_data:
wisdom_tree_data = {
"messages": [],
"total_messages": 0,
"last_update": ""
}
# 添加新消息
wisdom_tree_data["messages"].append(new_message)
wisdom_tree_data["total_messages"] = len(wisdom_tree_data["messages"])
wisdom_tree_data["last_update"] = new_message["timestamp"]
# 保持最多1000条消息
if len(wisdom_tree_data["messages"]) > 1000:
wisdom_tree_data["messages"] = wisdom_tree_data["messages"][-1000:]
wisdom_tree_data["total_messages"] = len(wisdom_tree_data["messages"])
# 保存到MongoDB
if self.mongo_api.update_wisdom_tree_config(wisdom_tree_data):
self.log('INFO', f"成功保存智慧树消息到MongoDB: {username}", 'SERVER')
return True
else:
self.log('ERROR', f"保存智慧树消息到MongoDB失败: {username}", 'SERVER')
except Exception as e:
self.log('ERROR', f"MongoDB保存智慧树消息异常: {e}", 'SERVER')
# 回退到JSON文件
wisdom_tree_data_path = os.path.join(os.path.dirname(__file__), "config", "wisdom_tree_data.json")
try:
# 读取现有数据
if os.path.exists(wisdom_tree_data_path):
with open(wisdom_tree_data_path, 'r', encoding='utf-8') as f:
wisdom_tree_data = json.load(f)
else:
wisdom_tree_data = {
"messages": [],
"total_messages": 0,
"last_update": ""
}
# 添加到消息列表
wisdom_tree_data["messages"].append(new_message)
wisdom_tree_data["total_messages"] = len(wisdom_tree_data["messages"])
wisdom_tree_data["last_update"] = new_message["timestamp"]
# 保持最多1000条消息
if len(wisdom_tree_data["messages"]) > 1000:
wisdom_tree_data["messages"] = wisdom_tree_data["messages"][-1000:]
wisdom_tree_data["total_messages"] = len(wisdom_tree_data["messages"])
# 保存数据
with open(wisdom_tree_data_path, 'w', encoding='utf-8') as f:
json.dump(wisdom_tree_data, f, ensure_ascii=False, indent=4)
self.log('INFO', f"成功保存智慧树消息到JSON文件: {username}", 'SERVER')
return True
except Exception as e:
self.log('ERROR', f"保存智慧树消息到JSON文件失败: {e}", 'SERVER')
return False
def check_wisdom_tree_health_decay(self):
"""检查智慧树生命值衰减"""
import time
current_time = int(time.time())
processed_count = 0
# 检查所有在线玩家
for client_id in self.user_data:
if self.user_data[client_id].get("logged_in", False):
username = self.user_data[client_id]["username"]
player_data = self.load_player_data(username)
if player_data and "智慧树配置" in player_data:
self._process_wisdom_tree_decay(player_data["智慧树配置"], username)
self.save_player_data(username, player_data)
processed_count += 1
# 注释:缓存机制已移除,只处理在线玩家的智慧树衰减
if processed_count > 0:
self.log('INFO', f"已处理 {processed_count} 个玩家的智慧树生命值衰减", 'SERVER')
def _process_wisdom_tree_decay(self, wisdom_tree_config, username):
"""处理单个玩家的智慧树生命值衰减"""
import time
import random
current_time = int(time.time())
# 获取上次除草和杀虫时间,处理空字符串和无效值
last_grass_time_raw = wisdom_tree_config.get("距离上一次除草时间", current_time)
last_bug_time_raw = wisdom_tree_config.get("距离上一次杀虫时间", current_time)
# 处理空字符串和无效时间戳
try:
last_grass_time = int(last_grass_time_raw) if last_grass_time_raw and str(last_grass_time_raw).strip() else current_time
except (ValueError, TypeError):
last_grass_time = current_time
try:
last_bug_time = int(last_bug_time_raw) if last_bug_time_raw and str(last_bug_time_raw).strip() else current_time
except (ValueError, TypeError):
last_bug_time = current_time
# 如果时间戳无效为0或负数设置为当前时间
if last_grass_time <= 0:
last_grass_time = current_time
if last_bug_time <= 0:
last_bug_time = current_time
# 检查是否3天没有除草
days_since_grass = (current_time - last_grass_time) / 86400 # 转换为天数
if days_since_grass >= 3:
# 计算应该衰减的天数
decay_days = int(days_since_grass)
if decay_days > 0:
# 每天减少1-3血量
total_decay = 0
for _ in range(decay_days):
daily_decay = random.randint(1, 3)
total_decay += daily_decay
wisdom_tree_config["当前生命值"] = max(0, wisdom_tree_config["当前生命值"] - total_decay)
self.log('INFO', f"玩家 {username} 的智慧树因{decay_days}天未除草,生命值减少{total_decay}", 'SERVER')
# 更新除草时间为当前时间,避免重复扣血
wisdom_tree_config["距离上一次除草时间"] = current_time
# 检查是否3天没有杀虫
days_since_bug = (current_time - last_bug_time) / 86400 # 转换为天数
if days_since_bug >= 3:
# 计算应该衰减的天数
decay_days = int(days_since_bug)
if decay_days > 0:
# 每天减少1-3血量
total_decay = 0
for _ in range(decay_days):
daily_decay = random.randint(1, 3)
total_decay += daily_decay
wisdom_tree_config["当前生命值"] = max(0, wisdom_tree_config["当前生命值"] - total_decay)
self.log('INFO', f"玩家 {username} 的智慧树因{decay_days}天未杀虫,生命值减少{total_decay}", 'SERVER')
# 更新杀虫时间为当前时间,避免重复扣血
wisdom_tree_config["距离上一次杀虫时间"] = current_time

View File

@@ -0,0 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
萌芽农场服务器模块包
包含所有服务器外置插件模块
"""
# 导入所有模块类
from .SMYMongoDBAPI import SMYMongoDBAPI
from .QQEmailSendAPI import QQMailAPI, EmailVerification
from .ConsoleCommandsAPI import ConsoleCommandsAPI # 明确导入类名,避免循环导入
from .SpecialFarm import SpecialFarmManager # 导入特殊农场管理器
from .WSRemoteCmdApi import WSRemoteCmdApi
from .NetworkCore import MessageHandler
# 定义模块导出列表
__all__ = [
'SMYMongoDBAPI',
'QQMailAPI',
'EmailVerification',
'ConsoleCommandsAPI',
'SpecialFarmManager',
'WSRemoteCmdApi'
]