diff --git a/Components/GameBGMPlayer.gd b/Components/GameBGMPlayer.gd index 2f763c6..c01f5c1 100644 --- a/Components/GameBGMPlayer.gd +++ b/Components/GameBGMPlayer.gd @@ -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() @@ -163,4 +172,71 @@ func add_music_file(file_path: String) -> bool: return true else: print("音乐文件不存在: ", file_path) - return false + 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 diff --git a/Components/GameBGMPlayer2.gd b/Components/GameBGMPlayer2.gd deleted file mode 100644 index d6341e5..0000000 --- a/Components/GameBGMPlayer2.gd +++ /dev/null @@ -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 "" diff --git a/Components/GameBGMPlayer2.gd.uid b/Components/GameBGMPlayer2.gd.uid deleted file mode 100644 index b511fa3..0000000 --- a/Components/GameBGMPlayer2.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cq0ug1c7nibf1 diff --git a/CopyItems/crop_item.tscn b/CopyItems/crop_item.tscn index 06dc577..dcab7c4 100644 --- a/CopyItems/crop_item.tscn +++ b/CopyItems/crop_item.tscn @@ -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") diff --git a/GUI/GameSettingPanel.gd b/GUI/GameSettingPanel.gd index 2628b13..672424a 100644 --- a/GUI/GameSettingPanel.gd +++ b/GUI/GameSettingPanel.gd @@ -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("已应用来自服务端的游戏设置") diff --git a/GUI/GameSettingPanel.tscn b/GUI/GameSettingPanel.tscn index 67e361b..61883ed 100644 --- a/GUI/GameSettingPanel.tscn +++ b/GUI/GameSettingPanel.tscn @@ -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 = "刷新" diff --git a/GUI/MainMenuPanel.gd b/GUI/MainMenuPanel.gd index 484a6ad..d5eab75 100644 --- a/GUI/MainMenuPanel.gd +++ b/GUI/MainMenuPanel.gd @@ -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 #游戏更新 diff --git a/GUI/MainMenuPanel.tscn b/GUI/MainMenuPanel.tscn index 8da9e46..27afb16 100644 --- a/GUI/MainMenuPanel.tscn +++ b/GUI/MainMenuPanel.tscn @@ -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 diff --git a/GameManager/DayNightSystem.gd b/GameManager/DayNightSystem.gd new file mode 100644 index 0000000..109e9c4 --- /dev/null +++ b/GameManager/DayNightSystem.gd @@ -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 diff --git a/GameManager/DayNightSystem.gd.uid b/GameManager/DayNightSystem.gd.uid new file mode 100644 index 0000000..34142fd --- /dev/null +++ b/GameManager/DayNightSystem.gd.uid @@ -0,0 +1 @@ +uid://di8wjflimodb0 diff --git a/GameManager/WeatherSystem.gd b/GameManager/WeatherSystem.gd index 138f18e..dd3f5fa 100644 --- a/GameManager/WeatherSystem.gd +++ b/GameManager/WeatherSystem.gd @@ -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: diff --git a/GlobalScript/GlobalVariables.gd b/GlobalScript/GlobalVariables.gd index 4800ec3..55aceef 100644 --- a/GlobalScript/GlobalVariables.gd +++ b/GlobalScript/GlobalVariables.gd @@ -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 #背景音乐音量 diff --git a/MainGame.gd b/MainGame.gd index c363721..8296125 100644 --- a/MainGame.gd +++ b/MainGame.gd @@ -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() diff --git a/MainGame.tscn b/MainGame.tscn index ab5478b..385fc98 100644 --- a/MainGame.tscn +++ b/MainGame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=96 format=3 uid="uid://dgh61dttaas5a"] +[gd_scene load_steps=97 format=3 uid="uid://dgh61dttaas5a"] [ext_resource type="Script" uid="uid://2pt11sfcaxf7" path="res://MainGame.gd" id="1_v3yaj"] [ext_resource type="Texture2D" uid="uid://du2pyiojliasy" path="res://assets/游戏UI/经验球.webp" id="2_6jgly"] @@ -39,6 +39,7 @@ [ext_resource type="Script" uid="uid://bvhupqlw2h1j8" path="res://Script/SmallPanel/DebugPanel.gd" id="28_8kysg"] [ext_resource type="Script" uid="uid://ca2chgx5w3g1y" path="res://Components/GameBGMPlayer.gd" id="28_m6fch"] [ext_resource type="Script" uid="uid://d4fvv2sjngajr" path="res://Script/Dialog/BatchBuyPopup.gd" id="29_5b81d"] +[ext_resource type="PackedScene" uid="uid://dos15dmc1b6bt" path="res://GUI/GameSettingPanel.tscn" id="30_game_setting"] [ext_resource type="Texture2D" uid="uid://2sdfbvf1isif" path="res://icon.svg" id="31_uc6q1"] [ext_resource type="Script" uid="uid://doo34ll0yb078" path="res://Script/SmallPanel/PetInformPanel.gd" id="31_vygm6"] [ext_resource type="Script" uid="uid://bakeq8tm6r4j4" path="res://Script/SmallPanel/GlobalServerBroadcastPanel.gd" id="34_k1sw4"] @@ -47,7 +48,6 @@ [ext_resource type="Texture2D" uid="uid://cbe0f8ex17csy" path="res://assets/稻草人图片/稻草人3.webp" id="37_6ylhg"] [ext_resource type="PackedScene" uid="uid://ibl5wbbw3pwc" path="res://CopyItems/item_button.tscn" id="39_cdkxt"] [ext_resource type="Script" uid="uid://dwf28j01ckg3y" path="res://Script/SmallPanel/WisdomTreePanel.gd" id="39_np7ck"] -[ext_resource type="Texture2D" uid="uid://bt1i2yhhlor5e" path="res://assets/地块/土块1.webp" id="41_cdkxt"] [ext_resource type="Script" uid="uid://b185o1hjnlrv5" path="res://Script/SmallPanel/CropInformPanel.gd" id="41_iluto"] [ext_resource type="Texture2D" uid="uid://3ff2lnbc0op7" path="res://assets/稻草人图片/稻草人1.webp" id="43_6rkns"] [ext_resource type="Script" uid="uid://dsmmxivba06ab" path="res://Script/Dialog/BatchSellPopup.gd" id="44_av1bx"] @@ -72,6 +72,7 @@ [ext_resource type="Texture2D" uid="uid://deow5dqdm412v" path="res://assets/装饰物图片/宠物商店.webp" id="57_rlmnt"] [ext_resource type="Script" uid="uid://o4mcuqoivqri" path="res://GameManager/WeatherSystem.gd" id="62_uyv6e"] [ext_resource type="Texture2D" uid="uid://bnv6wb0k443fv" path="res://assets/天气系统图片/柳叶2.webp" id="69_uyv6e"] +[ext_resource type="Script" uid="uid://di8wjflimodb0" path="res://GameManager/DayNightSystem.gd" id="73_6fhdl"] [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_adtqp"] @@ -145,16 +146,6 @@ border_width_top = 15 border_width_right = 15 border_width_bottom = 15 -[sub_resource type="Environment" id="Environment_m6fch"] -background_mode = 3 -ambient_light_energy = 0.0 -glow_intensity = 1.0 -glow_bloom = 0.3 -glow_blend_mode = 0 -glow_hdr_threshold = 2.0 -volumetric_fog_emission_energy = 281.25 -volumetric_fog_gi_inject = 1.38 - [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_6ylhg"] border_width_left = 10 border_width_top = 10 @@ -272,6 +263,15 @@ turbulence_noise_speed = Vector3(10, 0, 0) turbulence_influence_min = 0.02 turbulence_influence_max = 0.07 +[sub_resource type="Environment" id="Environment_uyv6e"] +background_mode = 3 +glow_enabled = true +glow_intensity = 1.0 +glow_strength = 0.75 +glow_bloom = 1.0 +glow_blend_mode = 1 +fog_enabled = true + [node name="main" type="Node"] script = ExtResource("1_v3yaj") @@ -678,7 +678,6 @@ theme_override_font_sizes/font_size = 40 text = "游玩小游戏" [node name="SettingButton" type="Button" parent="UI/GUI/OtherVBox"] -visible = false modulate = Color(0.345098, 0.764706, 1, 1) layout_mode = 2 theme_override_font_sizes/font_size = 40 @@ -912,6 +911,9 @@ visible = false [node name="LoginPanel" parent="UI/BigPanel" instance=ExtResource("11_6jgly")] +[node name="GameSettingPanel" parent="UI/BigPanel" instance=ExtResource("30_game_setting")] +visible = false + [node name="SmallPanel" type="CanvasLayer" parent="UI"] [node name="LoadProgressPanel" parent="UI/SmallPanel" instance=ExtResource("27_vygm6")] @@ -1866,7 +1868,6 @@ text = "取消" layer = -1 [node name="BackgroundSwitcher" type="Sprite2D" parent="BackgroundUI"] -self_modulate = Color(0.5, 0.5, 0.5, 1) show_behind_parent = true z_index = -100 z_as_relative = false @@ -1875,7 +1876,6 @@ scale = Vector2(0.92, 0.92) script = ExtResource("17_0igvr") [node name="Background2" type="Sprite2D" parent="BackgroundUI/BackgroundSwitcher"] -self_modulate = Color(0.5, 0.5, 0.5, 1) [node name="Timer" type="Timer" parent="BackgroundUI/BackgroundSwitcher"] @@ -1998,9 +1998,6 @@ bounds_enabled = true bounds_min = Vector2(-500, -400) bounds_max = Vector2(500, 500) -[node name="WorldEnvironment" type="WorldEnvironment" parent="."] -environment = SubResource("Environment_m6fch") - [node name="GameManager" type="Node" parent="."] [node name="GameBGMPlayer" type="Node" parent="."] @@ -2008,1292 +2005,6 @@ script = ExtResource("28_m6fch") play_mode = 1 music_files_list = Array[String](["res://assets/音乐/Anibli&RelaxingPianoMusic-StrollThroughtheSky.ogg", "res://assets/音乐/BanAM-Futatabi.ogg", "res://assets/音乐/MCMZebra-AlwaysandManyTimes.ogg", "res://assets/音乐/MicMusicbox-Ashitakasekki.ogg", "res://assets/音乐/Nemuネム-PromiseoftheWorld.ogg", "res://assets/音乐/α-WaveRelaxationHealingMusicLab-いつも何度でも[「千と千尋の神隠し」より][ピアノ].ogg", "res://assets/音乐/久石让-ふたたび.ogg", "res://assets/音乐/广桥真纪子-いのちの名前(生命之名).ogg", "res://assets/音乐/日本群星-PromiseoftheWorld.ogg"]) -[node name="GroundBackground" type="Node2D" parent="."] -visible = false -modulate = Color(0.929412, 1, 0.431373, 1) -position = Vector2(3786, -1016) -scale = Vector2(1.5, 1.5) - -[node name="土块1" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2203, 576) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块2" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2165, 598) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块3" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2240, 598) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块4" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2202, 620) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块5" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2127, 621) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块6" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2089, 643) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块7" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2164, 643) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块8" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2126, 665) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块9" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2278, 621) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块10" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2240, 643) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块11" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2315, 643) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块12" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2277, 665) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块13" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2202, 666) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块14" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2164, 688) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块15" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 688) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块16" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 710) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块17" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2052, 666) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块18" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2014, 688) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块19" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2089, 688) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块20" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2051, 710) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块21" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1976, 711) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块22" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1938, 733) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块23" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2013, 733) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块24" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1975, 755) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块25" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2127, 711) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块26" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2089, 733) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块27" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2164, 733) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块28" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2126, 755) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块29" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2051, 756) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块30" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2013, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块31" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2088, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块32" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2050, 800) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块33" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2353, 667) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块34" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2315, 689) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块35" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2390, 689) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块36" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2352, 711) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块37" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2277, 712) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块38" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 734) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块39" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 734) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块40" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 756) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块41" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2428, 712) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块42" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2390, 734) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块43" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2465, 734) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块44" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2427, 756) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块45" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2352, 757) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块46" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 779) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块47" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2389, 779) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块48" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2351, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块49" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2202, 757) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块50" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2164, 779) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块51" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 779) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块52" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块53" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2126, 802) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块54" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2088, 824) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块55" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 824) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块56" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2125, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块57" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2277, 802) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块58" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 824) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块59" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 824) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块60" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块61" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 847) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块62" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块63" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2238, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块64" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2200, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块65" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1900, 756) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块66" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1862, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块67" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1937, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块68" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1899, 800) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块69" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1824, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块70" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1786, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块71" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1861, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块72" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1823, 845) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块73" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1975, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块74" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1937, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块75" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2012, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块76" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1974, 845) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块77" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1899, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块78" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1861, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块79" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1936, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块80" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1898, 890) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块81" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1749, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块82" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1711, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块83" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1786, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块84" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1748, 890) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块85" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1673, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块86" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1635, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块87" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1710, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块88" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1672, 935) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块89" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1824, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块90" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1786, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块91" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1861, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块92" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1823, 935) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块93" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1748, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块94" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1710, 958) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块95" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1785, 958) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块96" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1747, 980) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块97" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2050, 847) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块98" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2012, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块99" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2087, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块100" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2049, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块101" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1974, 892) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块102" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1936, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块103" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2011, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块104" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1973, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块105" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2125, 892) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块106" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2087, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块107" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2162, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块108" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2124, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块109" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2049, 937) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块110" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2011, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块111" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2086, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块112" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2048, 981) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块113" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1899, 937) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块114" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1861, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块115" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1936, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块116" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1898, 981) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块117" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1823, 982) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块118" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1785, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块119" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1860, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块120" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1822, 1026) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块121" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1974, 982) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块122" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1936, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块123" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2011, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块124" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1973, 1026) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块125" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1898, 1027) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块126" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1860, 1049) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块127" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1935, 1049) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块128" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1897, 1071) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块129" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2504, 756) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块130" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2466, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块131" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2541, 778) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块132" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2503, 800) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块133" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2428, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块134" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2390, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块135" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2465, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块136" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2427, 845) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块137" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2579, 801) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块138" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2541, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块139" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2616, 823) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块140" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2578, 845) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块141" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2503, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块142" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2465, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块143" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2540, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块144" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2502, 890) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块145" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2353, 846) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块146" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2315, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块147" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2390, 868) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块148" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2352, 890) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块149" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2277, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块150" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块151" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块152" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 935) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块153" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2428, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块154" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2390, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块155" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2465, 913) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块156" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2427, 935) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块157" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2352, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块158" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 958) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块159" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2389, 958) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块160" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2351, 980) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块161" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2654, 847) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块162" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2616, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块163" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2691, 869) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块164" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2653, 891) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块165" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2578, 892) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块166" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2540, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块167" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2615, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块168" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2577, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块169" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2729, 892) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块170" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2691, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块171" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2766, 914) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块172" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2728, 936) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块173" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2653, 937) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块174" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2615, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块175" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2690, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块176" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2652, 981) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块177" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2503, 937) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块178" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2465, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块179" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2540, 959) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块180" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2502, 981) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块181" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2427, 982) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块182" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2389, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块183" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2464, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块184" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2426, 1026) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块185" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2578, 982) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块186" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2540, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块187" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2615, 1004) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块188" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2577, 1026) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块189" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2502, 1027) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块190" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2464, 1049) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块191" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2539, 1049) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块192" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2501, 1071) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块193" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2202, 935) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块194" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2164, 957) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块195" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 957) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块196" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 979) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块197" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2126, 980) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块198" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2088, 1002) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块199" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 1002) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块200" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2125, 1024) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块201" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2277, 980) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块202" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2239, 1002) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块203" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 1002) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块204" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 1024) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块205" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 1025) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块206" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 1047) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块207" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2238, 1047) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块208" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2200, 1069) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块209" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2051, 1025) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块210" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2013, 1047) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块211" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2088, 1047) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块212" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2050, 1069) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块213" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1975, 1070) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块214" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1937, 1092) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块215" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2012, 1092) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块216" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-1974, 1114) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块217" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2126, 1070) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块218" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2088, 1092) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块219" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 1092) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块220" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2125, 1114) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块221" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2050, 1115) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块222" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2012, 1137) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块223" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2087, 1137) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块224" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2049, 1159) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块225" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2352, 1026) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块226" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2314, 1048) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块227" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2389, 1048) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块228" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2351, 1070) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块229" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 1071) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块230" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2238, 1093) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块231" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2313, 1093) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块232" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2275, 1115) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块233" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2427, 1071) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块234" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2389, 1093) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块235" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2464, 1093) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块236" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2426, 1115) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块237" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2351, 1116) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块238" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2313, 1138) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块239" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2388, 1138) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块240" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2350, 1160) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块241" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2201, 1116) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块242" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2163, 1138) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块243" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2238, 1138) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块244" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2200, 1160) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块245" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2125, 1161) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块246" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2087, 1183) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块247" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2162, 1183) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块248" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2124, 1205) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块249" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2276, 1161) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块250" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2238, 1183) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块251" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2313, 1183) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块252" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2275, 1205) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块253" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2200, 1206) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块254" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2162, 1228) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块255" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2237, 1228) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - -[node name="土块256" type="Sprite2D" parent="GroundBackground"] -position = Vector2(-2199, 1250) -scale = Vector2(0.1, 0.1) -texture = ExtResource("41_cdkxt") - [node name="Decoration" type="Node2D" parent="."] [node name="ScareCrow" type="Button" parent="Decoration"] @@ -4837,6 +3548,13 @@ visibility_rect = Rect2(-900, 0, 2300, 2000) process_material = SubResource("ParticleProcessMaterial_nf3jg") [node name="DayNightSystem" type="Node2D" parent="."] +script = ExtResource("73_6fhdl") + +[node name="CanvasModulate" type="CanvasModulate" parent="DayNightSystem"] +visible = false + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_uyv6e") [connection signal="pressed" from="UI/GUI/GameInfoHBox3/WatchBroadcast" to="." method="_on_watch_broadcast_button_pressed"] [connection signal="pressed" from="UI/GUI/FarmVBox/SeedStoreButton" to="." method="_on_open_store_button_pressed"] diff --git a/Network/TCPNetworkManager.gd b/Network/TCPNetworkManager.gd index 9a997a1..6bda69e 100644 --- a/Network/TCPNetworkManager.gd +++ b/Network/TCPNetworkManager.gd @@ -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) # ============================= 客户端与服务端通信核心 ===================================== diff --git a/Script/BigPanel/LoginPanel.gd b/Script/BigPanel/LoginPanel.gd index 64f5672..e8029ff 100644 --- a/Script/BigPanel/LoginPanel.gd +++ b/Script/BigPanel/LoginPanel.gd @@ -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 diff --git a/Server/MongoDB迁移说明.md b/Server/MongoDB迁移说明.md new file mode 100644 index 0000000..3f5e345 --- /dev/null +++ b/Server/MongoDB迁移说明.md @@ -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文件的备选方案,为后续的全面迁移奠定了坚实的基础。 \ No newline at end of file diff --git a/Server/SMYMongoDBAPI.py b/Server/SMYMongoDBAPI.py new file mode 100644 index 0000000..5e1c7c6 --- /dev/null +++ b/Server/SMYMongoDBAPI.py @@ -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() \ No newline at end of file diff --git a/Server/TCPGameServer.py b/Server/TCPGameServer.py index 49d500b..614c02d 100644 --- a/Server/TCPGameServer.py +++ b/Server/TCPGameServer.py @@ -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) - - if is_weed: - 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 + is_weed = crop_info.get("是否杂草", False) + if is_weed: + return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能收获,只能铲除!请使用铲除功能清理杂草。") + + # 额外检查:如果作物收益为负数,也视为杂草 + crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0) + if crop_income < 0: + return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能收获,只能铲除!请使用铲除功能清理杂草。") + + # 获取作物经验 + crop_exp = crop_info.get("经验", 10) # 生成成熟物收获(1-5个) import random harvest_count = random.randint(1, 5) - 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) - - if is_weed: - 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 + is_weed = crop_info.get("是否杂草", False) + if is_weed: + return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能偷取,只能铲除!这是杂草,没有收益价值。") + + # 额外检查:如果作物收益为负数,也视为杂草 + crop_income = crop_info.get("收益", 100) + crop_info.get("花费", 0) + if crop_income < 0: + return self._send_action_error(client_id, "harvest_crop", f"{crop_type}不能偷取,只能铲除!这是杂草,没有收益价值。") + + # 获取作物经验(偷菜获得50%经验) + crop_exp = int(crop_info.get("经验", 10) * 0.5) # 生成成熟物收获(偷菜获得较少,1-3个) import random harvest_count = random.randint(1, 3) - 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,16 +1903,65 @@ 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']}" @@ -5417,7 +5578,8 @@ class TCPGameServer(TCPServer): stamina_system = player_data.get("体力系统", {}) current_stamina = stamina_system.get("当前体力值", 20) return current_stamina >= amount - + + def _check_and_update_register_time(self, player_data, username): """检查并更新已存在玩家的注册时间""" default_register_time = "2025年05月21日15时00分00秒" @@ -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,)) @@ -9584,4 +9942,4 @@ if __name__ == "__main__": except Exception as e: print(f"\n❌ 服务器启动失败: {str(e)}") print("🔧 请检查配置并重试") - sys.exit(1) \ No newline at end of file + sys.exit(1) \ No newline at end of file diff --git a/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc new file mode 100644 index 0000000..8eac005 Binary files /dev/null and b/Server/__pycache__/SMYMongoDBAPI.cpython-313.pyc differ diff --git a/Server/__pycache__/TCPGameServer.cpython-313.pyc b/Server/__pycache__/TCPGameServer.cpython-313.pyc index 74b2cb7..41499a1 100644 Binary files a/Server/__pycache__/TCPGameServer.cpython-313.pyc and b/Server/__pycache__/TCPGameServer.cpython-313.pyc differ diff --git a/Server/config/initial_player_data_template.json b/Server/config/initial_player_data_template.json index f847dd9..bb664df 100644 --- a/Server/config/initial_player_data_template.json +++ b/Server/config/initial_player_data_template.json @@ -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 + } } \ No newline at end of file diff --git a/Server/game_saves/2143323382.json b/Server/game_saves/2143323382.json index dcb9c04..66320b0 100644 --- a/Server/game_saves/2143323382.json +++ b/Server/game_saves/2143323382.json @@ -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 + } } \ No newline at end of file diff --git a/Server/test_mongodb_migration.py b/Server/test_mongodb_migration.py new file mode 100644 index 0000000..f9c25b1 --- /dev/null +++ b/Server/test_mongodb_migration.py @@ -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) \ No newline at end of file diff --git a/Server/test_server_mongodb.py b/Server/test_server_mongodb.py new file mode 100644 index 0000000..4d6034e --- /dev/null +++ b/Server/test_server_mongodb.py @@ -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) \ No newline at end of file diff --git a/__pycache__/SMYMongoDBAPI.cpython-313.pyc b/__pycache__/SMYMongoDBAPI.cpython-313.pyc new file mode 100644 index 0000000..767d4e9 Binary files /dev/null and b/__pycache__/SMYMongoDBAPI.cpython-313.pyc differ diff --git a/background.gd b/background.gd index 2e76580..26da36e 100644 --- a/background.gd +++ b/background.gd @@ -1,5 +1,5 @@ extends Sprite2D - +#昼夜循环只需调节该节点的modulate值即可 # 存储背景图片的路径数组 var backgrounds :Array = [ "res://assets/背景图片/背景1.webp", diff --git a/server/game_saves/3205788256.json b/server/game_saves/3205788256.json index a5f3a56..84ec0d9 100644 --- a/server/game_saves/3205788256.json +++ b/server/game_saves/3205788256.json @@ -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 + } } \ No newline at end of file