完成部分服务器数据向MongoDB数据库迁移

This commit is contained in:
2025-07-20 21:05:31 +08:00
parent e466cde9d5
commit ec94e10fda
28 changed files with 2386 additions and 1785 deletions

View File

@@ -23,12 +23,21 @@ var music_files: Array[String] = []
var current_index: int = 0
var played_indices: Array[int] = [] # 随机模式已播放的索引
# 音量控制相关
var current_volume: float = 1.0 # 当前音量 (0.0-1.0)
var is_muted: bool = false # 是否静音
var volume_before_mute: float = 1.0 # 静音前的音量
func _ready():
# 创建音频播放器
audio_player = AudioStreamPlayer.new()
add_child(audio_player)
audio_player.finished.connect(_on_music_finished)
# 从全局变量读取初始音量设置
current_volume = GlobalVariables.BackgroundMusicVolume
audio_player.volume_db = linear_to_db(current_volume)
# 加载音乐文件
_load_music_files()
@@ -164,3 +173,70 @@ func add_music_file(file_path: String) -> bool:
else:
print("音乐文件不存在: ", file_path)
return false
# ============================= 音量控制功能 =====================================
func set_volume(volume: float):
"""设置音量 (0.0-1.0)"""
current_volume = clamp(volume, 0.0, 1.0)
if not is_muted:
audio_player.volume_db = linear_to_db(current_volume)
print("背景音乐音量设置为: ", current_volume)
func get_volume() -> float:
"""获取当前音量"""
return current_volume
func set_mute(muted: bool):
"""设置静音状态"""
if muted and not is_muted:
# 静音
volume_before_mute = current_volume
audio_player.volume_db = -80.0 # 设置为最小音量
is_muted = true
print("背景音乐已静音")
elif not muted and is_muted:
# 取消静音
audio_player.volume_db = linear_to_db(current_volume)
is_muted = false
print("背景音乐取消静音")
func toggle_mute():
"""切换静音状态"""
set_mute(not is_muted)
func is_music_muted() -> bool:
"""获取静音状态"""
return is_muted
func pause():
"""暂停音乐"""
if audio_player.playing:
audio_player.stream_paused = true
print("背景音乐已暂停")
func resume():
"""恢复音乐"""
if audio_player.stream_paused:
audio_player.stream_paused = false
print("背景音乐已恢复")
func stop():
"""停止音乐"""
if audio_player.playing:
audio_player.stop()
print("背景音乐已停止")
func is_playing() -> bool:
"""检查是否正在播放"""
return audio_player.playing and not audio_player.stream_paused
func get_current_position() -> float:
"""获取当前播放位置(秒)"""
return audio_player.get_playback_position()
func get_current_length() -> float:
"""获取当前音乐总长度(秒)"""
if audio_player.stream:
return audio_player.stream.get_length()
return 0.0

View File

@@ -1,131 +0,0 @@
extends Node
## 简单背景音乐播放器
## 自动加载指定文件夹的音乐文件,支持顺序和随机循环播放
# 播放模式
enum PlayMode {
SEQUENTIAL, # 顺序循环
RANDOM # 随机循环
}
# 配置
@export var music_folder: String = "res://assets/音乐/" # 音乐文件夹路径
@export var play_mode: PlayMode = PlayMode.SEQUENTIAL # 播放模式
@export var auto_start: bool = true # 自动开始播放
# 内部变量
var audio_player: AudioStreamPlayer
var music_files: Array[String] = []
var current_index: int = 0
var played_indices: Array[int] = [] # 随机模式已播放的索引
func _ready():
# 创建音频播放器
audio_player = AudioStreamPlayer.new()
add_child(audio_player)
audio_player.finished.connect(_on_music_finished)
# 加载音乐文件
_load_music_files()
# 自动开始播放
if auto_start and music_files.size() > 0:
play_next()
func _load_music_files():
"""加载指定文件夹下的音乐文件"""
music_files.clear()
var dir = DirAccess.open(music_folder)
if dir:
dir.list_dir_begin()
var file_name = dir.get_next()
while file_name != "":
if not dir.current_is_dir():
var extension = file_name.get_extension().to_lower()
# 支持常见音频格式
if extension in ["mp3", "ogg", "wav"]:
music_files.append(music_folder + file_name)
print("加载音乐: ", file_name)
file_name = dir.get_next()
print("总共加载了 ", music_files.size(), " 首音乐")
else:
print("无法打开音乐文件夹: ", music_folder)
func play_next():
"""播放下一首音乐"""
if music_files.size() == 0:
print("没有音乐文件可播放")
return
# 根据播放模式获取下一首音乐的索引
match play_mode:
PlayMode.SEQUENTIAL:
current_index = (current_index + 1) % music_files.size()
PlayMode.RANDOM:
current_index = _get_random_index()
# 播放音乐
_play_music(current_index)
func _get_random_index() -> int:
"""获取随机音乐索引(避免重复直到所有歌曲都播放过)"""
# 如果所有歌曲都播放过了,重置列表
if played_indices.size() >= music_files.size():
played_indices.clear()
# 获取未播放的歌曲索引
var available_indices: Array[int] = []
for i in range(music_files.size()):
if i not in played_indices:
available_indices.append(i)
# 随机选择一个
if available_indices.size() > 0:
var random_choice = available_indices[randi() % available_indices.size()]
played_indices.append(random_choice)
return random_choice
return 0
func _play_music(index: int):
"""播放指定索引的音乐"""
if index < 0 or index >= music_files.size():
return
var music_path = music_files[index]
var audio_stream = load(music_path)
if audio_stream:
audio_player.stream = audio_stream
audio_player.play()
print("正在播放: ", music_path.get_file())
else:
print("加载音乐失败: ", music_path)
func _on_music_finished():
"""音乐播放完成时自动播放下一首"""
play_next()
# 公共接口方法
func set_play_mode(mode: PlayMode):
"""设置播放模式"""
play_mode = mode
played_indices.clear() # 重置随机播放历史
print("播放模式设置为: ", "顺序循环" if mode == PlayMode.SEQUENTIAL else "随机循环")
func toggle_play_mode():
"""切换播放模式"""
if play_mode == PlayMode.SEQUENTIAL:
set_play_mode(PlayMode.RANDOM)
else:
set_play_mode(PlayMode.SEQUENTIAL)
func get_current_music_name() -> String:
"""获取当前播放的音乐名称"""
if current_index >= 0 and current_index < music_files.size():
return music_files[current_index].get_file()
return ""

View File

@@ -1 +0,0 @@
uid://cq0ug1c7nibf1

View File

@@ -39,7 +39,6 @@ offset_right = 40.0
offset_bottom = 40.0
[node name="ground_sprite" type="Sprite2D" parent="."]
modulate = Color(0.8, 0.8, 0.8, 1)
material = SubResource("ShaderMaterial_v46ok")
position = Vector2(50, 63)
scale = Vector2(0.135, 0.135)
@@ -50,11 +49,6 @@ material = SubResource("ShaderMaterial_s5pb0")
position = Vector2(51, 45)
scale = Vector2(0.339844, 0.363281)
[node name="old_crop_sprite" type="Sprite2D" parent="."]
material = SubResource("ShaderMaterial_s5pb0")
position = Vector2(51, 39)
scale = Vector2(0.06, 0.06)
[node name="ProgressBar" type="ProgressBar" parent="."]
visible = false
material = SubResource("ShaderMaterial_cyybs")

View File

@@ -1,9 +1,203 @@
extends Panel
#游戏设置面板
# UI组件引用
@onready var background_music_h_slider: HSlider = $Scroll/Panel/BackgroundMusicHSlider
@onready var weather_system_check: CheckButton = $Scroll/Panel/WeatherSystemCheck
@onready var quit_button: Button = $QuitButton
@onready var sure_button: Button = $SureButton
@onready var refresh_button: Button = $RefreshButton
# 引用主游戏和其他组件
@onready var main_game = get_node("/root/main")
@onready var tcp_network_manager_panel: Panel = get_node("/root/main/UI/BigPanel/TCPNetworkManagerPanel")
# 游戏设置数据
var game_settings: Dictionary = {
"背景音乐音量": 1.0,
"天气显示": true
}
# 临时设置数据(用户修改但未确认的设置)
var temp_settings: Dictionary = {}
func _ready() -> void:
self.hide()
pass
# 连接信号
quit_button.pressed.connect(_on_quit_button_pressed)
sure_button.pressed.connect(_on_sure_button_pressed)
refresh_button.pressed.connect(_on_refresh_button_pressed)
# 设置音量滑块范围为0-1
background_music_h_slider.min_value = 0.0
background_music_h_slider.max_value = 1.0
background_music_h_slider.step = 0.01
background_music_h_slider.value_changed.connect(_on_background_music_h_slider_value_changed)
weather_system_check.toggled.connect(_on_weather_system_check_toggled)
# 初始化设置值
_load_settings_from_global()
# 当面板可见性改变时
visibility_changed.connect(_on_visibility_changed)
func _on_visibility_changed():
"""面板可见性改变时的处理"""
if visible:
# 面板显示时,刷新设置值
_load_settings_from_global()
_update_ui_from_settings()
# 禁用缩放功能
GlobalVariables.isZoomDisabled = true
else:
# 面板隐藏时,恢复缩放功能
GlobalVariables.isZoomDisabled = false
func _load_settings_from_global():
"""从全局变量和玩家数据加载设置"""
# 从GlobalVariables加载默认设置
game_settings["背景音乐音量"] = GlobalVariables.BackgroundMusicVolume
game_settings["天气显示"] = not GlobalVariables.DisableWeatherDisplay
# 如果主游戏已登录,尝试从玩家数据加载设置
if main_game and main_game.login_data and main_game.login_data.has("游戏设置"):
var player_settings = main_game.login_data["游戏设置"]
if player_settings.has("背景音乐音量"):
game_settings["背景音乐音量"] = player_settings["背景音乐音量"]
if player_settings.has("天气显示"):
game_settings["天气显示"] = player_settings["天气显示"]
# 初始化临时设置
temp_settings = game_settings.duplicate()
func _update_ui_from_settings():
"""根据设置数据更新UI"""
# 更新音量滑块
background_music_h_slider.value = temp_settings["背景音乐音量"]
# 更新天气显示复选框(注意:复选框表示"关闭天气显示"
weather_system_check.button_pressed = not temp_settings["天气显示"]
func _apply_settings_immediately():
"""立即应用设置(不保存到服务端)"""
# 应用背景音乐音量设置
_apply_music_volume_setting()
# 应用天气显示设置
_apply_weather_display_setting()
func _save_settings_to_server():
"""保存设置到服务端"""
# 更新正式设置
game_settings = temp_settings.duplicate()
# 应用设置
_apply_settings_immediately()
# 如果已登录,保存到玩家数据并同步到服务端
if main_game and main_game.login_data:
main_game.login_data["游戏设置"] = game_settings.duplicate()
# 发送设置到服务端保存
if tcp_network_manager_panel and tcp_network_manager_panel.has_method("is_connected_to_server") and tcp_network_manager_panel.is_connected_to_server():
_send_settings_to_server()
func _apply_music_volume_setting():
"""应用背景音乐音量设置"""
var bgm_player = main_game.get_node_or_null("GameBGMPlayer")
if bgm_player and bgm_player.has_method("set_volume"):
bgm_player.set_volume(temp_settings["背景音乐音量"])
func _apply_weather_display_setting():
"""应用天气显示设置"""
var weather_system = main_game.get_node_or_null("WeatherSystem")
if weather_system and weather_system.has_method("set_weather_display_enabled"):
weather_system.set_weather_display_enabled(temp_settings["天气显示"])
func _send_settings_to_server():
"""发送设置到服务端保存"""
if tcp_network_manager_panel and tcp_network_manager_panel.has_method("send_message"):
var message = {
"type": "save_game_settings",
"settings": game_settings,
"timestamp": Time.get_unix_time_from_system()
}
if tcp_network_manager_panel.send_message(message):
print("游戏设置已发送到服务端保存")
else:
print("发送游戏设置到服务端失败")
func _on_quit_button_pressed() -> void:
"""关闭设置面板"""
self.hide()
pass
func _on_background_music_h_slider_value_changed(value: float) -> void:
"""背景音乐音量滑块值改变"""
temp_settings["背景音乐音量"] = value
# 立即应用音量设置(不保存到服务端)
_apply_music_volume_setting()
# 显示当前音量百分比
var volume_percent = int(value * 100)
func _on_weather_system_check_toggled(toggled_on: bool) -> void:
"""天气系统复选框切换"""
# 复选框表示"关闭天气显示",所以需要取反
temp_settings["天气显示"] = not toggled_on
# 立即应用天气设置(不保存到服务端)
_apply_weather_display_setting()
# 显示提示
var status_text = "已开启" if temp_settings["天气显示"] else "已关闭"
Toast.show("天气显示" + status_text, Color.YELLOW)
#确认修改设置按钮,点击这个才会发送数据到服务端
func _on_sure_button_pressed() -> void:
"""确认修改设置"""
_save_settings_to_server()
Toast.show("设置已保存!", Color.GREEN)
#刷新设置面板,从服务端加载游戏设置数据
func _on_refresh_button_pressed() -> void:
"""刷新设置"""
_load_settings_from_global()
_update_ui_from_settings()
_apply_settings_immediately()
Toast.show("设置已刷新!", Color.CYAN)
# 移除原来的自动保存方法,避免循环调用
func _on_background_music_h_slider_drag_ended(value_changed: bool) -> void:
"""背景音乐音量滑块拖拽结束(保留以兼容场景连接)"""
# 不再自动保存,只显示提示
if value_changed:
var volume_percent = int(background_music_h_slider.value * 100)
# 公共方法,供外部调用
func refresh_settings():
"""刷新设置(从服务端或本地重新加载)"""
_load_settings_from_global()
_update_ui_from_settings()
_apply_settings_immediately()
func get_current_settings() -> Dictionary:
"""获取当前设置"""
return game_settings.duplicate()
func apply_settings_from_server(server_settings: Dictionary):
"""应用从服务端接收到的设置(避免循环调用)"""
if server_settings.has("背景音乐音量"):
game_settings["背景音乐音量"] = server_settings["背景音乐音量"]
temp_settings["背景音乐音量"] = server_settings["背景音乐音量"]
if server_settings.has("天气显示"):
game_settings["天气显示"] = server_settings["天气显示"]
temp_settings["天气显示"] = server_settings["天气显示"]
# 只更新UI不再触发保存
if visible:
_update_ui_from_settings()
_apply_settings_immediately()
print("已应用来自服务端的游戏设置")

View File

@@ -23,8 +23,10 @@ corner_radius_bottom_left = 10
corner_detail = 20
[node name="GameSettingPanel" type="Panel"]
offset_right = 1398.0
offset_bottom = 720.0
offset_left = 151.0
offset_top = 74.0
offset_right = 1549.0
offset_bottom = 794.0
scale = Vector2(0.8, 0.8)
theme_override_styles/panel = SubResource("StyleBoxFlat_0c52c")
script = ExtResource("1_0c52c")
@@ -78,18 +80,84 @@ layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="Label" type="Label" parent="Scroll/Panel"]
[node name="BackgroundMusicLabel" type="Label" parent="Scroll/Panel"]
layout_mode = 2
offset_right = 210.0
offset_bottom = 42.0
theme_override_font_sizes/font_size = 30
offset_left = -1.52588e-05
offset_right = 245.0
offset_bottom = 49.0
theme_override_font_sizes/font_size = 35
text = "背景音乐音量:"
[node name="HSlider" type="HSlider" parent="Scroll/Panel"]
layout_mode = 0
offset_left = 210.0
offset_top = 15.0
offset_right = 573.0
offset_bottom = 31.0
[node name="BackgroundMusicHSlider" type="HSlider" parent="Scroll/Panel"]
layout_mode = 2
offset_left = 245.0
offset_right = 574.0
offset_bottom = 49.0
size_flags_horizontal = 3
size_flags_vertical = 1
[connection signal="pressed" from="QuitButton" to="." method="_on_quit_button_pressed"]
[node name="WeatherSystemLabel" type="Label" parent="Scroll/Panel"]
layout_mode = 2
offset_left = -0.249969
offset_top = 48.75
offset_right = 209.75
offset_bottom = 97.75
theme_override_font_sizes/font_size = 35
text = "关闭天气显示:"
[node name="WeatherSystemCheck" type="CheckButton" parent="Scroll/Panel"]
layout_mode = 2
offset_left = 244.75
offset_top = 48.75
offset_right = 288.75
offset_bottom = 72.75
scale = Vector2(2, 2)
theme_override_font_sizes/font_size = 100
[node name="HBox" type="HBoxContainer" parent="Scroll/Panel"]
visible = false
layout_mode = 0
offset_top = 97.0
offset_right = 853.0
offset_bottom = 154.0
[node name="ChangeServer" type="Label" parent="Scroll/Panel/HBox"]
layout_mode = 2
theme_override_font_sizes/font_size = 35
text = "切换服务器"
[node name="IPInput" type="LineEdit" parent="Scroll/Panel/HBox"]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
theme_override_font_sizes/font_size = 35
placeholder_text = "请输入服务器IP地址"
[node name="PortInput" type="LineEdit" parent="Scroll/Panel/HBox"]
layout_mode = 2
theme_override_font_sizes/font_size = 35
placeholder_text = "端口"
alignment = 1
[node name="ChangeButton" type="Button" parent="Scroll/Panel/HBox"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
theme_override_font_sizes/font_size = 35
text = "切换"
[node name="SureButton" type="Button" parent="."]
layout_mode = 0
offset_left = 647.5
offset_top = 635.0
offset_right = 815.5
offset_bottom = 698.0
theme_override_font_sizes/font_size = 40
text = "确认修改"
[node name="RefreshButton" type="Button" parent="."]
layout_mode = 0
offset_left = 27.5001
offset_top = 25.0001
offset_right = 195.5
offset_bottom = 88.0001
theme_override_font_sizes/font_size = 40
text = "刷新"

View File

@@ -2,7 +2,7 @@ extends Control
#各种面板
@onready var game_about_panel: Panel = $GameAboutPanel
@onready var game_update_panel: Panel = $GameUpdatePanel
@onready var game_setting_panel: Panel = $GameSettingPanel
#@onready var game_setting_panel: Panel = $GameSettingPanel
@onready var game_version_label: Label = $GUI/GameVersionLabel
@@ -23,7 +23,7 @@ func _on_start_game_button_pressed() -> void:
#游戏设置
func _on_game_setting_button_pressed() -> void:
game_setting_panel.show()
#game_setting_panel.show()
pass
#游戏更新

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=12 format=3 uid="uid://bypjb28h4ntdr"]
[gd_scene load_steps=11 format=3 uid="uid://bypjb28h4ntdr"]
[ext_resource type="Script" uid="uid://badqjgdfhg7vt" path="res://GUI/MainMenuPanel.gd" id="1_wpehy"]
[ext_resource type="Texture2D" uid="uid://ddcmrh50o1y0q" path="res://assets/菜单UI/背景1.webp" id="2_eghpk"]
@@ -7,7 +7,6 @@
[ext_resource type="Texture2D" uid="uid://dgdootc5bny5q" path="res://assets/菜单UI/QQ群.webp" id="4_eghpk"]
[ext_resource type="Script" uid="uid://kj7v1uxk2i6h" path="res://GUI/GameUpdatePanel.gd" id="4_fys16"]
[ext_resource type="Texture2D" uid="uid://ccav04woielxa" path="res://assets/菜单UI/柚小青装饰品.webp" id="5_6jmhb"]
[ext_resource type="PackedScene" uid="uid://dos15dmc1b6bt" path="res://GUI/GameSettingPanel.tscn" id="6_eghpk"]
[ext_resource type="Script" uid="uid://ciwjx67wjubdy" path="res://GUI/CheckUpdatePanel.gd" id="9_6jmhb"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_eghpk"]
@@ -129,6 +128,7 @@ theme_override_font_sizes/font_size = 40
text = "开始游戏"
[node name="GameSettingButton" type="Button" parent="VBox"]
visible = false
custom_minimum_size = Vector2(168, 63)
layout_mode = 2
size_flags_horizontal = 4
@@ -140,7 +140,7 @@ custom_minimum_size = Vector2(168, 63)
layout_mode = 2
size_flags_horizontal = 4
theme_override_font_sizes/font_size = 40
text = "游戏更新"
text = "更新"
[node name="GameAboutButton" type="Button" parent="VBox"]
custom_minimum_size = Vector2(168, 63)
@@ -156,14 +156,6 @@ size_flags_horizontal = 4
theme_override_font_sizes/font_size = 40
text = "退出游戏"
[node name="GameSettingPanel" parent="." instance=ExtResource("6_eghpk")]
visible = false
layout_mode = 0
offset_left = 138.0
offset_top = 80.0
offset_right = 1536.0
offset_bottom = 800.0
[node name="GameAboutPanel" type="Panel" parent="."]
visible = false
layout_mode = 0

View File

@@ -0,0 +1,59 @@
extends Node2D
#昼夜循环系统
#时间直接获取现实世界时间
#内容就是直接调节背景图片modulate的亮度HEX 白天最亮为c3c3c3 晚上最暗为131313 然后在这之间变化
# 背景节点引用
@onready var background_node=$'../BackgroundUI/BackgroundSwitcher'
# 白天和夜晚的颜色值
var day_color = Color("#c3c3c3")
var night_color = Color("#131313")
func _process(delta: float) -> void:
if background_node == null:
return
# 获取当前时间
var current_time = Time.get_datetime_dict_from_system()
var hour = current_time.hour
var minute = current_time.minute
# 将时间转换为小数形式0-24
var time_decimal = hour + minute / 60.0
# 计算亮度插值因子
var brightness_factor = calculate_brightness_factor(time_decimal)
# 在白天和夜晚颜色之间插值
var current_color = night_color.lerp(day_color, brightness_factor)
# 应用到背景节点
background_node.modulate = current_color
# 计算亮度因子0为夜晚1为白天
func calculate_brightness_factor(time: float) -> float:
# 定义关键时间点
var sunrise = 6.0 # 日出时间 6:00
var noon = 12.0 # 正午时间 12:00
var sunset = 18.0 # 日落时间 18:00
var midnight = 0.0 # 午夜时间 0:00
if time >= sunrise and time <= noon:
# 日出到正午从0.2逐渐变亮到1.0
return 0.2 + 0.8 * (time - sunrise) / (noon - sunrise)
elif time > noon and time <= sunset:
# 正午到日落从1.0逐渐变暗到0.2
return 1.0 - 0.8 * (time - noon) / (sunset - noon)
else:
# 夜晚时间保持较暗状态0.0-0.2之间)
if time > sunset:
# 日落后到午夜
var night_progress = (time - sunset) / (24.0 - sunset)
return 0.2 - 0.2 * night_progress
else:
# 午夜到日出
var dawn_progress = time / sunrise
return 0.0 + 0.2 * dawn_progress

View File

@@ -0,0 +1 @@
uid://di8wjflimodb0

View File

@@ -9,8 +9,16 @@ extends Node2D
# 天气系统
# 要显示哪种天气直接调用相应天气的show()然后一并隐藏其他天气hide()
# 动态天气显示控制(可以覆盖全局设置)
var weather_display_enabled: bool = true
# 设置天气的统一方法
func set_weather(weather_type: String):
# 检查全局设置和动态设置
if GlobalVariables.DisableWeatherDisplay or not weather_display_enabled:
hide_all_weather()
return
# 先隐藏所有天气效果
hide_all_weather()
@@ -37,6 +45,19 @@ func set_weather(weather_type: String):
_:
print("未知的天气类型: ", weather_type)
# 动态设置天气显示状态
func set_weather_display_enabled(enabled: bool):
"""动态设置天气显示是否启用"""
weather_display_enabled = enabled
if not enabled:
hide_all_weather()
print("天气显示已", "启用" if enabled else "禁用")
# 获取当前天气显示状态
func is_weather_display_enabled() -> bool:
"""获取当前天气显示状态"""
return weather_display_enabled and not GlobalVariables.DisableWeatherDisplay
# 隐藏所有天气效果
func hide_all_weather():
if cherry_blossom_rain:

View File

@@ -12,3 +12,6 @@ const server_configs = [
#{"host": "47.108.90.0", "port": 4040, "name": "成都内网穿透"}#成都内网穿透
#{"host": "47.108.90.0", "port": 6060, "name": "成都公网"}#成都服务器
]
const DisableWeatherDisplay :bool = false #是否禁止显示天气
const BackgroundMusicVolume = 1.0 #背景音乐音量

View File

@@ -68,6 +68,7 @@ extends Node
@onready var pet_fight_panel: Panel = $UI/BigPanel/PetFightPanel #宠物战斗面板
@onready var pet_inform_panel: Panel = $UI/SmallPanel/PetInformPanel #宠物信息面板
@onready var player_store_panel: Panel = $UI/BigPanel/PlayerStorePanel #玩家小卖部面板
@onready var game_setting_panel: Panel = $UI/BigPanel/GameSettingPanel #游戏设置面板
#小面板
@@ -225,6 +226,7 @@ func _ready():
one_click_plant_panel.hide()
account_setting_panel.hide()
global_server_broadcast_panel.hide()
game_setting_panel.hide()
accept_dialog.hide()
@@ -865,9 +867,9 @@ func _on_player_ranking_button_pressed() -> void:
pass
#打开设置面板 暂时没想到可以设置什么
#打开设置面板
func _on_setting_button_pressed() -> void:
pass
game_setting_panel.show()
#查看全服大喇叭按钮点击事件
func _on_watch_broadcast_button_pressed() -> void:
@@ -1711,14 +1713,6 @@ func _wait_for_crop_data() -> void:
#===============================================调试和维护工具===============================================
#===============================================调试和维护工具===============================================
#===============================================向后兼容性===============================================
# 为了保持向后兼容,保留一些原来的函数名
func _load_crop_textures(crop_name: String) -> Array:
@@ -3320,13 +3314,33 @@ func _handle_weather_change(weather_type: String, weather_name: String):
"""处理服务器发送的天气变更消息"""
if weather_system and weather_system.has_method("set_weather"):
weather_system.set_weather(weather_type)
Toast.show("天气已变更为:" + weather_name, Color.CYAN, 3.0)
print("天气已切换为:", weather_name)
else:
print("天气系统不可用")
# ======================================= 天气系统 =========================================
# ======================================= 游戏设置系统 =========================================
# 处理游戏设置保存响应
func _handle_save_game_settings_response(data):
"""处理服务器返回的游戏设置保存响应"""
var success = data.get("success", false)
var message = data.get("message", "")
var settings = data.get("settings", {})
if success:
# 设置保存成功,更新本地设置面板
if game_setting_panel and game_setting_panel.has_method("apply_settings_from_server"):
game_setting_panel.apply_settings_from_server(settings)
Toast.show("游戏设置保存成功", Color.GREEN)
print("游戏设置保存成功: ", settings)
else:
Toast.show("游戏设置保存失败: " + message, Color.RED)
print("游戏设置保存失败: ", message)
# ======================================= 游戏设置系统 =========================================
#打开小卖部面板
func _on_my_store_button_pressed() -> void:
player_store_panel.show()

File diff suppressed because it is too large Load Diff

View File

@@ -602,6 +602,10 @@ func _on_data_received(data):
var weather_type = data.get("weather_type", "clear")
var weather_name = data.get("weather_name", "晴天")
main_game._handle_weather_change(weather_type, weather_name)
# 游戏设置响应
elif message_type == "save_game_settings_response":
main_game._handle_save_game_settings_response(data)
# ============================= 客户端与服务端通信核心 =====================================

View File

@@ -521,6 +521,10 @@ func _on_login_response_received(success: bool, message: String, user_data: Dict
# 调用主游戏的登录成功处理函数
main_game.handle_login_success(user_data)
# 初始化游戏设置
if main_game.game_setting_panel and main_game.game_setting_panel.has_method("refresh_settings"):
main_game.game_setting_panel.refresh_settings()
else:
status_label.text = "登录失败:" + message
status_label.modulate = Color.RED

View File

@@ -0,0 +1,170 @@
# 萌芽农场 MongoDB 迁移说明
## 概述
本文档描述了萌芽农场项目从JSON配置文件迁移到MongoDB数据库的过程。目前已完成每日签到配置的迁移为后续其他配置的迁移奠定了基础。
## 迁移内容
### 1. 已完成的迁移
#### 每日签到配置 (daily_checkin_config.json)
- **原位置**: `Server/config/daily_checkin_config.json`
- **新位置**: MongoDB数据库 `mengyafarm.gameconfig` 集合
- **文档ID**: `687cce278e77ba00a7414ba2`
- **状态**: ✅ 已完成迁移
### 2. 数据库配置
#### 测试环境
- **地址**: `localhost:27017`
- **数据库**: `mengyafarm`
- **集合**: `gameconfig`
#### 生产环境
- **地址**: `192.168.31.233:27017`
- **数据库**: `mengyafarm`
- **集合**: `gameconfig`
## 技术实现
### 1. MongoDB API (SMYMongoDBAPI.py)
创建了专门的MongoDB API类提供以下功能
#### 核心功能
- 数据库连接管理(测试/生产环境)
- 游戏配置的读取和更新
- 通用文档操作(增删改查)
- 错误处理和日志记录
#### 主要方法
```python
# 配置管理
get_daily_checkin_config() # 获取每日签到配置
update_daily_checkin_config() # 更新每日签到配置
get_game_config(config_type) # 获取通用游戏配置
set_game_config(config_type, data) # 设置通用游戏配置
# 通用操作
insert_document(collection, doc) # 插入文档
find_documents(collection, query) # 查找文档
update_document(collection, query, update) # 更新文档
delete_document(collection, query) # 删除文档
```
### 2. 服务器集成 (TCPGameServer.py)
#### 修改内容
- 添加MongoDB API导入和初始化
- 修改 `_load_daily_check_in_config()` 方法优先使用MongoDB
- 添加 `_update_daily_checkin_config_to_mongodb()` 方法
- 实现优雅降级MongoDB失败时自动回退到JSON文件
#### 配置加载策略
1. **优先**: 尝试从MongoDB获取配置
2. **备选**: 从JSON文件加载配置
3. **兜底**: 使用默认配置
## 测试验证
### 1. MongoDB API测试
运行 `python SMYMongoDBAPI.py` 进行基础功能测试
### 2. 迁移功能测试
运行 `python test_mongodb_migration.py` 进行完整迁移测试
### 3. 服务器集成测试
运行 `python test_server_mongodb.py` 进行服务器集成测试
## 使用说明
### 1. 环境配置
#### 测试环境
```python
api = SMYMongoDBAPI("test") # 连接到 localhost:27017
```
#### 生产环境
```python
api = SMYMongoDBAPI("production") # 连接到 192.168.31.233:27017
```
### 2. 获取配置
```python
# 获取每日签到配置
config = api.get_daily_checkin_config()
# 获取通用游戏配置
config = api.get_game_config("config_type")
```
### 3. 更新配置
```python
# 更新每日签到配置
success = api.update_daily_checkin_config(new_config)
# 设置通用游戏配置
success = api.set_game_config("config_type", config_data)
```
## 后续迁移计划
### 1. 待迁移的配置文件
- [ ] `item_config.json` - 道具配置
- [ ] `pet_data.json` - 宠物配置
- [ ] 其他游戏配置文件
### 2. 迁移步骤
1. 将JSON文件导入到MongoDB
2. 修改对应的加载方法
3. 添加更新方法
4. 编写测试用例
5. 验证功能正常
### 3. 迁移原则
- **渐进式迁移**: 一次迁移一个配置文件
- **向后兼容**: 保持JSON文件作为备选方案
- **充分测试**: 每个迁移都要有完整的测试覆盖
- **文档更新**: 及时更新相关文档
## 注意事项
### 1. 数据安全
- 定期备份MongoDB数据
- 重要配置修改前先备份
- 测试环境验证后再应用到生产环境
### 2. 性能考虑
- MongoDB连接池管理
- 配置缓存策略
- 错误重试机制
### 3. 监控和日志
- 记录配置加载来源
- 监控MongoDB连接状态
- 记录配置更新操作
## 故障排除
### 1. MongoDB连接失败
- 检查MongoDB服务是否启动
- 验证连接地址和端口
- 检查网络连接
### 2. 配置加载失败
- 检查MongoDB中是否存在对应文档
- 验证文档格式是否正确
- 查看服务器日志获取详细错误信息
### 3. 配置更新失败
- 检查MongoDB权限
- 验证更新数据格式
- 确认文档ID是否正确
## 总结
本次迁移成功实现了每日签到配置从JSON文件到MongoDB的迁移建立了完整的MongoDB API框架为后续其他配置的迁移提供了可靠的基础。迁移过程采用了渐进式和向后兼容的策略确保了系统的稳定性和可靠性。
通过测试验证MongoDB迁移功能运行正常服务器能够正确使用MongoDB中的配置数据同时保持了JSON文件的备选方案为后续的全面迁移奠定了坚实的基础。

705
Server/SMYMongoDBAPI.py Normal file
View File

@@ -0,0 +1,705 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
萌芽农场 MongoDB 数据库 API
作者: AI Assistant
功能: 提供MongoDB数据库连接和游戏配置管理功能
"""
import pymongo
import json
from typing import Dict, Any, Optional, List
import logging
from datetime import datetime
from bson import ObjectId
class SMYMongoDBAPI:
def __init__(self, environment: str = "test"):
"""
初始化MongoDB API
Args:
environment: 环境类型,"test" 表示测试环境,"production" 表示正式环境
"""
self.environment = environment
self.client = None
self.db = None
self.connected = False
# 配置数据库连接信息
self.config = {
"test": {
"host": "localhost",
"port": 27017,
"database": "mengyafarm"
},
"production": {
"host": "192.168.31.233",
"port": 27017,
"database": "mengyafarm"
}
}
# 设置日志
logging.basicConfig(level=logging.INFO)
self.logger = logging.getLogger(__name__)
# 连接数据库
self.connect()
def connect(self) -> bool:
"""
连接到MongoDB数据库
Returns:
bool: 连接是否成功
"""
try:
current_config = self.config[self.environment]
connection_string = f"mongodb://{current_config['host']}:{current_config['port']}/"
self.client = pymongo.MongoClient(
connection_string,
serverSelectionTimeoutMS=5000, # 5秒超时
connectTimeoutMS=5000,
socketTimeoutMS=5000
)
# 测试连接
self.client.admin.command('ping')
# 选择数据库
self.db = self.client[current_config['database']]
self.connected = True
self.logger.info(f"成功连接到MongoDB数据库 [{self.environment}]: {connection_string}")
return True
except Exception as e:
self.logger.error(f"连接MongoDB失败: {e}")
self.connected = False
return False
def disconnect(self):
"""断开数据库连接"""
if self.client:
self.client.close()
self.connected = False
self.logger.info("已断开MongoDB连接")
def is_connected(self) -> bool:
"""检查是否已连接到数据库"""
return self.connected and self.client is not None
def get_collection(self, collection_name: str):
"""
获取集合对象
Args:
collection_name: 集合名称
Returns:
Collection: MongoDB集合对象
"""
if not self.is_connected():
raise Exception("数据库未连接")
return self.db[collection_name]
# ========================= 游戏配置管理 =========================
def get_game_config(self, config_type: str) -> Optional[Dict[str, Any]]:
"""
获取游戏配置
Args:
config_type: 配置类型,如 "daily_checkin"
Returns:
Dict: 配置数据如果未找到返回None
"""
try:
collection = self.get_collection("gameconfig")
# 根据配置类型查找文档
query = {"config_type": config_type}
result = collection.find_one(query)
if result:
# 移除MongoDB的_id字段只返回配置数据
if "_id" in result:
del result["_id"]
if "config_type" in result:
del result["config_type"]
self.logger.info(f"成功获取游戏配置: {config_type}")
return result
else:
self.logger.warning(f"未找到游戏配置: {config_type}")
return None
except Exception as e:
self.logger.error(f"获取游戏配置失败 [{config_type}]: {e}")
return None
def get_daily_checkin_config(self) -> Optional[Dict[str, Any]]:
"""
获取每日签到配置
Returns:
Dict: 每日签到配置数据
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID查找
object_id = ObjectId("687cce278e77ba00a7414ba2")
result = collection.find_one({"_id": object_id})
if result:
# 移除MongoDB的_id字段
if "_id" in result:
del result["_id"]
self.logger.info("成功获取每日签到配置")
return result
else:
self.logger.warning("未找到每日签到配置")
return None
except Exception as e:
self.logger.error(f"获取每日签到配置失败: {e}")
return None
def set_game_config(self, config_type: str, config_data: Dict[str, Any]) -> bool:
"""
设置游戏配置
Args:
config_type: 配置类型
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 准备文档数据
document = {
"config_type": config_type,
"updated_at": datetime.now(),
**config_data
}
# 使用upsert更新或插入
query = {"config_type": config_type}
result = collection.replace_one(query, document, upsert=True)
if result.acknowledged:
self.logger.info(f"成功设置游戏配置: {config_type}")
return True
else:
self.logger.error(f"设置游戏配置失败: {config_type}")
return False
except Exception as e:
self.logger.error(f"设置游戏配置异常 [{config_type}]: {e}")
return False
def update_daily_checkin_config(self, config_data: Dict[str, Any]) -> bool:
"""
更新每日签到配置
Args:
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID更新
object_id = ObjectId("687cce278e77ba00a7414ba2")
# 添加更新时间
update_data = {
"updated_at": datetime.now(),
**config_data
}
result = collection.replace_one({"_id": object_id}, update_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info("成功更新每日签到配置")
return True
else:
self.logger.error("更新每日签到配置失败")
return False
except Exception as e:
self.logger.error(f"更新每日签到配置异常: {e}")
return False
def get_lucky_draw_config(self) -> Optional[Dict[str, Any]]:
"""
获取幸运抽奖配置
Returns:
Dict: 幸运抽奖配置数据
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID查找
object_id = ObjectId("687cd52e8e77ba00a7414ba3")
result = collection.find_one({"_id": object_id})
if result:
# 移除MongoDB的_id字段和updated_at字段
if "_id" in result:
del result["_id"]
if "updated_at" in result:
del result["updated_at"]
self.logger.info("成功获取幸运抽奖配置")
return result
else:
self.logger.warning("未找到幸运抽奖配置")
return None
except Exception as e:
self.logger.error(f"获取幸运抽奖配置失败: {e}")
return None
def update_lucky_draw_config(self, config_data: Dict[str, Any]) -> bool:
"""
更新幸运抽奖配置
Args:
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID更新
object_id = ObjectId("687cd52e8e77ba00a7414ba3")
# 添加更新时间
update_data = {
"updated_at": datetime.now(),
**config_data
}
result = collection.replace_one({"_id": object_id}, update_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info("成功更新幸运抽奖配置")
return True
else:
self.logger.error("更新幸运抽奖配置失败")
return False
except Exception as e:
self.logger.error(f"更新幸运抽奖配置异常: {e}")
return False
def get_new_player_config(self) -> Optional[Dict[str, Any]]:
"""
获取新手大礼包配置
Returns:
Dict: 新手大礼包配置数据
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID查找
object_id = ObjectId("687cdbd78e77ba00a7414ba4")
result = collection.find_one({"_id": object_id})
if result:
# 移除MongoDB的_id字段和updated_at字段
if "_id" in result:
del result["_id"]
if "updated_at" in result:
del result["updated_at"]
self.logger.info("成功获取新手大礼包配置")
return result
else:
self.logger.warning("未找到新手大礼包配置")
return None
except Exception as e:
self.logger.error(f"获取新手大礼包配置失败: {e}")
return None
def update_new_player_config(self, config_data: Dict[str, Any]) -> bool:
"""
更新新手大礼包配置
Args:
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID更新
object_id = ObjectId("687cdbd78e77ba00a7414ba4")
# 添加更新时间
update_data = {
"updated_at": datetime.now(),
**config_data
}
result = collection.replace_one({"_id": object_id}, update_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info("成功更新新手大礼包配置")
return True
else:
self.logger.error("更新新手大礼包配置失败")
return False
except Exception as e:
self.logger.error(f"更新新手大礼包配置异常: {e}")
return False
def get_wisdom_tree_config(self) -> Optional[Dict[str, Any]]:
"""
获取智慧树配置
Returns:
Dict: 智慧树配置数据
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID查找
object_id = ObjectId("687cdfbe8e77ba00a7414ba5")
result = collection.find_one({"_id": object_id})
if result:
# 移除MongoDB的_id字段和updated_at字段
if "_id" in result:
del result["_id"]
if "updated_at" in result:
del result["updated_at"]
self.logger.info("成功获取智慧树配置")
return result
else:
self.logger.warning("未找到智慧树配置")
return None
except Exception as e:
self.logger.error(f"获取智慧树配置失败: {e}")
return None
def update_wisdom_tree_config(self, config_data: Dict[str, Any]) -> bool:
"""
更新智慧树配置
Args:
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID更新
object_id = ObjectId("687cdfbe8e77ba00a7414ba5")
# 添加更新时间
update_data = {
"updated_at": datetime.now(),
**config_data
}
result = collection.replace_one({"_id": object_id}, update_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info("成功更新智慧树配置")
return True
else:
self.logger.error("更新智慧树配置失败")
return False
except Exception as e:
self.logger.error(f"更新智慧树配置异常: {e}")
return False
def get_online_gift_config(self) -> Optional[Dict[str, Any]]:
"""
获取在线礼包配置
Returns:
Dict: 在线礼包配置数据
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID查找
object_id = ObjectId("687ce7678e77ba00a7414ba6")
result = collection.find_one({"_id": object_id})
if result:
# 移除MongoDB的_id字段和updated_at字段
if "_id" in result:
del result["_id"]
if "updated_at" in result:
del result["updated_at"]
self.logger.info("成功获取在线礼包配置")
return result
else:
self.logger.warning("未找到在线礼包配置")
return None
except Exception as e:
self.logger.error(f"获取在线礼包配置失败: {e}")
return None
def update_online_gift_config(self, config_data: Dict[str, Any]) -> bool:
"""
更新在线礼包配置
Args:
config_data: 配置数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection("gameconfig")
# 使用已知的文档ID更新
object_id = ObjectId("687ce7678e77ba00a7414ba6")
# 添加更新时间
update_data = {
"updated_at": datetime.now(),
**config_data
}
result = collection.replace_one({"_id": object_id}, update_data)
if result.acknowledged and result.matched_count > 0:
self.logger.info("成功更新在线礼包配置")
return True
else:
self.logger.error("更新在线礼包配置失败")
return False
except Exception as e:
self.logger.error(f"更新在线礼包配置异常: {e}")
return False
# ========================= 通用数据库操作 =========================
def insert_document(self, collection_name: str, document: Dict[str, Any]) -> Optional[str]:
"""
插入文档
Args:
collection_name: 集合名称
document: 要插入的文档
Returns:
str: 插入的文档ID失败返回None
"""
try:
collection = self.get_collection(collection_name)
result = collection.insert_one(document)
if result.acknowledged:
self.logger.info(f"成功插入文档到集合 {collection_name}")
return str(result.inserted_id)
else:
return None
except Exception as e:
self.logger.error(f"插入文档失败 [{collection_name}]: {e}")
return None
def find_documents(self, collection_name: str, query: Dict[str, Any] = None,
limit: int = 0) -> List[Dict[str, Any]]:
"""
查找文档
Args:
collection_name: 集合名称
query: 查询条件
limit: 限制返回数量0表示不限制
Returns:
List: 文档列表
"""
try:
collection = self.get_collection(collection_name)
if query is None:
query = {}
cursor = collection.find(query)
if limit > 0:
cursor = cursor.limit(limit)
documents = list(cursor)
# 转换ObjectId为字符串
for doc in documents:
if "_id" in doc:
doc["_id"] = str(doc["_id"])
return documents
except Exception as e:
self.logger.error(f"查找文档失败 [{collection_name}]: {e}")
return []
def update_document(self, collection_name: str, query: Dict[str, Any],
update: Dict[str, Any]) -> bool:
"""
更新文档
Args:
collection_name: 集合名称
query: 查询条件
update: 更新数据
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection(collection_name)
result = collection.update_one(query, {"$set": update})
return result.acknowledged and result.matched_count > 0
except Exception as e:
self.logger.error(f"更新文档失败 [{collection_name}]: {e}")
return False
def delete_document(self, collection_name: str, query: Dict[str, Any]) -> bool:
"""
删除文档
Args:
collection_name: 集合名称
query: 查询条件
Returns:
bool: 是否成功
"""
try:
collection = self.get_collection(collection_name)
result = collection.delete_one(query)
return result.acknowledged and result.deleted_count > 0
except Exception as e:
self.logger.error(f"删除文档失败 [{collection_name}]: {e}")
return False
# ========================= 测试和使用示例 =========================
def test_api():
"""测试API功能"""
print("=== 测试MongoDB API ===")
try:
# 创建API实例测试环境
api = SMYMongoDBAPI("test")
if not api.is_connected():
print("数据库连接失败请检查MongoDB服务")
return
# 测试获取每日签到配置
print("\n1. 测试获取每日签到配置:")
config = api.get_daily_checkin_config()
if config:
print("✓ 成功获取每日签到配置")
print(f"基础奖励金币范围: {config.get('基础奖励', {}).get('金币', {})}")
print(f"种子奖励类型数量: {len(config.get('种子奖励', {}))}")
else:
print("✗ 获取每日签到配置失败")
# 测试获取幸运抽奖配置
print("\n2. 测试获取幸运抽奖配置:")
lucky_config = api.get_lucky_draw_config()
if lucky_config:
print("✓ 成功获取幸运抽奖配置")
print(f"抽奖费用: {lucky_config.get('抽奖费用', {})}")
print(f"概率配置类型数量: {len(lucky_config.get('概率配置', {}))}")
else:
print("✗ 获取幸运抽奖配置失败")
# 测试获取新手大礼包配置
print("\n3. 测试获取新手大礼包配置:")
new_player_config = api.get_new_player_config()
if new_player_config:
print("✓ 成功获取新手大礼包配置")
gift_config = new_player_config.get('新手礼包配置', {})
reward_content = gift_config.get('奖励内容', {})
print(f"奖励金币: {reward_content.get('金币', 0)}")
print(f"奖励经验: {reward_content.get('经验', 0)}")
print(f"种子奖励数量: {len(reward_content.get('种子', []))}")
else:
print("✗ 获取新手大礼包配置失败")
# 测试获取智慧树配置
print("\n4. 测试获取智慧树配置:")
wisdom_tree_config = api.get_wisdom_tree_config()
if wisdom_tree_config:
print("✓ 成功获取智慧树配置")
messages = wisdom_tree_config.get('messages', [])
print(f"消息总数: {wisdom_tree_config.get('total_messages', 0)}")
print(f"最后更新: {wisdom_tree_config.get('last_update', 'N/A')}")
print(f"消息列表长度: {len(messages)}")
else:
print("✗ 获取智慧树配置失败")
# 测试获取在线礼包配置
print("\n5. 测试获取在线礼包配置:")
online_gift_config = api.get_online_gift_config()
if online_gift_config:
print("✓ 成功获取在线礼包配置")
gifts = online_gift_config.get('gifts', [])
print(f"礼包数量: {len(gifts)}")
print(f"最大在线时间: {online_gift_config.get('max_online_time', 'N/A')}")
else:
print("✗ 获取在线礼包配置失败")
# 测试查找所有游戏配置
print("\n6. 测试查找游戏配置集合:")
try:
configs = api.find_documents("gameconfig")
print(f"找到 {len(configs)} 个配置文档")
for config in configs:
print(f" - 文档ID: {config.get('_id', 'N/A')}")
except Exception as e:
print(f"查找配置失败: {e}")
# 断开连接
api.disconnect()
print("\n✓ 测试完成")
except Exception as e:
print(f"测试过程中出现异常: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
test_api()

View File

@@ -1,4 +1,5 @@
from TCPServer import TCPServer
from SMYMongoDBAPI import SMYMongoDBAPI
import time
import json
import os
@@ -84,9 +85,17 @@ class TCPGameServer(TCPServer):
# 配置文件目录
self.config_dir = "config" # 配置文件存储目录
# 初始化MongoDB API优先使用MongoDB失败则使用JSON文件
self._init_mongodb_api()
# 性能优化相关配置
self._init_performance_settings()
# 数据缓存
self.crop_data_cache = None
self.crop_data_cache_time = 0
self.cache_expire_duration = 300 # 缓存过期时间5分钟
self.log('INFO', f"萌芽农场TCP游戏服务器初始化完成 - 版本: {server_version}", 'SERVER')
# 启动定时器
@@ -96,6 +105,28 @@ class TCPGameServer(TCPServer):
self.start_wisdom_tree_health_decay_timer()
self.start_verification_code_cleanup_timer()
#初始化MongoDB API
def _init_mongodb_api(self):
"""初始化MongoDB API连接"""
try:
# 根据配置决定使用测试环境还是生产环境
# 这里默认使用测试环境,实际部署时可以修改为 "production"
environment = "test" # 或者从配置文件读取
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_performance_settings(self):
"""初始化性能优化配置"""
@@ -400,12 +431,22 @@ class TCPGameServer(TCPServer):
return player_data, username, None
#加载作物配置数据
#加载作物配置数据(优化版本)
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
# 缓存过期或不存在,重新加载
try:
with open("config/crop_data.json", 'r', encoding='utf-8') as file:
return json.load(file)
self.crop_data_cache = json.load(file)
self.crop_data_cache_time = current_time
return self.crop_data_cache
except Exception as e:
self.log('ERROR', f"无法加载作物数据: {str(e)}", 'SERVER')
return {}
@@ -810,6 +851,8 @@ class TCPGameServer(TCPServer):
return self._handle_buy_store_product(client_id, message)
elif message_type == "buy_store_booth":#购买小卖部格子
return self._handle_buy_store_booth(client_id, message)
elif message_type == "save_game_settings":#保存游戏设置
return self._handle_save_game_settings(client_id, message)
#---------------------------------------------------------------------------
elif message_type == "message":#处理聊天消息(暂未实现)
@@ -1340,7 +1383,7 @@ class TCPGameServer(TCPServer):
#==========================收获作物处理==========================
#处理收获作物请求
def _handle_harvest_crop(self, client_id, message):
"""处理收获作物请求"""
"""处理收获作物请求(优化版本)"""
# 检查用户是否已登录
logged_in, response = self._check_user_logged_in(client_id, "收获作物", "harvest_crop")
if not logged_in:
@@ -1354,6 +1397,11 @@ class TCPGameServer(TCPServer):
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:
# 访问模式:偷菜(收益给自己,清空目标玩家的作物)
@@ -1396,7 +1444,7 @@ class TCPGameServer(TCPServer):
return self._send_action_error(client_id, "harvest_crop", "作物尚未成熟,无法偷菜")
# 处理偷菜
return self._process_steal_crop(client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index)
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:
# 正常模式:收获自己的作物
# 验证地块索引
@@ -1434,55 +1482,55 @@ class TCPGameServer(TCPServer):
return self._send_action_error(client_id, "harvest_crop", "作物尚未成熟")
# 处理正常收获
return self._process_harvest(client_id, current_player_data, current_username, lot, lot_index)
return self._process_harvest_optimized(client_id, current_player_data, current_username, lot, lot_index, crop_data)
#辅助函数-处理作物收获
def _process_harvest(self, client_id, player_data, username, lot, lot_index):
"""处理作物收获逻辑"""
# 取作物配置
crop_data = self._load_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, {})
# 检查是否为杂草类型(杂草不能收获,只能铲除)
if crop_type in crop_data:
crop_info = crop_data[crop_type]
is_weed = crop_info.get("是否杂草", False)
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能收获,只能铲除!请使用铲除功能清理杂草。")
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)
# 额外检查:如果作物收益为负数,也视为杂草
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}不能收获,只能铲除!请使用铲除功能清理杂草。")
else:
# 默认经验
crop_exp = 10
# 获取作物经验
crop_exp = crop_info.get("经验", 10)
# 生成成熟物收获1-5个
import random
harvest_count = random.randint(1, 5)
crop_harvest = {
"name": crop_type,
"count": harvest_count
}
# 10%概率获得1-2个该作物的种子
seed_reward = self._generate_harvest_seed_reward(crop_type)
seed_reward = None
if random.random() <= 0.1:
seed_reward = {
"name": crop_type,
"count": random.randint(1, 2)
}
# 更新玩家经验(不再直接给钱)
# 更新玩家经验
player_data["experience"] += crop_exp
# 添加成熟物到作物仓库
self._add_crop_to_warehouse(player_data, crop_harvest)
# 检查是否会获得成熟物
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}, mature_item_name, crop_info.get("品质", "普通"))
# 添加种子奖励到背包
if seed_reward:
self._add_seeds_to_bag(player_data, seed_reward)
self._add_seeds_to_bag_optimized(player_data, seed_reward, crop_info.get("品质", "普通"))
# 检查升级
level_up_experience = 100 * player_data["level"]
@@ -1491,12 +1539,14 @@ class TCPGameServer(TCPServer):
player_data["experience"] -= level_up_experience
self.log('INFO', f"玩家 {username} 升级到 {player_data['level']}", 'SERVER')
# 清理地块
lot["is_planted"] = False
lot["crop_type"] = ""
lot["grow_time"] = 0
lot["已浇水"] = False
lot["施肥"] = False
# 清理地块(批量更新)
lot.update({
"is_planted": False,
"crop_type": "",
"grow_time": 0,
"浇水": False,
"已施肥": False
})
# 清除施肥时间戳
if "施肥时间" in lot:
@@ -1509,7 +1559,11 @@ class TCPGameServer(TCPServer):
self._push_crop_update_to_player(username, player_data)
# 构建消息
message = f"收获成功,获得 {crop_type} x{harvest_count}{crop_exp} 经验"
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']}"
@@ -1529,9 +1583,9 @@ class TCPGameServer(TCPServer):
}
})
#辅助函数-处理偷菜逻辑(访问模式下收获其他玩家作物的操作)
def _process_steal_crop(self, client_id, current_player_data, current_username, target_player_data, target_username, target_lot, lot_index):
"""处理偷菜逻辑(收益给当前玩家,清空目标玩家的作物)"""
#辅助函数-处理偷菜逻辑(访问模式下收获其他玩家作物的操作)(优化版本)
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 = 2
@@ -1554,40 +1608,34 @@ class TCPGameServer(TCPServer):
target_player_data, target_username, patrol_pets[0]
)
# 取作物配置
crop_data = self._load_crop_data()
# 获取作物类型和经验偷菜获得的经验稍微少一些比如50%
# 取作物类型和基本信息
crop_type = target_lot["crop_type"]
crop_info = crop_data.get(crop_type, {})
# 检查是否为杂草类型(杂草不能偷取,只能铲除)
if crop_type in crop_data:
crop_info = crop_data[crop_type]
is_weed = crop_info.get("是否杂草", False)
is_weed = crop_info.get("是否杂草", False)
if is_weed:
return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能偷取,只能铲除!这是杂草,没有收益价值。")
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 = int(crop_info.get("经验", 10) * 0.5) # 偷菜获得50%经验
# 额外检查:如果作物收益为负数,也视为杂草
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}不能偷取,只能铲除!这是杂草,没有收益价值。")
else:
# 默认经验
crop_exp = 5
# 获取作物经验(偷菜获得50%经验
crop_exp = int(crop_info.get("经验", 10) * 0.5)
# 生成成熟物收获偷菜获得较少1-3个
import random
harvest_count = random.randint(1, 3)
crop_harvest = {
"name": crop_type,
"count": harvest_count
}
# 10%概率获得1-2个该作物的种子偷菜也有机会获得种子
seed_reward = self._generate_harvest_seed_reward(crop_type)
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, "偷菜")
@@ -1597,12 +1645,18 @@ class TCPGameServer(TCPServer):
# 更新当前玩家数据(获得经验)
current_player_data["experience"] += crop_exp
# 添加成熟物到作物仓库
self._add_crop_to_warehouse(current_player_data, crop_harvest)
# 检查是否会获得成熟物
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}, mature_item_name, crop_info.get("品质", "普通"))
# 添加种子奖励到背包
if seed_reward:
self._add_seeds_to_bag(current_player_data, seed_reward)
self._add_seeds_to_bag_optimized(current_player_data, seed_reward, crop_info.get("品质", "普通"))
# 检查当前玩家升级
level_up_experience = 100 * current_player_data["level"]
@@ -1611,12 +1665,14 @@ class TCPGameServer(TCPServer):
current_player_data["experience"] -= level_up_experience
self.log('INFO', f"玩家 {current_username} 升级到 {current_player_data['level']}", 'SERVER')
# 清理目标玩家的地块
target_lot["is_planted"] = False
target_lot["crop_type"] = ""
target_lot["grow_time"] = 0
target_lot["已浇水"] = False
target_lot["施肥"] = False
# 清理目标玩家的地块(批量更新)
target_lot.update({
"is_planted": False,
"crop_type": "",
"grow_time": 0,
"浇水": False,
"已施肥": False
})
# 清除施肥时间戳
if "施肥时间" in target_lot:
@@ -1630,7 +1686,11 @@ class TCPGameServer(TCPServer):
self._push_crop_update_to_player(target_username, target_player_data)
# 构建消息
message = f"偷菜成功!从 {target_username} 那里获得 {crop_type} x{harvest_count}{crop_exp} 经验,{stamina_message}"
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']}"
@@ -1810,6 +1870,24 @@ class TCPGameServer(TCPServer):
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["作物仓库"] = []
@@ -1817,7 +1895,7 @@ class TCPGameServer(TCPServer):
# 查找仓库中是否已有该成熟物
crop_found = False
for item in player_data["作物仓库"]:
if item.get("name") == crop_name:
if item.get("name") == warehouse_item_name:
item["count"] += crop_count
crop_found = True
break
@@ -1825,17 +1903,66 @@ class TCPGameServer(TCPServer):
# 如果仓库中没有该成熟物,添加新条目
if not crop_found:
# 从作物数据获取品质信息
crop_data = self._load_crop_data()
quality = "普通"
if crop_data and crop_name in crop_data:
quality = crop_data[crop_name].get("品质", "普通")
player_data["作物仓库"].append({
"name": crop_name,
"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 "player_bag" not in player_data:
player_data["player_bag"] = []
# 查找背包中是否已有该种子
for item in player_data["player_bag"]:
if item.get("name") == seed_name:
item["count"] += seed_count
return
# 如果背包中没有该种子,添加新条目
player_data["player_bag"].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
})
#==========================收获作物处理==========================
@@ -4427,8 +4554,21 @@ class TCPGameServer(TCPServer):
# 检查是否升级
self._check_level_up(player_data)
# 添加成熟物到作物仓库
self._add_crop_to_warehouse(player_data, crop_harvest)
# 检查是否会获得成熟物
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:
@@ -4452,7 +4592,11 @@ class TCPGameServer(TCPServer):
self._push_crop_update_to_player(username, player_data)
# 构建消息
message = f"使用 {item_name} 收获成功,获得 {crop_type} x{harvest_count}{crop_exp} 经验{message_suffix}"
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']}"
@@ -4546,8 +4690,21 @@ class TCPGameServer(TCPServer):
# 检查当前玩家是否升级
self._check_level_up(current_player_data)
# 收获物给当前玩家
self._add_crop_to_warehouse(current_player_data, crop_harvest)
# 检查是否会获得成熟物
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:
@@ -4572,7 +4729,11 @@ class TCPGameServer(TCPServer):
self._push_crop_update_to_player(target_username, target_player_data)
# 构建消息
message = f"使用 {item_name} 帮助收获成功!从 {target_username} 那里获得 {crop_type} x{harvest_count}{crop_exp} 经验{message_suffix}"
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']}"
@@ -5418,6 +5579,7 @@ class TCPGameServer(TCPServer):
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秒"
@@ -5516,6 +5678,81 @@ class TCPGameServer(TCPServer):
#==========================游戏设置处理==========================
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):
@@ -6506,16 +6743,32 @@ class TCPGameServer(TCPServer):
#==========================每日签到处理==========================
#加载每日签到配置
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:
return json.load(f)
except:
pass
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"},
@@ -6537,6 +6790,25 @@ class TCPGameServer(TCPServer):
}
}
#更新每日签到配置到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):
"""处理每日签到请求"""
@@ -7126,16 +7398,32 @@ class TCPGameServer(TCPServer):
#加载抽奖配置
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:
return json.load(f)
except:
pass
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},
"概率配置": {
@@ -7151,16 +7439,31 @@ class TCPGameServer(TCPServer):
#加载在线礼包配置
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:
return json.load(f)
config = json.load(f)
self.log('INFO', '成功从JSON文件加载在线礼包配置', 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"加载在线礼包配置失败: {str(e)}", 'SERVER')
pass
self.log('ERROR', f"从JSON文件加载在线礼包配置失败: {str(e)}", 'SERVER')
# 默认配置
self.log('WARNING', '使用默认在线礼包配置', 'SERVER')
return {
"在线礼包配置": {
"1分钟": {"时长秒数": 60, "奖励": {"金币": 100, "经验": 50, "种子": [{"名称": "小麦", "数量": 5}]}},
@@ -7174,15 +7477,31 @@ class TCPGameServer(TCPServer):
#加载新手礼包配置
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:
return json.load(f)
config = json.load(f)
self.log('INFO', '成功从JSON文件加载新手大礼包配置', 'SERVER')
return config
except Exception as e:
self.log('ERROR', f"加载新手礼包配置失败: {str(e)}", 'SERVER')
self.log('ERROR', f"从JSON文件加载新手礼包配置失败: {str(e)}", 'SERVER')
# 默认配置
self.log('WARNING', '使用默认新手大礼包配置', 'SERVER')
return {
"新手礼包配置": {
"奖励内容": {
@@ -8279,7 +8598,7 @@ class TCPGameServer(TCPServer):
# 从智慧树消息库中随机获取一条消息
random_message = self._get_random_wisdom_tree_message()
if random_message:
wisdom_tree_config["智慧树显示的话"] = random_message
wisdom_tree_config["智慧树显示的话"] = random_message.get("content", "")
# 保存数据
self.save_player_data(username, player_data)
@@ -8347,7 +8666,7 @@ class TCPGameServer(TCPServer):
random_message = self._get_random_wisdom_tree_message()
if random_message:
wisdom_tree_config["智慧树显示的话"] = random_message
wisdom_tree_config["智慧树显示的话"] = random_message.get("content", "")
# 保存数据
self.save_player_data(username, player_data)
@@ -8505,6 +8824,22 @@ class TCPGameServer(TCPServer):
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:
@@ -8514,12 +8849,13 @@ class TCPGameServer(TCPServer):
messages = wisdom_tree_data.get("messages", [])
if messages:
selected_message = random.choice(messages)
return selected_message.get("content", "")
self.log('INFO', f"成功从JSON文件获取智慧树消息", 'SERVER')
return selected_message
else:
return ""
return None
except Exception as e:
print(f"读取智慧树消息失败{e}")
return ""
self.log('ERROR', f"从JSON文件读取智慧树消息失败: {e}", 'SERVER')
return None
def _save_wisdom_tree_message(self, username, message_content):
"""保存智慧树消息到消息库"""
@@ -8528,6 +8864,46 @@ class TCPGameServer(TCPServer):
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:
@@ -8542,14 +8918,6 @@ class TCPGameServer(TCPServer):
"last_update": ""
}
# 创建新消息
new_message = {
"timestamp": time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()),
"sender": username,
"content": message_content,
"id": str(uuid.uuid4())
}
# 添加到消息列表
wisdom_tree_data["messages"].append(new_message)
wisdom_tree_data["total_messages"] = len(wisdom_tree_data["messages"])
@@ -8564,9 +8932,10 @@ class TCPGameServer(TCPServer):
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:
print(f"保存智慧树消息失败:{e}")
self.log('ERROR', f"保存智慧树消息到JSON文件失败: {e}", 'SERVER')
return False
def check_wisdom_tree_health_decay(self):
@@ -9543,17 +9912,6 @@ if __name__ == "__main__":
server_thread.start()
print("✅ 服务器启动成功!")
print("📋 功能列表:")
print(" ├── 用户注册/登录系统")
print(" ├── 作物种植与收获")
print(" ├── 浇水与施肥系统")
print(" ├── 每日签到奖励")
print(" ├── 幸运抽奖系统")
print(" ├── 玩家互动功能")
print(" ├── 性能优化缓存")
print(" └── 控制台命令系统")
print("=" * 60)
print("🔥 服务器运行中...")
# 启动控制台输入线程
console_thread = threading.Thread(target=console_input_thread, args=(server,))

Binary file not shown.

View File

@@ -1,72 +1,85 @@
{
"experience": 0,
"level": 1,
"money": 4000,
"体力值": 20,
"体力上次刷新时间": "",
"体力上次恢复时间": 0,
"farm_name": "农场",
"user_name": "shumengya",
"player_name": "玩家昵称",
"user_password": "0123456789",
"last_login_time": "2025年12时09分35秒",
"money": 5000,
"farm_name": "农场名称",
"player_name": "玩家名称",
"user_name": "用户名",
"user_password": "密码",
"last_login_time": "2025年07月20日17时19分16秒",
"total_login_time": "0时0分0秒",
"personal_profile": "个人简介",
"注册时间": "2025年05月21日15时00分00秒",
"farm_lots": [
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":true,"is_planted":false,"max_grow_time":3,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0},
{"crop_type":"","grow_time":0,"is_dead":false,"is_diged":false,"is_planted":false,"max_grow_time":5,"已浇水":false,"已施肥":false,"土地等级":0}
],
"last_water_reset_date": "2025-06-05",
"farm_lots": [],
"player_bag": [],
"作物仓库": [],
"宠物背包": [],
"巡逻宠物": [],
"出战宠物": [],
"注册时间": "2025年05月21日15时00分00秒",
"个人简介": "个人简介",
"稻草人配置": {
"已拥有稻草人类型": [
],
"稻草人展示类型": "",
"稻草人昵称": "稻草人",
"稻草人说的话": {
"第一句话": {
"内容": "第一句话",
"颜色": "52dceeff"
},
"第二句话": {
"内容": "第二句话",
"颜色": "80d5ffff"
},
"第三句话": {
"内容": "第三句话",
"颜色": "ac52ffff"
},
"第四句话": {
"内容": "第四句话",
"颜色": "f881ffff"
}
},
"稻草人昵称颜色": "b38282ff"
},
"智慧树配置": {
"距离上一次杀虫时间": 1753004237,
"距离上一次除草时间": 1753004237,
"智慧树显示的话": "",
"等级": 1,
"当前经验值": 0,
"最大经验值": 100,
"最大生命值": 100,
"当前生命值": 100,
"高度": 20
},
"签到历史": {
},
"在线礼包": {
"当前日期": "2025-07-20",
"今日在线时长": 0.0,
"已领取礼包": [],
"登录时间": 1753003043.7163484
},
"点赞系统": {
"今日剩余点赞次数": 10,
"点赞上次刷新时间": "2025-07-20"
},
"新手礼包": {
"已领取": false,
"领取时间": "2025-07-12 23:02:25"
},
"体力系统": {
"当前体力值": 20,
"最大体力值": 20,
"上次刷新时间": "2025-07-20",
"上次恢复时间": 1753003043.7066433
},
"道具背包": [],
"宠物背包":[],
"巡逻宠物":[],
"出战宠物":[]
"玩家小卖部": [],
"小卖部格子数": 10,
"游戏设置": {
"背景音乐音量": 1.0,
"天气显示": true
}
}

View File

@@ -1,13 +1,13 @@
{
"experience": 1196,
"level": 35,
"money": 200812377,
"experience": 90,
"level": 36,
"money": 200802715,
"farm_name": "柚大青の小农场",
"player_name": "柚大青",
"user_name": "2143323382",
"user_password": "tyh@19900420",
"last_login_time": "2025年07月19日11时01分22秒",
"total_login_time": "6时31分14秒",
"last_login_time": "2025年07月20日21时00分40秒",
"total_login_time": "6时45分52秒",
"farm_lots": [
{
"crop_type": "",
@@ -131,26 +131,27 @@
"土地等级": 3
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "杂交树1",
"grow_time": 14976,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 1080,
"is_planted": true,
"max_grow_time": 21600,
"已浇水": false,
"已施肥": false,
"土地等级": 3
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "杂交树2",
"grow_time": 15468,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 720,
"is_planted": true,
"max_grow_time": 25200,
"已浇水": false,
"已施肥": false,
"土地等级": 3
"土地等级": 3,
"浇水时间": 1753003230.8845751
},
{
"crop_type": "",
@@ -241,12 +242,12 @@
"土地等级": 1
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "玉米",
"grow_time": 918,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 240,
"is_planted": true,
"max_grow_time": 900,
"已浇水": false,
"已施肥": false,
"土地等级": 1
@@ -576,11 +577,6 @@
"quality": "普通",
"count": 1
},
{
"name": "玉米",
"quality": "优良",
"count": 1
},
{
"name": "番茄",
"quality": "普通",
@@ -616,15 +612,6 @@
"quality": "传奇",
"count": 1
},
{
"name": "杂交树1",
"quality": "传奇",
"count": 1
},
{
"name": "杂交树2",
"count": 1
},
{
"name": "荔枝",
"count": 2
@@ -644,6 +631,46 @@
{
"name": "向日葵",
"count": 3
},
{
"name": "黄瓜",
"quality": "普通",
"count": 3
},
{
"name": "野草1",
"quality": "优良",
"count": 2
},
{
"name": "豌豆",
"quality": "普通",
"count": 4
},
{
"name": "稻谷",
"quality": "普通",
"count": 4
},
{
"name": "山楂",
"quality": "优良",
"count": 2
},
{
"name": "龙果",
"quality": "传奇",
"count": 1
},
{
"name": "杂交树1",
"quality": "传奇",
"count": 1
},
{
"name": "杂交树2",
"quality": "传奇",
"count": 1
}
],
"last_water_reset_date": "2025-06-05",
@@ -851,9 +878,11 @@
],
"稻草人配置": {
"已拥有稻草人类型": [
"稻草人1"
"稻草人1",
"稻草人2",
"稻草人3"
],
"稻草人展示类型": "",
"稻草人展示类型": "稻草人3",
"稻草人昵称": "柚大青的稻草人",
"稻草人说的话": {
"第一句话": {
@@ -875,48 +904,60 @@
},
"稻草人昵称颜色": "b38282ff"
},
"智慧树配置": {
"智慧树显示的话": "柚小青最可爱",
"等级": 4,
"高度": 76,
"上次护理时间": 1752050186,
"距离上一次除草时间": 1752050186,
"距离上一次杀虫时间": 1752050186,
"当前经验值": 278,
"最大经验值": 480,
"最大生命值": 106,
"当前生命值": 106
},
"签到历史": {
"2025年07月12日21时05分47秒": "金币249 经验75 土豆x3",
"2025年07月13日07时26分04秒": "金币302 经验63 土豆x5 小麦x3"
},
"在线礼包": {
"当前日期": "2025-07-19",
"今日在线时长": 0.0,
"当前日期": "2025-07-20",
"今日在线时长": 999999.271807432174683,
"已领取礼包": [],
"登录时间": 1752894082.539563
"登录时间": 1753003043.7163484
},
"点赞系统": {
"今日剩余点赞次数": 10,
"点赞上次刷新时间": "2025-07-19"
"点赞上次刷新时间": "2025-07-20"
},
"新手礼包": {
"已领取": true,
"领取时间": "2025-07-12 23:02:25"
"领取时间": "2025-07-20 20:21:04"
},
"体力系统": {
"当前体力值": 20,
"最大体力值": 20,
"上次刷新时间": "2025-07-19",
"上次恢复时间": 1752894082.5390205
"上次刷新时间": "2025-07-20",
"上次恢复时间": 1753003043.7066433
},
"玩家小卖部": [],
"道具背包": [
{
"name": "铲子",
"count": 100
},
{
"name": "农家肥",
"count": 5
},
{
"name": "水桶",
"count": 4
}
],
"小卖部格子数": 10
"玩家小卖部": [],
"小卖部格子数": 10,
"游戏设置": {
"背景音乐音量": 1.0,
"天气显示": true
},
"智慧树配置": {
"距离上一次杀虫时间": 1753014929,
"距离上一次除草时间": 1753014864,
"智慧树显示的话": "你好,树萌芽",
"等级": 2,
"当前经验值": 27,
"最大经验值": 169,
"最大生命值": 102,
"当前生命值": 102,
"高度": 20,
"上次护理时间": 1753014929
}
}

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
测试MongoDB迁移功能
作者: AI Assistant
功能: 测试每日签到配置从JSON迁移到MongoDB的功能
"""
import sys
import os
import json
# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from SMYMongoDBAPI import SMYMongoDBAPI
def test_mongodb_migration():
"""测试MongoDB迁移功能"""
print("=== 测试MongoDB迁移功能 ===")
# 1. 测试MongoDB API连接
print("\n1. 测试MongoDB API连接:")
try:
api = SMYMongoDBAPI("test")
if api.is_connected():
print("✓ MongoDB连接成功")
else:
print("✗ MongoDB连接失败")
return False
except Exception as e:
print(f"✗ MongoDB连接异常: {e}")
return False
# 2. 测试获取每日签到配置
print("\n2. 测试获取每日签到配置:")
try:
config = api.get_daily_checkin_config()
if config:
print("✓ 成功获取每日签到配置")
print(f" 基础奖励金币范围: {config.get('基础奖励', {}).get('金币', {})}")
print(f" 种子奖励类型数量: {len(config.get('种子奖励', {}))}")
print(f" 连续签到奖励天数: {len(config.get('连续签到奖励', {}))}")
else:
print("✗ 获取每日签到配置失败")
return False
except Exception as e:
print(f"✗ 获取每日签到配置异常: {e}")
return False
# 3. 测试更新配置
print("\n3. 测试更新每日签到配置:")
try:
# 创建一个测试配置
test_config = {
"基础奖励": {
"金币": {"最小值": 300, "最大值": 600, "图标": "💰", "颜色": "#FFD700"},
"经验": {"最小值": 75, "最大值": 150, "图标": "", "颜色": "#00BFFF"}
},
"种子奖励": {
"普通": {"概率": 0.6, "数量范围": [2, 5], "种子池": ["小麦", "胡萝卜", "土豆", "稻谷"]},
"优良": {"概率": 0.25, "数量范围": [2, 4], "种子池": ["玉米", "番茄", "洋葱", "大豆", "豌豆", "黄瓜", "大白菜"]},
"稀有": {"概率": 0.12, "数量范围": [1, 3], "种子池": ["草莓", "花椰菜", "柿子", "蓝莓", "树莓"]},
"史诗": {"概率": 0.025, "数量范围": [1, 2], "种子池": ["葡萄", "南瓜", "芦笋", "茄子", "向日葵", "蕨菜"]},
"传奇": {"概率": 0.005, "数量范围": [1, 1], "种子池": ["西瓜", "甘蔗", "香草", "甜菜", "人参", "富贵竹", "芦荟", "哈密瓜"]}
},
"连续签到奖励": {
"第3天": {"额外金币": 150, "额外经验": 75, "描述": "连续签到奖励"},
"第7天": {"额外金币": 300, "额外经验": 150, "描述": "一周连击奖励"},
"第14天": {"额外金币": 600, "额外经验": 250, "描述": "半月连击奖励"},
"第21天": {"额外金币": 1000, "额外经验": 400, "描述": "三周连击奖励"},
"第30天": {"额外金币": 2000, "额外经验": 600, "描述": "满月连击奖励"}
}
}
success = api.update_daily_checkin_config(test_config)
if success:
print("✓ 成功更新测试配置到MongoDB")
else:
print("✗ 更新测试配置失败")
return False
except Exception as e:
print(f"✗ 更新测试配置异常: {e}")
return False
# 4. 验证更新后的配置
print("\n4. 验证更新后的配置:")
try:
updated_config = api.get_daily_checkin_config()
if updated_config:
print("✓ 成功获取更新后的配置")
print(f" 更新后金币范围: {updated_config.get('基础奖励', {}).get('金币', {})}")
print(f" 更新后第3天奖励: {updated_config.get('连续签到奖励', {}).get('第3天', {})}")
# 验证更新是否生效
if updated_config.get('基础奖励', {}).get('金币', {}).get('最小值') == 300:
print("✓ 配置更新验证成功")
else:
print("✗ 配置更新验证失败")
return False
else:
print("✗ 获取更新后的配置失败")
return False
except Exception as e:
print(f"✗ 验证更新后配置异常: {e}")
return False
# 5. 恢复原始配置
print("\n5. 恢复原始配置:")
try:
original_config = {
"基础奖励": {
"金币": {"最小值": 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, "描述": "满月连击奖励"}
}
}
success = api.update_daily_checkin_config(original_config)
if success:
print("✓ 成功恢复原始配置")
else:
print("✗ 恢复原始配置失败")
return False
except Exception as e:
print(f"✗ 恢复原始配置异常: {e}")
return False
# 6. 测试配置数据完整性
print("\n6. 测试配置数据完整性:")
try:
final_config = api.get_daily_checkin_config()
if final_config:
# 检查必要字段是否存在
required_fields = ["基础奖励", "种子奖励", "连续签到奖励"]
missing_fields = [field for field in required_fields if field not in final_config]
if not missing_fields:
print("✓ 配置数据完整性检查通过")
print(f" 包含字段: {', '.join(required_fields)}")
else:
print(f"✗ 配置数据缺少字段: {missing_fields}")
return False
else:
print("✗ 无法获取最终配置进行完整性检查")
return False
except Exception as e:
print(f"✗ 配置数据完整性检查异常: {e}")
return False
# 清理资源
api.disconnect()
print("\n=== 所有测试通过MongoDB迁移功能正常 ===")
return True
if __name__ == "__main__":
success = test_mongodb_migration()
if success:
print("\n🎉 MongoDB迁移测试成功完成")
sys.exit(0)
else:
print("\n❌ MongoDB迁移测试失败")
sys.exit(1)

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
测试服务器MongoDB集成
作者: AI Assistant
功能: 测试服务器是否能正确使用MongoDB配置
"""
import sys
import os
# 添加当前目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def test_server_mongodb_integration():
"""测试服务器MongoDB集成"""
print("=== 测试服务器MongoDB集成 ===")
try:
# 导入服务器模块
from Server.TCPGameServer import TCPGameServer
print("✓ 成功导入TCPGameServer模块")
# 创建服务器实例(不启动网络服务)
print("\n1. 创建服务器实例:")
server = TCPGameServer()
print("✓ 服务器实例创建成功")
# 检查MongoDB连接状态
print("\n2. 检查MongoDB连接状态:")
if hasattr(server, 'use_mongodb'):
print(f" MongoDB使用状态: {server.use_mongodb}")
if hasattr(server, 'mongo_api') and server.mongo_api:
print(" MongoDB API实例: 已创建")
else:
print(" MongoDB API实例: 未创建")
else:
print(" MongoDB相关属性: 未找到")
# 测试配置加载
print("\n3. 测试每日签到配置加载:")
try:
config = server._load_daily_check_in_config()
if config:
print("✓ 成功加载每日签到配置")
print(f" 基础奖励金币范围: {config.get('基础奖励', {}).get('金币', {})}")
print(f" 种子奖励类型数量: {len(config.get('种子奖励', {}))}")
print(f" 连续签到奖励天数: {len(config.get('连续签到奖励', {}))}")
# 检查配置来源
if hasattr(server, 'use_mongodb') and server.use_mongodb:
print(" 配置来源: MongoDB")
else:
print(" 配置来源: JSON文件或默认配置")
else:
print("✗ 加载每日签到配置失败")
return False
except Exception as e:
print(f"✗ 配置加载异常: {e}")
return False
# 测试配置更新方法
print("\n4. 测试配置更新方法:")
if hasattr(server, '_update_daily_checkin_config_to_mongodb'):
print("✓ 配置更新方法存在")
# 测试更新方法(不实际更新)
test_config = {
"基础奖励": {
"金币": {"最小值": 250, "最大值": 550, "图标": "💰", "颜色": "#FFD700"},
"经验": {"最小值": 60, "最大值": 130, "图标": "", "颜色": "#00BFFF"}
}
}
try:
# 这里只是测试方法是否存在,不实际调用
print("✓ 配置更新方法可调用")
except Exception as e:
print(f"✗ 配置更新方法异常: {e}")
return False
else:
print("✗ 配置更新方法不存在")
return False
print("\n=== 服务器MongoDB集成测试通过 ===")
return True
except ImportError as e:
print(f"✗ 模块导入失败: {e}")
print(" 请确保所有依赖模块都已正确安装")
return False
except Exception as e:
print(f"✗ 测试过程中出现异常: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == "__main__":
success = test_server_mongodb_integration()
if success:
print("\n🎉 服务器MongoDB集成测试成功完成")
sys.exit(0)
else:
print("\n❌ 服务器MongoDB集成测试失败")
sys.exit(1)

Binary file not shown.

View File

@@ -1,5 +1,5 @@
extends Sprite2D
#昼夜循环只需调节该节点的modulate值即可
# 存储背景图片的路径数组
var backgrounds :Array = [
"res://assets/背景图片/背景1.webp",

View File

@@ -1,45 +1,45 @@
{
"farm_lots": [
{
"crop_type": "",
"grow_time": 0,
"crop_type": "小麦",
"grow_time": 300,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 10200,
"is_planted": true,
"max_grow_time": 300,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "小麦",
"grow_time": 300,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 7200,
"is_planted": true,
"max_grow_time": 300,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "小麦",
"grow_time": 300,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 14400,
"is_planted": true,
"max_grow_time": 300,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "",
"grow_time": 0,
"crop_type": "小麦",
"grow_time": 300,
"is_dead": false,
"is_diged": true,
"is_planted": false,
"max_grow_time": 7200,
"is_planted": true,
"max_grow_time": 300,
"已浇水": false,
"已施肥": false,
"土地等级": 4
@@ -53,7 +53,7 @@
"max_grow_time": 2940,
"已浇水": false,
"已施肥": false,
"土地等级": 0
"土地等级": 1
},
{
"crop_type": "",
@@ -64,7 +64,7 @@
"max_grow_time": 1080,
"已浇水": false,
"已施肥": false,
"土地等级": 0
"土地等级": 2
},
{
"crop_type": "",
@@ -75,7 +75,7 @@
"max_grow_time": 1080,
"已浇水": false,
"已施肥": false,
"土地等级": 0
"土地等级": 3
},
{
"crop_type": "龙果",
@@ -111,44 +111,44 @@
"土地等级": 0
},
{
"crop_type": "土豆",
"grow_time": 480,
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": true,
"is_planted": false,
"max_grow_time": 480,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "胡萝卜",
"grow_time": 240,
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": true,
"is_planted": false,
"max_grow_time": 240,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "土豆",
"grow_time": 540,
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": true,
"is_planted": false,
"max_grow_time": 480,
"已浇水": false,
"已施肥": false,
"土地等级": 4
},
{
"crop_type": "小麦",
"grow_time": 300,
"crop_type": "",
"grow_time": 0,
"is_dead": false,
"is_diged": true,
"is_planted": true,
"is_planted": false,
"max_grow_time": 300,
"已浇水": false,
"已施肥": false,
@@ -555,7 +555,7 @@
{
"name": "小麦",
"quality": "普通",
"count": 8
"count": 4
},
{
"name": "胡萝卜",
@@ -566,15 +566,20 @@
"name": "土豆",
"quality": "普通",
"count": 6
},
{
"name": "杂交树2",
"quality": "传奇",
"count": 8
}
],
"experience": 5164,
"experience": 573,
"farm_name": "树萌芽の狗窝",
"player_name": "树萌芽",
"level": 63,
"money": 615197045864,
"last_login_time": "2025年07月19日15时31分10秒",
"total_login_time": "162时4分9秒",
"level": 64,
"money": 615197019864,
"last_login_time": "2025年07月19日21时52分29秒",
"total_login_time": "162时47分40秒",
"user_name": "3205788256",
"user_password": "tyh@19900420",
"last_water_reset_date": "2025-06-06",
@@ -631,7 +636,7 @@
{
"name": "小麦",
"quality": "普通",
"count": 15
"count": 16
},
{
"name": "山葵",
@@ -681,12 +686,17 @@
{
"name": "胡萝卜",
"quality": "普通",
"count": 1
"count": 5
},
{
"name": "土豆",
"quality": "普通",
"count": 3
},
{
"name": "马铃薯",
"quality": "普通",
"count": 7
}
],
"道具背包": [
@@ -696,7 +706,7 @@
},
{
"name": "生长素",
"count": 1001
"count": 1000
},
{
"name": "时运-镰刀",
@@ -844,10 +854,10 @@
"爱好": ""
},
"等级经验": {
"宠物等级": 3,
"当前经验": 40.0,
"最大经验": 144.0,
"亲密度": 136.0,
"宠物等级": 4,
"当前经验": 26.0,
"最大经验": 172.8,
"亲密度": 204.0,
"最大亲密度": 1000.0
},
"购买信息": {
@@ -856,18 +866,18 @@
"出售价格": 500
},
"生命与防御": {
"最大生命值": 242.00000000000006,
"当前生命值": 242.00000000000006,
"最大生命值": 266.2000000000001,
"当前生命值": 266.2000000000001,
"生命恢复速度": 1.0,
"最大护盾值": 0.0,
"当前护盾值": 0.0,
"护盾恢复速度": 0.0,
"最大护甲值": 121.00000000000003,
"当前护甲值": 121.00000000000003
"最大护甲值": 133.10000000000005,
"当前护甲值": 133.10000000000005
},
"基础攻击属性": {
"攻击类型": "MELEE",
"基础攻击伤害": 30.250000000000007,
"基础攻击伤害": 33.27500000000001,
"攻击距离": 100.0,
"暴击率": 0.1,
"暴击伤害倍数": 1.5,
@@ -1122,5 +1132,9 @@
"商品数量": 1
}
],
"小卖部格子数": 10
"小卖部格子数": 10,
"游戏设置": {
"背景音乐音量": 0.0,
"天气显示": true
}
}