diff --git a/doc/classes/EditorPaths.xml b/doc/classes/EditorPaths.xml index eacb366d840..c4b9eda24cd 100644 --- a/doc/classes/EditorPaths.xml +++ b/doc/classes/EditorPaths.xml @@ -64,7 +64,9 @@ Returns [code]true[/code] if the editor is marked as self-contained, [code]false[/code] otherwise. When self-contained mode is enabled, user configuration, data and cache files are saved in an [code]editor_data/[/code] folder next to the editor binary. This makes portable usage easier and ensures the Godot editor minimizes file writes outside its own folder. Self-contained mode is not available for exported projects. - Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary while the editor is not running. See also [method get_self_contained_file]. + Self-contained mode can be enabled by creating a file named [code]._sc_[/code] or [code]_sc_[/code] in the same folder as the editor binary or macOS .app bundle while the editor is not running. See also [method get_self_contained_file]. + [b]Note:[/b] On macOS, quarantine flag should be manually removed before using self-contained mode, see [url=https://docs.godotengine.org/en/stable/tutorials/export/running_on_macos.html]Running on macOS[/url]. + [b]Note:[/b] On macOS, placing [code]_sc_[/code] or any other file inside .app bundle will break digital signature and make it non-portable, consider placing it in the same folder as the .app bundle instead. [b]Note:[/b] The Steam release of Godot uses self-contained mode by default. diff --git a/editor/editor_paths.cpp b/editor/editor_paths.cpp index 389c16fd66e..d5ba8418018 100644 --- a/editor/editor_paths.cpp +++ b/editor/editor_paths.cpp @@ -117,14 +117,20 @@ EditorPaths::EditorPaths() { // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir. String exe_path = OS::get_singleton()->get_executable_path().get_base_dir(); + Ref d = DirAccess::create_for_path(exe_path); + if (d->file_exists(exe_path + "/._sc_")) { + self_contained = true; + self_contained_file = exe_path + "/._sc_"; + } else if (d->file_exists(exe_path + "/_sc_")) { + self_contained = true; + self_contained_file = exe_path + "/_sc_"; + } // On macOS, look outside .app bundle, since .app bundle is read-only. + // Note: This will not work if Gatekeeper path randomization is active. if (OS::get_singleton()->has_feature("macos") && exe_path.ends_with("MacOS") && exe_path.path_join("..").simplify_path().ends_with("Contents")) { exe_path = exe_path.path_join("../../..").simplify_path(); - } - { - Ref d = DirAccess::create_for_path(exe_path); - + d = DirAccess::create_for_path(exe_path); if (d->file_exists(exe_path + "/._sc_")) { self_contained = true; self_contained_file = exe_path + "/._sc_"; diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp index d08dedcfcbf..bff7d04b551 100644 --- a/modules/mono/godotsharp_dirs.cpp +++ b/modules/mono/godotsharp_dirs.cpp @@ -66,23 +66,25 @@ String _get_mono_user_dir() { if (EditorPaths::get_singleton()) { return EditorPaths::get_singleton()->get_data_dir().path_join("mono"); } else { - String settings_path; + String settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name()); // Self-contained mode if a `._sc_` or `_sc_` file is present in executable dir. String exe_dir = OS::get_singleton()->get_executable_path().get_base_dir(); - - // On macOS, look outside .app bundle, since .app bundle is read-only. - if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) { - exe_dir = exe_dir.path_join("../../..").simplify_path(); - } - Ref d = DirAccess::create_for_path(exe_dir); - if (d->file_exists("._sc_") || d->file_exists("_sc_")) { // contain yourself settings_path = exe_dir.path_join("editor_data"); - } else { - settings_path = OS::get_singleton()->get_data_path().path_join(OS::get_singleton()->get_godot_dir_name()); + } + + // On macOS, look outside .app bundle, since .app bundle is read-only. + // Note: This will not work if Gatekeeper path randomization is active. + if (OS::get_singleton()->has_feature("macos") && exe_dir.ends_with("MacOS") && exe_dir.path_join("..").simplify_path().ends_with("Contents")) { + exe_dir = exe_dir.path_join("../../..").simplify_path(); + d = DirAccess::create_for_path(exe_dir); + if (d->file_exists("._sc_") || d->file_exists("_sc_")) { + // contain yourself + settings_path = exe_dir.path_join("editor_data"); + } } return settings_path.path_join("mono");