From 7aaed6393b93cb6cfde56193a82bf61f439be5a4 Mon Sep 17 00:00:00 2001 From: Voyager Date: Sun, 16 Aug 2026 20:53:15 -0400 Subject: [PATCH] fix: isolate controller settings side effects --- main/main.gd | 14 +++----------- network/player_data_root.gd | 4 ---- scripts/run_validations.sh | 2 ++ settings/player_settings_manager.gd | 12 ++++++++++++ ...ontroller_menu_accessibility_validation.gd | 19 +++++++++++++++++++ ui/settings_panel.gd | 5 ++++- 6 files changed, 40 insertions(+), 16 deletions(-) diff --git a/main/main.gd b/main/main.gd index 3a85307..9359d4f 100644 --- a/main/main.gd +++ b/main/main.gd @@ -773,7 +773,6 @@ func _initialize_application(dedicated: bool) -> void: _ui_pixelation.setup_controller_mapping(_controller_mapping_manager) _game_ui.setup_keyboard_mouse_mapping(_keyboard_mouse_mapping_manager) _data_root.conflict_detected.connect(_on_portable_conflict) - _data_root.status_changed.connect(_on_data_root_status) if ( PortableFileGuard.has_syncthing_conflict( _data_root.root_path.path_join("player") @@ -1103,11 +1102,7 @@ func _on_portable_conflict(message: String, _path: String) -> void: dialog.dialog_text = ( message + "\n\nDo not play the same profile on two devices at the same time." - ) - dialog.add_button("Open Data Folder", false, "open") - dialog.custom_action.connect(func(action: StringName) -> void: - if action == &"open": - _data_root.open_folder() + + "\n\nThe data folder can be opened from Settings > Data." ) dialog.confirmed.connect(dialog.queue_free) _game_ui.add_child(dialog) @@ -1121,11 +1116,8 @@ func _on_data_root_status(message: String) -> void: var dialog: AcceptDialog = AcceptDialog.new() _interface_fonts.apply_utility_theme(dialog) dialog.title = "Synced data needs review" - dialog.dialog_text = message - dialog.add_button("Open Data Folder", false, "open") - dialog.custom_action.connect(func(action: StringName) -> void: - if action == &"open": - _data_root.open_folder() + dialog.dialog_text = ( + message + "\n\nThe data folder can be opened from Settings > Data." ) dialog.confirmed.connect(dialog.queue_free) _game_ui.add_child(dialog) diff --git a/network/player_data_root.gd b/network/player_data_root.gd index 525c545..6eb5061 100644 --- a/network/player_data_root.gd +++ b/network/player_data_root.gd @@ -211,10 +211,6 @@ func storage_mode_text() -> String: }.get(mode, "Unavailable") -func open_folder() -> bool: - return not root_path.is_empty() and OS.shell_open(root_path) == OK - - func report_conflict(message: String, conflict_path: String) -> void: error_message = message conflict_detected.emit(message, conflict_path) diff --git a/scripts/run_validations.sh b/scripts/run_validations.sh index 912a59c..7fb248b 100755 --- a/scripts/run_validations.sh +++ b/scripts/run_validations.sh @@ -8,6 +8,8 @@ readonly GODOT_BIN="${GODOT_BIN:-godot}" readonly TEST_TIMEOUT_SECONDS="${TEST_TIMEOUT_SECONDS:-120}" readonly RUN_ROOT="$(mktemp -d -t netfishing-validations.XXXXXX)" +export NETFISHING_ISOLATED_VALIDATION_STORAGE=1 + readonly -a QUICK_TESTS=( "scripts/validate_animalese_samples.gd" "tests/android_readiness_validation.gd" diff --git a/settings/player_settings_manager.gd b/settings/player_settings_manager.gd index 84b3f44..7032c6f 100644 --- a/settings/player_settings_manager.gd +++ b/settings/player_settings_manager.gd @@ -6,6 +6,9 @@ const SETTINGS_PATH: String = "user://player_settings.json" const TEMP_PATH: String = "user://player_settings.json.tmp" const BACKUP_PATH: String = "user://player_settings.json.backup" const SILENT_VOLUME_DB: float = -80.0 +const HEADLESS_WRITE_ENVIRONMENT: String = ( + "NETFISHING_ISOLATED_VALIDATION_STORAGE" +) signal settings_changed(settings: PlayerSettings) @@ -197,6 +200,15 @@ func save_chat_preferences() -> bool: func save_now() -> bool: + if ( + OS.has_feature("editor") + and DisplayServer.get_name() == "headless" + and not OS.has_environment(HEADLESS_WRITE_ENVIRONMENT) + ): + push_error( + "Refusing to write player settings from an unisolated headless editor run." + ) + return false if current_settings == null or not current_settings.is_valid(): return false var data: Dictionary = { diff --git a/tests/controller_menu_accessibility_validation.gd b/tests/controller_menu_accessibility_validation.gd index 10e3624..f16e571 100644 --- a/tests/controller_menu_accessibility_validation.gd +++ b/tests/controller_menu_accessibility_validation.gd @@ -210,6 +210,25 @@ func _validate_join_game_navigation() -> void: func _validate_data_settings_navigation() -> void: + var main_source: String = FileAccess.get_file_as_string("res://main/main.gd") + var data_root_source: String = FileAccess.get_file_as_string( + "res://network/player_data_root.gd" + ) + var settings_source: String = FileAccess.get_file_as_string( + "res://ui/settings_panel.gd" + ) + _expect( + not main_source.contains("OS.shell_open") + and not main_source.contains( + "status_changed.connect(_on_data_root_status)" + ) + and not data_root_source.contains("OS.shell_open") + and settings_source.count("OS.shell_open") == 1, + ( + "Opening the data folder is not owned exclusively by the Settings " + + "Data action." + ), + ) var panel := SettingsPanelScene.instantiate() as SettingsPanel root.add_child(panel) await process_frame diff --git a/ui/settings_panel.gd b/ui/settings_panel.gd index f1c4707..28df7f1 100644 --- a/ui/settings_panel.gd +++ b/ui/settings_panel.gd @@ -623,7 +623,10 @@ func _open_data_folder() -> void: if _data_root == null: _feedback.text = "the data folder is unavailable." return - if not _data_root.open_folder(): + if ( + _data_root.root_path.is_empty() + or OS.shell_open(_data_root.root_path) != OK + ): _feedback.text = "could not open the data folder."