summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/BuildManager.cs13
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs22
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs6
-rw-r--r--modules/mono/editor/csharp_project.cpp2
-rw-r--r--modules/mono/editor/editor_internal_calls.cpp51
-rw-r--r--modules/mono/editor/godotsharp_export.cpp14
-rw-r--r--modules/mono/editor/godotsharp_export.h3
-rw-r--r--modules/mono/godotsharp_dirs.cpp18
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp288
-rw-r--r--modules/mono/mono_gd/gd_mono.h66
-rw-r--r--modules/mono/mono_gd/gd_mono_utils.cpp2
12 files changed, 305 insertions, 183 deletions
diff --git a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
index 417032da54..ab37d89955 100644
--- a/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/BuildManager.cs
@@ -160,9 +160,16 @@ namespace GodotTools
if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
return true; // No solution to build
- // Make sure to update the API assemblies if they happen to be missing. Just in
- // case the user decided to delete them at some point after they were loaded.
- Internal.UpdateApiAssembliesFromPrebuilt();
+ // Make sure the API assemblies are up to date before building the project.
+ // We may not have had the chance to update the release API assemblies, and the debug ones
+ // may have been deleted by the user at some point after they were loaded by the Godot editor.
+ string apiAssembliesUpdateError = Internal.UpdateApiAssembliesFromPrebuilt(config == "Release" ? "Release" : "Debug");
+
+ if (!string.IsNullOrEmpty(apiAssembliesUpdateError))
+ {
+ ShowBuildErrorDialog("Failed to update the Godot API assemblies");
+ return false;
+ }
var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
var buildTool = (BuildTool) editorSettings.GetSetting("mono/builds/build_tool");
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 7da7cff933..12edd651df 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -34,7 +34,7 @@ namespace GodotTools
private bool CreateProjectSolution()
{
- using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 2))
+ using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...".TTR(), 3))
{
pr.Step("Generating C# project...".TTR());
@@ -73,9 +73,23 @@ namespace GodotTools
return false;
}
- // Make sure to update the API assemblies if they happen to be missing. Just in
- // case the user decided to delete them at some point after they were loaded.
- Internal.UpdateApiAssembliesFromPrebuilt();
+ pr.Step("Updating Godot API assemblies...".TTR());
+
+ string debugApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Debug");
+
+ if (!string.IsNullOrEmpty(debugApiAssembliesError))
+ {
+ ShowErrorDialog("Failed to update the Godot API assemblies: " + debugApiAssembliesError);
+ return false;
+ }
+
+ string releaseApiAssembliesError = Internal.UpdateApiAssembliesFromPrebuilt("Release");
+
+ if (!string.IsNullOrEmpty(releaseApiAssembliesError))
+ {
+ ShowErrorDialog("Failed to update the Godot API assemblies: " + releaseApiAssembliesError);
+ return false;
+ }
pr.Step("Done".TTR());
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
index 9e24138143..01aa0d0ab1 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/GodotIdeManager.cs
@@ -40,8 +40,7 @@ namespace GodotTools.Ides
protected ILogger Logger
{
- get => logger ?? (logger = new ConsoleLogger());
- set => logger = value;
+ get => logger ?? (logger = new GodotLogger());
}
private void StartServer()
diff --git a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
index 7783576910..836c9c11e4 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
@@ -10,8 +10,8 @@ namespace GodotTools.Internals
public const string CSharpLanguageType = "CSharpScript";
public const string CSharpLanguageExtension = "cs";
- public static string UpdateApiAssembliesFromPrebuilt() =>
- internal_UpdateApiAssembliesFromPrebuilt();
+ public static string UpdateApiAssembliesFromPrebuilt(string config) =>
+ internal_UpdateApiAssembliesFromPrebuilt(config);
public static string FullTemplatesDir =>
internal_FullTemplatesDir();
@@ -55,7 +55,7 @@ namespace GodotTools.Internals
// Internal Calls
[MethodImpl(MethodImplOptions.InternalCall)]
- private static extern string internal_UpdateApiAssembliesFromPrebuilt();
+ private static extern string internal_UpdateApiAssembliesFromPrebuilt(string config);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern string internal_FullTemplatesDir();
diff --git a/modules/mono/editor/csharp_project.cpp b/modules/mono/editor/csharp_project.cpp
index 0e6c58c9d7..748447005f 100644
--- a/modules/mono/editor/csharp_project.cpp
+++ b/modules/mono/editor/csharp_project.cpp
@@ -75,7 +75,7 @@ bool generate_api_solution(const String &p_solution_dir, const String &p_core_pr
p_editor_proj_dir, p_editor_compile_items,
GDMono::get_singleton()->get_tools_project_editor_assembly());
} else {
- MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.ApiSolutionGenerationDomain");
+ MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ApiSolutionGeneration");
CRASH_COND(temp_domain == NULL);
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
diff --git a/modules/mono/editor/editor_internal_calls.cpp b/modules/mono/editor/editor_internal_calls.cpp
index 5a84d9e3b8..1564d73c2a 100644
--- a/modules/mono/editor/editor_internal_calls.cpp
+++ b/modules/mono/editor/editor_internal_calls.cpp
@@ -230,31 +230,9 @@ uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString
return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);
}
-float godot_icall_Globals_EditorScale() {
- return EDSCALE;
-}
-
-MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
- Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
-}
-
-MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
- String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
- Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
- Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
- return GDMonoMarshal::variant_to_mono_object(result);
-}
-
-MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
- String text = GDMonoMarshal::mono_string_to_godot(p_text);
- return GDMonoMarshal::mono_string_from_godot(TTR(text));
-}
-
-MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt() {
- String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt();
+MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {
+ String config = GDMonoMarshal::mono_string_to_godot(p_config);
+ String error_str = GDMono::get_singleton()->update_api_assemblies_from_prebuilt(config);
return GDMonoMarshal::mono_string_from_godot(error_str);
}
@@ -365,6 +343,29 @@ void godot_icall_Internal_ScriptEditorDebugger_ReloadScripts() {
}
}
+float godot_icall_Globals_EditorScale() {
+ return EDSCALE;
+}
+
+MonoObject *godot_icall_Globals_GlobalDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
+ String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+ Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+ Variant result = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
+ return GDMonoMarshal::variant_to_mono_object(result);
+}
+
+MonoObject *godot_icall_Globals_EditorDef(MonoString *p_setting, MonoObject *p_default_value, MonoBoolean p_restart_if_changed) {
+ String setting = GDMonoMarshal::mono_string_to_godot(p_setting);
+ Variant default_value = GDMonoMarshal::mono_object_to_variant(p_default_value);
+ Variant result = _EDITOR_DEF(setting, default_value, (bool)p_restart_if_changed);
+ return GDMonoMarshal::variant_to_mono_object(result);
+}
+
+MonoString *godot_icall_Globals_TTR(MonoString *p_text) {
+ String text = GDMonoMarshal::mono_string_to_godot(p_text);
+ return GDMonoMarshal::mono_string_from_godot(TTR(text));
+}
+
MonoString *godot_icall_Utils_OS_GetPlatformName() {
String os_name = OS::get_singleton()->get_name();
return GDMonoMarshal::mono_string_from_godot(os_name);
diff --git a/modules/mono/editor/godotsharp_export.cpp b/modules/mono/editor/godotsharp_export.cpp
index 80a7335b1d..e83152d668 100644
--- a/modules/mono/editor/godotsharp_export.cpp
+++ b/modules/mono/editor/godotsharp_export.cpp
@@ -32,9 +32,13 @@
#include <mono/metadata/image.h>
+#include "core/os/os.h"
+
#include "../mono_gd/gd_mono.h"
#include "../mono_gd/gd_mono_assembly.h"
+namespace GodotSharpExport {
+
String get_assemblyref_name(MonoImage *p_image, int index) {
const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
@@ -45,7 +49,7 @@ String get_assemblyref_name(MonoImage *p_image, int index) {
return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
}
-Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
+Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
MonoImage *image = p_assembly->get_image();
for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
@@ -96,8 +100,8 @@ Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, co
return OK;
}
-Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies) {
- MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
+Error get_exported_assembly_dependencies(const String &p_project_dll_name, const String &p_project_dll_src_path, const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
+ MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
ERR_FAIL_NULL_V(export_domain, FAILED);
_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
@@ -110,7 +114,9 @@ Error GodotSharpExport::get_exported_assembly_dependencies(const String &p_proje
ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + p_project_dll_name + "'.");
Vector<String> search_dirs;
- GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir);
+ GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);
}
+
+} // namespace GodotSharpExport
diff --git a/modules/mono/editor/godotsharp_export.h b/modules/mono/editor/godotsharp_export.h
index 8d121a6bc3..58e46e2f2d 100644
--- a/modules/mono/editor/godotsharp_export.h
+++ b/modules/mono/editor/godotsharp_export.h
@@ -39,10 +39,11 @@
namespace GodotSharpExport {
+Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
+
Error get_exported_assembly_dependencies(const String &p_project_dll_name,
const String &p_project_dll_src_path, const String &p_build_config,
const String &p_custom_lib_dir, Dictionary &r_dependencies);
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
} // namespace GodotSharpExport
diff --git a/modules/mono/godotsharp_dirs.cpp b/modules/mono/godotsharp_dirs.cpp
index 4b2525c692..5fa8aed5a9 100644
--- a/modules/mono/godotsharp_dirs.cpp
+++ b/modules/mono/godotsharp_dirs.cpp
@@ -43,6 +43,8 @@
#include "utils/android_utils.h"
#endif
+#include "mono_gd/gd_mono.h"
+
namespace GodotSharpDirs {
String _get_expected_build_config() {
@@ -59,20 +61,6 @@ String _get_expected_build_config() {
#endif
}
-String _get_expected_api_build_config() {
-#ifdef TOOLS_ENABLED
- return "Debug";
-#else
-
-#ifdef DEBUG_ENABLED
- return "Debug";
-#else
- return "Release";
-#endif
-
-#endif
-}
-
String _get_mono_user_dir() {
#ifdef TOOLS_ENABLED
if (EditorSettings::get_singleton()) {
@@ -134,7 +122,7 @@ private:
res_data_dir = "res://.mono";
res_metadata_dir = res_data_dir.plus_file("metadata");
res_assemblies_base_dir = res_data_dir.plus_file("assemblies");
- res_assemblies_dir = res_assemblies_base_dir.plus_file(_get_expected_api_build_config());
+ res_assemblies_dir = res_assemblies_base_dir.plus_file(GDMono::get_expected_api_build_config());
res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
// TODO use paths from csproj
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 544bfc4615..0a34404154 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -381,10 +381,10 @@ void GDMono::initialize_load_assemblies() {
}
bool GDMono::_are_api_assemblies_out_of_sync() {
- bool out_of_sync = core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);
+ bool out_of_sync = core_api_assembly.assembly && (core_api_assembly.out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated);
#ifdef TOOLS_ENABLED
if (!out_of_sync)
- out_of_sync = editor_api_assembly && editor_api_assembly_out_of_sync;
+ out_of_sync = editor_api_assembly.assembly && editor_api_assembly.out_of_sync;
#endif
return out_of_sync;
}
@@ -523,10 +523,10 @@ bool GDMono::load_assembly_from(const String &p_name, const String &p_path, GDMo
return true;
}
-APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, APIAssembly::Type p_api_type) {
- APIAssembly::Version api_assembly_version;
+ApiAssemblyInfo::Version ApiAssemblyInfo::Version::get_from_loaded_assembly(GDMonoAssembly *p_api_assembly, ApiAssemblyInfo::Type p_api_type) {
+ ApiAssemblyInfo::Version api_assembly_version;
- const char *nativecalls_name = p_api_type == APIAssembly::API_CORE ?
+ const char *nativecalls_name = p_api_type == ApiAssemblyInfo::API_CORE ?
BINDINGS_CLASS_NATIVECALLS :
BINDINGS_CLASS_NATIVECALLS_EDITOR;
@@ -549,8 +549,8 @@ APIAssembly::Version APIAssembly::Version::get_from_loaded_assembly(GDMonoAssemb
return api_assembly_version;
}
-String APIAssembly::to_string(APIAssembly::Type p_type) {
- return p_type == APIAssembly::API_CORE ? "API_CORE" : "API_EDITOR";
+String ApiAssemblyInfo::to_string(ApiAssemblyInfo::Type p_type) {
+ return p_type == ApiAssemblyInfo::API_CORE ? "API_CORE" : "API_EDITOR";
}
bool GDMono::_load_corlib_assembly() {
@@ -567,16 +567,12 @@ bool GDMono::_load_corlib_assembly() {
}
#ifdef TOOLS_ENABLED
-bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config) {
-
- bool &api_assembly_out_of_sync = (p_api_type == APIAssembly::API_CORE) ?
- GDMono::get_singleton()->core_api_assembly_out_of_sync :
- GDMono::get_singleton()->editor_api_assembly_out_of_sync;
+bool GDMono::copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config) {
String src_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String dst_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
- String assembly_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
+ String assembly_name = p_api_type == ApiAssemblyInfo::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
// Create destination directory if needed
if (!DirAccess::exists(dst_dir)) {
@@ -590,35 +586,102 @@ bool GDMono::copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const Stri
}
}
+ DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+ String xml_file = assembly_name + ".xml";
+ if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK)
+ WARN_PRINTS("Failed to copy '" + xml_file + "'.");
+
+ String pdb_file = assembly_name + ".pdb";
+ if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK)
+ WARN_PRINTS("Failed to copy '" + pdb_file + "'.");
+
String assembly_file = assembly_name + ".dll";
- String assembly_src = src_dir.plus_file(assembly_file);
- String assembly_dst = dst_dir.plus_file(assembly_file);
+ if (da->copy(src_dir.plus_file(assembly_file), dst_dir.plus_file(assembly_file)) != OK) {
+ ERR_PRINTS("Failed to copy '" + assembly_file + "'.");
+ return false;
+ }
- if (!FileAccess::exists(assembly_dst) || api_assembly_out_of_sync) {
- DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+ return true;
+}
- String xml_file = assembly_name + ".xml";
- if (da->copy(src_dir.plus_file(xml_file), dst_dir.plus_file(xml_file)) != OK)
- WARN_PRINTS("Failed to copy '" + xml_file + "'.");
+static bool try_get_cached_api_hash_for(const String &p_api_assemblies_dir, bool &r_out_of_sync) {
+ String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+ String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
- String pdb_file = assembly_name + ".pdb";
- if (da->copy(src_dir.plus_file(pdb_file), dst_dir.plus_file(pdb_file)) != OK)
- WARN_PRINTS("Failed to copy '" + pdb_file + "'.");
+ if (!FileAccess::exists(core_api_assembly_path) || !FileAccess::exists(editor_api_assembly_path))
+ return false;
- Error err = da->copy(assembly_src, assembly_dst);
+ String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
- if (err != OK) {
- ERR_PRINTS("Failed to copy '" + assembly_file + "'.");
- return false;
- }
+ if (!FileAccess::exists(cached_api_hash_path))
+ return false;
+
+ Ref<ConfigFile> cfg;
+ cfg.instance();
+ Error cfg_err = cfg->load(cached_api_hash_path);
+ ERR_FAIL_COND_V(cfg_err != OK, false);
- api_assembly_out_of_sync = false;
+ // Checking the modified time is good enough
+ if (FileAccess::get_modified_time(core_api_assembly_path) != (uint64_t)cfg->get_value("core", "modified_time") ||
+ FileAccess::get_modified_time(editor_api_assembly_path) != (uint64_t)cfg->get_value("editor", "modified_time")) {
+ return false;
}
+ r_out_of_sync = GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("core", "bindings_version") ||
+ GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("core", "cs_glue_version") ||
+ GodotSharpBindings::get_bindings_version() != (uint32_t)cfg->get_value("editor", "bindings_version") ||
+ GodotSharpBindings::get_cs_glue_version() != (uint32_t)cfg->get_value("editor", "cs_glue_version") ||
+ GodotSharpBindings::get_core_api_hash() != (uint64_t)cfg->get_value("core", "api_hash") ||
+ GodotSharpBindings::get_editor_api_hash() != (uint64_t)cfg->get_value("editor", "api_hash");
+
return true;
}
-String GDMono::update_api_assemblies_from_prebuilt() {
+static void create_cached_api_hash_for(const String &p_api_assemblies_dir) {
+
+ String core_api_assembly_path = p_api_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+ String editor_api_assembly_path = p_api_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ String cached_api_hash_path = p_api_assemblies_dir.plus_file("api_hash_cache.cfg");
+
+ Ref<ConfigFile> cfg;
+ cfg.instance();
+
+ cfg->set_value("core", "modified_time", FileAccess::get_modified_time(core_api_assembly_path));
+ cfg->set_value("editor", "modified_time", FileAccess::get_modified_time(editor_api_assembly_path));
+
+ cfg->set_value("core", "bindings_version", GodotSharpBindings::get_bindings_version());
+ cfg->set_value("core", "cs_glue_version", GodotSharpBindings::get_cs_glue_version());
+ cfg->set_value("editor", "bindings_version", GodotSharpBindings::get_bindings_version());
+ cfg->set_value("editor", "cs_glue_version", GodotSharpBindings::get_cs_glue_version());
+
+ // This assumes the prebuilt api assemblies we copied to the project are not out of sync
+ cfg->set_value("core", "api_hash", GodotSharpBindings::get_core_api_hash());
+ cfg->set_value("editor", "api_hash", GodotSharpBindings::get_editor_api_hash());
+
+ Error err = cfg->save(cached_api_hash_path);
+ ERR_FAIL_COND(err != OK);
+}
+
+bool GDMono::_temp_domain_load_are_assemblies_out_of_sync(const String &p_config) {
+ MonoDomain *temp_domain = GDMonoUtils::create_domain("GodotEngine.Domain.CheckApiAssemblies");
+ ERR_FAIL_NULL_V(temp_domain, "Failed to create temporary domain to check API assemblies");
+ _GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
+
+ _GDMONO_SCOPE_DOMAIN_(temp_domain);
+
+ GDMono::LoadedApiAssembly temp_core_api_assembly;
+ GDMono::LoadedApiAssembly temp_editor_api_assembly;
+
+ if (!_try_load_api_assemblies(temp_core_api_assembly, temp_editor_api_assembly,
+ p_config, /* refonly: */ true, /* loaded_callback: */ NULL)) {
+ return temp_core_api_assembly.out_of_sync || temp_editor_api_assembly.out_of_sync;
+ }
+
+ return true; // Failed to load, assume they're outdated assemblies
+}
+
+String GDMono::update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync, const bool *p_editor_api_out_of_sync) {
#define FAIL_REASON(m_out_of_sync, m_prebuilt_exists) \
( \
@@ -629,46 +692,55 @@ String GDMono::update_api_assemblies_from_prebuilt() {
String("and the prebuilt assemblies are missing.") : \
String("and we failed to copy the prebuilt assemblies.")))
- bool api_assembly_out_of_sync = core_api_assembly_out_of_sync || editor_api_assembly_out_of_sync;
+ String dst_assemblies_dir = GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config);
- String core_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String editor_assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ String core_assembly_path = dst_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+ String editor_assembly_path = dst_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
- if (!api_assembly_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path))
- return String(); // No update needed
+ bool api_assemblies_out_of_sync = false;
- const int CONFIGS_LEN = 2;
- String configs[CONFIGS_LEN] = { String("Debug"), String("Release") };
+ if (p_core_api_out_of_sync && p_editor_api_out_of_sync) {
+ api_assemblies_out_of_sync = p_core_api_out_of_sync || p_editor_api_out_of_sync;
+ } else if (FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path)) {
+ // Determine if they're out of sync
+ if (!try_get_cached_api_hash_for(dst_assemblies_dir, api_assemblies_out_of_sync)) {
+ api_assemblies_out_of_sync = _temp_domain_load_are_assemblies_out_of_sync(p_config);
+ }
+ }
- for (int i = 0; i < CONFIGS_LEN; i++) {
- String config = configs[i];
+ // Note: Even if only one of the assemblies if missing or out of sync, we update both
- print_verbose("Updating '" + config + "' API assemblies");
+ if (!api_assemblies_out_of_sync && FileAccess::exists(core_assembly_path) && FileAccess::exists(editor_assembly_path))
+ return String(); // No update needed
- String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(config);
- String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
- String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+ print_verbose("Updating '" + p_config + "' API assemblies");
- if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) {
- return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ false);
- }
+ String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
+ String prebuilt_core_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+ String prebuilt_editor_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
- // Copy the prebuilt Api
- if (!copy_prebuilt_api_assembly(APIAssembly::API_CORE, config) ||
- !copy_prebuilt_api_assembly(APIAssembly::API_EDITOR, config)) {
- return FAIL_REASON(api_assembly_out_of_sync, /* prebuilt_exists: */ true);
- }
+ if (!FileAccess::exists(prebuilt_core_dll_path) || !FileAccess::exists(prebuilt_editor_dll_path)) {
+ return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ false);
}
+ // Copy the prebuilt Api
+ if (!copy_prebuilt_api_assembly(ApiAssemblyInfo::API_CORE, p_config) ||
+ !copy_prebuilt_api_assembly(ApiAssemblyInfo::API_EDITOR, p_config)) {
+ return FAIL_REASON(api_assemblies_out_of_sync, /* prebuilt_exists: */ true);
+ }
+
+ // Cache the api hash of the assemblies we just copied
+ create_cached_api_hash_for(dst_assemblies_dir);
+
return String(); // Updated successfully
#undef FAIL_REASON
}
#endif
-bool GDMono::_load_core_api_assembly() {
+bool GDMono::_load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (core_api_assembly)
+ if (r_loaded_api_assembly.assembly)
return true;
#ifdef TOOLS_ENABLED
@@ -676,101 +748,115 @@ bool GDMono::_load_core_api_assembly() {
// If running the project manager, load it from the prebuilt API directory
String assembly_dir = !Main::is_project_manager() ?
- GodotSharpDirs::get_res_assemblies_dir() :
- GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+ GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
+ GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly);
+ load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
#else
- bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
+ bool success = load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly, p_refonly);
#endif
if (success) {
- APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(core_api_assembly, APIAssembly::API_CORE);
- core_api_assembly_out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
- if (!core_api_assembly_out_of_sync) {
- GDMonoUtils::update_godot_api_cache();
-
- _install_trace_listener();
- }
+ ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_CORE);
+ r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_core_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
} else {
- core_api_assembly_out_of_sync = false;
+ r_loaded_api_assembly.out_of_sync = false;
}
return success;
}
#ifdef TOOLS_ENABLED
-bool GDMono::_load_editor_api_assembly() {
+bool GDMono::_load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly) {
- if (editor_api_assembly)
+ if (r_loaded_api_assembly.assembly)
return true;
// For the editor and the editor player we want to load it from a specific path to make sure we can keep it up to date
// If running the project manager, load it from the prebuilt API directory
String assembly_dir = !Main::is_project_manager() ?
- GodotSharpDirs::get_res_assemblies_dir() :
- GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+ GodotSharpDirs::get_res_assemblies_base_dir().plus_file(p_config) :
+ GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file(p_config);
String assembly_path = assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
bool success = FileAccess::exists(assembly_path) &&
- load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly);
+ load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &r_loaded_api_assembly.assembly, p_refonly);
if (success) {
- APIAssembly::Version api_assembly_ver = APIAssembly::Version::get_from_loaded_assembly(editor_api_assembly, APIAssembly::API_EDITOR);
- editor_api_assembly_out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
- GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
- GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
+ ApiAssemblyInfo::Version api_assembly_ver = ApiAssemblyInfo::Version::get_from_loaded_assembly(r_loaded_api_assembly.assembly, ApiAssemblyInfo::API_EDITOR);
+ r_loaded_api_assembly.out_of_sync = GodotSharpBindings::get_editor_api_hash() != api_assembly_ver.godot_api_hash ||
+ GodotSharpBindings::get_bindings_version() != api_assembly_ver.bindings_version ||
+ GodotSharpBindings::get_cs_glue_version() != api_assembly_ver.cs_glue_version;
} else {
- editor_api_assembly_out_of_sync = false;
+ r_loaded_api_assembly.out_of_sync = false;
}
return success;
}
#endif
-bool GDMono::_try_load_api_assemblies() {
-
- if (!_load_core_api_assembly()) {
+bool GDMono::_try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
+ const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback) {
+ if (!_load_core_api_assembly(r_core_api_assembly, p_config, p_refonly)) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Core API assembly");
return false;
}
#ifdef TOOLS_ENABLED
- if (!_load_editor_api_assembly()) {
+ if (!_load_editor_api_assembly(r_editor_api_assembly, p_config, p_refonly)) {
if (OS::get_singleton()->is_stdout_verbose())
print_error("Mono: Failed to load Editor API assembly");
return false;
}
- if (editor_api_assembly_out_of_sync)
+ if (r_editor_api_assembly.out_of_sync)
return false;
#endif
// Check if the core API assembly is out of sync only after trying to load the
// editor API assembly. Otherwise, if both assemblies are out of sync, we would
// only update the former as we won't know the latter also needs to be updated.
- if (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated)
+ if (r_core_api_assembly.out_of_sync)
return false;
+ if (p_callback)
+ return p_callback();
+
return true;
}
+bool GDMono::_on_core_api_assembly_loaded() {
+ GDMonoUtils::update_godot_api_cache();
+
+ if (!GDMonoUtils::mono_cache.godot_api_cache_updated)
+ return false;
+
+ get_singleton()->_install_trace_listener();
+
+ return true;
+}
+
+bool GDMono::_try_load_api_assemblies_preset() {
+ return _try_load_api_assemblies(core_api_assembly, editor_api_assembly,
+ get_expected_api_build_config(), /* refonly: */ false, _on_core_api_assembly_loaded);
+}
+
void GDMono::_load_api_assemblies() {
- bool api_assemblies_loaded = _try_load_api_assemblies();
+ bool api_assemblies_loaded = _try_load_api_assemblies_preset();
if (!api_assemblies_loaded) {
#ifdef TOOLS_ENABLED
- // The API assemblies are out of sync. Fine, try one more time, but this time
- // update them from the prebuilt assemblies directory before trying to load them.
+ // The API assemblies are out of sync or some other error happened. Fine, try one more time, but
+ // this time update them from the prebuilt assemblies directory before trying to load them again.
// Shouldn't happen. The project manager loads the prebuilt API assemblies
CRASH_COND_MSG(Main::is_project_manager(), "Failed to load one of the prebuilt API assemblies.");
@@ -780,7 +866,7 @@ void GDMono::_load_api_assemblies() {
CRASH_COND_MSG(domain_unload_err != OK, "Mono: Failed to unload scripts domain.");
// 2. Update the API assemblies
- String update_error = update_api_assemblies_from_prebuilt();
+ String update_error = update_api_assemblies_from_prebuilt("Debug", &core_api_assembly.out_of_sync, &editor_api_assembly.out_of_sync);
CRASH_COND_MSG(!update_error.empty(), update_error);
// 3. Load the scripts domain again
@@ -788,7 +874,7 @@ void GDMono::_load_api_assemblies() {
CRASH_COND_MSG(domain_load_err != OK, "Mono: Failed to load scripts domain.");
// 4. Try loading the updated assemblies
- api_assemblies_loaded = _try_load_api_assemblies();
+ api_assemblies_loaded = _try_load_api_assemblies_preset();
#endif
}
@@ -796,14 +882,14 @@ void GDMono::_load_api_assemblies() {
// welp... too bad
if (_are_api_assemblies_out_of_sync()) {
- if (core_api_assembly_out_of_sync) {
+ if (core_api_assembly.out_of_sync) {
ERR_PRINT("The assembly '" CORE_API_ASSEMBLY_NAME "' is out of sync.");
} else if (!GDMonoUtils::mono_cache.godot_api_cache_updated) {
ERR_PRINT("The loaded assembly '" CORE_API_ASSEMBLY_NAME "' is in sync, but the cache update failed.");
}
#ifdef TOOLS_ENABLED
- if (editor_api_assembly_out_of_sync) {
+ if (editor_api_assembly.out_of_sync) {
ERR_PRINT("The assembly '" EDITOR_API_ASSEMBLY_NAME "' is out of sync.");
}
#endif
@@ -852,15 +938,14 @@ void GDMono::_install_trace_listener() {
#ifdef DEBUG_ENABLED
// Install the trace listener now before the project assembly is loaded
- typedef void (*DebuggingUtils_InstallTraceListener)(MonoObject **);
+ GDMonoClass *debug_utils = get_core_api_assembly()->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
+ GDMonoMethod *install_func = debug_utils->get_method("InstallTraceListener");
+
MonoException *exc = NULL;
- GDMonoClass *debug_utils = core_api_assembly->get_class(BINDINGS_NAMESPACE, "DebuggingUtils");
- DebuggingUtils_InstallTraceListener install_func =
- (DebuggingUtils_InstallTraceListener)debug_utils->get_method_thunk("InstallTraceListener");
- install_func((MonoObject **)&exc);
+ install_func->invoke_raw(NULL, NULL, &exc);
if (exc) {
- ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
GDMonoUtils::debug_print_unhandled_exception(exc);
+ ERR_PRINT("Failed to install 'System.Diagnostics.Trace' listener.");
}
#endif
}
@@ -871,7 +956,7 @@ Error GDMono::_load_scripts_domain() {
print_verbose("Mono: Loading scripts domain...");
- scripts_domain = GDMonoUtils::create_domain("GodotEngine.ScriptsDomain");
+ scripts_domain = GDMonoUtils::create_domain("GodotEngine.Domain.Scripts");
ERR_FAIL_NULL_V_MSG(scripts_domain, ERR_CANT_CREATE, "Mono: Could not create scripts app domain.");
@@ -903,10 +988,8 @@ Error GDMono::_unload_scripts_domain() {
_domain_assemblies_cleanup(mono_domain_get_id(scripts_domain));
- core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
- editor_api_assembly = NULL;
tools_assembly = NULL;
tools_project_editor_assembly = NULL;
#endif
@@ -1076,16 +1159,9 @@ GDMono::GDMono() {
root_domain = NULL;
scripts_domain = NULL;
- core_api_assembly_out_of_sync = false;
-#ifdef TOOLS_ENABLED
- editor_api_assembly_out_of_sync = false;
-#endif
-
corlib_assembly = NULL;
- core_api_assembly = NULL;
project_assembly = NULL;
#ifdef TOOLS_ENABLED
- editor_api_assembly = NULL;
tools_assembly = NULL;
tools_project_editor_assembly = NULL;
#endif
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 343d68bc2d..e14a0d8409 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -41,7 +41,7 @@
#include "../utils/mono_reg_utils.h"
#endif
-namespace APIAssembly {
+namespace ApiAssemblyInfo {
enum Type {
API_CORE,
API_EDITOR
@@ -76,7 +76,7 @@ struct Version {
};
String to_string(Type p_type);
-} // namespace APIAssembly
+} // namespace ApiAssemblyInfo
class GDMono {
@@ -86,44 +86,58 @@ public:
POLICY_LOG_ERROR
};
+ struct LoadedApiAssembly {
+ GDMonoAssembly *assembly;
+ bool out_of_sync;
+
+ LoadedApiAssembly() :
+ assembly(NULL),
+ out_of_sync(false) {
+ }
+ };
+
private:
bool runtime_initialized;
bool finalizing_scripts_domain;
+ UnhandledExceptionPolicy unhandled_exception_policy;
+
MonoDomain *root_domain;
MonoDomain *scripts_domain;
- bool core_api_assembly_out_of_sync;
-#ifdef TOOLS_ENABLED
- bool editor_api_assembly_out_of_sync;
-#endif
+ HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
GDMonoAssembly *corlib_assembly;
- GDMonoAssembly *core_api_assembly;
GDMonoAssembly *project_assembly;
#ifdef TOOLS_ENABLED
- GDMonoAssembly *editor_api_assembly;
GDMonoAssembly *tools_assembly;
GDMonoAssembly *tools_project_editor_assembly;
#endif
- HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
-
- UnhandledExceptionPolicy unhandled_exception_policy;
+ LoadedApiAssembly core_api_assembly;
+ LoadedApiAssembly editor_api_assembly;
- void _domain_assemblies_cleanup(uint32_t p_domain_id);
+ typedef bool (*CoreApiAssemblyLoadedCallback)();
bool _are_api_assemblies_out_of_sync();
+ bool _temp_domain_load_are_assemblies_out_of_sync(const String &p_config);
+
+ bool _load_core_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
+#ifdef TOOLS_ENABLED
+ bool _load_editor_api_assembly(LoadedApiAssembly &r_loaded_api_assembly, const String &p_config, bool p_refonly);
+#endif
+
+ static bool _on_core_api_assembly_loaded();
bool _load_corlib_assembly();
- bool _load_core_api_assembly();
#ifdef TOOLS_ENABLED
- bool _load_editor_api_assembly();
bool _load_tools_assemblies();
#endif
bool _load_project_assembly();
- bool _try_load_api_assemblies();
+ bool _try_load_api_assemblies(LoadedApiAssembly &r_core_api_assembly, LoadedApiAssembly &r_editor_api_assembly,
+ const String &p_config, bool p_refonly, CoreApiAssemblyLoadedCallback p_callback);
+ bool _try_load_api_assemblies_preset();
void _load_api_assemblies();
void _install_trace_listener();
@@ -133,6 +147,8 @@ private:
Error _load_scripts_domain();
Error _unload_scripts_domain();
+ void _domain_assemblies_cleanup(uint32_t p_domain_id);
+
uint64_t api_core_hash;
#ifdef TOOLS_ENABLED
uint64_t api_editor_hash;
@@ -166,9 +182,21 @@ public:
#endif // TOOLS_ENABLED
#endif // DEBUG_METHODS_ENABLED
+ _FORCE_INLINE_ static String get_expected_api_build_config() {
+#ifdef TOOLS_ENABLED
+ return "Debug";
+#else
+#ifdef DEBUG_ENABLED
+ return "Debug";
+#else
+ return "Release";
+#endif
+#endif
+ }
+
#ifdef TOOLS_ENABLED
- bool copy_prebuilt_api_assembly(APIAssembly::Type p_api_type, const String &p_config);
- String update_api_assemblies_from_prebuilt();
+ bool copy_prebuilt_api_assembly(ApiAssemblyInfo::Type p_api_type, const String &p_config);
+ String update_api_assemblies_from_prebuilt(const String &p_config, const bool *p_core_api_out_of_sync = NULL, const bool *p_editor_api_out_of_sync = NULL);
#endif
static GDMono *get_singleton() { return singleton; }
@@ -188,10 +216,10 @@ public:
_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
- _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_core_api_assembly() const { return core_api_assembly.assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
#ifdef TOOLS_ENABLED
- _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly; }
+ _FORCE_INLINE_ GDMonoAssembly *get_editor_api_assembly() const { return editor_api_assembly.assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_assembly() const { return tools_assembly; }
_FORCE_INLINE_ GDMonoAssembly *get_tools_project_editor_assembly() const { return tools_project_editor_assembly; }
#endif
diff --git a/modules/mono/mono_gd/gd_mono_utils.cpp b/modules/mono/mono_gd/gd_mono_utils.cpp
index e385f4c601..6504fbe423 100644
--- a/modules/mono/mono_gd/gd_mono_utils.cpp
+++ b/modules/mono/mono_gd/gd_mono_utils.cpp
@@ -550,6 +550,8 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)
}
MonoDomain *create_domain(const String &p_friendly_name) {
+ print_verbose("Mono: Creating domain '" + p_friendly_name + "'...");
+
MonoDomain *domain = mono_domain_create_appdomain((char *)p_friendly_name.utf8().get_data(), NULL);
if (domain) {