Browse Source

Merge pull request #30282 from neikeq/editor_in_cs_equals_win

 Re-write mono module editor code in C#
Rémi Verschelde 6 years ago
parent
commit
6e9cb44004
97 changed files with 5703 additions and 4120 deletions
  1. 4 0
      core/project_settings.cpp
  2. 2 2
      core/reference.cpp
  3. 1 1
      editor/editor_plugin.cpp
  4. 1 1
      editor/editor_plugin.h
  5. 2 0
      editor/editor_settings.cpp
  6. 1 1
      editor/plugins/spatial_editor_plugin.h
  7. 20 8
      modules/mono/SCsub
  8. 0 0
      modules/mono/__init__.py
  9. 66 0
      modules/mono/build_scripts/api_solution_build.py
  10. 108 0
      modules/mono/build_scripts/godot_tools_build.py
  11. 6 55
      modules/mono/build_scripts/mono_configure.py
  12. 44 64
      modules/mono/build_scripts/solution_builder.py
  13. 344 100
      modules/mono/csharp_script.cpp
  14. 21 1
      modules/mono/csharp_script.h
  15. 0 2
      modules/mono/editor/GodotSharpTools/.gitignore
  16. 0 425
      modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs
  17. 0 75
      modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs
  18. 0 17
      modules/mono/editor/GodotSharpTools/GodotSharpTools.sln
  19. 0 62
      modules/mono/editor/GodotSharpTools/Utils/OS.cs
  20. 356 0
      modules/mono/editor/GodotTools/.gitignore
  21. 186 0
      modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs
  22. 59 0
      modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj
  23. 35 0
      modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs
  24. 38 0
      modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj
  25. 38 0
      modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs
  26. 26 0
      modules/mono/editor/GodotTools/GodotTools.Core/Properties/AssemblyInfo.cs
  27. 28 5
      modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs
  28. 15 0
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiAssembliesInfo.cs
  29. 52 0
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiSolutionGenerator.cs
  30. 122 0
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs
  31. 25 16
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj
  32. 6 8
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs
  33. 2 1
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs
  34. 20 18
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs
  35. 3 2
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
  36. 2 2
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs
  37. 1 1
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/packages.config
  38. 172 0
      modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs
  39. 210 0
      modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs
  40. 115 0
      modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs
  41. 396 0
      modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs
  42. 538 0
      modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
  43. 197 0
      modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs
  44. 81 0
      modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
  45. 47 0
      modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs
  46. 87 0
      modules/mono/editor/GodotTools/GodotTools/Internals/BindingsGenerator.cs
  47. 50 0
      modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs
  48. 91 0
      modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs
  49. 127 0
      modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs
  50. 52 0
      modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs
  51. 342 0
      modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs
  52. 47 0
      modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs
  53. 260 0
      modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs
  54. 26 29
      modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs
  55. 26 0
      modules/mono/editor/GodotTools/GodotTools/Properties/AssemblyInfo.cs
  56. 20 0
      modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs
  57. 40 0
      modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs
  58. 43 0
      modules/mono/editor/GodotTools/GodotTools/Utils/File.cs
  59. 127 0
      modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs
  60. 88 81
      modules/mono/editor/bindings_generator.cpp
  61. 4 4
      modules/mono/editor/bindings_generator.h
  62. 36 170
      modules/mono/editor/csharp_project.cpp
  63. 2 5
      modules/mono/editor/csharp_project.h
  64. 0 140
      modules/mono/editor/dotnet_solution.cpp
  65. 0 63
      modules/mono/editor/dotnet_solution.h
  66. 429 0
      modules/mono/editor/editor_internal_calls.cpp
  67. 5 24
      modules/mono/editor/editor_internal_calls.h
  68. 0 632
      modules/mono/editor/godotsharp_builds.cpp
  69. 0 103
      modules/mono/editor/godotsharp_builds.h
  70. 0 582
      modules/mono/editor/godotsharp_editor.cpp
  71. 0 134
      modules/mono/editor/godotsharp_editor.h
  72. 30 174
      modules/mono/editor/godotsharp_export.cpp
  73. 9 19
      modules/mono/editor/godotsharp_export.h
  74. 0 528
      modules/mono/editor/mono_bottom_panel.cpp
  75. 0 150
      modules/mono/editor/mono_bottom_panel.h
  76. 0 62
      modules/mono/editor/mono_build_info.cpp
  77. 0 85
      modules/mono/editor/monodevelop_instance.cpp
  78. 0 56
      modules/mono/editor/monodevelop_instance.h
  79. 2 0
      modules/mono/glue/Managed/.gitignore
  80. 8 8
      modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs
  81. 8 0
      modules/mono/glue/Managed/Files/Interfaces/ISerializationListener.cs
  82. 5 4
      modules/mono/glue/Managed/Files/StringExtensions.cs
  83. 6 6
      modules/mono/glue/string_glue.cpp
  84. 2 1
      modules/mono/godotsharp_defs.h
  85. 21 1
      modules/mono/godotsharp_dirs.cpp
  86. 1 0
      modules/mono/godotsharp_dirs.h
  87. 150 83
      modules/mono/mono_gd/gd_mono.cpp
  88. 9 18
      modules/mono/mono_gd/gd_mono.h
  89. 22 0
      modules/mono/mono_gd/gd_mono_assembly.cpp
  90. 7 10
      modules/mono/mono_gd/gd_mono_class.cpp
  91. 1 0
      modules/mono/mono_gd/gd_mono_class.h
  92. 20 8
      modules/mono/mono_gd/gd_mono_field.cpp
  93. 3 4
      modules/mono/mono_gd/gd_mono_internals.cpp
  94. 22 14
      modules/mono/mono_gd/gd_mono_marshal.cpp
  95. 67 47
      modules/mono/mono_gd/gd_mono_utils.cpp
  96. 17 7
      modules/mono/mono_gd/gd_mono_utils.h
  97. 1 1
      modules/mono/signal_awaiter_utils.cpp

+ 4 - 0
core/project_settings.cpp

@@ -83,6 +83,10 @@ String ProjectSettings::localize_path(const String &p_path) const {
 		// `plus_file("")` is an easy way to ensure we have a trailing '/'.
 		// `plus_file("")` is an easy way to ensure we have a trailing '/'.
 		const String res_path = resource_path.plus_file("");
 		const String res_path = resource_path.plus_file("");
 
 
+		// DirAccess::get_current_dir() is not guaranteed to return a path that with a trailing '/',
+		// so we must make sure we have it as well in order to compare with 'res_path'.
+		cwd = cwd.plus_file("");
+
 		if (!cwd.begins_with(res_path)) {
 		if (!cwd.begins_with(res_path)) {
 			return p_path;
 			return p_path;
 		};
 		};

+ 2 - 2
core/reference.cpp

@@ -70,7 +70,7 @@ bool Reference::reference() {
 		if (get_script_instance()) {
 		if (get_script_instance()) {
 			get_script_instance()->refcount_incremented();
 			get_script_instance()->refcount_incremented();
 		}
 		}
-		if (instance_binding_count > 0) {
+		if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {
 				if (_script_instance_bindings[i]) {
 					ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
 					ScriptServer::get_language(i)->refcount_incremented_instance_binding(this);
@@ -91,7 +91,7 @@ bool Reference::unreference() {
 			bool script_ret = get_script_instance()->refcount_decremented();
 			bool script_ret = get_script_instance()->refcount_decremented();
 			die = die && script_ret;
 			die = die && script_ret;
 		}
 		}
-		if (instance_binding_count > 0) {
+		if (instance_binding_count > 0 && !ScriptServer::are_languages_finished()) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 			for (int i = 0; i < MAX_SCRIPT_INSTANCE_BINDINGS; i++) {
 				if (_script_instance_bindings[i]) {
 				if (_script_instance_bindings[i]) {
 					bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);
 					bool script_ret = ScriptServer::get_language(i)->refcount_decremented_instance_binding(this);

+ 1 - 1
editor/editor_plugin.cpp

@@ -220,7 +220,7 @@ EditorSelection *EditorInterface::get_selection() {
 	return EditorNode::get_singleton()->get_editor_selection();
 	return EditorNode::get_singleton()->get_editor_selection();
 }
 }
 
 
-EditorSettings *EditorInterface::get_editor_settings() {
+Ref<EditorSettings> EditorInterface::get_editor_settings() {
 	return EditorSettings::get_singleton();
 	return EditorSettings::get_singleton();
 }
 }
 
 

+ 1 - 1
editor/editor_plugin.h

@@ -87,7 +87,7 @@ public:
 
 
 	EditorSelection *get_selection();
 	EditorSelection *get_selection();
 	//EditorImportExport *get_import_export();
 	//EditorImportExport *get_import_export();
-	EditorSettings *get_editor_settings();
+	Ref<EditorSettings> get_editor_settings();
 	EditorResourcePreview *get_resource_previewer();
 	EditorResourcePreview *get_resource_previewer();
 	EditorFileSystem *get_resource_file_system();
 	EditorFileSystem *get_resource_file_system();
 
 

+ 2 - 0
editor/editor_settings.cpp

@@ -1579,6 +1579,8 @@ void EditorSettings::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs);
 	ClassDB::bind_method(D_METHOD("get_recent_dirs"), &EditorSettings::get_recent_dirs);
 
 
 	ADD_SIGNAL(MethodInfo("settings_changed"));
 	ADD_SIGNAL(MethodInfo("settings_changed"));
+
+	BIND_CONSTANT(NOTIFICATION_EDITOR_SETTINGS_CHANGED);
 }
 }
 
 
 EditorSettings::EditorSettings() {
 EditorSettings::EditorSettings() {

+ 1 - 1
editor/plugins/spatial_editor_plugin.h

@@ -119,7 +119,7 @@ public:
 
 
 	void set_spatial_node(Spatial *p_node);
 	void set_spatial_node(Spatial *p_node);
 	Spatial *get_spatial_node() const { return spatial_node; }
 	Spatial *get_spatial_node() const { return spatial_node; }
-	EditorSpatialGizmoPlugin *get_plugin() const { return gizmo_plugin; }
+	Ref<EditorSpatialGizmoPlugin> get_plugin() const { return gizmo_plugin; }
 	Vector3 get_handle_pos(int p_idx) const;
 	Vector3 get_handle_pos(int p_idx) const;
 	bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
 	bool intersect_frustum(const Camera *p_camera, const Vector<Plane> &p_frustum);
 	bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);
 	bool intersect_ray(Camera *p_camera, const Point2 &p_point, Vector3 &r_pos, Vector3 &r_normal, int *r_gizmo_handle = NULL, bool p_sec_first = false);

+ 20 - 8
modules/mono/SCsub

@@ -1,5 +1,8 @@
 #!/usr/bin/env python
 #!/usr/bin/env python
 
 
+import build_scripts.tls_configure as tls_configure
+import build_scripts.mono_configure as mono_configure
+
 Import('env')
 Import('env')
 Import('env_modules')
 Import('env_modules')
 
 
@@ -26,27 +29,36 @@ if env_mono['mono_glue']:
 
 
     import os.path
     import os.path
     if not os.path.isfile('glue/mono_glue.gen.cpp'):
     if not os.path.isfile('glue/mono_glue.gen.cpp'):
-        raise RuntimeError('Missing mono glue sources. Did you forget to generate them?')
+        raise RuntimeError("Mono glue sources not found. Did you forget to run '--generate-mono-glue'?")
 
 
 if env_mono['tools'] or env_mono['target'] != 'release':
 if env_mono['tools'] or env_mono['target'] != 'release':
     env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
     env_mono.Append(CPPDEFINES=['GD_MONO_HOT_RELOAD'])
 
 
 # Configure Thread Local Storage
 # Configure Thread Local Storage
 
 
-import build_scripts.tls_configure as tls_configure
-
 conf = Configure(env_mono)
 conf = Configure(env_mono)
 tls_configure.configure(conf)
 tls_configure.configure(conf)
 env_mono = conf.Finish()
 env_mono = conf.Finish()
 
 
 # Configure Mono
 # Configure Mono
 
 
-import build_scripts.mono_configure as mono_configure
-
 mono_configure.configure(env, env_mono)
 mono_configure.configure(env, env_mono)
 
 
-# Build GodotSharpTools
+# Build Godot API solution
+
+if env_mono['tools'] and env_mono['mono_glue']:
+    import build_scripts.api_solution_build as api_solution_build
+    api_solution_build.build(env_mono)
 
 
-import build_scripts.godotsharptools_build as godotsharptools_build
+# Build GodotTools
 
 
-godotsharptools_build.build(env_mono)
+if env_mono['tools']:
+    import build_scripts.godot_tools_build as godot_tools_build
+    if env_mono['mono_glue']:
+        godot_tools_build.build(env_mono)
+    else:
+        # Building without the glue sources so the Godot API solution may be missing.
+        # GodotTools depends on the Godot API solution. As such, we will only build
+        # GodotTools.ProjectEditor which doesn't depend on the Godot API solution and
+        # is required by the bindings generator in order to be able to generated it.
+        godot_tools_build.build_project_editor_only(env_mono)

+ 0 - 0
modules/mono/__init__.py


+ 66 - 0
modules/mono/build_scripts/api_solution_build.py

@@ -0,0 +1,66 @@
+# Build the Godot API solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_api_solution(source, target, env):
+    # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+    module_dir = env['module_dir']
+
+    solution_path = os.path.join(module_dir, 'glue/Managed/Generated/GodotSharp.sln')
+
+    if not os.path.isfile(solution_path):
+        raise RuntimeError("Godot API solution not found. Did you forget to run '--generate-mono-glue'?")
+
+    build_config = env['solution_build_config']
+
+    extra_msbuild_args = ['/p:NoWarn=1591']  # Ignore missing documentation warnings
+
+    from .solution_builder import build_solution
+    build_solution(env, solution_path, build_config, extra_msbuild_args=extra_msbuild_args)
+
+    # Copy targets
+
+    core_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharp', 'bin', build_config))
+    editor_src_dir = os.path.abspath(os.path.join(solution_path, os.pardir, 'GodotSharpEditor', 'bin', build_config))
+
+    dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+    if not os.path.isdir(dst_dir):
+        assert not os.path.isfile(dst_dir)
+        os.makedirs(dst_dir)
+
+    def copy_target(target_path):
+        from shutil import copy
+        filename = os.path.basename(target_path)
+
+        src_path = os.path.join(core_src_dir, filename)
+        if not os.path.isfile(src_path):
+            src_path = os.path.join(editor_src_dir, filename)
+
+        copy(src_path, target_path)
+
+    for scons_target in target:
+        copy_target(str(scons_target))
+
+
+def build(env_mono):
+    assert env_mono['tools']
+
+    target_filenames = [
+        'GodotSharp.dll', 'GodotSharp.pdb', 'GodotSharp.xml',
+        'GodotSharpEditor.dll', 'GodotSharpEditor.pdb', 'GodotSharpEditor.xml'
+    ]
+
+    for build_config in ['Debug', 'Release']:
+        output_dir = Dir('#bin').abspath
+        editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', build_config)
+
+        targets = [os.path.join(editor_api_dir, filename) for filename in target_filenames]
+
+        cmd = env_mono.CommandNoCache(targets, [], build_api_solution,
+                                      module_dir=os.getcwd(), solution_build_config=build_config)
+        env_mono.AlwaysBuild(cmd)

+ 108 - 0
modules/mono/build_scripts/godot_tools_build.py

@@ -0,0 +1,108 @@
+# Build GodotTools solution
+
+import os
+
+from SCons.Script import Dir
+
+
+def build_godot_tools(source, target, env):
+    # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+    module_dir = env['module_dir']
+
+    solution_path = os.path.join(module_dir, 'editor/GodotTools/GodotTools.sln')
+    build_config = 'Debug' if env['target'] == 'debug' else 'Release'
+
+    from . solution_builder import build_solution, nuget_restore
+    nuget_restore(env, solution_path)
+    build_solution(env, solution_path, build_config)
+
+    # Copy targets
+
+    solution_dir = os.path.abspath(os.path.join(solution_path, os.pardir))
+
+    src_dir = os.path.join(solution_dir, 'GodotTools', 'bin', build_config)
+    dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+    if not os.path.isdir(dst_dir):
+        assert not os.path.isfile(dst_dir)
+        os.makedirs(dst_dir)
+
+    def copy_target(target_path):
+        from shutil import copy
+        filename = os.path.basename(target_path)
+        copy(os.path.join(src_dir, filename), target_path)
+
+    for scons_target in target:
+        copy_target(str(scons_target))
+
+
+def build_godot_tools_project_editor(source, target, env):
+    # source and target elements are of type SCons.Node.FS.File, hence why we convert them to str
+
+    module_dir = env['module_dir']
+
+    project_name = 'GodotTools.ProjectEditor'
+
+    csproj_dir = os.path.join(module_dir, 'editor/GodotTools', project_name)
+    csproj_path = os.path.join(csproj_dir, project_name + '.csproj')
+    build_config = 'Debug' if env['target'] == 'debug' else 'Release'
+
+    from . solution_builder import build_solution, nuget_restore
+
+    # Make sure to restore NuGet packages in the project directory for the project to find it
+    nuget_restore(env, os.path.join(csproj_dir, 'packages.config'), '-PackagesDirectory',
+                  os.path.join(csproj_dir, 'packages'))
+
+    build_solution(env, csproj_path, build_config)
+
+    # Copy targets
+
+    src_dir = os.path.join(csproj_dir, 'bin', build_config)
+    dst_dir = os.path.abspath(os.path.join(str(target[0]), os.pardir))
+
+    if not os.path.isdir(dst_dir):
+        assert not os.path.isfile(dst_dir)
+        os.makedirs(dst_dir)
+
+    def copy_target(target_path):
+        from shutil import copy
+        filename = os.path.basename(target_path)
+        copy(os.path.join(src_dir, filename), target_path)
+
+    for scons_target in target:
+        copy_target(str(scons_target))
+
+
+def build(env_mono):
+    assert env_mono['tools']
+
+    output_dir = Dir('#bin').abspath
+    editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+    editor_api_dir = os.path.join(output_dir, 'GodotSharp', 'Api', 'Debug')
+
+    source_filenames = ['GodotSharp.dll', 'GodotSharpEditor.dll']
+    sources = [os.path.join(editor_api_dir, filename) for filename in source_filenames]
+
+    target_filenames = ['GodotTools.dll', 'GodotTools.BuildLogger.dll', 'GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll']
+
+    if env_mono['target'] == 'debug':
+        target_filenames += ['GodotTools.pdb', 'GodotTools.BuildLogger.dll', 'GodotTools.ProjectEditor.dll', 'GodotTools.Core.dll']
+
+    targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
+
+    cmd = env_mono.CommandNoCache(targets, sources, build_godot_tools, module_dir=os.getcwd())
+    env_mono.AlwaysBuild(cmd)
+
+
+def build_project_editor_only(env_mono):
+    assert env_mono['tools']
+
+    output_dir = Dir('#bin').abspath
+    editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+
+    target_filenames = ['GodotTools.ProjectEditor.dll', 'DotNet.Glob.dll', 'GodotTools.Core.dll']
+    targets = [os.path.join(editor_tools_dir, filename) for filename in target_filenames]
+
+    cmd = env_mono.CommandNoCache(targets, [], build_godot_tools_project_editor, module_dir=os.getcwd())
+    env_mono.AlwaysBuild(cmd)

+ 6 - 55
modules/mono/build_scripts/mono_configure.py

@@ -1,10 +1,8 @@
-import imp
 import os
 import os
 import os.path
 import os.path
 import sys
 import sys
 import subprocess
 import subprocess
 
 
-from distutils.version import LooseVersion
 from SCons.Script import Dir, Environment
 from SCons.Script import Dir, Environment
 
 
 if os.name == 'nt':
 if os.name == 'nt':
@@ -58,6 +56,12 @@ def configure(env, env_mono):
 
 
     mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
     mono_lib_names = ['mono-2.0-sgen', 'monosgen-2.0']
 
 
+    is_travis = os.environ.get('TRAVIS') == 'true'
+
+    if is_travis:
+        # Travis CI may have a Mono version lower than 5.12
+        env_mono.Append(CPPDEFINES=['NO_PENDING_EXCEPTIONS'])
+
     if is_android and not env['android_arch'] in android_arch_dirs:
     if is_android and not env['android_arch'] in android_arch_dirs:
         raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
         raise RuntimeError('This module does not support for the specified \'android_arch\': ' + env['android_arch'])
 
 
@@ -83,9 +87,6 @@ def configure(env, env_mono):
 
 
         print('Found Mono root directory: ' + mono_root)
         print('Found Mono root directory: ' + mono_root)
 
 
-        mono_version = mono_root_try_find_mono_version(mono_root)
-        configure_for_mono_version(env_mono, mono_version)
-
         mono_lib_path = os.path.join(mono_root, 'lib')
         mono_lib_path = os.path.join(mono_root, 'lib')
 
 
         env.Append(LIBPATH=mono_lib_path)
         env.Append(LIBPATH=mono_lib_path)
@@ -164,9 +165,6 @@ def configure(env, env_mono):
         if mono_root:
         if mono_root:
             print('Found Mono root directory: ' + mono_root)
             print('Found Mono root directory: ' + mono_root)
 
 
-            mono_version = mono_root_try_find_mono_version(mono_root)
-            configure_for_mono_version(env_mono, mono_version)
-
             mono_lib_path = os.path.join(mono_root, 'lib')
             mono_lib_path = os.path.join(mono_root, 'lib')
 
 
             env.Append(LIBPATH=mono_lib_path)
             env.Append(LIBPATH=mono_lib_path)
@@ -209,9 +207,6 @@ def configure(env, env_mono):
             # TODO: Add option to force using pkg-config
             # TODO: Add option to force using pkg-config
             print('Mono root directory not found. Using pkg-config instead')
             print('Mono root directory not found. Using pkg-config instead')
 
 
-            mono_version = pkgconfig_try_find_mono_version()
-            configure_for_mono_version(env_mono, mono_version)
-
             env.ParseConfig('pkg-config monosgen-2 --libs')
             env.ParseConfig('pkg-config monosgen-2 --libs')
             env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
             env_mono.ParseConfig('pkg-config monosgen-2 --cflags')
 
 
@@ -401,17 +396,6 @@ def copy_mono_shared_libs(env, mono_root, target_mono_root_dir):
                 copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
                 copy_if_exists(os.path.join(mono_root, 'lib', lib_file_name), target_mono_lib_dir)
 
 
 
 
-def configure_for_mono_version(env, mono_version):
-    if mono_version is None:
-        if os.getenv('MONO_VERSION'):
-            mono_version = os.getenv('MONO_VERSION')
-        else:
-            raise RuntimeError("Mono JIT compiler version not found; specify one manually with the 'MONO_VERSION' environment variable")
-    print('Found Mono JIT compiler version: ' + str(mono_version))
-    if mono_version >= LooseVersion('5.12.0'):
-        env.Append(CPPDEFINES=['HAS_PENDING_EXCEPTIONS'])
-
-
 def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
 def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
     tmpenv = Environment()
     tmpenv = Environment()
     tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
     tmpenv.AppendENVPath('PKG_CONFIG_PATH', os.getenv('PKG_CONFIG_PATH'))
@@ -421,36 +405,3 @@ def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext):
         if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
         if name_found and os.path.isdir(os.path.join(hint_dir, '..', 'include', 'mono-2.0')):
             return os.path.join(hint_dir, '..')
             return os.path.join(hint_dir, '..')
     return ''
     return ''
-
-
-def pkgconfig_try_find_mono_version():
-    from compat import decode_utf8
-
-    lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines()
-    greater_version = None
-    for line in lines:
-        try:
-            version = LooseVersion(decode_utf8(line))
-            if greater_version is None or version > greater_version:
-                greater_version = version
-        except ValueError:
-            pass
-    return greater_version
-
-
-def mono_root_try_find_mono_version(mono_root):
-    from compat import decode_utf8
-
-    mono_bin = os.path.join(mono_root, 'bin')
-    if os.path.isfile(os.path.join(mono_bin, 'mono')):
-        mono_binary = os.path.join(mono_bin, 'mono')
-    elif os.path.isfile(os.path.join(mono_bin, 'mono.exe')):
-        mono_binary = os.path.join(mono_bin, 'mono.exe')
-    else:
-        return None
-    output = subprocess.check_output([mono_binary, '--version'])
-    first_line = decode_utf8(output.splitlines()[0])
-    try:
-        return LooseVersion(first_line.split()[len('Mono JIT compiler version'.split())])
-    except (ValueError, IndexError):
-        return None

+ 44 - 64
modules/mono/build_scripts/godotsharptools_build.py → modules/mono/build_scripts/solution_builder.py

@@ -1,8 +1,8 @@
-# Build GodotSharpTools solution
 
 
 import os
 import os
 
 
-from SCons.Script import Builder, Dir
+
+verbose = False
 
 
 
 
 def find_nuget_unix():
 def find_nuget_unix():
@@ -131,12 +131,46 @@ def find_msbuild_windows(env):
     return None
     return None
 
 
 
 
-def mono_build_solution(source, target, env):
+def run_command(command, args, env_override=None, name=None):
+    def cmd_args_to_str(cmd_args):
+        return ' '.join([arg if not ' ' in arg else '"%s"' % arg for arg in cmd_args])
+
+    args = [command] + args
+
+    if name is None:
+        name = os.path.basename(command)
+
+    if verbose:
+        print("Running '%s': %s" % (name, cmd_args_to_str(args)))
+
     import subprocess
     import subprocess
-    from shutil import copyfile
+    try:
+        if env_override is None:
+            subprocess.check_call(args)
+        else:
+            subprocess.check_call(args, env=env_override)
+    except subprocess.CalledProcessError as e:
+        raise RuntimeError("'%s' exited with error code: %s" % (name, e.returncode))
+
+
+def nuget_restore(env, *args):
+    global verbose
+    verbose = env['verbose']
+
+    # Find NuGet
+    nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
+    if nuget_path is None:
+        raise RuntimeError('Cannot find NuGet executable')
+
+    print('NuGet path: ' + nuget_path)
 
 
-    sln_path = os.path.abspath(str(source[0]))
-    target_path = os.path.abspath(str(target[0]))
+    # Do NuGet restore
+    run_command(nuget_path, ['restore'] + list(args), name='nuget restore')
+
+
+def build_solution(env, solution_path, build_config, extra_msbuild_args=[]):
+    global verbose
+    verbose = env['verbose']
 
 
     framework_path = ''
     framework_path = ''
     msbuild_env = os.environ.copy()
     msbuild_env = os.environ.copy()
@@ -175,64 +209,10 @@ def mono_build_solution(source, target, env):
 
 
     print('MSBuild path: ' + msbuild_path)
     print('MSBuild path: ' + msbuild_path)
 
 
-    # Find NuGet
-    nuget_path = find_nuget_windows(env) if os.name == 'nt' else find_nuget_unix()
-    if nuget_path is None:
-        raise RuntimeError('Cannot find NuGet executable')
-
-    print('NuGet path: ' + nuget_path)
-
-    # Do NuGet restore
-
-    try:
-        subprocess.check_call([nuget_path, 'restore', sln_path])
-    except subprocess.CalledProcessError:
-        raise RuntimeError('GodotSharpTools: NuGet restore failed')
-
     # Build solution
     # Build solution
 
 
-    build_config = 'Release'
-
-    msbuild_args = [
-        msbuild_path,
-        sln_path,
-        '/p:Configuration=' + build_config,
-    ]
-
-    if framework_path:
-        msbuild_args += ['/p:FrameworkPathOverride=' + framework_path]
-
-    try:
-        subprocess.check_call(msbuild_args, env=msbuild_env)
-    except subprocess.CalledProcessError:
-        raise RuntimeError('GodotSharpTools: Build failed')
-
-    # Copy files
-
-    src_dir = os.path.abspath(os.path.join(sln_path, os.pardir, 'bin', build_config))
-    dst_dir = os.path.abspath(os.path.join(target_path, os.pardir))
-    asm_file = 'GodotSharpTools.dll'
-
-    if not os.path.isdir(dst_dir):
-        if os.path.exists(dst_dir):
-            raise RuntimeError('Target directory is a file')
-        os.makedirs(dst_dir)
-
-    copyfile(os.path.join(src_dir, asm_file), os.path.join(dst_dir, asm_file))
-
-    # Dependencies
-    copyfile(os.path.join(src_dir, "DotNet.Glob.dll"), os.path.join(dst_dir, "DotNet.Glob.dll"))
-
-def build(env_mono):
-    if not env_mono['tools']:
-        return
-
-    output_dir = Dir('#bin').abspath
-    editor_tools_dir = os.path.join(output_dir, 'GodotSharp', 'Tools')
+    msbuild_args = [solution_path, '/p:Configuration=' + build_config]
+    msbuild_args += ['/p:FrameworkPathOverride=' + framework_path] if framework_path else []
+    msbuild_args += extra_msbuild_args
 
 
-    mono_sln_builder = Builder(action=mono_build_solution)
-    env_mono.Append(BUILDERS={'MonoBuildSolution': mono_sln_builder})
-    env_mono.MonoBuildSolution(
-        os.path.join(editor_tools_dir, 'GodotSharpTools.dll'),
-        'editor/GodotSharpTools/GodotSharpTools.sln'
-    )
+    run_command(msbuild_path, msbuild_args, env_override=msbuild_env, name='msbuild')

+ 344 - 100
modules/mono/csharp_script.cpp

@@ -42,9 +42,9 @@
 #include "editor/bindings_generator.h"
 #include "editor/bindings_generator.h"
 #include "editor/csharp_project.h"
 #include "editor/csharp_project.h"
 #include "editor/editor_node.h"
 #include "editor/editor_node.h"
-#include "editor/godotsharp_editor.h"
 #endif
 #endif
 
 
+#include "editor/editor_internal_calls.h"
 #include "godotsharp_dirs.h"
 #include "godotsharp_dirs.h"
 #include "mono_gd/gd_mono_class.h"
 #include "mono_gd/gd_mono_class.h"
 #include "mono_gd/gd_mono_marshal.h"
 #include "mono_gd/gd_mono_marshal.h"
@@ -65,8 +65,8 @@ static bool _create_project_solution_if_needed() {
 	if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
 	if (!FileAccess::exists(sln_path) || !FileAccess::exists(csproj_path)) {
 		// A solution does not yet exist, create a new one
 		// A solution does not yet exist, create a new one
 
 
-		CRASH_COND(GodotSharpEditor::get_singleton() == NULL);
-		return GodotSharpEditor::get_singleton()->call("_create_project_solution");
+		CRASH_COND(CSharpLanguage::get_singleton()->get_godotsharp_editor() == NULL);
+		return CSharpLanguage::get_singleton()->get_godotsharp_editor()->call("CreateProjectSolution");
 	}
 	}
 
 
 	return true;
 	return true;
@@ -96,14 +96,6 @@ Error CSharpLanguage::execute_file(const String &p_path) {
 	return OK;
 	return OK;
 }
 }
 
 
-#ifdef TOOLS_ENABLED
-void gdsharp_editor_init_callback() {
-
-	EditorNode *editor = EditorNode::get_singleton();
-	editor->add_child(memnew(GodotSharpEditor(editor)));
-}
-#endif
-
 void CSharpLanguage::init() {
 void CSharpLanguage::init() {
 
 
 	gdmono = memnew(GDMono);
 	gdmono = memnew(GDMono);
@@ -114,14 +106,12 @@ void CSharpLanguage::init() {
 #endif
 #endif
 
 
 #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
 #if defined(TOOLS_ENABLED) && defined(DEBUG_METHODS_ENABLED)
-	if (gdmono->get_editor_tools_assembly() != NULL) {
-		List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
-		BindingsGenerator::handle_cmdline_args(cmdline_args);
-	}
+	List<String> cmdline_args = OS::get_singleton()->get_cmdline_args();
+	BindingsGenerator::handle_cmdline_args(cmdline_args);
 #endif
 #endif
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	EditorNode::add_init_callback(&gdsharp_editor_init_callback);
+	EditorNode::add_init_callback(&_editor_init_callback);
 
 
 	GLOBAL_DEF("mono/export/include_scripts_content", false);
 	GLOBAL_DEF("mono/export/include_scripts_content", false);
 #endif
 #endif
@@ -664,7 +654,7 @@ void CSharpLanguage::reload_tool_script(const Ref<Script> &p_script, bool p_soft
 	CRASH_COND(!Engine::get_singleton()->is_editor_hint());
 	CRASH_COND(!Engine::get_singleton()->is_editor_hint());
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	MonoReloadNode::get_singleton()->restart_reload_timer();
+	get_godotsharp_editor()->get_node(NodePath("HotReloadAssemblyWatcher"))->call("RestartTimer");
 #endif
 #endif
 
 
 #ifdef GD_MONO_HOT_RELOAD
 #ifdef GD_MONO_HOT_RELOAD
@@ -731,58 +721,93 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 		SCOPED_MUTEX_LOCK(script_instances_mutex);
 		SCOPED_MUTEX_LOCK(script_instances_mutex);
 
 
 		for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) {
 		for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) {
-			if (elem->self()->get_path().is_resource_file()) {
-				// Cast to CSharpScript to avoid being erased by accident
-				scripts.push_back(Ref<CSharpScript>(elem->self()));
-			}
+			// Cast to CSharpScript to avoid being erased by accident
+			scripts.push_back(Ref<CSharpScript>(elem->self()));
 		}
 		}
 	}
 	}
 
 
 	List<Ref<CSharpScript> > to_reload;
 	List<Ref<CSharpScript> > to_reload;
 
 
+	// We need to keep reference instances alive during reloading
+	List<Ref<Reference> > ref_instances;
+
+	for (Map<Object *, CSharpScriptBinding>::Element *E = script_bindings.front(); E; E = E->next()) {
+		CSharpScriptBinding &script_binding = E->value();
+		Reference *ref = Object::cast_to<Reference>(script_binding.owner);
+		if (ref) {
+			ref_instances.push_back(Ref<Reference>(ref));
+		}
+	}
+
 	// As scripts are going to be reloaded, must proceed without locking here
 	// As scripts are going to be reloaded, must proceed without locking here
 
 
 	scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
 	scripts.sort_custom<CSharpScriptDepSort>(); // Update in inheritance dependency order
 
 
 	for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
 	for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
-
 		Ref<CSharpScript> &script = E->get();
 		Ref<CSharpScript> &script = E->get();
 
 
 		to_reload.push_back(script);
 		to_reload.push_back(script);
 
 
+		if (script->get_path().empty()) {
+			script->tied_class_name_for_reload = script->script_class->get_name();
+			script->tied_class_namespace_for_reload = script->script_class->get_namespace();
+		}
+
 		// Script::instances are deleted during managed object disposal, which happens on domain finalize.
 		// Script::instances are deleted during managed object disposal, which happens on domain finalize.
 		// Only placeholders are kept. Therefore we need to keep a copy before that happens.
 		// Only placeholders are kept. Therefore we need to keep a copy before that happens.
 
 
 		for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
 		for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
-			script->pending_reload_instances.insert(F->get()->get_instance_id());
+			Object *obj = F->get();
+			script->pending_reload_instances.insert(obj->get_instance_id());
+
+			Reference *ref = Object::cast_to<Reference>(obj);
+			if (ref) {
+				ref_instances.push_back(Ref<Reference>(ref));
+			}
 		}
 		}
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 		for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
 		for (Set<PlaceHolderScriptInstance *>::Element *F = script->placeholders.front(); F; F = F->next()) {
-			script->pending_reload_instances.insert(F->get()->get_owner()->get_instance_id());
+			Object *obj = F->get()->get_owner();
+			script->pending_reload_instances.insert(obj->get_instance_id());
+
+			Reference *ref = Object::cast_to<Reference>(obj);
+			if (ref) {
+				ref_instances.push_back(Ref<Reference>(ref));
+			}
 		}
 		}
 #endif
 #endif
 
 
-		// FIXME: What about references? Need to keep them alive if only managed code references them.
-
 		// Save state and remove script from instances
 		// Save state and remove script from instances
 		Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
 		Map<ObjectID, CSharpScript::StateBackup> &owners_map = script->pending_reload_state;
 
 
-		while (script->instances.front()) {
-			Object *obj = script->instances.front()->get();
-			// Save instance info
-			CSharpScript::StateBackup state;
+		for (Set<Object *>::Element *F = script->instances.front(); F; F = F->next()) {
+			Object *obj = F->get();
 
 
 			ERR_CONTINUE(!obj->get_script_instance());
 			ERR_CONTINUE(!obj->get_script_instance());
 
 
-			// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
-			obj->get_script_instance()->get_property_state(state.properties);
+			CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
 
 
-			Ref<MonoGCHandle> gchandle = CAST_CSHARP_INSTANCE(obj->get_script_instance())->gchandle;
-			if (gchandle.is_valid())
-				gchandle->release();
+			// Call OnBeforeSerialize
+			if (csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
+				obj->get_script_instance()->call_multilevel(string_names.on_before_serialize);
+
+			// Save instance info
+			CSharpScript::StateBackup state;
+
+			// TODO: Proper state backup (Not only variants, serialize managed state of scripts)
+			csi->get_properties_state_for_reloading(state.properties);
 
 
 			owners_map[obj->get_instance_id()] = state;
 			owners_map[obj->get_instance_id()] = state;
+		}
+	}
+
+	// After the state of all instances is saved, clear scripts and script instances
+	for (List<Ref<CSharpScript> >::Element *E = scripts.front(); E; E = E->next()) {
+		Ref<CSharpScript> &script = E->get();
+
+		while (script->instances.front()) {
+			Object *obj = script->instances.front()->get();
 			obj->set_script(RefPtr()); // Remove script and existing script instances (placeholder are not removed before domain reload)
 			obj->set_script(RefPtr()); // Remove script and existing script instances (placeholder are not removed before domain reload)
 		}
 		}
 
 
@@ -825,26 +850,76 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 				scr->pending_reload_state.erase(obj_id);
 				scr->pending_reload_state.erase(obj_id);
 			}
 			}
 		}
 		}
+
 		return;
 		return;
 	}
 	}
 
 
+	List<Ref<CSharpScript> > to_reload_state;
+
 	for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
 	for (List<Ref<CSharpScript> >::Element *E = to_reload.front(); E; E = E->next()) {
+		Ref<CSharpScript> script = E->get();
 
 
-		Ref<CSharpScript> scr = E->get();
+		if (!script->get_path().empty()) {
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-		scr->exports_invalidated = true;
+			script->exports_invalidated = true;
 #endif
 #endif
-		scr->signals_invalidated = true;
-		scr->reload(p_soft_reload);
-		scr->update_exports();
+			script->signals_invalidated = true;
+
+			script->reload(p_soft_reload);
+			script->update_exports();
+		} else {
+			const StringName &class_namespace = script->tied_class_namespace_for_reload;
+			const StringName &class_name = script->tied_class_name_for_reload;
+			GDMonoAssembly *project_assembly = gdmono->get_project_assembly();
+			GDMonoAssembly *tools_assembly = gdmono->get_tools_assembly();
+
+			// Search in project and tools assemblies first as those are the most likely to have the class
+			GDMonoClass *script_class = (project_assembly ? project_assembly->get_class(class_namespace, class_name) : NULL);
+			if (!script_class) {
+				script_class = (tools_assembly ? tools_assembly->get_class(class_namespace, class_name) : NULL);
+			}
+			if (!script_class) {
+				script_class = gdmono->get_class(class_namespace, class_name);
+			}
+
+			if (!script_class) {
+				// The class was removed, can't reload
+				script->pending_reload_instances.clear();
+				continue;
+			}
+
+			bool obj_type = CACHED_CLASS(GodotObject)->is_assignable_from(script_class);
+			if (!obj_type) {
+				// The class no longer inherits Godot.Object, can't reload
+				script->pending_reload_instances.clear();
+				continue;
+			}
+
+			GDMonoClass *native = GDMonoUtils::get_class_native_base(script_class);
+
+			Ref<CSharpScript> new_script = CSharpScript::create_for_managed_type(script_class, native);
+			CRASH_COND(new_script.is_null());
+
+			new_script->pending_reload_instances = script->pending_reload_instances;
+			new_script->pending_reload_state = script->pending_reload_state;
+			script = new_script;
+		}
+
+		String native_name = NATIVE_GDMONOCLASS_NAME(script->native);
 
 
 		{
 		{
-			for (Set<ObjectID>::Element *F = scr->pending_reload_instances.front(); F; F = F->next()) {
+			for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
 				ObjectID obj_id = F->get();
 				ObjectID obj_id = F->get();
 				Object *obj = ObjectDB::get_instance(obj_id);
 				Object *obj = ObjectDB::get_instance(obj_id);
 
 
 				if (!obj) {
 				if (!obj) {
-					scr->pending_reload_state.erase(obj_id);
+					script->pending_reload_state.erase(obj_id);
+					continue;
+				}
+
+				if (!ClassDB::is_parent_class(obj->get_class_name(), native_name)) {
+					// No longer inherits the same compatible type, can't reload
+					script->pending_reload_state.erase(obj_id);
 					continue;
 					continue;
 				}
 				}
 
 
@@ -856,28 +931,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 					// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
 					// Non-placeholder script instances are removed in godot_icall_Object_Disposed.
 					CRASH_COND(!si->is_placeholder());
 					CRASH_COND(!si->is_placeholder());
 
 
-					if (scr->is_tool() || ScriptServer::is_scripting_enabled()) {
+					if (script->is_tool() || ScriptServer::is_scripting_enabled()) {
 						// Replace placeholder with a script instance
 						// Replace placeholder with a script instance
 
 
-						CSharpScript::StateBackup &state_backup = scr->pending_reload_state[obj_id];
+						CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
 
 
 						// Backup placeholder script instance state before replacing it with a script instance
 						// Backup placeholder script instance state before replacing it with a script instance
 						si->get_property_state(state_backup.properties);
 						si->get_property_state(state_backup.properties);
 
 
-						ScriptInstance *script_instance = scr->instance_create(obj);
+						ScriptInstance *script_instance = script->instance_create(obj);
 
 
 						if (script_instance) {
 						if (script_instance) {
-							scr->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
+							script->placeholders.erase(static_cast<PlaceHolderScriptInstance *>(si));
 							obj->set_script_instance(script_instance);
 							obj->set_script_instance(script_instance);
 						}
 						}
-
-						// TODO: Restore serialized state
-
-						for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) {
-							script_instance->set(G->get().first, G->get().second);
-						}
-
-						scr->pending_reload_state.erase(obj_id);
 					}
 					}
 
 
 					continue;
 					continue;
@@ -887,19 +954,42 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
 #endif
 #endif
 				// Re-create script instance
 				// Re-create script instance
 
 
-				obj->set_script(scr.get_ref_ptr()); // will create the script instance as well
+				obj->set_script(script.get_ref_ptr()); // will create the script instance as well
+			}
+		}
 
 
-				// TODO: Restore serialized state
+		to_reload_state.push_back(script);
+	}
 
 
-				for (List<Pair<StringName, Variant> >::Element *G = scr->pending_reload_state[obj_id].properties.front(); G; G = G->next()) {
-					obj->get_script_instance()->set(G->get().first, G->get().second);
-				}
+	for (List<Ref<CSharpScript> >::Element *E = to_reload_state.front(); E; E = E->next()) {
+		Ref<CSharpScript> script = E->get();
 
 
-				scr->pending_reload_state.erase(obj_id);
+		for (Set<ObjectID>::Element *F = script->pending_reload_instances.front(); F; F = F->next()) {
+			ObjectID obj_id = F->get();
+			Object *obj = ObjectDB::get_instance(obj_id);
+
+			if (!obj) {
+				script->pending_reload_state.erase(obj_id);
+				continue;
 			}
 			}
 
 
-			scr->pending_reload_instances.clear();
+			ERR_CONTINUE(!obj->get_script_instance());
+
+			// TODO: Restore serialized state
+
+			CSharpScript::StateBackup &state_backup = script->pending_reload_state[obj_id];
+
+			for (List<Pair<StringName, Variant> >::Element *G = state_backup.properties.front(); G; G = G->next()) {
+				obj->get_script_instance()->set(G->get().first, G->get().second);
+			}
+
+			// Call OnAfterDeserialization
+			CSharpInstance *csi = CAST_CSHARP_INSTANCE(obj->get_script_instance());
+			if (csi && csi->script->script_class->implements_interface(CACHED_CLASS(ISerializationListener)))
+				obj->get_script_instance()->call_multilevel(string_names.on_after_deserialize);
 		}
 		}
+
+		script->pending_reload_instances.clear();
 	}
 	}
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -964,12 +1054,12 @@ void CSharpLanguage::get_recognized_extensions(List<String> *p_extensions) const
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 Error CSharpLanguage::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
 Error CSharpLanguage::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
 
 
-	return GodotSharpEditor::get_singleton()->open_in_external_editor(p_script, p_line, p_col);
+	return (Error)(int)get_godotsharp_editor()->call("OpenInExternalEditor", p_script, p_line, p_col);
 }
 }
 
 
 bool CSharpLanguage::overrides_external_editor() {
 bool CSharpLanguage::overrides_external_editor() {
 
 
-	return GodotSharpEditor::get_singleton()->overrides_external_editor();
+	return get_godotsharp_editor()->call("OverridesExternalEditor");
 }
 }
 #endif
 #endif
 
 
@@ -1027,6 +1117,34 @@ void CSharpLanguage::_on_scripts_domain_unloaded() {
 	scripts_metadata_invalidated = true;
 	scripts_metadata_invalidated = true;
 }
 }
 
 
+#ifdef TOOLS_ENABLED
+void CSharpLanguage::_editor_init_callback() {
+
+	register_editor_internal_calls();
+
+	// Initialize GodotSharpEditor
+
+	GDMonoClass *editor_klass = GDMono::get_singleton()->get_tools_assembly()->get_class("GodotTools", "GodotSharpEditor");
+	CRASH_COND(editor_klass == NULL);
+
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), editor_klass->get_mono_ptr());
+	CRASH_COND(mono_object == NULL);
+
+	MonoException *exc = NULL;
+	GDMonoUtils::runtime_object_init(mono_object, editor_klass, &exc);
+	UNHANDLED_EXCEPTION(exc);
+
+	EditorPlugin *godotsharp_editor = Object::cast_to<EditorPlugin>(GDMonoMarshal::mono_object_to_variant(mono_object));
+	CRASH_COND(godotsharp_editor == NULL);
+
+	// Enable it as a plugin
+	EditorNode::add_editor_plugin(godotsharp_editor);
+	godotsharp_editor->enable_plugin();
+
+	get_singleton()->godotsharp_editor = godotsharp_editor;
+}
+#endif
+
 void CSharpLanguage::set_language_index(int p_idx) {
 void CSharpLanguage::set_language_index(int p_idx) {
 
 
 	ERR_FAIL_COND(lang_idx != -1);
 	ERR_FAIL_COND(lang_idx != -1);
@@ -1084,6 +1202,8 @@ CSharpLanguage::CSharpLanguage() {
 	lang_idx = -1;
 	lang_idx = -1;
 
 
 	scripts_metadata_invalidated = true;
 	scripts_metadata_invalidated = true;
+
+	godotsharp_editor = NULL;
 }
 }
 
 
 CSharpLanguage::~CSharpLanguage() {
 CSharpLanguage::~CSharpLanguage() {
@@ -1139,6 +1259,7 @@ bool CSharpLanguage::setup_csharp_script_binding(CSharpScriptBinding &r_script_b
 	r_script_binding.type_name = type_name;
 	r_script_binding.type_name = type_name;
 	r_script_binding.wrapper_class = type_class; // cache
 	r_script_binding.wrapper_class = type_class; // cache
 	r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
 	r_script_binding.gchandle = MonoGCHandle::create_strong(mono_object);
+	r_script_binding.owner = p_object;
 
 
 	// Tie managed to unmanaged
 	// Tie managed to unmanaged
 	Reference *ref = Object::cast_to<Reference>(p_object);
 	Reference *ref = Object::cast_to<Reference>(p_object);
@@ -1223,6 +1344,9 @@ void CSharpLanguage::refcount_incremented_instance_binding(Object *p_object) {
 	CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
 	CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
 	Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
 	Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
 
 
+	if (!script_binding.inited)
+		return;
+
 	if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
 	if (ref_owner->reference_get_count() > 1 && gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
 		// The reference count was increased after the managed side was the only one referencing our owner.
 		// The reference count was increased after the managed side was the only one referencing our owner.
 		// This means the owner is being referenced again by the unmanaged side,
 		// This means the owner is being referenced again by the unmanaged side,
@@ -1247,14 +1371,17 @@ bool CSharpLanguage::refcount_decremented_instance_binding(Object *p_object) {
 	CRASH_COND(!ref_owner);
 	CRASH_COND(!ref_owner);
 #endif
 #endif
 
 
-	int refcount = ref_owner->reference_get_count();
-
 	void *data = p_object->get_script_instance_binding(get_language_index());
 	void *data = p_object->get_script_instance_binding(get_language_index());
 	CRASH_COND(!data);
 	CRASH_COND(!data);
 
 
 	CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
 	CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
 	Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
 	Ref<MonoGCHandle> &gchandle = script_binding.gchandle;
 
 
+	int refcount = ref_owner->reference_get_count();
+
+	if (!script_binding.inited)
+		return refcount == 0;
+
 	if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
 	if (refcount == 1 && gchandle.is_valid() && !gchandle->is_weak()) { // The managed side also holds a reference, hence 1 instead of 0
 		// If owner owner is no longer referenced by the unmanaged side,
 		// If owner owner is no longer referenced by the unmanaged side,
 		// the managed instance takes responsibility of deleting the owner when GCed.
 		// the managed instance takes responsibility of deleting the owner when GCed.
@@ -1417,6 +1544,31 @@ bool CSharpInstance::get(const StringName &p_name, Variant &r_ret) const {
 	return false;
 	return false;
 }
 }
 
 
+void CSharpInstance::get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state) {
+
+	List<PropertyInfo> pinfo;
+	get_property_list(&pinfo);
+
+	for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) {
+		Pair<StringName, Variant> state_pair;
+		state_pair.first = E->get().name;
+
+		ManagedType managedType;
+
+		GDMonoField *field = script->script_class->get_field(state_pair.first);
+		if (!field)
+			continue; // Properties ignored. We get the property baking fields instead.
+
+		managedType = field->get_type();
+
+		if (GDMonoMarshal::managed_to_variant_type(managedType) != Variant::NIL) { // If we can marshal it
+			if (get(state_pair.first, state_pair.second)) {
+				r_state.push_back(state_pair);
+			}
+		}
+	}
+}
+
 void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 void CSharpInstance::get_property_list(List<PropertyInfo> *p_properties) const {
 
 
 	for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
 	for (Map<StringName, PropertyInfo>::Element *E = script->member_info.front(); E; E = E->next()) {
@@ -1614,17 +1766,18 @@ MonoObject *CSharpInstance::_internal_new_managed() {
 	ERR_FAIL_NULL_V(owner, NULL);
 	ERR_FAIL_NULL_V(owner, NULL);
 	ERR_FAIL_COND_V(script.is_null(), NULL);
 	ERR_FAIL_COND_V(script.is_null(), NULL);
 
 
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script->script_class->get_mono_ptr());
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), script->script_class->get_mono_ptr());
 
 
 	if (!mono_object) {
 	if (!mono_object) {
 		// Important to clear this before destroying the script instance here
 		// Important to clear this before destroying the script instance here
 		script = Ref<CSharpScript>();
 		script = Ref<CSharpScript>();
-		owner = NULL;
 
 
 		bool die = _unreference_owner_unsafe();
 		bool die = _unreference_owner_unsafe();
 		// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
 		// Not ok for the owner to die here. If there is a situation where this can happen, it will be considered a bug.
 		CRASH_COND(die == true);
 		CRASH_COND(die == true);
 
 
+		owner = NULL;
+
 		ERR_EXPLAIN("Failed to allocate memory for the object");
 		ERR_EXPLAIN("Failed to allocate memory for the object");
 		ERR_FAIL_V(NULL);
 		ERR_FAIL_V(NULL);
 	}
 	}
@@ -1940,7 +2093,16 @@ CSharpInstance::~CSharpInstance() {
 		CRASH_COND(data == NULL);
 		CRASH_COND(data == NULL);
 
 
 		CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
 		CSharpScriptBinding &script_binding = ((Map<Object *, CSharpScriptBinding>::Element *)data)->get();
-		CRASH_COND(!script_binding.inited);
+
+		if (!script_binding.inited) {
+			SCOPED_MUTEX_LOCK(CSharpLanguage::get_singleton()->get_language_bind_mutex());
+
+			if (!script_binding.inited) { // Other thread may have set it up
+				// Already had a binding that needs to be setup
+				CSharpLanguage::get_singleton()->setup_csharp_script_binding(script_binding, owner);
+				CRASH_COND(!script_binding.inited);
+			}
+		}
 
 
 		bool die = _unreference_owner_unsafe();
 		bool die = _unreference_owner_unsafe();
 		CRASH_COND(die == true); // The "instance binding" should be holding a reference
 		CRASH_COND(die == true); // The "instance binding" should be holding a reference
@@ -1984,6 +2146,52 @@ void CSharpScript::_update_exports_values(Map<StringName, Variant> &values, List
 }
 }
 #endif
 #endif
 
 
+void CSharpScript::_update_member_info_no_exports() {
+
+	if (exports_invalidated) {
+		exports_invalidated = false;
+
+		member_info.clear();
+
+		GDMonoClass *top = script_class;
+
+		while (top && top != native) {
+			PropertyInfo prop_info;
+			bool exported;
+
+			const Vector<GDMonoField *> &fields = top->get_all_fields();
+
+			for (int i = fields.size() - 1; i >= 0; i--) {
+				GDMonoField *field = fields[i];
+
+				if (_get_member_export(field, /* inspect export: */ false, prop_info, exported)) {
+					StringName member_name = field->get_name();
+
+					member_info[member_name] = prop_info;
+					exported_members_cache.push_front(prop_info);
+					exported_members_defval_cache[member_name] = Variant();
+				}
+			}
+
+			const Vector<GDMonoProperty *> &properties = top->get_all_properties();
+
+			for (int i = properties.size() - 1; i >= 0; i--) {
+				GDMonoProperty *property = properties[i];
+
+				if (_get_member_export(property, /* inspect export: */ false, prop_info, exported)) {
+					StringName member_name = property->get_name();
+
+					member_info[member_name] = prop_info;
+					exported_members_cache.push_front(prop_info);
+					exported_members_defval_cache[member_name] = Variant();
+				}
+			}
+
+			top = top->get_parent_class();
+		}
+	}
+}
+
 bool CSharpScript::_update_exports() {
 bool CSharpScript::_update_exports() {
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -2008,7 +2216,7 @@ bool CSharpScript::_update_exports() {
 
 
 		// Here we create a temporary managed instance of the class to get the initial values
 		// Here we create a temporary managed instance of the class to get the initial values
 
 
-		MonoObject *tmp_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
+		MonoObject *tmp_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
 
 
 		if (!tmp_object) {
 		if (!tmp_object) {
 			ERR_PRINT("Failed to allocate temporary MonoObject");
 			ERR_PRINT("Failed to allocate temporary MonoObject");
@@ -2049,18 +2257,18 @@ bool CSharpScript::_update_exports() {
 			for (int i = fields.size() - 1; i >= 0; i--) {
 			for (int i = fields.size() - 1; i >= 0; i--) {
 				GDMonoField *field = fields[i];
 				GDMonoField *field = fields[i];
 
 
-				if (_get_member_export(field, prop_info, exported)) {
-					StringName name = field->get_name();
+				if (_get_member_export(field, /* inspect export: */ true, prop_info, exported)) {
+					StringName member_name = field->get_name();
 
 
 					if (exported) {
 					if (exported) {
-						member_info[name] = prop_info;
+						member_info[member_name] = prop_info;
 						exported_members_cache.push_front(prop_info);
 						exported_members_cache.push_front(prop_info);
 
 
 						if (tmp_object) {
 						if (tmp_object) {
-							exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
+							exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(field->get_value(tmp_object));
 						}
 						}
 					} else {
 					} else {
-						member_info[name] = prop_info;
+						member_info[member_name] = prop_info;
 					}
 					}
 				}
 				}
 			}
 			}
@@ -2070,25 +2278,25 @@ bool CSharpScript::_update_exports() {
 			for (int i = properties.size() - 1; i >= 0; i--) {
 			for (int i = properties.size() - 1; i >= 0; i--) {
 				GDMonoProperty *property = properties[i];
 				GDMonoProperty *property = properties[i];
 
 
-				if (_get_member_export(property, prop_info, exported)) {
-					StringName name = property->get_name();
+				if (_get_member_export(property, /* inspect export: */ true, prop_info, exported)) {
+					StringName member_name = property->get_name();
 
 
 					if (exported) {
 					if (exported) {
-						member_info[name] = prop_info;
+						member_info[member_name] = prop_info;
 						exported_members_cache.push_front(prop_info);
 						exported_members_cache.push_front(prop_info);
 
 
 						if (tmp_object) {
 						if (tmp_object) {
 							MonoException *exc = NULL;
 							MonoException *exc = NULL;
 							MonoObject *ret = property->get_value(tmp_object, &exc);
 							MonoObject *ret = property->get_value(tmp_object, &exc);
 							if (exc) {
 							if (exc) {
-								exported_members_defval_cache[name] = Variant();
+								exported_members_defval_cache[member_name] = Variant();
 								GDMonoUtils::debug_print_unhandled_exception(exc);
 								GDMonoUtils::debug_print_unhandled_exception(exc);
 							} else {
 							} else {
-								exported_members_defval_cache[name] = GDMonoMarshal::mono_object_to_variant(ret);
+								exported_members_defval_cache[member_name] = GDMonoMarshal::mono_object_to_variant(ret);
 							}
 							}
 						}
 						}
 					} else {
 					} else {
-						member_info[name] = prop_info;
+						member_info[member_name] = prop_info;
 					}
 					}
 				}
 				}
 			}
 			}
@@ -2197,7 +2405,7 @@ bool CSharpScript::_get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Ve
  * Returns false if there was an error, otherwise true.
  * Returns false if there was an error, otherwise true.
  * If there was an error, r_prop_info and r_exported are not assigned any value.
  * If there was an error, r_prop_info and r_exported are not assigned any value.
  */
  */
-bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported) {
+bool CSharpScript::_get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported) {
 
 
 	// Goddammit, C++. All I wanted was some nested functions.
 	// Goddammit, C++. All I wanted was some nested functions.
 #define MEMBER_FULL_QUALIFIED_NAME(m_member) \
 #define MEMBER_FULL_QUALIFIED_NAME(m_member) \
@@ -2222,26 +2430,30 @@ bool CSharpScript::_get_member_export(IMonoClassMember *p_member, PropertyInfo &
 		CRASH_NOW();
 		CRASH_NOW();
 	}
 	}
 
 
-	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
-
-	if (!p_member->has_attribute(CACHED_CLASS(ExportAttribute))) {
-		r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
-		r_exported = false;
-		return true;
-	}
+	bool exported = p_member->has_attribute(CACHED_CLASS(ExportAttribute));
 
 
 	if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
 	if (p_member->get_member_type() == IMonoClassMember::MEMBER_TYPE_PROPERTY) {
 		GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
 		GDMonoProperty *property = static_cast<GDMonoProperty *>(p_member);
 		if (!property->has_getter()) {
 		if (!property->has_getter()) {
-			ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+			if (exported)
+				ERR_PRINTS("Read-only property cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
 			return false;
 			return false;
 		}
 		}
 		if (!property->has_setter()) {
 		if (!property->has_setter()) {
-			ERR_PRINTS("Set-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
+			if (exported)
+				ERR_PRINTS("Write-only property (without getter) cannot be exported: " + MEMBER_FULL_QUALIFIED_NAME(p_member));
 			return false;
 			return false;
 		}
 		}
 	}
 	}
 
 
+	Variant::Type variant_type = GDMonoMarshal::managed_to_variant_type(type);
+
+	if (!p_inspect_export || !exported) {
+		r_prop_info = PropertyInfo(variant_type, (String)p_member->get_name(), PROPERTY_HINT_NONE, "", PROPERTY_USAGE_SCRIPT_VARIABLE);
+		r_exported = false;
+		return true;
+	}
+
 	MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
 	MonoObject *attr = p_member->get_attribute(CACHED_CLASS(ExportAttribute));
 
 
 	PropertyHint hint = PROPERTY_HINT_NONE;
 	PropertyHint hint = PROPERTY_HINT_NONE;
@@ -2463,9 +2675,9 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
 
 
 	// This method should not fail
 	// This method should not fail
 
 
-	CRASH_COND(!p_class);
+	CRASH_COND(p_class == NULL);
 
 
-	// TODO: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
+	// TODO OPTIMIZE: Cache the 'CSharpScript' associated with this 'p_class' instead of allocating a new one every time
 	Ref<CSharpScript> script = memnew(CSharpScript);
 	Ref<CSharpScript> script = memnew(CSharpScript);
 
 
 	script->name = p_class->get_name();
 	script->name = p_class->get_name();
@@ -2479,6 +2691,20 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
 	if (base != script->native)
 	if (base != script->native)
 		script->base = base;
 		script->base = base;
 
 
+	script->valid = true;
+	script->tool = script->script_class->has_attribute(CACHED_CLASS(ToolAttribute));
+
+	if (!script->tool) {
+		GDMonoClass *nesting_class = script->script_class->get_nesting_class();
+		script->tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
+	}
+
+#if TOOLS_ENABLED
+	if (!script->tool) {
+		script->tool = script->script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
+	}
+#endif
+
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	// For debug builds, we must fetch from all native base methods as well.
 	// For debug builds, we must fetch from all native base methods as well.
 	// Native base methods must be fetched before the current class.
 	// Native base methods must be fetched before the current class.
@@ -2507,6 +2733,7 @@ Ref<CSharpScript> CSharpScript::create_for_managed_type(GDMonoClass *p_class, GD
 	}
 	}
 
 
 	script->load_script_signals(script->script_class, script->native);
 	script->load_script_signals(script->script_class, script->native);
+	script->_update_member_info_no_exports();
 
 
 	return script;
 	return script;
 }
 }
@@ -2516,7 +2743,8 @@ bool CSharpScript::can_instance() const {
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	if (Engine::get_singleton()->is_editor_hint()) {
 	if (Engine::get_singleton()->is_editor_hint()) {
 
 
-		if (get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
+		// Hack to lower the risk of attached scripts not being added to the C# project
+		if (!get_path().empty() && get_path().find("::") == -1) { // Ignore if built-in script. Can happen if the file is deleted...
 			if (_create_project_solution_if_needed()) {
 			if (_create_project_solution_if_needed()) {
 				CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
 				CSharpProject::add_item(GodotSharpDirs::get_project_csproj_path(),
 						"Compile",
 						"Compile",
@@ -2568,7 +2796,9 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
 	GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
 	GDMonoMethod *ctor = script_class->get_method(CACHED_STRING_NAME(dotctor), p_argcount);
 	if (ctor == NULL) {
 	if (ctor == NULL) {
 		if (p_argcount == 0) {
 		if (p_argcount == 0) {
-			ERR_PRINTS("Cannot create script instance because the class does not define a parameterless constructor: " + get_path());
+			String path = get_path();
+			ERR_PRINTS("Cannot create script instance. The class '" + script_class->get_full_name() +
+					   "' does not define a parameterless constructor." + (path.empty() ? String() : ". Path: " + path));
 		}
 		}
 
 
 		ERR_EXPLAIN("Constructor not found");
 		ERR_EXPLAIN("Constructor not found");
@@ -2610,7 +2840,7 @@ CSharpInstance *CSharpScript::_create_instance(const Variant **p_args, int p_arg
 
 
 	/* STEP 2, INITIALIZE AND CONSTRUCT */
 	/* STEP 2, INITIALIZE AND CONSTRUCT */
 
 
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, script_class->get_mono_ptr());
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), script_class->get_mono_ptr());
 
 
 	if (!mono_object) {
 	if (!mono_object) {
 		// Important to clear this before destroying the script instance here
 		// Important to clear this before destroying the script instance here
@@ -2691,7 +2921,7 @@ ScriptInstance *CSharpScript::instance_create(Object *p_this) {
 #endif
 #endif
 
 
 	if (native) {
 	if (native) {
-		String native_name = native->get_name();
+		String native_name = NATIVE_GDMONOCLASS_NAME(native);
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
 		if (!ClassDB::is_parent_class(p_this->get_class_name(), native_name)) {
 			if (ScriptDebugger::get_singleton()) {
 			if (ScriptDebugger::get_singleton()) {
 				CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
 				CSharpLanguage::get_singleton()->debug_break_parse(get_path(), 0, "Script inherits from native type '" + native_name + "', so it can't be instanced in object of type: '" + p_this->get_class() + "'");
@@ -2817,11 +3047,22 @@ Error CSharpScript::reload(bool p_keep_state) {
 
 
 		if (script_class) {
 		if (script_class) {
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
-			print_verbose("Found class " + script_class->get_namespace() + "." + script_class->get_name() + " for script " + get_path());
+			print_verbose("Found class " + script_class->get_full_name() + " for script " + get_path());
 #endif
 #endif
 
 
 			tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
 			tool = script_class->has_attribute(CACHED_CLASS(ToolAttribute));
 
 
+			if (!tool) {
+				GDMonoClass *nesting_class = script_class->get_nesting_class();
+				tool = nesting_class && nesting_class->has_attribute(CACHED_CLASS(ToolAttribute));
+			}
+
+#if TOOLS_ENABLED
+			if (!tool) {
+				tool = script_class->get_assembly() == GDMono::get_singleton()->get_tools_assembly();
+			}
+#endif
+
 			native = GDMonoUtils::get_class_native_base(script_class);
 			native = GDMonoUtils::get_class_native_base(script_class);
 
 
 			CRASH_COND(native == NULL);
 			CRASH_COND(native == NULL);
@@ -3019,7 +3260,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
 #endif
 #endif
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	if (Engine::get_singleton()->is_editor_hint() && mono_domain_get() == NULL) {
+	MonoDomain *domain = mono_domain_get();
+	if (Engine::get_singleton()->is_editor_hint() && domain == NULL) {
 
 
 		CRASH_COND(Thread::get_caller_id() == Thread::get_main_id());
 		CRASH_COND(Thread::get_caller_id() == Thread::get_main_id());
 
 
@@ -3027,8 +3269,8 @@ RES ResourceFormatLoaderCSharpScript::load(const String &p_path, const String &p
 		// because this may be called by one of the editor's worker threads.
 		// because this may be called by one of the editor's worker threads.
 		// Attach this thread temporarily to reload the script.
 		// Attach this thread temporarily to reload the script.
 
 
-		if (SCRIPTS_DOMAIN) {
-			MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
+		if (domain) {
+			MonoThread *mono_thread = mono_thread_attach(domain);
 			CRASH_COND(mono_thread == NULL);
 			CRASH_COND(mono_thread == NULL);
 			script->reload();
 			script->reload();
 			mono_thread_detach(mono_thread);
 			mono_thread_detach(mono_thread);
@@ -3128,5 +3370,7 @@ CSharpLanguage::StringNameCache::StringNameCache() {
 	_get_property_list = StaticCString::create("_get_property_list");
 	_get_property_list = StaticCString::create("_get_property_list");
 	_notification = StaticCString::create("_notification");
 	_notification = StaticCString::create("_notification");
 	_script_source = StaticCString::create("script/source");
 	_script_source = StaticCString::create("script/source");
+	on_before_serialize = StaticCString::create("OnBeforeSerialize");
+	on_after_deserialize = StaticCString::create("OnAfterDeserialize");
 	dotctor = StaticCString::create(".ctor");
 	dotctor = StaticCString::create(".ctor");
 }
 }

+ 21 - 1
modules/mono/csharp_script.h

@@ -41,6 +41,10 @@
 #include "mono_gd/gd_mono_header.h"
 #include "mono_gd/gd_mono_header.h"
 #include "mono_gd/gd_mono_internals.h"
 #include "mono_gd/gd_mono_internals.h"
 
 
+#ifdef TOOLS_ENABLED
+#include "editor/editor_plugin.h"
+#endif
+
 class CSharpScript;
 class CSharpScript;
 class CSharpInstance;
 class CSharpInstance;
 class CSharpLanguage;
 class CSharpLanguage;
@@ -92,6 +96,8 @@ class CSharpScript : public Script {
 
 
 	Set<ObjectID> pending_reload_instances;
 	Set<ObjectID> pending_reload_instances;
 	Map<ObjectID, StateBackup> pending_reload_state;
 	Map<ObjectID, StateBackup> pending_reload_state;
+	StringName tied_class_name_for_reload;
+	StringName tied_class_namespace_for_reload;
 #endif
 #endif
 
 
 	String source;
 	String source;
@@ -125,9 +131,10 @@ class CSharpScript : public Script {
 	void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
 	void load_script_signals(GDMonoClass *p_class, GDMonoClass *p_native_class);
 	bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
 	bool _get_signal(GDMonoClass *p_class, GDMonoClass *p_delegate, Vector<Argument> &params);
 
 
+	void _update_member_info_no_exports();
 	bool _update_exports();
 	bool _update_exports();
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-	bool _get_member_export(IMonoClassMember *p_member, PropertyInfo &r_prop_info, bool &r_exported);
+	bool _get_member_export(IMonoClassMember *p_member, bool p_inspect_export, PropertyInfo &r_prop_info, bool &r_exported);
 	static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
 	static int _try_get_member_export_hint(IMonoClassMember *p_member, ManagedType p_type, Variant::Type p_variant_type, bool p_allow_generics, PropertyHint &r_hint, String &r_hint_string);
 #endif
 #endif
 
 
@@ -226,6 +233,8 @@ class CSharpInstance : public ScriptInstance {
 
 
 	MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
 	MultiplayerAPI::RPCMode _member_get_rpc_mode(IMonoClassMember *p_member) const;
 
 
+	void get_properties_state_for_reloading(List<Pair<StringName, Variant> > &r_state);
+
 public:
 public:
 	MonoObject *get_mono_object() const;
 	MonoObject *get_mono_object() const;
 
 
@@ -276,6 +285,7 @@ struct CSharpScriptBinding {
 	StringName type_name;
 	StringName type_name;
 	GDMonoClass *wrapper_class;
 	GDMonoClass *wrapper_class;
 	Ref<MonoGCHandle> gchandle;
 	Ref<MonoGCHandle> gchandle;
+	Object *owner;
 };
 };
 
 
 class CSharpLanguage : public ScriptLanguage {
 class CSharpLanguage : public ScriptLanguage {
@@ -305,6 +315,8 @@ class CSharpLanguage : public ScriptLanguage {
 		StringName _notification;
 		StringName _notification;
 		StringName _script_source;
 		StringName _script_source;
 		StringName dotctor; // .ctor
 		StringName dotctor; // .ctor
+		StringName on_before_serialize; // OnBeforeSerialize
+		StringName on_after_deserialize; // OnAfterDeserialize
 
 
 		StringNameCache();
 		StringNameCache();
 	};
 	};
@@ -324,6 +336,12 @@ class CSharpLanguage : public ScriptLanguage {
 	friend class GDMono;
 	friend class GDMono;
 	void _on_scripts_domain_unloaded();
 	void _on_scripts_domain_unloaded();
 
 
+#ifdef TOOLS_ENABLED
+	EditorPlugin *godotsharp_editor;
+
+	static void _editor_init_callback();
+#endif
+
 public:
 public:
 	StringNameCache string_names;
 	StringNameCache string_names;
 
 
@@ -336,6 +354,8 @@ public:
 
 
 	_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
 	_FORCE_INLINE_ static CSharpLanguage *get_singleton() { return singleton; }
 
 
+	_FORCE_INLINE_ EditorPlugin *get_godotsharp_editor() const { return godotsharp_editor; }
+
 	static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle);
 	static void release_script_gchandle(Ref<MonoGCHandle> &p_gchandle);
 	static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle);
 	static void release_script_gchandle(MonoObject *p_expected_obj, Ref<MonoGCHandle> &p_gchandle);
 
 

+ 0 - 2
modules/mono/editor/GodotSharpTools/.gitignore

@@ -1,2 +0,0 @@
-# nuget packages
-packages

+ 0 - 425
modules/mono/editor/GodotSharpTools/Build/BuildSystem.cs

@@ -1,425 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Diagnostics;
-using System.IO;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using System.Security;
-using Microsoft.Build.Framework;
-
-namespace GodotSharpTools.Build
-{
-    public class BuildInstance : IDisposable
-    {
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static void godot_icall_BuildInstance_ExitCallback(string solution, string config, int exitCode);
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static string godot_icall_BuildInstance_get_MSBuildPath();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static string godot_icall_BuildInstance_get_MonoWindowsBinDir();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static bool godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static bool godot_icall_BuildInstance_get_PrintBuildOutput();
-
-        private static string GetMSBuildPath()
-        {
-            string msbuildPath = godot_icall_BuildInstance_get_MSBuildPath();
-
-            if (msbuildPath == null)
-                throw new FileNotFoundException("Cannot find the MSBuild executable.");
-
-            return msbuildPath;
-        }
-
-        private static string MonoWindowsBinDir
-        {
-            get
-            {
-                string monoWinBinDir = godot_icall_BuildInstance_get_MonoWindowsBinDir();
-
-                if (monoWinBinDir == null)
-                    throw new FileNotFoundException("Cannot find the Windows Mono binaries directory.");
-
-                return monoWinBinDir;
-            }
-        }
-
-        private static bool UsingMonoMSBuildOnWindows
-        {
-            get
-            {
-                return godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows();
-            }
-        }
-
-        private static bool PrintBuildOutput
-        {
-            get
-            {
-                return godot_icall_BuildInstance_get_PrintBuildOutput();
-            }
-        }
-
-        private string solution;
-        private string config;
-
-        private Process process;
-
-        private int exitCode;
-        public int ExitCode { get { return exitCode; } }
-
-        public bool IsRunning { get { return process != null && !process.HasExited; } }
-
-        public BuildInstance(string solution, string config)
-        {
-            this.solution = solution;
-            this.config = config;
-        }
-
-        public bool Build(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
-        {
-            List<string> customPropertiesList = new List<string>();
-
-            if (customProperties != null)
-                customPropertiesList.AddRange(customProperties);
-
-            string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
-
-            ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
-
-            bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
-
-            if (!redirectOutput) // TODO: or if stdout verbose
-                Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
-
-            startInfo.RedirectStandardOutput = redirectOutput;
-            startInfo.RedirectStandardError = redirectOutput;
-            startInfo.UseShellExecute = false;
-
-            if (UsingMonoMSBuildOnWindows)
-            {
-                // These environment variables are required for Mono's MSBuild to find the compilers.
-                // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
-                string monoWinBinDir = MonoWindowsBinDir;
-                startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
-                startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
-                startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
-            }
-
-            // Needed when running from Developer Command Prompt for VS
-            RemovePlatformVariable(startInfo.EnvironmentVariables);
-
-            using (Process process = new Process())
-            {
-                process.StartInfo = startInfo;
-
-                process.Start();
-
-                if (redirectOutput)
-                {
-                    process.BeginOutputReadLine();
-                    process.BeginErrorReadLine();
-                }
-
-                process.WaitForExit();
-
-                exitCode = process.ExitCode;
-            }
-
-            return true;
-        }
-
-        public bool BuildAsync(string loggerAssemblyPath, string loggerOutputDir, string[] customProperties = null)
-        {
-            if (process != null)
-                throw new InvalidOperationException("Already in use");
-
-            List<string> customPropertiesList = new List<string>();
-
-            if (customProperties != null)
-                customPropertiesList.AddRange(customProperties);
-
-            string compilerArgs = BuildArguments(loggerAssemblyPath, loggerOutputDir, customPropertiesList);
-
-            ProcessStartInfo startInfo = new ProcessStartInfo(GetMSBuildPath(), compilerArgs);
-
-            bool redirectOutput = !IsDebugMSBuildRequested() && !PrintBuildOutput;
-
-            if (!redirectOutput) // TODO: or if stdout verbose
-                Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
-
-            startInfo.RedirectStandardOutput = redirectOutput;
-            startInfo.RedirectStandardError = redirectOutput;
-            startInfo.UseShellExecute = false;
-
-            if (UsingMonoMSBuildOnWindows)
-            {
-                // These environment variables are required for Mono's MSBuild to find the compilers.
-                // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
-                string monoWinBinDir = MonoWindowsBinDir;
-                startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
-                startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
-                startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
-            }
-
-            // Needed when running from Developer Command Prompt for VS
-            RemovePlatformVariable(startInfo.EnvironmentVariables);
-
-            process = new Process();
-            process.StartInfo = startInfo;
-            process.EnableRaisingEvents = true;
-            process.Exited += new EventHandler(BuildProcess_Exited);
-
-            process.Start();
-
-            if (redirectOutput)
-            {
-                process.BeginOutputReadLine();
-                process.BeginErrorReadLine();
-            }
-
-            return true;
-        }
-
-        private string BuildArguments(string loggerAssemblyPath, string loggerOutputDir, List<string> customProperties)
-        {
-            string arguments = string.Format(@"""{0}"" /v:normal /t:Rebuild ""/p:{1}"" ""/l:{2},{3};{4}""",
-                solution,
-                "Configuration=" + config,
-                typeof(GodotBuildLogger).FullName,
-                loggerAssemblyPath,
-                loggerOutputDir
-            );
-
-            foreach (string customProperty in customProperties)
-            {
-                arguments += " /p:" + customProperty;
-            }
-
-            return arguments;
-        }
-
-        private void RemovePlatformVariable(StringDictionary environmentVariables)
-        {
-            // EnvironmentVariables is case sensitive? Seriously?
-
-            List<string> platformEnvironmentVariables = new List<string>();
-
-            foreach (string env in environmentVariables.Keys)
-            {
-                if (env.ToUpper() == "PLATFORM")
-                    platformEnvironmentVariables.Add(env);
-            }
-
-            foreach (string env in platformEnvironmentVariables)
-                environmentVariables.Remove(env);
-        }
-
-        private void BuildProcess_Exited(object sender, System.EventArgs e)
-        {
-            exitCode = process.ExitCode;
-
-            godot_icall_BuildInstance_ExitCallback(solution, config, exitCode);
-
-            Dispose();
-        }
-
-        private static bool IsDebugMSBuildRequested()
-        {
-            return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
-        }
-
-        public void Dispose()
-        {
-            if (process != null)
-            {
-                process.Dispose();
-                process = null;
-            }
-        }
-    }
-
-    public class GodotBuildLogger : ILogger
-    {
-        public string Parameters { get; set; }
-        public LoggerVerbosity Verbosity { get; set; }
-
-        public void Initialize(IEventSource eventSource)
-        {
-            if (null == Parameters)
-                throw new LoggerException("Log directory was not set.");
-
-            string[] parameters = Parameters.Split(new[] { ';' });
-
-            string logDir = parameters[0];
-
-            if (String.IsNullOrEmpty(logDir))
-                throw new LoggerException("Log directory was not set.");
-
-            if (parameters.Length > 1)
-                throw new LoggerException("Too many parameters passed.");
-
-            string logFile = Path.Combine(logDir, "msbuild_log.txt");
-            string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
-
-            try
-            {
-                if (!Directory.Exists(logDir))
-                    Directory.CreateDirectory(logDir);
-
-                this.logStreamWriter = new StreamWriter(logFile);
-                this.issuesStreamWriter = new StreamWriter(issuesFile);
-            }
-            catch (Exception ex)
-            {
-                if
-                (
-                    ex is UnauthorizedAccessException
-                    || ex is ArgumentNullException
-                    || ex is PathTooLongException
-                    || ex is DirectoryNotFoundException
-                    || ex is NotSupportedException
-                    || ex is ArgumentException
-                    || ex is SecurityException
-                    || ex is IOException
-                )
-                {
-                    throw new LoggerException("Failed to create log file: " + ex.Message);
-                }
-                else
-                {
-                    // Unexpected failure
-                    throw;
-                }
-            }
-
-            eventSource.ProjectStarted += new ProjectStartedEventHandler(eventSource_ProjectStarted);
-            eventSource.TaskStarted += new TaskStartedEventHandler(eventSource_TaskStarted);
-            eventSource.MessageRaised += new BuildMessageEventHandler(eventSource_MessageRaised);
-            eventSource.WarningRaised += new BuildWarningEventHandler(eventSource_WarningRaised);
-            eventSource.ErrorRaised += new BuildErrorEventHandler(eventSource_ErrorRaised);
-            eventSource.ProjectFinished += new ProjectFinishedEventHandler(eventSource_ProjectFinished);
-        }
-
-        void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
-        {
-            string line = String.Format("{0}({1},{2}): error {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message);
-
-            if (e.ProjectFile.Length > 0)
-                line += string.Format(" [{0}]", e.ProjectFile);
-
-            WriteLine(line);
-
-            string errorLine = String.Format(@"error,{0},{1},{2},{3},{4},{5}",
-                                    e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
-                                    e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile.CsvEscape());
-            issuesStreamWriter.WriteLine(errorLine);
-        }
-
-        void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
-        {
-            string line = String.Format("{0}({1},{2}): warning {3}: {4}", e.File, e.LineNumber, e.ColumnNumber, e.Code, e.Message, e.ProjectFile);
-
-            if (e.ProjectFile != null && e.ProjectFile.Length > 0)
-                line += string.Format(" [{0}]", e.ProjectFile);
-
-            WriteLine(line);
-
-            string warningLine = String.Format(@"warning,{0},{1},{2},{3},{4},{5}",
-                                    e.File.CsvEscape(), e.LineNumber, e.ColumnNumber,
-                                    e.Code.CsvEscape(), e.Message.CsvEscape(), e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty);
-            issuesStreamWriter.WriteLine(warningLine);
-        }
-
-        void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
-        {
-            // BuildMessageEventArgs adds Importance to BuildEventArgs
-            // Let's take account of the verbosity setting we've been passed in deciding whether to log the message
-            if ((e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal))
-                || (e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal))
-                || (e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
-                )
-            {
-                WriteLineWithSenderAndMessage(String.Empty, e);
-            }
-        }
-
-        void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
-        {
-            // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
-            // To keep this log clean, this logger will ignore these events.
-        }
-
-        void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
-        {
-            WriteLine(e.Message);
-            indent++;
-        }
-
-        void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
-        {
-            indent--;
-            WriteLine(e.Message);
-        }
-
-        /// <summary>
-        /// Write a line to the log, adding the SenderName
-        /// </summary>
-        private void WriteLineWithSender(string line, BuildEventArgs e)
-        {
-            if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
-            {
-                // Well, if the sender name is MSBuild, let's leave it out for prettiness
-                WriteLine(line);
-            }
-            else
-            {
-                WriteLine(e.SenderName + ": " + line);
-            }
-        }
-
-        /// <summary>
-        /// Write a line to the log, adding the SenderName and Message
-        /// (these parameters are on all MSBuild event argument objects)
-        /// </summary>
-        private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
-        {
-            if (0 == String.Compare(e.SenderName, "MSBuild", true /*ignore case*/))
-            {
-                // Well, if the sender name is MSBuild, let's leave it out for prettiness
-                WriteLine(line + e.Message);
-            }
-            else
-            {
-                WriteLine(e.SenderName + ": " + line + e.Message);
-            }
-        }
-
-        private void WriteLine(string line)
-        {
-            for (int i = indent; i > 0; i--)
-            {
-                logStreamWriter.Write("\t");
-            }
-            logStreamWriter.WriteLine(line);
-        }
-
-        public void Shutdown()
-        {
-            logStreamWriter.Close();
-            issuesStreamWriter.Close();
-        }
-
-        public bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
-        {
-            return this.Verbosity >= checkVerbosity;
-        }
-
-        private StreamWriter logStreamWriter;
-        private StreamWriter issuesStreamWriter;
-        private int indent;
-    }
-}

+ 0 - 75
modules/mono/editor/GodotSharpTools/Editor/GodotSharpExport.cs

@@ -1,75 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Runtime.CompilerServices;
-
-namespace GodotSharpTools.Editor
-{
-    public static class GodotSharpExport
-    {
-        public static void _ExportBegin(string[] features, bool debug, string path, int flags)
-        {
-            var featureSet = new HashSet<string>(features);
-
-            if (PlatformHasTemplateDir(featureSet))
-            {
-                string templateDirName = "data.mono";
-
-                if (featureSet.Contains("Windows"))
-                {
-                    templateDirName += ".windows";
-                    templateDirName += featureSet.Contains("64") ? ".64" : ".32";
-                }
-                else if (featureSet.Contains("X11"))
-                {
-                    templateDirName += ".x11";
-                    templateDirName += featureSet.Contains("64") ? ".64" : ".32";
-                }
-                else
-                {
-                    throw new NotSupportedException("Target platform not supported");
-                }
-
-                templateDirName += debug ? ".release_debug" : ".release";
-
-                string templateDirPath = Path.Combine(GetTemplatesDir(), templateDirName);
-
-                if (!Directory.Exists(templateDirPath))
-                    throw new FileNotFoundException("Data template directory not found");
-
-                string outputDir = new FileInfo(path).Directory.FullName;
-
-                string outputDataDir = Path.Combine(outputDir, GetDataDirName());
-
-                if (Directory.Exists(outputDataDir))
-                    Directory.Delete(outputDataDir, recursive: true); // Clean first
-
-                Directory.CreateDirectory(outputDataDir);
-
-                foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
-                {
-                    Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
-                }
-
-                foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
-                {
-                    File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
-                }
-            }
-        }
-
-        public static bool PlatformHasTemplateDir(HashSet<string> featureSet)
-        {
-            // OSX export templates are contained in a zip, so we place
-            // our custom template inside it and let Godot do the rest.
-            return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f));
-        }
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        extern static string GetTemplatesDir();
-
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        extern static string GetDataDirName();
-    }
-}

+ 0 - 17
modules/mono/editor/GodotSharpTools/GodotSharpTools.sln

@@ -1,17 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GodotSharpTools", "GodotSharpTools.csproj", "{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-EndGlobal

+ 0 - 62
modules/mono/editor/GodotSharpTools/Utils/OS.cs

@@ -1,62 +0,0 @@
-using System;
-using System.Linq;
-using System.Runtime.CompilerServices;
-
-namespace GodotSharpTools.Utils
-{
-    public static class OS
-    {
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        extern static string GetPlatformName();
-
-        const string HaikuName = "Haiku";
-        const string OSXName = "OSX";
-        const string ServerName = "Server";
-        const string UWPName = "UWP";
-        const string WindowsName = "Windows";
-        const string X11Name = "X11";
-
-        public static bool IsHaiku()
-        {
-            return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        public static bool IsOSX()
-        {
-            return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        public static bool IsServer()
-        {
-            return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        public static bool IsUWP()
-        {
-            return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        public static bool IsWindows()
-        {
-            return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        public static bool IsX11()
-        {
-            return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
-        }
-
-        static bool? IsUnixCache = null;
-        static readonly string[] UnixPlatforms = new string[] { HaikuName, OSXName, ServerName, X11Name };
-
-        public static bool IsUnix()
-        {
-            if (IsUnixCache.HasValue)
-                return IsUnixCache.Value;
-
-            string osName = GetPlatformName();
-            IsUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
-            return IsUnixCache.Value;
-        }
-    }
-}

+ 356 - 0
modules/mono/editor/GodotTools/.gitignore

@@ -0,0 +1,356 @@
+# Rider
+.idea/
+
+# Visual Studio Code
+.vscode/
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+

+ 186 - 0
modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotBuildLogger.cs

@@ -0,0 +1,186 @@
+using System;
+using System.IO;
+using System.Security;
+using Microsoft.Build.Framework;
+using GodotTools.Core;
+
+namespace GodotTools.BuildLogger
+{
+    public class GodotBuildLogger : ILogger
+    {
+        public static readonly string AssemblyPath = Path.GetFullPath(typeof(GodotBuildLogger).Assembly.Location);
+
+        public string Parameters { get; set; }
+        public LoggerVerbosity Verbosity { get; set; }
+
+        public void Initialize(IEventSource eventSource)
+        {
+            if (null == Parameters)
+                throw new LoggerException("Log directory was not set.");
+
+            var parameters = Parameters.Split(new[] {';'});
+
+            string logDir = parameters[0];
+
+            if (string.IsNullOrEmpty(logDir))
+                throw new LoggerException("Log directory was not set.");
+
+            if (parameters.Length > 1)
+                throw new LoggerException("Too many parameters passed.");
+
+            string logFile = Path.Combine(logDir, "msbuild_log.txt");
+            string issuesFile = Path.Combine(logDir, "msbuild_issues.csv");
+
+            try
+            {
+                if (!Directory.Exists(logDir))
+                    Directory.CreateDirectory(logDir);
+
+                logStreamWriter = new StreamWriter(logFile);
+                issuesStreamWriter = new StreamWriter(issuesFile);
+            }
+            catch (Exception ex)
+            {
+                if (ex is UnauthorizedAccessException
+                    || ex is ArgumentNullException
+                    || ex is PathTooLongException
+                    || ex is DirectoryNotFoundException
+                    || ex is NotSupportedException
+                    || ex is ArgumentException
+                    || ex is SecurityException
+                    || ex is IOException)
+                {
+                    throw new LoggerException("Failed to create log file: " + ex.Message);
+                }
+                else
+                {
+                    // Unexpected failure
+                    throw;
+                }
+            }
+
+            eventSource.ProjectStarted += eventSource_ProjectStarted;
+            eventSource.TaskStarted += eventSource_TaskStarted;
+            eventSource.MessageRaised += eventSource_MessageRaised;
+            eventSource.WarningRaised += eventSource_WarningRaised;
+            eventSource.ErrorRaised += eventSource_ErrorRaised;
+            eventSource.ProjectFinished += eventSource_ProjectFinished;
+        }
+
+        void eventSource_ErrorRaised(object sender, BuildErrorEventArgs e)
+        {
+            string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): error {e.Code}: {e.Message}";
+
+            if (e.ProjectFile.Length > 0)
+                line += $" [{e.ProjectFile}]";
+
+            WriteLine(line);
+
+            string errorLine = $@"error,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber}," +
+                               $@"{e.Code.CsvEscape()},{e.Message.CsvEscape()},{e.ProjectFile.CsvEscape()}";
+            issuesStreamWriter.WriteLine(errorLine);
+        }
+
+        void eventSource_WarningRaised(object sender, BuildWarningEventArgs e)
+        {
+            string line = $"{e.File}({e.LineNumber},{e.ColumnNumber}): warning {e.Code}: {e.Message}";
+
+            if (!string.IsNullOrEmpty(e.ProjectFile))
+                line += $" [{e.ProjectFile}]";
+
+            WriteLine(line);
+
+            string warningLine = $@"warning,{e.File.CsvEscape()},{e.LineNumber},{e.ColumnNumber},{e.Code.CsvEscape()}," +
+                                 $@"{e.Message.CsvEscape()},{(e.ProjectFile != null ? e.ProjectFile.CsvEscape() : string.Empty)}";
+            issuesStreamWriter.WriteLine(warningLine);
+        }
+
+        private void eventSource_MessageRaised(object sender, BuildMessageEventArgs e)
+        {
+            // BuildMessageEventArgs adds Importance to BuildEventArgs
+            // Let's take account of the verbosity setting we've been passed in deciding whether to log the message
+            if (e.Importance == MessageImportance.High && IsVerbosityAtLeast(LoggerVerbosity.Minimal)
+                || e.Importance == MessageImportance.Normal && IsVerbosityAtLeast(LoggerVerbosity.Normal)
+                || e.Importance == MessageImportance.Low && IsVerbosityAtLeast(LoggerVerbosity.Detailed))
+            {
+                WriteLineWithSenderAndMessage(string.Empty, e);
+            }
+        }
+
+        private void eventSource_TaskStarted(object sender, TaskStartedEventArgs e)
+        {
+            // TaskStartedEventArgs adds ProjectFile, TaskFile, TaskName
+            // To keep this log clean, this logger will ignore these events.
+        }
+
+        private void eventSource_ProjectStarted(object sender, ProjectStartedEventArgs e)
+        {
+            WriteLine(e.Message);
+            indent++;
+        }
+
+        private void eventSource_ProjectFinished(object sender, ProjectFinishedEventArgs e)
+        {
+            indent--;
+            WriteLine(e.Message);
+        }
+
+        /// <summary>
+        /// Write a line to the log, adding the SenderName
+        /// </summary>
+        private void WriteLineWithSender(string line, BuildEventArgs e)
+        {
+            if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
+            {
+                // Well, if the sender name is MSBuild, let's leave it out for prettiness
+                WriteLine(line);
+            }
+            else
+            {
+                WriteLine(e.SenderName + ": " + line);
+            }
+        }
+
+        /// <summary>
+        /// Write a line to the log, adding the SenderName and Message
+        /// (these parameters are on all MSBuild event argument objects)
+        /// </summary>
+        private void WriteLineWithSenderAndMessage(string line, BuildEventArgs e)
+        {
+            if (0 == string.Compare(e.SenderName, "MSBuild", StringComparison.OrdinalIgnoreCase))
+            {
+                // Well, if the sender name is MSBuild, let's leave it out for prettiness
+                WriteLine(line + e.Message);
+            }
+            else
+            {
+                WriteLine(e.SenderName + ": " + line + e.Message);
+            }
+        }
+
+        private void WriteLine(string line)
+        {
+            for (int i = indent; i > 0; i--)
+            {
+                logStreamWriter.Write("\t");
+            }
+
+            logStreamWriter.WriteLine(line);
+        }
+
+        public void Shutdown()
+        {
+            logStreamWriter.Close();
+            issuesStreamWriter.Close();
+        }
+
+        private bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity)
+        {
+            return Verbosity >= checkVerbosity;
+        }
+
+        private StreamWriter logStreamWriter;
+        private StreamWriter issuesStreamWriter;
+        private int indent;
+    }
+}

+ 59 - 0
modules/mono/editor/GodotTools/GodotTools.BuildLogger/GodotTools.BuildLogger.csproj

@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{6CE9A984-37B1-4F8A-8FE9-609F05F071B3}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>GodotTools.BuildLogger</RootNamespace>
+    <AssemblyName>GodotTools.BuildLogger</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>portable</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>portable</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Microsoft.Build.Framework" />
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="GodotBuildLogger.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
+      <Project>{639e48bd-44e5-4091-8edd-22d36dc0768d}</Project>
+      <Name>GodotTools.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+         Other similar extension points exist, see Microsoft.Common.targets.
+    <Target Name="BeforeBuild">
+    </Target>
+    <Target Name="AfterBuild">
+    </Target>
+    -->
+</Project>

+ 35 - 0
modules/mono/editor/GodotTools/GodotTools.BuildLogger/Properties/AssemblyInfo.cs

@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("GodotTools.BuildLogger")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Godot Engine contributors")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("6CE9A984-37B1-4F8A-8FE9-609F05F071B3")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 38 - 0
modules/mono/editor/GodotTools/GodotTools.Core/GodotTools.Core.csproj

@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>GodotTools.Core</RootNamespace>
+    <AssemblyName>GodotTools.Core</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="ProcessExtensions.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="StringExtensions.cs" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>

+ 38 - 0
modules/mono/editor/GodotTools/GodotTools.Core/ProcessExtensions.cs

@@ -0,0 +1,38 @@
+using System;
+using System.Diagnostics;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace GodotTools.Core
+{
+    public static class ProcessExtensions
+    {
+        public static async Task WaitForExitAsync(this Process process, CancellationToken cancellationToken = default(CancellationToken))
+        {
+            var tcs = new TaskCompletionSource<bool>();
+
+            void ProcessExited(object sender, EventArgs e)
+            {
+                tcs.TrySetResult(true);
+            }
+
+            process.EnableRaisingEvents = true;
+            process.Exited += ProcessExited;
+
+            try
+            {
+                if (process.HasExited)
+                    return;
+
+                using (cancellationToken.Register(() => tcs.TrySetCanceled()))
+                {
+                    await tcs.Task;
+                }
+            }
+            finally
+            {
+                process.Exited -= ProcessExited;
+            }
+        }
+    }
+}

+ 26 - 0
modules/mono/editor/GodotTools/GodotTools.Core/Properties/AssemblyInfo.cs

@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("GodotTools.Core")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Godot Engine contributors")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]

+ 28 - 5
modules/mono/editor/GodotSharpTools/StringExtensions.cs → modules/mono/editor/GodotTools/GodotTools.Core/StringExtensions.cs

@@ -1,7 +1,8 @@
 using System;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.IO;
 
 
-namespace GodotSharpTools
+namespace GodotTools.Core
 {
 {
     public static class StringExtensions
     public static class StringExtensions
     {
     {
@@ -25,7 +26,7 @@ namespace GodotSharpTools
 
 
             path = path.Replace('\\', '/');
             path = path.Replace('\\', '/');
 
 
-            string[] parts = path.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
+            string[] parts = path.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries);
 
 
             path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
             path = string.Join(Path.DirectorySeparatorChar.ToString(), parts).Trim();
 
 
@@ -37,18 +38,40 @@ namespace GodotSharpTools
         public static bool IsAbsolutePath(this string path)
         public static bool IsAbsolutePath(this string path)
         {
         {
             return path.StartsWith("/", StringComparison.Ordinal) ||
             return path.StartsWith("/", StringComparison.Ordinal) ||
-                       path.StartsWith("\\", StringComparison.Ordinal) ||
-                       path.StartsWith(driveRoot, StringComparison.Ordinal);
+                   path.StartsWith("\\", StringComparison.Ordinal) ||
+                   path.StartsWith(driveRoot, StringComparison.Ordinal);
         }
         }
 
 
         public static string CsvEscape(this string value, char delimiter = ',')
         public static string CsvEscape(this string value, char delimiter = ',')
         {
         {
-            bool hasSpecialChar = value.IndexOfAny(new char[] { '\"', '\n', '\r', delimiter }) != -1;
+            bool hasSpecialChar = value.IndexOfAny(new char[] {'\"', '\n', '\r', delimiter}) != -1;
 
 
             if (hasSpecialChar)
             if (hasSpecialChar)
                 return "\"" + value.Replace("\"", "\"\"") + "\"";
                 return "\"" + value.Replace("\"", "\"\"") + "\"";
 
 
             return value;
             return value;
         }
         }
+
+        public static string ToSafeDirName(this string dirName, bool allowDirSeparator)
+        {
+            var invalidChars = new List<string> {":", "*", "?", "\"", "<", ">", "|"};
+            
+            if (allowDirSeparator)
+            {
+                // Directory separators are allowed, but disallow ".." to avoid going up the filesystem
+                invalidChars.Add("..");
+            }
+            else
+            {
+                invalidChars.Add("/");
+            }
+
+            string safeDirName = dirName.Replace("\\", "/").Trim();
+
+            foreach (string invalidChar in invalidChars)
+                safeDirName = safeDirName.Replace(invalidChar, "-");
+
+            return safeDirName;
+        }
     }
     }
 }
 }

+ 15 - 0
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiAssembliesInfo.cs

@@ -0,0 +1,15 @@
+namespace GodotTools
+{
+    public static class ApiAssemblyNames
+    {
+        public const string SolutionName = "GodotSharp";
+        public const string Core = "GodotSharp";
+        public const string Editor = "GodotSharpEditor";
+    }
+
+    public enum ApiAssemblyType
+    {
+        Core,
+        Editor
+    }
+}

+ 52 - 0
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ApiSolutionGenerator.cs

@@ -0,0 +1,52 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace GodotTools.ProjectEditor
+{
+    public static class ApiSolutionGenerator
+    {
+        public static void GenerateApiSolution(string solutionDir,
+            string coreProjDir, IEnumerable<string> coreCompileItems,
+            string editorProjDir, IEnumerable<string> editorCompileItems)
+        {
+            var solution = new DotNetSolution(ApiAssemblyNames.SolutionName);
+
+            solution.DirectoryPath = solutionDir;
+
+            // GodotSharp project
+
+            const string coreApiAssemblyName = ApiAssemblyNames.Core;
+
+            string coreGuid = ProjectGenerator.GenCoreApiProject(coreProjDir, coreCompileItems);
+
+            var coreProjInfo = new DotNetSolution.ProjectInfo
+            {
+                Guid = coreGuid,
+                PathRelativeToSolution = Path.Combine(coreApiAssemblyName, $"{coreApiAssemblyName}.csproj")
+            };
+            coreProjInfo.Configs.Add("Debug");
+            coreProjInfo.Configs.Add("Release");
+
+            solution.AddNewProject(coreApiAssemblyName, coreProjInfo);
+
+            // GodotSharpEditor project
+
+            const string editorApiAssemblyName = ApiAssemblyNames.Editor;
+
+            string editorGuid = ProjectGenerator.GenEditorApiProject(editorProjDir,
+                $"../{coreApiAssemblyName}/{coreApiAssemblyName}.csproj", editorCompileItems);
+
+            var editorProjInfo = new DotNetSolution.ProjectInfo();
+            editorProjInfo.Guid = editorGuid;
+            editorProjInfo.PathRelativeToSolution = Path.Combine(editorApiAssemblyName, $"{editorApiAssemblyName}.csproj");
+            editorProjInfo.Configs.Add("Debug");
+            editorProjInfo.Configs.Add("Release");
+
+            solution.AddNewProject(editorApiAssemblyName, editorProjInfo);
+
+            // Save solution
+
+            solution.Save();
+        }
+    }
+}

+ 122 - 0
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/DotNetSolution.cs

@@ -0,0 +1,122 @@
+using GodotTools.Core;
+using System.Collections.Generic;
+using System.IO;
+
+namespace GodotTools.ProjectEditor
+{
+    public class DotNetSolution
+    {
+        private string directoryPath;
+        private readonly Dictionary<string, ProjectInfo> projects = new Dictionary<string, ProjectInfo>();
+
+        public string Name { get; }
+
+        public string DirectoryPath
+        {
+            get => directoryPath;
+            set => directoryPath = value.IsAbsolutePath() ? value : Path.GetFullPath(value);
+        }
+
+        public class ProjectInfo
+        {
+            public string Guid;
+            public string PathRelativeToSolution;
+            public List<string> Configs = new List<string>();
+        }
+
+        public void AddNewProject(string name, ProjectInfo projectInfo)
+        {
+            projects[name] = projectInfo;
+        }
+
+        public bool HasProject(string name)
+        {
+            return projects.ContainsKey(name);
+        }
+
+        public ProjectInfo GetProjectInfo(string name)
+        {
+            return projects[name];
+        }
+
+        public bool RemoveProject(string name)
+        {
+            return projects.Remove(name);
+        }
+
+        public void Save()
+        {
+            if (!Directory.Exists(DirectoryPath))
+                throw new FileNotFoundException("The solution directory does not exist.");
+
+            string projectsDecl = string.Empty;
+            string slnPlatformsCfg = string.Empty;
+            string projPlatformsCfg = string.Empty;
+
+            bool isFirstProject = true;
+
+            foreach (var pair in projects)
+            {
+                string name = pair.Key;
+                ProjectInfo projectInfo = pair.Value;
+
+                if (!isFirstProject)
+                    projectsDecl += "\n";
+
+                projectsDecl += string.Format(ProjectDeclaration,
+                    name, projectInfo.PathRelativeToSolution.Replace("/", "\\"), projectInfo.Guid);
+
+                for (int i = 0; i < projectInfo.Configs.Count; i++)
+                {
+                    string config = projectInfo.Configs[i];
+
+                    if (i != 0 || !isFirstProject)
+                    {
+                        slnPlatformsCfg += "\n";
+                        projPlatformsCfg += "\n";
+                    }
+
+                    slnPlatformsCfg += string.Format(SolutionPlatformsConfig, config);
+                    projPlatformsCfg += string.Format(ProjectPlatformsConfig, projectInfo.Guid, config);
+                }
+
+                isFirstProject = false;
+            }
+
+            string solutionPath = Path.Combine(DirectoryPath, Name + ".sln");
+            string content = string.Format(SolutionTemplate, projectsDecl, slnPlatformsCfg, projPlatformsCfg);
+
+            File.WriteAllText(solutionPath, content);
+        }
+
+        public DotNetSolution(string name)
+        {
+            Name = name;
+        }
+
+        const string SolutionTemplate =
+@"Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+{0}
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+{1}
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+{2}
+	EndGlobalSection
+EndGlobal
+";
+
+        const string ProjectDeclaration =
+@"Project(""{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}"") = ""{0}"", ""{1}"", ""{{{2}}}""
+EndProject";
+
+        const string SolutionPlatformsConfig =
+@"	{0}|Any CPU = {0}|Any CPU";
+
+        const string ProjectPlatformsConfig =
+@"		{{{0}}}.{1}|Any CPU.ActiveCfg = {1}|Any CPU
+		{{{0}}}.{1}|Any CPU.Build.0 = {1}|Any CPU";
+    }
+}

+ 25 - 16
modules/mono/editor/GodotSharpTools/GodotSharpTools.csproj → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/GodotTools.ProjectEditor.csproj

@@ -1,12 +1,12 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
     <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
     <ProjectGuid>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</ProjectGuid>
     <OutputType>Library</OutputType>
     <OutputType>Library</OutputType>
-    <RootNamespace>GodotSharpTools</RootNamespace>
-    <AssemblyName>GodotSharpTools</AssemblyName>
+    <RootNamespace>GodotTools.ProjectEditor</RootNamespace>
+    <AssemblyName>GodotTools.ProjectEditor</AssemblyName>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
     <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
     <BaseIntermediateOutputPath>obj</BaseIntermediateOutputPath>
   </PropertyGroup>
   </PropertyGroup>
@@ -21,7 +21,6 @@
     <ConsolePause>false</ConsolePause>
     <ConsolePause>false</ConsolePause>
   </PropertyGroup>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
-    <DebugType>portable</DebugType>
     <Optimize>true</Optimize>
     <Optimize>true</Optimize>
     <OutputPath>bin\Release</OutputPath>
     <OutputPath>bin\Release</OutputPath>
     <ErrorReport>prompt</ErrorReport>
     <ErrorReport>prompt</ErrorReport>
@@ -31,25 +30,35 @@
   <ItemGroup>
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="System" />
     <Reference Include="Microsoft.Build" />
     <Reference Include="Microsoft.Build" />
-    <Reference Include="Microsoft.Build.Framework" />
     <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL">
     <Reference Include="DotNet.Glob, Version=2.1.1.0, Culture=neutral, PublicKeyToken=b68cc888b4f632d1, processorArchitecture=MSIL">
-      <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
+      <!--
+      When building Godot with 'mono_glue=no' SCons will build this project alone instead of the
+      entire solution. $(SolutionDir) is not defined in that case, so we need to workaround that.
+      We make SCons restore the NuGet packages in the project directory instead in this case.
+      -->
+      <HintPath Condition=" '$(SolutionDir)' != '' ">$(SolutionDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
+      <HintPath>$(ProjectDir)\packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath>
+      <HintPath>packages\DotNet.Glob.2.1.1\lib\net45\DotNet.Glob.dll</HintPath> <!-- Are you happy CI? -->
     </Reference>
     </Reference>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
-    <Compile Include="StringExtensions.cs" />
-    <Compile Include="Build\BuildSystem.cs" />
-    <Compile Include="Editor\MonoDevelopInstance.cs" />
-    <Compile Include="Project\ProjectExtensions.cs" />
-    <Compile Include="Project\IdentifierUtils.cs" />
-    <Compile Include="Project\ProjectGenerator.cs" />
-    <Compile Include="Project\ProjectUtils.cs" />
+    <Compile Include="ApiAssembliesInfo.cs" />
+    <Compile Include="ApiSolutionGenerator.cs" />
+    <Compile Include="DotNetSolution.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
-    <Compile Include="Utils\OS.cs" />
-    <Compile Include="Editor\GodotSharpExport.cs" />
+    <Compile Include="IdentifierUtils.cs" />
+    <Compile Include="ProjectExtensions.cs" />
+    <Compile Include="ProjectGenerator.cs" />
+    <Compile Include="ProjectUtils.cs" />
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <None Include="packages.config" />
     <None Include="packages.config" />
   </ItemGroup>
   </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
+      <Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
+      <Name>GodotTools.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-</Project>
+</Project>

+ 6 - 8
modules/mono/editor/GodotSharpTools/Project/IdentifierUtils.cs → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/IdentifierUtils.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Text;
 using System.Text;
 
 
-namespace GodotSharpTools.Project
+namespace GodotTools.ProjectEditor
 {
 {
     public static class IdentifierUtils
     public static class IdentifierUtils
     {
     {
@@ -12,7 +12,7 @@ namespace GodotSharpTools.Project
             if (string.IsNullOrEmpty(qualifiedIdentifier))
             if (string.IsNullOrEmpty(qualifiedIdentifier))
                 throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
                 throw new ArgumentException($"{nameof(qualifiedIdentifier)} cannot be empty", nameof(qualifiedIdentifier));
 
 
-            string[] identifiers = qualifiedIdentifier.Split(new[] { '.' });
+            string[] identifiers = qualifiedIdentifier.Split('.');
 
 
             for (int i = 0; i < identifiers.Length; i++)
             for (int i = 0; i < identifiers.Length; i++)
             {
             {
@@ -66,8 +66,6 @@ namespace GodotSharpTools.Project
                         if (identifierBuilder.Length > startIndex || @char == '_')
                         if (identifierBuilder.Length > startIndex || @char == '_')
                             identifierBuilder.Append(@char);
                             identifierBuilder.Append(@char);
                         break;
                         break;
-                    default:
-                        break;
                 }
                 }
             }
             }
 
 
@@ -97,14 +95,14 @@ namespace GodotSharpTools.Project
             }
             }
             else
             else
             {
             {
-                if (_doubleUnderscoreKeywords.Contains(value))
+                if (DoubleUnderscoreKeywords.Contains(value))
                     return true;
                     return true;
             }
             }
 
 
-            return _keywords.Contains(value);
+            return Keywords.Contains(value);
         }
         }
 
 
-        static HashSet<string> _doubleUnderscoreKeywords = new HashSet<string>
+        private static readonly HashSet<string> DoubleUnderscoreKeywords = new HashSet<string>
         {
         {
             "__arglist",
             "__arglist",
             "__makeref",
             "__makeref",
@@ -112,7 +110,7 @@ namespace GodotSharpTools.Project
             "__refvalue",
             "__refvalue",
         };
         };
 
 
-        static HashSet<string> _keywords = new HashSet<string>
+        private static readonly HashSet<string> Keywords = new HashSet<string>
         {
         {
             "as",
             "as",
             "do",
             "do",

+ 2 - 1
modules/mono/editor/GodotSharpTools/Project/ProjectExtensions.cs → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectExtensions.cs

@@ -1,8 +1,9 @@
+using GodotTools.Core;
 using System;
 using System;
 using DotNet.Globbing;
 using DotNet.Globbing;
 using Microsoft.Build.Construction;
 using Microsoft.Build.Construction;
 
 
-namespace GodotSharpTools.Project
+namespace GodotTools.ProjectEditor
 {
 {
     public static class ProjectExtensions
     public static class ProjectExtensions
     {
     {

+ 20 - 18
modules/mono/editor/GodotSharpTools/Project/ProjectGenerator.cs → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectGenerator.cs

@@ -1,17 +1,19 @@
+using GodotTools.Core;
 using System;
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using Microsoft.Build.Construction;
 using Microsoft.Build.Construction;
 
 
-namespace GodotSharpTools.Project
+namespace GodotTools.ProjectEditor
 {
 {
     public static class ProjectGenerator
     public static class ProjectGenerator
     {
     {
-        public const string CoreApiProjectName = "GodotSharp";
-        public const string EditorApiProjectName = "GodotSharpEditor";
-        const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}";
-        const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}";
+        private const string CoreApiProjectName = "GodotSharp";
+        private const string EditorApiProjectName = "GodotSharpEditor";
+        private const string CoreApiProjectGuid = "{AEBF0036-DA76-4341-B651-A3F2856AB2FA}";
+        private const string EditorApiProjectGuid = "{8FBEC238-D944-4074-8548-B3B524305905}";
 
 
-        public static string GenCoreApiProject(string dir, string[] compileItems)
+        public static string GenCoreApiProject(string dir, IEnumerable<string> compileItems)
         {
         {
             string path = Path.Combine(dir, CoreApiProjectName + ".csproj");
             string path = Path.Combine(dir, CoreApiProjectName + ".csproj");
 
 
@@ -24,8 +26,8 @@ namespace GodotSharpTools.Project
             mainGroup.SetProperty("BaseIntermediateOutputPath", "obj");
             mainGroup.SetProperty("BaseIntermediateOutputPath", "obj");
 
 
             GenAssemblyInfoFile(root, dir, CoreApiProjectName,
             GenAssemblyInfoFile(root, dir, CoreApiProjectName,
-                                new string[] { "[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]" },
-                                new string[] { "System.Runtime.CompilerServices" });
+                new[] {"[assembly: InternalsVisibleTo(\"" + EditorApiProjectName + "\")]"},
+                new[] {"System.Runtime.CompilerServices"});
 
 
             foreach (var item in compileItems)
             foreach (var item in compileItems)
             {
             {
@@ -37,7 +39,7 @@ namespace GodotSharpTools.Project
             return CoreApiProjectGuid;
             return CoreApiProjectGuid;
         }
         }
 
 
-        public static string GenEditorApiProject(string dir, string coreApiProjPath, string[] compileItems)
+        public static string GenEditorApiProject(string dir, string coreApiProjPath, IEnumerable<string> compileItems)
         {
         {
             string path = Path.Combine(dir, EditorApiProjectName + ".csproj");
             string path = Path.Combine(dir, EditorApiProjectName + ".csproj");
 
 
@@ -64,7 +66,7 @@ namespace GodotSharpTools.Project
             return EditorApiProjectGuid;
             return EditorApiProjectGuid;
         }
         }
 
 
-        public static string GenGameProject(string dir, string name, string[] compileItems)
+        public static string GenGameProject(string dir, string name, IEnumerable<string> compileItems)
         {
         {
             string path = Path.Combine(dir, name + ".csproj");
             string path = Path.Combine(dir, name + ".csproj");
 
 
@@ -74,6 +76,8 @@ namespace GodotSharpTools.Project
             mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
             mainGroup.SetProperty("OutputPath", Path.Combine(".mono", "temp", "bin", "$(Configuration)"));
             mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
             mainGroup.SetProperty("BaseIntermediateOutputPath", Path.Combine(".mono", "temp", "obj"));
             mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
             mainGroup.SetProperty("IntermediateOutputPath", Path.Combine("$(BaseIntermediateOutputPath)", "$(Configuration)"));
+            mainGroup.SetProperty("ApiConfiguration", "Debug").Condition = " '$(Configuration)' != 'Release' ";
+            mainGroup.SetProperty("ApiConfiguration", "Release").Condition = " '$(Configuration)' == 'Release' ";
 
 
             var toolsGroup = root.AddPropertyGroup();
             var toolsGroup = root.AddPropertyGroup();
             toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
             toolsGroup.Condition = " '$(Configuration)|$(Platform)' == 'Tools|AnyCPU' ";
@@ -86,11 +90,13 @@ namespace GodotSharpTools.Project
             toolsGroup.AddProperty("ConsolePause", "false");
             toolsGroup.AddProperty("ConsolePause", "false");
 
 
             var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
             var coreApiRef = root.AddItem("Reference", CoreApiProjectName);
+            coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", CoreApiProjectName + ".dll"));
             coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll"));
             coreApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", CoreApiProjectName + ".dll"));
             coreApiRef.AddMetadata("Private", "False");
             coreApiRef.AddMetadata("Private", "False");
 
 
             var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
             var editorApiRef = root.AddItem("Reference", EditorApiProjectName);
             editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
             editorApiRef.Condition = " '$(Configuration)' == 'Tools' ";
+            editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", "$(ApiConfiguration)", EditorApiProjectName + ".dll"));
             editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll"));
             editorApiRef.AddMetadata("HintPath", Path.Combine("$(ProjectDir)", ".mono", "assemblies", EditorApiProjectName + ".dll"));
             editorApiRef.AddMetadata("Private", "False");
             editorApiRef.AddMetadata("Private", "False");
 
 
@@ -108,7 +114,6 @@ namespace GodotSharpTools.Project
 
 
         public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
         public static void GenAssemblyInfoFile(ProjectRootElement root, string dir, string name, string[] assemblyLines = null, string[] usingDirectives = null)
         {
         {
-
             string propertiesDir = Path.Combine(dir, "Properties");
             string propertiesDir = Path.Combine(dir, "Properties");
             if (!Directory.Exists(propertiesDir))
             if (!Directory.Exists(propertiesDir))
                 Directory.CreateDirectory(propertiesDir);
                 Directory.CreateDirectory(propertiesDir);
@@ -124,12 +129,9 @@ namespace GodotSharpTools.Project
             string assemblyLinesText = string.Empty;
             string assemblyLinesText = string.Empty;
 
 
             if (assemblyLines != null)
             if (assemblyLines != null)
-            {
-                foreach (var assemblyLine in assemblyLines)
-                    assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
-            }
+                assemblyLinesText += string.Join("\n", assemblyLines) + "\n";
 
 
-            string content = string.Format(assemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
+            string content = string.Format(AssemblyInfoTemplate, usingDirectivesText, name, assemblyLinesText);
 
 
             string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
             string assemblyInfoFile = Path.Combine(propertiesDir, "AssemblyInfo.cs");
 
 
@@ -194,8 +196,8 @@ namespace GodotSharpTools.Project
             }
             }
         }
         }
 
 
-        private const string assemblyInfoTemplate =
-@"using System.Reflection;{0}
+        private const string AssemblyInfoTemplate =
+            @"using System.Reflection;{0}
 
 
 // Information about this assembly is defined by the following attributes.
 // Information about this assembly is defined by the following attributes.
 // Change them to the values specific to your project.
 // Change them to the values specific to your project.

+ 3 - 2
modules/mono/editor/GodotSharpTools/Project/ProjectUtils.cs → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs

@@ -1,9 +1,10 @@
+using GodotTools.Core;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.IO;
 using System.IO;
 using DotNet.Globbing;
 using DotNet.Globbing;
 using Microsoft.Build.Construction;
 using Microsoft.Build.Construction;
 
 
-namespace GodotSharpTools.Project
+namespace GodotTools.ProjectEditor
 {
 {
     public static class ProjectUtils
     public static class ProjectUtils
     {
     {
@@ -53,7 +54,7 @@ namespace GodotSharpTools.Project
 
 
                     var glob = Glob.Parse(normalizedInclude, globOptions);
                     var glob = Glob.Parse(normalizedInclude, globOptions);
 
 
-                    // TODO Check somehow if path has no blog to avoid the following loop...
+                    // TODO Check somehow if path has no blob to avoid the following loop...
 
 
                     foreach (var existingFile in existingFiles)
                     foreach (var existingFile in existingFiles)
                     {
                     {

+ 2 - 2
modules/mono/editor/GodotSharpTools/Properties/AssemblyInfo.cs → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/Properties/AssemblyInfo.cs

@@ -4,12 +4,12 @@ using System.Runtime.CompilerServices;
 // Information about this assembly is defined by the following attributes.
 // Information about this assembly is defined by the following attributes.
 // Change them to the values specific to your project.
 // Change them to the values specific to your project.
 
 
-[assembly: AssemblyTitle("GodotSharpTools")]
+[assembly: AssemblyTitle("GodotTools.ProjectEditor")]
 [assembly: AssemblyDescription("")]
 [assembly: AssemblyDescription("")]
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyConfiguration("")]
 [assembly: AssemblyCompany("")]
 [assembly: AssemblyCompany("")]
 [assembly: AssemblyProduct("")]
 [assembly: AssemblyProduct("")]
-[assembly: AssemblyCopyright("ignacio")]
+[assembly: AssemblyCopyright("Godot Engine contributors")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 [assembly: AssemblyCulture("")]
 
 

+ 1 - 1
modules/mono/editor/GodotSharpTools/packages.config → modules/mono/editor/GodotTools/GodotTools.ProjectEditor/packages.config

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <packages>
 <packages>
   <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" />
   <package id="DotNet.Glob" version="2.1.1" targetFramework="net45" />
 </packages>
 </packages>

+ 172 - 0
modules/mono/editor/GodotTools/GodotTools/Build/BuildSystem.cs

@@ -0,0 +1,172 @@
+using GodotTools.Core;
+using System;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.IO;
+using System.Threading.Tasks;
+using GodotTools.BuildLogger;
+using GodotTools.Internals;
+using GodotTools.Utils;
+using Directory = System.IO.Directory;
+
+namespace GodotTools.Build
+{
+    public static class BuildSystem
+    {
+        private static string GetMsBuildPath()
+        {
+            string msbuildPath = MsBuildFinder.FindMsBuild();
+
+            if (msbuildPath == null)
+                throw new FileNotFoundException("Cannot find the MSBuild executable.");
+
+            return msbuildPath;
+        }
+
+        private static string MonoWindowsBinDir
+        {
+            get
+            {
+                string monoWinBinDir = Path.Combine(Internal.MonoWindowsInstallRoot, "bin");
+
+                if (!Directory.Exists(monoWinBinDir))
+                    throw new FileNotFoundException("Cannot find the Windows Mono install bin directory.");
+
+                return monoWinBinDir;
+            }
+        }
+
+        private static Godot.EditorSettings EditorSettings =>
+            GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+
+        private static bool UsingMonoMsBuildOnWindows
+        {
+            get
+            {
+                if (OS.IsWindows())
+                {
+                    return (GodotSharpBuilds.BuildTool) EditorSettings.GetSetting("mono/builds/build_tool")
+                           == GodotSharpBuilds.BuildTool.MsBuildMono;
+                }
+
+                return false;
+            }
+        }
+
+        private static bool PrintBuildOutput =>
+            (bool) EditorSettings.GetSetting("mono/builds/print_build_output");
+
+        private static Process LaunchBuild(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+        {
+            var customPropertiesList = new List<string>();
+
+            if (customProperties != null)
+                customPropertiesList.AddRange(customProperties);
+
+            string compilerArgs = BuildArguments(solution, config, loggerOutputDir, customPropertiesList);
+
+            var startInfo = new ProcessStartInfo(GetMsBuildPath(), compilerArgs);
+
+            bool redirectOutput = !IsDebugMsBuildRequested() && !PrintBuildOutput;
+
+            if (!redirectOutput || Godot.OS.IsStdoutVerbose())
+                Console.WriteLine($"Running: \"{startInfo.FileName}\" {startInfo.Arguments}");
+
+            startInfo.RedirectStandardOutput = redirectOutput;
+            startInfo.RedirectStandardError = redirectOutput;
+            startInfo.UseShellExecute = false;
+
+            if (UsingMonoMsBuildOnWindows)
+            {
+                // These environment variables are required for Mono's MSBuild to find the compilers.
+                // We use the batch files in Mono's bin directory to make sure the compilers are executed with mono.
+                string monoWinBinDir = MonoWindowsBinDir;
+                startInfo.EnvironmentVariables.Add("CscToolExe", Path.Combine(monoWinBinDir, "csc.bat"));
+                startInfo.EnvironmentVariables.Add("VbcToolExe", Path.Combine(monoWinBinDir, "vbc.bat"));
+                startInfo.EnvironmentVariables.Add("FscToolExe", Path.Combine(monoWinBinDir, "fsharpc.bat"));
+            }
+
+            // Needed when running from Developer Command Prompt for VS
+            RemovePlatformVariable(startInfo.EnvironmentVariables);
+
+            var process = new Process {StartInfo = startInfo};
+
+            process.Start();
+
+            if (redirectOutput)
+            {
+                process.BeginOutputReadLine();
+                process.BeginErrorReadLine();
+            }
+
+            return process;
+        }
+
+        public static int Build(MonoBuildInfo monoBuildInfo)
+        {
+            return Build(monoBuildInfo.Solution, monoBuildInfo.Configuration,
+                monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties);
+        }
+
+        public static async Task<int> BuildAsync(MonoBuildInfo monoBuildInfo)
+        {
+            return await BuildAsync(monoBuildInfo.Solution, monoBuildInfo.Configuration,
+                monoBuildInfo.LogsDirPath, monoBuildInfo.CustomProperties);
+        }
+
+        public static int Build(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+        {
+            using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
+            {
+                process.WaitForExit();
+
+                return process.ExitCode;
+            }
+        }
+
+        public static async Task<int> BuildAsync(string solution, string config, string loggerOutputDir, IEnumerable<string> customProperties = null)
+        {
+            using (var process = LaunchBuild(solution, config, loggerOutputDir, customProperties))
+            {
+                await process.WaitForExitAsync();
+
+                return process.ExitCode;
+            }
+        }
+
+        private static string BuildArguments(string solution, string config, string loggerOutputDir, List<string> customProperties)
+        {
+            string arguments = $@"""{solution}"" /v:normal /t:Rebuild ""/p:{"Configuration=" + config}"" " +
+                               $@"""/l:{typeof(GodotBuildLogger).FullName},{GodotBuildLogger.AssemblyPath};{loggerOutputDir}""";
+
+            foreach (string customProperty in customProperties)
+            {
+                arguments += " /p:" + customProperty;
+            }
+
+            return arguments;
+        }
+
+        private static void RemovePlatformVariable(StringDictionary environmentVariables)
+        {
+            // EnvironmentVariables is case sensitive? Seriously?
+
+            var platformEnvironmentVariables = new List<string>();
+
+            foreach (string env in environmentVariables.Keys)
+            {
+                if (env.ToUpper() == "PLATFORM")
+                    platformEnvironmentVariables.Add(env);
+            }
+
+            foreach (string env in platformEnvironmentVariables)
+                environmentVariables.Remove(env);
+        }
+
+        private static bool IsDebugMsBuildRequested()
+        {
+            return Environment.GetEnvironmentVariable("GODOT_DEBUG_MSBUILD")?.Trim() == "1";
+        }
+    }
+}

+ 210 - 0
modules/mono/editor/GodotTools/GodotTools/Build/MsBuildFinder.cs

@@ -0,0 +1,210 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Godot;
+using GodotTools.Internals;
+using Directory = System.IO.Directory;
+using Environment = System.Environment;
+using File = System.IO.File;
+using Path = System.IO.Path;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools.Build
+{
+    public static class MsBuildFinder
+    {
+        private static string _msbuildToolsPath = string.Empty;
+        private static string _msbuildUnixPath = string.Empty;
+        private static string _xbuildUnixPath = string.Empty;
+
+        public static string FindMsBuild()
+        {
+            var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+            var buildTool = (GodotSharpBuilds.BuildTool) editorSettings.GetSetting("mono/builds/build_tool");
+
+            if (OS.IsWindows())
+            {
+                switch (buildTool)
+                {
+                    case GodotSharpBuilds.BuildTool.MsBuildVs:
+                    {
+                        if (_msbuildToolsPath.Empty() || !File.Exists(_msbuildToolsPath))
+                        {
+                            // Try to search it again if it wasn't found last time or if it was removed from its location
+                            _msbuildToolsPath = FindMsBuildToolsPathOnWindows();
+
+                            if (_msbuildToolsPath.Empty())
+                            {
+                                throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildVs}'. Tried with path: {_msbuildToolsPath}");
+                            }
+                        }
+
+                        if (!_msbuildToolsPath.EndsWith("\\"))
+                            _msbuildToolsPath += "\\";
+
+                        return Path.Combine(_msbuildToolsPath, "MSBuild.exe");
+                    }
+
+                    case GodotSharpBuilds.BuildTool.MsBuildMono:
+                    {
+                        string msbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "msbuild.bat");
+
+                        if (!File.Exists(msbuildPath))
+                        {
+                            throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameMsbuildMono}'. Tried with path: {msbuildPath}");
+                        }
+
+                        return msbuildPath;
+                    }
+
+                    case GodotSharpBuilds.BuildTool.XBuild:
+                    {
+                        string xbuildPath = Path.Combine(Internal.MonoWindowsInstallRoot, "bin", "xbuild.bat");
+
+                        if (!File.Exists(xbuildPath))
+                        {
+                            throw new FileNotFoundException($"Cannot find executable for '{GodotSharpBuilds.PropNameXbuild}'. Tried with path: {xbuildPath}");
+                        }
+
+                        return xbuildPath;
+                    }
+
+                    default:
+                        throw new IndexOutOfRangeException("Invalid build tool in editor settings");
+                }
+            }
+
+            if (OS.IsUnix())
+            {
+                if (buildTool == GodotSharpBuilds.BuildTool.XBuild)
+                {
+                    if (_xbuildUnixPath.Empty() || !File.Exists(_xbuildUnixPath))
+                    {
+                        // Try to search it again if it wasn't found last time or if it was removed from its location
+                        _xbuildUnixPath = FindBuildEngineOnUnix("msbuild");
+                    }
+
+                    if (_xbuildUnixPath.Empty())
+                    {
+                        throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameXbuild}'");
+                    }
+                }
+                else
+                {
+                    if (_msbuildUnixPath.Empty() || !File.Exists(_msbuildUnixPath))
+                    {
+                        // Try to search it again if it wasn't found last time or if it was removed from its location
+                        _msbuildUnixPath = FindBuildEngineOnUnix("msbuild");
+                    }
+
+                    if (_msbuildUnixPath.Empty())
+                    {
+                        throw new FileNotFoundException($"Cannot find binary for '{GodotSharpBuilds.PropNameMsbuildMono}'");
+                    }
+                }
+
+                return buildTool != GodotSharpBuilds.BuildTool.XBuild ? _msbuildUnixPath : _xbuildUnixPath;
+            }
+
+            throw new PlatformNotSupportedException();
+        }
+
+        private static IEnumerable<string> MsBuildHintDirs
+        {
+            get
+            {
+                var result = new List<string>();
+
+                if (OS.IsOSX())
+                {
+                    result.Add("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
+                    result.Add("/usr/local/var/homebrew/linked/mono/bin/");
+                }
+
+                result.Add("/opt/novell/mono/bin/");
+
+                return result;
+            }
+        }
+
+        private static string FindBuildEngineOnUnix(string name)
+        {
+            string ret = OS.PathWhich(name);
+
+            if (!ret.Empty())
+                return ret;
+
+            string retFallback = OS.PathWhich($"{name}.exe");
+
+            if (!retFallback.Empty())
+                return retFallback;
+
+            foreach (string hintDir in MsBuildHintDirs)
+            {
+                string hintPath = Path.Combine(hintDir, name);
+
+                if (File.Exists(hintPath))
+                    return hintPath;
+            }
+
+            return string.Empty;
+        }
+
+        private static string FindMsBuildToolsPathOnWindows()
+        {
+            if (!OS.IsWindows())
+                throw new PlatformNotSupportedException();
+
+            // Try to find 15.0 with vswhere
+
+            string vsWherePath = Environment.GetEnvironmentVariable(Internal.GodotIs32Bits() ? "ProgramFiles" : "ProgramFiles(x86)");
+            vsWherePath += "\\Microsoft Visual Studio\\Installer\\vswhere.exe";
+
+            var vsWhereArgs = new[] {"-latest", "-products", "*", "-requires", "Microsoft.Component.MSBuild"};
+
+            var outputArray = new Godot.Collections.Array<string>();
+            int exitCode = Godot.OS.Execute(vsWherePath, vsWhereArgs,
+                blocking: true, output: (Godot.Collections.Array) outputArray);
+
+            if (exitCode == 0)
+                return string.Empty;
+
+            if (outputArray.Count == 0)
+                return string.Empty;
+
+            var lines = outputArray[1].Split('\n');
+
+            foreach (string line in lines)
+            {
+                int sepIdx = line.IndexOf(':');
+
+                if (sepIdx <= 0)
+                    continue;
+
+                string key = line.Substring(0, sepIdx); // No need to trim
+
+                if (key != "installationPath")
+                    continue;
+
+                string value = line.Substring(sepIdx + 1).StripEdges();
+
+                if (value.Empty())
+                    throw new FormatException("installationPath value is empty");
+
+                if (!value.EndsWith("\\"))
+                    value += "\\";
+
+                // Since VS2019, the directory is simply named "Current"
+                string msbuildDir = Path.Combine(value, "MSBuild\\Current\\Bin");
+
+                if (Directory.Exists(msbuildDir))
+                    return msbuildDir;
+
+                // Directory name "15.0" is used in VS 2017
+                return Path.Combine(value, "MSBuild\\15.0\\Bin");
+            }
+
+            return string.Empty;
+        }
+    }
+}

+ 115 - 0
modules/mono/editor/GodotTools/GodotTools/CSharpProject.cs

@@ -0,0 +1,115 @@
+using Godot;
+using System;
+using System.Collections.Generic;
+using Godot.Collections;
+using GodotTools.Internals;
+using GodotTools.ProjectEditor;
+using File = GodotTools.Utils.File;
+using Directory = GodotTools.Utils.Directory;
+
+namespace GodotTools
+{
+    public static class CSharpProject
+    {
+        public static string GenerateGameProject(string dir, string name, IEnumerable<string> files = null)
+        {
+            try
+            {
+                return ProjectGenerator.GenGameProject(dir, name, files);
+            }
+            catch (Exception e)
+            {
+                GD.PushError(e.ToString());
+                return string.Empty;
+            }
+        }
+
+        public static void AddItem(string projectPath, string itemType, string include)
+        {
+            if (!(bool) Internal.GlobalDef("mono/project/auto_update_project", true))
+                return;
+
+            ProjectUtils.AddItemToProjectChecked(projectPath, itemType, include);
+        }
+
+        private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+        private static ulong ConvertToTimestamp(this DateTime value)
+        {
+            TimeSpan elapsedTime = value - Epoch;
+            return (ulong) elapsedTime.TotalSeconds;
+        }
+
+        public static void GenerateScriptsMetadata(string projectPath, string outputPath)
+        {
+            if (File.Exists(outputPath))
+                File.Delete(outputPath);
+
+            var oldDict = Internal.GetScriptsMetadataOrNothing();
+            var newDict = new Godot.Collections.Dictionary<string, object>();
+
+            foreach (var includeFile in ProjectUtils.GetIncludeFiles(projectPath, "Compile"))
+            {
+                string projectIncludeFile = ("res://" + includeFile).SimplifyGodotPath();
+
+                ulong modifiedTime = File.GetLastWriteTime(projectIncludeFile).ConvertToTimestamp();
+
+                if (oldDict.TryGetValue(projectIncludeFile, out var oldFileVar))
+                {
+                    var oldFileDict = (Dictionary) oldFileVar;
+
+                    if (ulong.TryParse((string) oldFileDict["modified_time"], out ulong storedModifiedTime))
+                    {
+                        if (storedModifiedTime == modifiedTime)
+                        {
+                            // No changes so no need to parse again
+                            newDict[projectIncludeFile] = oldFileDict;
+                            continue;
+                        }
+                    }
+                }
+
+                ScriptClassParser.ParseFileOrThrow(projectIncludeFile, out var classes);
+
+                string searchName = System.IO.Path.GetFileNameWithoutExtension(projectIncludeFile);
+
+                var classDict = new Dictionary();
+
+                foreach (var classDecl in classes)
+                {
+                    if (classDecl.BaseCount == 0)
+                        continue; // Does not inherit nor implement anything, so it can't be a script class
+
+                    string classCmp = classDecl.Nested ?
+                        classDecl.Name.Substring(classDecl.Name.LastIndexOf(".", StringComparison.Ordinal) + 1) :
+                        classDecl.Name;
+
+                    if (classCmp != searchName)
+                        continue;
+
+                    classDict["namespace"] = classDecl.Namespace;
+                    classDict["class_name"] = classDecl.Name;
+                    classDict["nested"] = classDecl.Nested;
+                    break;
+                }
+
+                if (classDict.Count == 0)
+                    continue; // Not found
+
+                newDict[projectIncludeFile] = new Dictionary {["modified_time"] = $"{modifiedTime}", ["class"] = classDict};
+            }
+
+            if (newDict.Count > 0)
+            {
+                string json = JSON.Print(newDict);
+
+                string baseDir = outputPath.GetBaseDir();
+
+                if (!Directory.Exists(baseDir))
+                    Directory.CreateDirectory(baseDir);
+
+                File.WriteAllText(outputPath, json);
+            }
+        }
+    }
+}

+ 396 - 0
modules/mono/editor/GodotTools/GodotTools/GodotSharpBuilds.cs

@@ -0,0 +1,396 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using GodotTools.Build;
+using GodotTools.Internals;
+using GodotTools.Utils;
+using Error = Godot.Error;
+using File = GodotTools.Utils.File;
+using Directory = GodotTools.Utils.Directory;
+
+namespace GodotTools
+{
+    public static class GodotSharpBuilds
+    {
+        private static readonly List<MonoBuildInfo> BuildsInProgress = new List<MonoBuildInfo>();
+
+        public const string PropNameMsbuildMono = "MSBuild (Mono)";
+        public const string PropNameMsbuildVs = "MSBuild (VS Build Tools)";
+        public const string PropNameXbuild = "xbuild (Deprecated)";
+
+        public const string MsBuildIssuesFileName = "msbuild_issues.csv";
+        public const string MsBuildLogFileName = "msbuild_log.txt";
+
+        public enum BuildTool
+        {
+            MsBuildMono,
+            MsBuildVs,
+            XBuild // Deprecated
+        }
+
+        private static void RemoveOldIssuesFile(MonoBuildInfo buildInfo)
+        {
+            var issuesFile = GetIssuesFilePath(buildInfo);
+
+            if (!File.Exists(issuesFile))
+                return;
+
+            File.Delete(issuesFile);
+        }
+
+        private static string _ApiFolderName(ApiAssemblyType apiType)
+        {
+            ulong apiHash = apiType == ApiAssemblyType.Core ?
+                Internal.GetCoreApiHash() :
+                Internal.GetEditorApiHash();
+            return $"{apiHash}_{BindingsGenerator.Version}_{BindingsGenerator.CsGlueVersion}";
+        }
+
+        private static void ShowBuildErrorDialog(string message)
+        {
+            GodotSharpEditor.Instance.ShowErrorDialog(message, "Build error");
+            GodotSharpEditor.Instance.MonoBottomPanel.ShowBuildTab();
+        }
+
+        public static void RestartBuild(MonoBuildTab buildTab) => throw new NotImplementedException();
+        public static void StopBuild(MonoBuildTab buildTab) => throw new NotImplementedException();
+
+        private static string GetLogFilePath(MonoBuildInfo buildInfo)
+        {
+            return Path.Combine(buildInfo.LogsDirPath, MsBuildLogFileName);
+        }
+
+        private static string GetIssuesFilePath(MonoBuildInfo buildInfo)
+        {
+            return Path.Combine(Godot.ProjectSettings.LocalizePath(buildInfo.LogsDirPath), MsBuildIssuesFileName);
+        }
+
+        private static void PrintVerbose(string text)
+        {
+            if (Godot.OS.IsStdoutVerbose())
+                Godot.GD.Print(text);
+        }
+
+        public static bool Build(MonoBuildInfo buildInfo)
+        {
+            if (BuildsInProgress.Contains(buildInfo))
+                throw new InvalidOperationException("A build is already in progress");
+
+            BuildsInProgress.Add(buildInfo);
+
+            try
+            {
+                MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo);
+                buildTab.OnBuildStart();
+
+                // Required in order to update the build tasks list
+                Internal.GodotMainIteration();
+
+                try
+                {
+                    RemoveOldIssuesFile(buildInfo);
+                }
+                catch (IOException e)
+                {
+                    buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+                    Console.Error.WriteLine(e);
+                }
+
+                try
+                {
+                    int exitCode = BuildSystem.Build(buildInfo);
+
+                    if (exitCode != 0)
+                        PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
+
+                    buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error);
+
+                    return exitCode == 0;
+                }
+                catch (Exception e)
+                {
+                    buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+                    Console.Error.WriteLine(e);
+                    return false;
+                }
+            }
+            finally
+            {
+                BuildsInProgress.Remove(buildInfo);
+            }
+        }
+
+        public static async Task<bool> BuildAsync(MonoBuildInfo buildInfo)
+        {
+            if (BuildsInProgress.Contains(buildInfo))
+                throw new InvalidOperationException("A build is already in progress");
+
+            BuildsInProgress.Add(buildInfo);
+
+            try
+            {
+                MonoBuildTab buildTab = GodotSharpEditor.Instance.MonoBottomPanel.GetBuildTabFor(buildInfo);
+
+                try
+                {
+                    RemoveOldIssuesFile(buildInfo);
+                }
+                catch (IOException e)
+                {
+                    buildTab.OnBuildExecFailed($"Cannot remove issues file: {GetIssuesFilePath(buildInfo)}");
+                    Console.Error.WriteLine(e);
+                }
+
+                try
+                {
+                    int exitCode = await BuildSystem.BuildAsync(buildInfo);
+
+                    if (exitCode != 0)
+                        PrintVerbose($"MSBuild exited with code: {exitCode}. Log file: {GetLogFilePath(buildInfo)}");
+
+                    buildTab.OnBuildExit(exitCode == 0 ? MonoBuildTab.BuildResults.Success : MonoBuildTab.BuildResults.Error);
+
+                    return exitCode == 0;
+                }
+                catch (Exception e)
+                {
+                    buildTab.OnBuildExecFailed($"The build method threw an exception.\n{e.GetType().FullName}: {e.Message}");
+                    Console.Error.WriteLine(e);
+                    return false;
+                }
+            }
+            finally
+            {
+                BuildsInProgress.Remove(buildInfo);
+            }
+        }
+
+        public static bool BuildApiSolution(string apiSlnDir, string config)
+        {
+            string apiSlnFile = Path.Combine(apiSlnDir, $"{ApiAssemblyNames.SolutionName}.sln");
+
+            string coreApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Core, "bin", config);
+            string coreApiAssemblyFile = Path.Combine(coreApiAssemblyDir, $"{ApiAssemblyNames.Core}.dll");
+
+            string editorApiAssemblyDir = Path.Combine(apiSlnDir, ApiAssemblyNames.Editor, "bin", config);
+            string editorApiAssemblyFile = Path.Combine(editorApiAssemblyDir, $"{ApiAssemblyNames.Editor}.dll");
+
+            if (File.Exists(coreApiAssemblyFile) && File.Exists(editorApiAssemblyFile))
+                return true; // The assemblies are in the output folder; assume the solution is already built
+
+            var apiBuildInfo = new MonoBuildInfo(apiSlnFile, config);
+
+            // TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
+            // once we start to actively document manually maintained C# classes
+            apiBuildInfo.CustomProperties.Add("NoWarn=1591"); // Ignore missing documentation warnings
+
+            if (Build(apiBuildInfo))
+                return true;
+
+            ShowBuildErrorDialog($"Failed to build {ApiAssemblyNames.SolutionName} solution.");
+            return false;
+        }
+
+        private static bool CopyApiAssembly(string srcDir, string dstDir, string assemblyName, ApiAssemblyType apiType)
+        {
+            // Create destination directory if needed
+            if (!Directory.Exists(dstDir))
+            {
+                try
+                {
+                    Directory.CreateDirectory(dstDir);
+                }
+                catch (IOException e)
+                {
+                    ShowBuildErrorDialog($"Failed to create destination directory for the API assemblies. Exception message: {e.Message}");
+                    return false;
+                }
+            }
+
+            string assemblyFile = assemblyName + ".dll";
+            string assemblySrc = Path.Combine(srcDir, assemblyFile);
+            string assemblyDst = Path.Combine(dstDir, assemblyFile);
+
+            if (!File.Exists(assemblyDst) || File.GetLastWriteTime(assemblySrc) > File.GetLastWriteTime(assemblyDst) ||
+                Internal.MetadataIsApiAssemblyInvalidated(apiType))
+            {
+                string xmlFile = $"{assemblyName}.xml";
+                string pdbFile = $"{assemblyName}.pdb";
+
+                try
+                {
+                    File.Copy(Path.Combine(srcDir, xmlFile), Path.Combine(dstDir, xmlFile));
+                }
+                catch (IOException e)
+                {
+                    Godot.GD.PushWarning(e.ToString());
+                }
+
+                try
+                {
+                    File.Copy(Path.Combine(srcDir, pdbFile), Path.Combine(dstDir, pdbFile));
+                }
+                catch (IOException e)
+                {
+                    Godot.GD.PushWarning(e.ToString());
+                }
+
+                try
+                {
+                    File.Copy(assemblySrc, assemblyDst);
+                }
+                catch (IOException e)
+                {
+                    ShowBuildErrorDialog($"Failed to copy {assemblyFile}. Exception message: {e.Message}");
+                    return false;
+                }
+
+                Internal.MetadataSetApiAssemblyInvalidated(apiType, false);
+            }
+
+            return true;
+        }
+
+        public static bool MakeApiAssembly(ApiAssemblyType apiType, string config)
+        {
+            string apiName = apiType == ApiAssemblyType.Core ? ApiAssemblyNames.Core : ApiAssemblyNames.Editor;
+
+            string editorPrebuiltApiDir = Path.Combine(GodotSharpDirs.DataEditorPrebuiltApiDir, config);
+            string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, config);
+
+            if (File.Exists(Path.Combine(editorPrebuiltApiDir, $"{apiName}.dll")))
+            {
+                using (var copyProgress = new EditorProgress("mono_copy_prebuilt_api_assembly", $"Copying prebuilt {apiName} assembly...", 1))
+                {
+                    copyProgress.Step($"Copying {apiName} assembly", 0);
+                    return CopyApiAssembly(editorPrebuiltApiDir, resAssembliesDir, apiName, apiType);
+                }
+            }
+
+            const string apiSolutionName = ApiAssemblyNames.SolutionName;
+
+            using (var pr = new EditorProgress($"mono_build_release_{apiSolutionName}", $"Building {apiSolutionName} solution...", 3))
+            {
+                pr.Step($"Generating {apiSolutionName} solution", 0);
+
+                string apiSlnDir = Path.Combine(GodotSharpDirs.MonoSolutionsDir, _ApiFolderName(ApiAssemblyType.Core));
+                string apiSlnFile = Path.Combine(apiSlnDir, $"{apiSolutionName}.sln");
+
+                if (!Directory.Exists(apiSlnDir) || !File.Exists(apiSlnFile))
+                {
+                    var bindingsGenerator = new BindingsGenerator();
+
+                    if (!Godot.OS.IsStdoutVerbose())
+                        bindingsGenerator.LogPrintEnabled = false;
+
+                    Error err = bindingsGenerator.GenerateCsApi(apiSlnDir);
+                    if (err != Error.Ok)
+                    {
+                        ShowBuildErrorDialog($"Failed to generate {apiSolutionName} solution. Error: {err}");
+                        return false;
+                    }
+                }
+
+                pr.Step($"Building {apiSolutionName} solution", 1);
+
+                if (!BuildApiSolution(apiSlnDir, config))
+                    return false;
+
+                pr.Step($"Copying {apiName} assembly", 2);
+
+                // Copy the built assembly to the assemblies directory
+                string apiAssemblyDir = Path.Combine(apiSlnDir, apiName, "bin", config);
+                if (!CopyApiAssembly(apiAssemblyDir, resAssembliesDir, apiName, apiType))
+                    return false;
+            }
+
+            return true;
+        }
+
+        public static bool BuildProjectBlocking(string config, IEnumerable<string> godotDefines)
+        {
+            if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+                return true; // No solution to build
+
+            string apiConfig = config == "Release" ? "Release" : "Debug";
+
+            if (!MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
+                return false;
+
+            if (!MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
+                return false;
+
+            using (var pr = new EditorProgress("mono_project_debug_build", "Building project solution...", 1))
+            {
+                pr.Step("Building project solution", 0);
+
+                var buildInfo = new MonoBuildInfo(GodotSharpDirs.ProjectSlnPath, config);
+
+                // Add Godot defines
+                string constants = OS.IsWindows() ? "GodotDefineConstants=\"" : "GodotDefineConstants=\\\"";
+
+                foreach (var godotDefine in godotDefines)
+                    constants += $"GODOT_{godotDefine.ToUpper().Replace("-", "_").Replace(" ", "_").Replace(";", "_")};";
+
+                if (Internal.GodotIsRealTDouble())
+                    constants += "GODOT_REAL_T_IS_DOUBLE;";
+
+                constants += OS.IsWindows() ? "\"" : "\\\"";
+
+                buildInfo.CustomProperties.Add(constants);
+
+                if (!Build(buildInfo))
+                {
+                    ShowBuildErrorDialog("Failed to build project solution");
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public static bool EditorBuildCallback()
+        {
+            if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+                return true; // No solution to build
+
+            string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
+            string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
+
+            CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
+
+            if (File.Exists(editorScriptsMetadataPath))
+                File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+
+            var godotDefines = new[]
+            {
+                Godot.OS.GetName(),
+                Internal.GodotIs32Bits() ? "32" : "64"
+            };
+
+            return BuildProjectBlocking("Tools", godotDefines);
+        }
+
+        public static void Initialize()
+        {
+            // Build tool settings
+
+            Internal.EditorDef("mono/builds/build_tool", OS.IsWindows() ? BuildTool.MsBuildVs : BuildTool.MsBuildMono);
+
+            var editorSettings = GodotSharpEditor.Instance.GetEditorInterface().GetEditorSettings();
+
+            editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+            {
+                ["type"] = Godot.Variant.Type.Int,
+                ["name"] = "mono/builds/build_tool",
+                ["hint"] = Godot.PropertyHint.Enum,
+                ["hint_string"] = OS.IsWindows() ?
+                    $"{PropNameMsbuildMono},{PropNameMsbuildVs},{PropNameXbuild}" :
+                    $"{PropNameMsbuildMono},{PropNameXbuild}"
+            });
+
+            Internal.EditorDef("mono/builds/print_build_output", false);
+        }
+    }
+}

+ 538 - 0
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -0,0 +1,538 @@
+using Godot;
+using GodotTools.Utils;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using GodotTools.Internals;
+using GodotTools.ProjectEditor;
+using File = GodotTools.Utils.File;
+using Path = System.IO.Path;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools
+{
+    public class GodotSharpEditor : EditorPlugin, ISerializationListener
+    {
+        private EditorSettings editorSettings;
+
+        private PopupMenu menuPopup;
+
+        private AcceptDialog errorDialog;
+        private AcceptDialog aboutDialog;
+        private CheckBox aboutDialogCheckBox;
+
+        private ToolButton bottomPanelBtn;
+
+        private MonoDevelopInstance monoDevelopInstance;
+        private MonoDevelopInstance visualStudioForMacInstance;
+
+        public MonoBottomPanel MonoBottomPanel { get; private set; }
+
+        private bool CreateProjectSolution()
+        {
+            using (var pr = new EditorProgress("create_csharp_solution", "Generating solution...", 2)) // TTR("Generating solution...")
+            {
+                pr.Step("Generating C# project..."); // TTR("Generating C# project...")
+
+                string resourceDir = ProjectSettings.GlobalizePath("res://");
+
+                string path = resourceDir;
+                string name = (string) ProjectSettings.GetSetting("application/config/name");
+                if (name.Empty())
+                    name = "UnnamedProject";
+
+                string guid = CSharpProject.GenerateGameProject(path, name);
+
+                if (guid.Length > 0)
+                {
+                    var solution = new DotNetSolution(name)
+                    {
+                        DirectoryPath = path
+                    };
+
+                    var projectInfo = new DotNetSolution.ProjectInfo
+                    {
+                        Guid = guid,
+                        PathRelativeToSolution = name + ".csproj",
+                        Configs = new List<string> {"Debug", "Release", "Tools"}
+                    };
+
+                    solution.AddNewProject(name, projectInfo);
+
+                    try
+                    {
+                        solution.Save();
+                    }
+                    catch (IOException e)
+                    {
+                        ShowErrorDialog($"Failed to save solution. Exception message: {e.Message}"); // TTR
+                        return false;
+                    }
+
+                    string apiConfig = "Debug";
+
+                    if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, apiConfig))
+                        return false;
+
+                    if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, apiConfig))
+                        return false;
+
+                    pr.Step("Done"); // TTR("Done")
+
+                    // Here, after all calls to progress_task_step
+                    CallDeferred(nameof(_RemoveCreateSlnMenuOption));
+                }
+                else
+                {
+                    ShowErrorDialog("Failed to create C# project."); // TTR
+                }
+
+                return true;
+            }
+        }
+
+        private static int _makeApiSolutionsAttempts = 100;
+        private static bool _makeApiSolutionsRecursionGuard = false;
+
+        private void _MakeApiSolutionsIfNeeded()
+        {
+            // I'm sick entirely of ProgressDialog
+
+            if (Internal.IsMessageQueueFlushing() || Engine.GetMainLoop() == null)
+            {
+                if (_makeApiSolutionsAttempts == 0) // This better never happen or I swear...
+                    throw new TimeoutException();
+
+                if (Engine.GetMainLoop() != null)
+                {
+                    if (!Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
+                        Engine.GetMainLoop().Connect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
+                }
+                else
+                {
+                    CallDeferred(nameof(_MakeApiSolutionsIfNeededImpl));
+                }
+
+                _makeApiSolutionsAttempts--;
+                return;
+            }
+
+            // Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
+            // the message queue, with signals the collateral damage should be minimal in the worst case.
+            if (!_makeApiSolutionsRecursionGuard)
+            {
+                _makeApiSolutionsRecursionGuard = true;
+
+                // Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
+                if (Engine.GetMainLoop().IsConnected("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded)))
+                    Engine.GetMainLoop().Disconnect("idle_frame", this, nameof(_MakeApiSolutionsIfNeeded));
+
+                _MakeApiSolutionsIfNeededImpl();
+
+                _makeApiSolutionsRecursionGuard = false;
+            }
+        }
+
+        private void _MakeApiSolutionsIfNeededImpl()
+        {
+            // If the project has a solution and C# project make sure the API assemblies are present and up to date
+
+            string api_config = "Debug";
+            string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, api_config);
+
+            if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Core}.dll")) ||
+                Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Core))
+            {
+                if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Core, api_config))
+                    return;
+            }
+
+            if (!File.Exists(Path.Combine(resAssembliesDir, $"{ApiAssemblyNames.Editor}.dll")) ||
+                Internal.MetadataIsApiAssemblyInvalidated(ApiAssemblyType.Editor))
+            {
+                if (!GodotSharpBuilds.MakeApiAssembly(ApiAssemblyType.Editor, api_config))
+                    return; // Redundant? I don't think so!
+            }
+        }
+
+        private void _RemoveCreateSlnMenuOption()
+        {
+            menuPopup.RemoveItem(menuPopup.GetItemIndex((int) MenuOptions.CreateSln));
+            bottomPanelBtn.Show();
+        }
+
+        private void _ShowAboutDialog()
+        {
+            bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
+            aboutDialogCheckBox.Pressed = showOnStart;
+            aboutDialog.PopupCenteredMinsize();
+        }
+
+        private void _ToggleAboutDialogOnStart(bool enabled)
+        {
+            bool showOnStart = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
+            if (showOnStart != enabled)
+                editorSettings.SetSetting("mono/editor/show_info_on_start", enabled);
+        }
+
+        private void _MenuOptionPressed(MenuOptions id)
+        {
+            switch (id)
+            {
+                case MenuOptions.CreateSln:
+                    CreateProjectSolution();
+                    break;
+                case MenuOptions.AboutCSharp:
+                    _ShowAboutDialog();
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid menu option");
+            }
+        }
+
+        private void _BuildSolutionPressed()
+        {
+            if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+            {
+                if (!CreateProjectSolution())
+                    return; // Failed to create solution
+            }
+
+            Instance.MonoBottomPanel.BuildProjectPressed();
+        }
+
+        public override void _Notification(int what)
+        {
+            base._Notification(what);
+
+            if (what == NotificationReady)
+            {
+                bool showInfoDialog = (bool) editorSettings.GetSetting("mono/editor/show_info_on_start");
+                if (showInfoDialog)
+                {
+                    aboutDialog.PopupExclusive = true;
+                    _ShowAboutDialog();
+                    // Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive from that time on.
+                    aboutDialog.PopupExclusive = false;
+                }
+            }
+        }
+
+        public enum MenuOptions
+        {
+            CreateSln,
+            AboutCSharp,
+        }
+
+        public enum ExternalEditor
+        {
+            None,
+            VisualStudio, // TODO (Windows-only)
+            VisualStudioForMac, // Mac-only
+            MonoDevelop,
+            VsCode
+        }
+
+        public void ShowErrorDialog(string message, string title = "Error")
+        {
+            errorDialog.WindowTitle = title;
+            errorDialog.DialogText = message;
+            errorDialog.PopupCenteredMinsize();
+        }
+
+        private static string _vsCodePath = string.Empty;
+
+        private static readonly string[] VsCodeNames =
+        {
+            "code", "code-oss", "vscode", "vscode-oss", "visual-studio-code", "visual-studio-code-oss"
+        };
+
+        public Error OpenInExternalEditor(Script script, int line, int col)
+        {
+            var editor = (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor");
+
+            switch (editor)
+            {
+                case ExternalEditor.VsCode:
+                {
+                    if (_vsCodePath.Empty() || !File.Exists(_vsCodePath))
+                    {
+                        // Try to search it again if it wasn't found last time or if it was removed from its location
+                        _vsCodePath = VsCodeNames.SelectFirstNotNull(OS.PathWhich, orElse: string.Empty);
+                    }
+
+                    var args = new List<string>();
+
+                    bool osxAppBundleInstalled = false;
+
+                    if (OS.IsOSX())
+                    {
+                        // The package path is '/Applications/Visual Studio Code.app'
+                        const string vscodeBundleId = "com.microsoft.VSCode";
+
+                        osxAppBundleInstalled = Internal.IsOsxAppBundleInstalled(vscodeBundleId);
+
+                        if (osxAppBundleInstalled)
+                        {
+                            args.Add("-b");
+                            args.Add(vscodeBundleId);
+
+                            // The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
+                            // editing our folder. It's better to ask for a new window and let VSCode do the window management.
+                            args.Add("-n");
+
+                            // The open process must wait until the application finishes (which is instant in VSCode's case)
+                            args.Add("--wait-apps");
+
+                            args.Add("--args");
+                        }
+                    }
+
+                    var resourcePath = ProjectSettings.GlobalizePath("res://");
+                    args.Add(resourcePath);
+
+                    string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+                    if (line >= 0)
+                    {
+                        args.Add("-g");
+                        args.Add($"{scriptPath}:{line + 1}:{col}");
+                    }
+                    else
+                    {
+                        args.Add(scriptPath);
+                    }
+
+                    string command;
+
+                    if (OS.IsOSX())
+                    {
+                        if (!osxAppBundleInstalled && _vsCodePath.Empty())
+                        {
+                            GD.PushError("Cannot find code editor: VSCode");
+                            return Error.FileNotFound;
+                        }
+
+                        command = osxAppBundleInstalled ? "/usr/bin/open" : _vsCodePath;
+                    }
+                    else
+                    {
+                        if (_vsCodePath.Empty())
+                        {
+                            GD.PushError("Cannot find code editor: VSCode");
+                            return Error.FileNotFound;
+                        }
+
+                        command = _vsCodePath;
+                    }
+
+                    try
+                    {
+                        OS.RunProcess(command, args);
+                    }
+                    catch (Exception e)
+                    {
+                        GD.PushError($"Error when trying to run code editor: VSCode. Exception message: '{e.Message}'");
+                    }
+
+                    break;
+                }
+
+                case ExternalEditor.VisualStudioForMac:
+                    goto case ExternalEditor.MonoDevelop;
+                case ExternalEditor.MonoDevelop:
+                {
+                    MonoDevelopInstance GetMonoDevelopInstance(string solutionPath)
+                    {
+                        if (OS.IsOSX() && editor == ExternalEditor.VisualStudioForMac)
+                        {
+                            if (visualStudioForMacInstance == null)
+                                visualStudioForMacInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.VisualStudioForMac);
+
+                            return visualStudioForMacInstance;
+                        }
+
+                        if (monoDevelopInstance == null)
+                            monoDevelopInstance = new MonoDevelopInstance(solutionPath, MonoDevelopInstance.EditorId.MonoDevelop);
+
+                        return monoDevelopInstance;
+                    }
+
+                    string scriptPath = ProjectSettings.GlobalizePath(script.ResourcePath);
+
+                    if (line >= 0)
+                        scriptPath += $";{line + 1};{col}";
+
+                    GetMonoDevelopInstance(GodotSharpDirs.ProjectSlnPath).Execute(scriptPath);
+
+                    break;
+                }
+
+                case ExternalEditor.None:
+                    return Error.Unavailable;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+
+            return Error.Ok;
+        }
+
+        public bool OverridesExternalEditor()
+        {
+            return (ExternalEditor) editorSettings.GetSetting("mono/editor/external_editor") != ExternalEditor.None;
+        }
+
+        public override bool Build()
+        {
+            return GodotSharpBuilds.EditorBuildCallback();
+        }
+
+        public override void EnablePlugin()
+        {
+            base.EnablePlugin();
+
+            if (Instance != null)
+                throw new InvalidOperationException();
+            Instance = this;
+
+            var editorInterface = GetEditorInterface();
+            var editorBaseControl = editorInterface.GetBaseControl();
+
+            editorSettings = editorInterface.GetEditorSettings();
+
+            errorDialog = new AcceptDialog();
+            editorBaseControl.AddChild(errorDialog);
+
+            MonoBottomPanel = new MonoBottomPanel();
+
+            bottomPanelBtn = AddControlToBottomPanel(MonoBottomPanel, "Mono"); // TTR("Mono")
+
+            AddChild(new HotReloadAssemblyWatcher {Name = "HotReloadAssemblyWatcher"});
+
+            menuPopup = new PopupMenu();
+            menuPopup.Hide();
+            menuPopup.SetAsToplevel(true);
+
+            AddToolSubmenuItem("Mono", menuPopup);
+
+            // TODO: Remove or edit this info dialog once Mono support is no longer in alpha
+            {
+                menuPopup.AddItem("About C# support", (int) MenuOptions.AboutCSharp); // TTR("About C# support")
+                aboutDialog = new AcceptDialog();
+                editorBaseControl.AddChild(aboutDialog);
+                aboutDialog.WindowTitle = "Important: C# support is not feature-complete";
+
+                // We don't use DialogText as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
+                // we'll add. Instead we add containers and a new autowrapped Label inside.
+
+                // Main VBoxContainer (icon + label on top, checkbox at bottom)
+                var aboutVBox = new VBoxContainer();
+                aboutDialog.AddChild(aboutVBox);
+
+                // HBoxContainer for icon + label
+                var aboutHBox = new HBoxContainer();
+                aboutVBox.AddChild(aboutHBox);
+
+                var aboutIcon = new TextureRect();
+                aboutIcon.Texture = aboutIcon.GetIcon("NodeWarning", "EditorIcons");
+                aboutHBox.AddChild(aboutIcon);
+
+                var aboutLabel = new Label();
+                aboutHBox.AddChild(aboutLabel);
+                aboutLabel.RectMinSize = new Vector2(600, 150) * Internal.EditorScale;
+                aboutLabel.SizeFlagsVertical = (int) Control.SizeFlags.ExpandFill;
+                aboutLabel.Autowrap = true;
+                aboutLabel.Text =
+                    "C# support in Godot Engine is in late alpha stage and, while already usable, " +
+                    "it is not meant for use in production.\n\n" +
+                    "Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " +
+                    "Bugs and usability issues will be addressed gradually over future releases, " +
+                    "potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
+                    "If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
+                    "        https://github.com/godotengine/godot/issues\n\n" +
+                    "Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
+
+                Internal.EditorDef("mono/editor/show_info_on_start", true);
+
+                // CheckBox in main container
+                aboutDialogCheckBox = new CheckBox {Text = "Show this warning when starting the editor"};
+                aboutDialogCheckBox.Connect("toggled", this, nameof(_ToggleAboutDialogOnStart));
+                aboutVBox.AddChild(aboutDialogCheckBox);
+            }
+
+            if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
+            {
+                // Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
+                CallDeferred(nameof(_MakeApiSolutionsIfNeeded));
+            }
+            else
+            {
+                bottomPanelBtn.Hide();
+                menuPopup.AddItem("Create C# solution", (int) MenuOptions.CreateSln); // TTR("Create C# solution")
+            }
+
+            menuPopup.Connect("id_pressed", this, nameof(_MenuOptionPressed));
+
+            var buildButton = new ToolButton
+            {
+                Text = "Build",
+                HintTooltip = "Build solution",
+                FocusMode = Control.FocusModeEnum.None
+            };
+            buildButton.Connect("pressed", this, nameof(_BuildSolutionPressed));
+            AddControlToContainer(CustomControlContainer.Toolbar, buildButton);
+
+            // External editor settings
+            Internal.EditorDef("mono/editor/external_editor", ExternalEditor.None);
+
+            string settingsHintStr = "Disabled";
+
+            if (OS.IsWindows())
+            {
+                settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
+                                   $",Visual Studio Code:{(int) ExternalEditor.VsCode}";
+            }
+            else if (OS.IsOSX())
+            {
+                settingsHintStr += $",Visual Studio:{(int) ExternalEditor.VisualStudioForMac}" +
+                                   $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
+                                   $",Visual Studio Code:{(int) ExternalEditor.VsCode}";
+            }
+            else if (OS.IsUnix())
+            {
+                settingsHintStr += $",MonoDevelop:{(int) ExternalEditor.MonoDevelop}" +
+                                   $",Visual Studio Code:{(int) ExternalEditor.VsCode}";
+            }
+
+            editorSettings.AddPropertyInfo(new Godot.Collections.Dictionary
+            {
+                ["type"] = Variant.Type.Int,
+                ["name"] = "mono/editor/external_editor",
+                ["hint"] = PropertyHint.Enum,
+                ["hint_string"] = settingsHintStr
+            });
+
+            // Export plugin
+            AddExportPlugin(new GodotSharpExport());
+
+            GodotSharpBuilds.Initialize();
+        }
+
+        public void OnBeforeSerialize()
+        {
+        }
+
+        public void OnAfterDeserialize()
+        {
+            Instance = this;
+        }
+
+        // Singleton
+
+        public static GodotSharpEditor Instance { get; private set; }
+
+        private GodotSharpEditor()
+        {
+        }
+    }
+}

+ 197 - 0
modules/mono/editor/GodotTools/GodotTools/GodotSharpExport.cs

@@ -0,0 +1,197 @@
+using Godot;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using GodotTools.Core;
+using GodotTools.Internals;
+using Directory = GodotTools.Utils.Directory;
+using File = GodotTools.Utils.File;
+using Path = System.IO.Path;
+
+namespace GodotTools
+{
+    public class GodotSharpExport : EditorExportPlugin
+    {
+        private void AddFile(string srcPath, string dstPath, bool remap = false)
+        {
+            AddFile(dstPath, File.ReadAllBytes(srcPath), remap);
+        }
+
+        public override void _ExportFile(string path, string type, string[] features)
+        {
+            base._ExportFile(path, type, features);
+
+            if (type != Internal.CSharpLanguageType)
+                return;
+
+            if (Path.GetExtension(path) != $".{Internal.CSharpLanguageExtension}")
+                throw new ArgumentException($"Resource of type {Internal.CSharpLanguageType} has an invalid file extension: {path}", nameof(path));
+
+            // TODO What if the source file is not part of the game's C# project
+
+            bool includeScriptsContent = (bool) ProjectSettings.GetSetting("mono/export/include_scripts_content");
+
+            if (!includeScriptsContent)
+            {
+                // We don't want to include the source code on exported games
+                AddFile(path, new byte[] { }, remap: false);
+                Skip();
+            }
+        }
+
+        public override void _ExportBegin(string[] features, bool isDebug, string path, int flags)
+        {
+            base._ExportBegin(features, isDebug, path, flags);
+
+            try
+            {
+                _ExportBeginImpl(features, isDebug, path, flags);
+            }
+            catch (Exception e)
+            {
+                GD.PushError($"Failed to export project. Exception message: {e.Message}");
+                Console.Error.WriteLine(e);
+            }
+        }
+
+        public void _ExportBeginImpl(string[] features, bool isDebug, string path, int flags)
+        {
+            // TODO Right now there is no way to stop the export process with an error
+
+            if (File.Exists(GodotSharpDirs.ProjectSlnPath))
+            {
+                string buildConfig = isDebug ? "Debug" : "Release";
+
+                string scriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, $"scripts_metadata.{(isDebug ? "debug" : "release")}");
+                CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, scriptsMetadataPath);
+
+                AddFile(scriptsMetadataPath, scriptsMetadataPath);
+
+                // Turn export features into defines
+                var godotDefines = features;
+
+                if (!GodotSharpBuilds.BuildProjectBlocking(buildConfig, godotDefines))
+                {
+                    GD.PushError("Failed to build project");
+                    return;
+                }
+
+                // Add dependency assemblies
+
+                var dependencies = new Godot.Collections.Dictionary<string, string>();
+
+                var projectDllName = (string) ProjectSettings.GetSetting("application/config/name");
+                if (projectDllName.Empty())
+                {
+                    projectDllName = "UnnamedProject";
+                }
+
+                string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
+                string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
+
+                dependencies[projectDllName] = projectDllSrcPath;
+
+                {
+                    string templatesDir = Internal.FullTemplatesDir;
+                    string androidBclDir = Path.Combine(templatesDir, "android-bcl");
+
+                    string customLibDir = features.Contains("Android") && Directory.Exists(androidBclDir) ? androidBclDir : string.Empty;
+
+                    GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
+                }
+
+                string apiConfig = isDebug ? "Debug" : "Release";
+                string resAssembliesDir = Path.Combine(GodotSharpDirs.ResAssembliesBaseDir, apiConfig);
+
+                foreach (var dependency in dependencies)
+                {
+                    string dependSrcPath = dependency.Value;
+                    string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
+                    AddFile(dependSrcPath, dependDstPath);
+                }
+            }
+
+            // Mono specific export template extras (data dir)
+            ExportDataDirectory(features, isDebug, path);
+        }
+
+        private static void ExportDataDirectory(IEnumerable<string> features, bool debug, string path)
+        {
+            var featureSet = new HashSet<string>(features);
+
+            if (!PlatformHasTemplateDir(featureSet))
+                return;
+
+            string templateDirName = "data.mono";
+
+            if (featureSet.Contains("Windows"))
+            {
+                templateDirName += ".windows";
+                templateDirName += featureSet.Contains("64") ? ".64" : ".32";
+            }
+            else if (featureSet.Contains("X11"))
+            {
+                templateDirName += ".x11";
+                templateDirName += featureSet.Contains("64") ? ".64" : ".32";
+            }
+            else
+            {
+                throw new NotSupportedException("Target platform not supported");
+            }
+
+            templateDirName += debug ? ".release_debug" : ".release";
+
+            string templateDirPath = Path.Combine(Internal.FullTemplatesDir, templateDirName);
+
+            if (!Directory.Exists(templateDirPath))
+                throw new FileNotFoundException("Data template directory not found");
+
+            string outputDir = new FileInfo(path).Directory?.FullName ??
+                               throw new FileNotFoundException("Base directory not found");
+
+            string outputDataDir = Path.Combine(outputDir, DataDirName);
+
+            if (Directory.Exists(outputDataDir))
+                Directory.Delete(outputDataDir, recursive: true); // Clean first
+
+            Directory.CreateDirectory(outputDataDir);
+
+            foreach (string dir in Directory.GetDirectories(templateDirPath, "*", SearchOption.AllDirectories))
+            {
+                Directory.CreateDirectory(Path.Combine(outputDataDir, dir.Substring(templateDirPath.Length + 1)));
+            }
+
+            foreach (string file in Directory.GetFiles(templateDirPath, "*", SearchOption.AllDirectories))
+            {
+                File.Copy(file, Path.Combine(outputDataDir, file.Substring(templateDirPath.Length + 1)));
+            }
+        }
+
+        private static bool PlatformHasTemplateDir(IEnumerable<string> featureSet)
+        {
+            // OSX export templates are contained in a zip, so we place
+            // our custom template inside it and let Godot do the rest.
+            return !featureSet.Any(f => new[] {"OSX", "Android"}.Contains(f));
+        }
+
+        private static string DataDirName
+        {
+            get
+            {
+                var appName = (string) ProjectSettings.GetSetting("application/config/name");
+                string appNameSafe = appName.ToSafeDirName(allowDirSeparator: false);
+                return $"data_{appNameSafe}";
+            }
+        }
+
+        private static void GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
+            string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies) =>
+            internal_GetExportedAssemblyDependencies(projectDllName, projectDllSrcPath, buildConfig, customLibDir, dependencies);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_GetExportedAssemblyDependencies(string projectDllName, string projectDllSrcPath,
+            string buildConfig, string customLibDir, Godot.Collections.Dictionary<string, string> dependencies);
+    }
+}

+ 81 - 0
modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj

@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{27B00618-A6F2-4828-B922-05CAEB08C286}</ProjectGuid>
+    <OutputType>Library</OutputType>
+    <RootNamespace>GodotTools</RootNamespace>
+    <AssemblyName>GodotTools</AssemblyName>
+    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
+    <GodotSourceRootPath>$(SolutionDir)/../../../../</GodotSourceRootPath>
+    <GodotApiConfiguration>Debug</GodotApiConfiguration>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>portable</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug</OutputPath>
+    <DefineConstants>DEBUG;</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release</OutputPath>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <ConsolePause>false</ConsolePause>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="GodotSharp">
+      <HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharp.dll</HintPath>
+    </Reference>
+    <Reference Include="GodotSharpEditor">
+      <HintPath>$(GodotSourceRootPath)/bin/GodotSharp/Api/$(GodotApiConfiguration)/GodotSharpEditor.dll</HintPath>
+    </Reference>
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="Build\MsBuildFinder.cs" />
+    <Compile Include="Internals\BindingsGenerator.cs" />
+    <Compile Include="Internals\EditorProgress.cs" />
+    <Compile Include="Internals\GodotSharpDirs.cs" />
+    <Compile Include="Internals\Internal.cs" />
+    <Compile Include="Internals\ScriptClassParser.cs" />
+    <Compile Include="MonoDevelopInstance.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Build\BuildSystem.cs" />
+    <Compile Include="Utils\Directory.cs" />
+    <Compile Include="Utils\File.cs" />
+    <Compile Include="Utils\OS.cs" />
+    <Compile Include="GodotSharpEditor.cs" />
+    <Compile Include="GodotSharpBuilds.cs" />
+    <Compile Include="HotReloadAssemblyWatcher.cs" />
+    <Compile Include="MonoBuildInfo.cs" />
+    <Compile Include="MonoBuildTab.cs" />
+    <Compile Include="MonoBottomPanel.cs" />
+    <Compile Include="GodotSharpExport.cs" />
+    <Compile Include="CSharpProject.cs" />
+    <Compile Include="Utils\CollectionExtensions.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\GodotTools.BuildLogger\GodotTools.BuildLogger.csproj">
+      <Project>{6ce9a984-37b1-4f8a-8fe9-609f05f071b3}</Project>
+      <Name>GodotTools.BuildLogger</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\GodotTools.ProjectEditor\GodotTools.ProjectEditor.csproj">
+      <Project>{A8CDAD94-C6D4-4B19-A7E7-76C53CC92984}</Project>
+      <Name>GodotTools.ProjectEditor</Name>
+    </ProjectReference>
+    <ProjectReference Include="..\GodotTools.Core\GodotTools.Core.csproj">
+      <Project>{639E48BD-44E5-4091-8EDD-22D36DC0768D}</Project>
+      <Name>GodotTools.Core</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="Editor" />
+  </ItemGroup>
+  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>

+ 47 - 0
modules/mono/editor/GodotTools/GodotTools/HotReloadAssemblyWatcher.cs

@@ -0,0 +1,47 @@
+using Godot;
+using GodotTools.Internals;
+
+namespace GodotTools
+{
+    public class HotReloadAssemblyWatcher : Node
+    {
+        private Timer watchTimer;
+
+        public override void _Notification(int what)
+        {
+            if (what == MainLoop.NotificationWmFocusIn)
+            {
+                RestartTimer();
+
+                if (Internal.IsAssembliesReloadingNeeded())
+                    Internal.ReloadAssemblies(softReload: false);
+            }
+        }
+
+        private void TimerTimeout()
+        {
+            if (Internal.IsAssembliesReloadingNeeded())
+                Internal.ReloadAssemblies(softReload: false);
+        }
+
+        public void RestartTimer()
+        {
+            watchTimer.Stop();
+            watchTimer.Start();
+        }
+
+        public override void _Ready()
+        {
+            base._Ready();
+
+            watchTimer = new Timer
+            {
+                OneShot = false,
+                WaitTime = (float) Internal.EditorDef("mono/assembly_watch_interval_sec", 0.5)
+            };
+            watchTimer.Connect("timeout", this, nameof(TimerTimeout));
+            AddChild(watchTimer);
+            watchTimer.Start();
+        }
+    }
+}

+ 87 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/BindingsGenerator.cs

@@ -0,0 +1,87 @@
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace GodotTools.Internals
+{
+    public class BindingsGenerator : IDisposable
+    {
+        class BindingsGeneratorSafeHandle : SafeHandle
+        {
+            public BindingsGeneratorSafeHandle(IntPtr handle) : base(IntPtr.Zero, true)
+            {
+                this.handle = handle;
+            }
+
+            public override bool IsInvalid => handle == IntPtr.Zero;
+
+            protected override bool ReleaseHandle()
+            {
+                internal_Dtor(handle);
+                return true;
+            }
+        }
+
+        private BindingsGeneratorSafeHandle safeHandle;
+        private bool disposed = false;
+
+        public bool LogPrintEnabled
+        {
+            get => internal_LogPrintEnabled(GetPtr());
+            set => internal_SetLogPrintEnabled(GetPtr(), value);
+        }
+
+        public static uint Version => internal_Version();
+        public static uint CsGlueVersion => internal_CsGlueVersion();
+
+        public Godot.Error GenerateCsApi(string outputDir) => internal_GenerateCsApi(GetPtr(), outputDir);
+
+        internal IntPtr GetPtr()
+        {
+            if (disposed)
+                throw new ObjectDisposedException(GetType().FullName);
+
+            return safeHandle.DangerousGetHandle();
+        }
+
+        public void Dispose()
+        {
+            if (disposed)
+                return;
+
+            if (safeHandle != null && !safeHandle.IsInvalid)
+            {
+                safeHandle.Dispose();
+                safeHandle = null;
+            }
+
+            disposed = true;
+        }
+
+        public BindingsGenerator()
+        {
+            safeHandle = new BindingsGeneratorSafeHandle(internal_Ctor());
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern IntPtr internal_Ctor();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_Dtor(IntPtr handle);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_LogPrintEnabled(IntPtr handle);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_SetLogPrintEnabled(IntPtr handle, bool enabled);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Godot.Error internal_GenerateCsApi(IntPtr handle, string outputDir);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern uint internal_Version();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern uint internal_CsGlueVersion();
+    }
+}

+ 50 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/EditorProgress.cs

@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot;
+
+namespace GodotTools.Internals
+{
+    public class EditorProgress : IDisposable
+    {
+        public string Task { get; }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_Create(string task, string label, int amount, bool canCancel);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_Dispose(string task);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_Step(string task, string state, int step, bool forceRefresh);
+
+        public EditorProgress(string task, string label, int amount, bool canCancel = false)
+        {
+            Task = task;
+            internal_Create(task, label, amount, canCancel);
+        }
+
+        ~EditorProgress()
+        {
+            // Should never rely on the GC to dispose EditorProgress.
+            // It should be disposed immediately when the task finishes.
+            GD.PushError("EditorProgress disposed by the Garbage Collector");
+            Dispose();
+        }
+
+        public void Dispose()
+        {
+            internal_Dispose(Task);
+            GC.SuppressFinalize(this);
+        }
+
+        public void Step(string state, int step = -1, bool forceRefresh = true)
+        {
+            internal_Step(Task, state, step, forceRefresh);
+        }
+
+        public bool TryStep(string state, int step = -1, bool forceRefresh = true)
+        {
+            return internal_Step(Task, state, step, forceRefresh);
+        }
+    }
+}

+ 91 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/GodotSharpDirs.cs

@@ -0,0 +1,91 @@
+using System.Runtime.CompilerServices;
+
+namespace GodotTools.Internals
+{
+    public static class GodotSharpDirs
+    {
+        public static string ResDataDir => internal_ResDataDir();
+        public static string ResMetadataDir => internal_ResMetadataDir();
+        public static string ResAssembliesBaseDir => internal_ResAssembliesBaseDir();
+        public static string ResAssembliesDir => internal_ResAssembliesDir();
+        public static string ResConfigDir => internal_ResConfigDir();
+        public static string ResTempDir => internal_ResTempDir();
+        public static string ResTempAssembliesBaseDir => internal_ResTempAssembliesBaseDir();
+        public static string ResTempAssembliesDir => internal_ResTempAssembliesDir();
+
+        public static string MonoUserDir => internal_MonoUserDir();
+        public static string MonoLogsDir => internal_MonoLogsDir();
+
+        #region Tools-only
+        public static string MonoSolutionsDir => internal_MonoSolutionsDir();
+        public static string BuildLogsDirs => internal_BuildLogsDirs();
+
+        public static string ProjectSlnPath => internal_ProjectSlnPath();
+        public static string ProjectCsProjPath => internal_ProjectCsProjPath();
+
+        public static string DataEditorToolsDir => internal_DataEditorToolsDir();
+        public static string DataEditorPrebuiltApiDir => internal_DataEditorPrebuiltApiDir();
+        #endregion
+
+        public static string DataMonoEtcDir => internal_DataMonoEtcDir();
+        public static string DataMonoLibDir => internal_DataMonoLibDir();
+
+        #region Windows-only
+        public static string DataMonoBinDir => internal_DataMonoBinDir();
+        #endregion
+        
+        
+        #region Internal
+        
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResDataDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResMetadataDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResAssembliesBaseDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResAssembliesDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResConfigDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResTempDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResTempAssembliesBaseDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ResTempAssembliesDir();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_MonoUserDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_MonoLogsDir();
+
+        #region Tools-only
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_MonoSolutionsDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_BuildLogsDirs();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ProjectSlnPath();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_ProjectCsProjPath();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_DataEditorToolsDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_DataEditorPrebuiltApiDir();
+        #endregion
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_DataMonoEtcDir();
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_DataMonoLibDir();
+
+        #region Windows-only
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_DataMonoBinDir();
+        #endregion
+        
+        #endregion
+    }
+}

+ 127 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/Internal.cs

@@ -0,0 +1,127 @@
+using System;
+using System.Runtime.CompilerServices;
+using Godot;
+using Godot.Collections;
+
+namespace GodotTools.Internals
+{
+    public static class Internal
+    {
+        public const string CSharpLanguageType = "CSharpScript";
+        public const string CSharpLanguageExtension = "cs";
+
+        public static float EditorScale => internal_EditorScale();
+
+        public static object GlobalDef(string setting, object defaultValue, bool restartIfChanged = false) =>
+            internal_GlobalDef(setting, defaultValue, restartIfChanged);
+
+        public static object EditorDef(string setting, object defaultValue, bool restartIfChanged = false) =>
+            internal_EditorDef(setting, defaultValue, restartIfChanged);
+
+        public static string FullTemplatesDir =>
+            internal_FullTemplatesDir();
+
+        public static string SimplifyGodotPath(this string path) => internal_SimplifyGodotPath(path);
+
+        public static bool IsOsxAppBundleInstalled(string bundleId) => internal_IsOsxAppBundleInstalled(bundleId);
+
+        public static bool MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType) =>
+            internal_MetadataIsApiAssemblyInvalidated(apiType);
+
+        public static void MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated) =>
+            internal_MetadataSetApiAssemblyInvalidated(apiType, invalidated);
+
+        public static bool IsMessageQueueFlushing() => internal_IsMessageQueueFlushing();
+
+        public static bool GodotIs32Bits() => internal_GodotIs32Bits();
+
+        public static bool GodotIsRealTDouble() => internal_GodotIsRealTDouble();
+
+        public static void GodotMainIteration() => internal_GodotMainIteration();
+
+        public static ulong GetCoreApiHash() => internal_GetCoreApiHash();
+
+        public static ulong GetEditorApiHash() => internal_GetEditorApiHash();
+
+        public static bool IsAssembliesReloadingNeeded() => internal_IsAssembliesReloadingNeeded();
+
+        public static void ReloadAssemblies(bool softReload) => internal_ReloadAssemblies(softReload);
+
+        public static void ScriptEditorDebuggerReloadScripts() => internal_ScriptEditorDebuggerReloadScripts();
+
+        public static bool ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus = true) =>
+            internal_ScriptEditorEdit(resource, line, col, grabFocus);
+
+        public static void EditorNodeShowScriptScreen() => internal_EditorNodeShowScriptScreen();
+
+        public static Dictionary<string, object> GetScriptsMetadataOrNothing() =>
+            internal_GetScriptsMetadataOrNothing(typeof(Dictionary<string, object>));
+
+        public static string MonoWindowsInstallRoot => internal_MonoWindowsInstallRoot();
+
+        // Internal Calls
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float internal_EditorScale();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object internal_GlobalDef(string setting, object defaultValue, bool restartIfChanged);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object internal_EditorDef(string setting, object defaultValue, bool restartIfChanged);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_FullTemplatesDir();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_SimplifyGodotPath(this string path);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_IsOsxAppBundleInstalled(string bundleId);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_MetadataIsApiAssemblyInvalidated(ApiAssemblyType apiType);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_MetadataSetApiAssemblyInvalidated(ApiAssemblyType apiType, bool invalidated);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_IsMessageQueueFlushing();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_GodotIs32Bits();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_GodotIsRealTDouble();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_GodotMainIteration();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern ulong internal_GetCoreApiHash();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern ulong internal_GetEditorApiHash();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_IsAssembliesReloadingNeeded();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_ReloadAssemblies(bool softReload);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_ScriptEditorDebuggerReloadScripts();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool internal_ScriptEditorEdit(Resource resource, int line, int col, bool grabFocus);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void internal_EditorNodeShowScriptScreen();
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Dictionary<string, object> internal_GetScriptsMetadataOrNothing(Type dictType);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern string internal_MonoWindowsInstallRoot();
+    }
+}

+ 52 - 0
modules/mono/editor/GodotTools/GodotTools/Internals/ScriptClassParser.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.CompilerServices;
+using Godot;
+using Godot.Collections;
+
+namespace GodotTools.Internals
+{
+    public static class ScriptClassParser
+    {
+        public class ClassDecl
+        {
+            public string Name { get; }
+            public string Namespace { get; }
+            public bool Nested { get; }
+            public int BaseCount { get; }
+
+            public ClassDecl(string name, string @namespace, bool nested, int baseCount)
+            {
+                Name = name;
+                Namespace = @namespace;
+                Nested = nested;
+                BaseCount = baseCount;
+            }
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Error internal_ParseFile(string filePath, Array<Dictionary> classes);
+
+        public static void ParseFileOrThrow(string filePath, out IEnumerable<ClassDecl> classes)
+        {
+            var classesArray = new Array<Dictionary>();
+            var error = internal_ParseFile(filePath, classesArray);
+            if (error != Error.Ok)
+                throw new Exception($"Failed to determine namespace and class for script: {filePath}. Parse error: {error}");
+
+            var classesList = new List<ClassDecl>();
+
+            foreach (var classDeclDict in classesArray)
+            {
+                classesList.Add(new ClassDecl(
+                    (string) classDeclDict["name"],
+                    (string) classDeclDict["namespace"],
+                    (bool) classDeclDict["nested"],
+                    (int) classDeclDict["base_count"]
+                ));
+            }
+
+            classes = classesList;
+        }
+    }
+}

+ 342 - 0
modules/mono/editor/GodotTools/GodotTools/MonoBottomPanel.cs

@@ -0,0 +1,342 @@
+using Godot;
+using System;
+using System.IO;
+using Godot.Collections;
+using GodotTools.Internals;
+using File = GodotTools.Utils.File;
+using Path = System.IO.Path;
+
+namespace GodotTools
+{
+    public class MonoBottomPanel : VBoxContainer
+    {
+        private EditorInterface editorInterface;
+
+        private TabContainer panelTabs;
+
+        private VBoxContainer panelBuildsTab;
+
+        private ItemList buildTabsList;
+        private TabContainer buildTabs;
+
+        private ToolButton warningsBtn;
+        private ToolButton errorsBtn;
+        private Button viewLogBtn;
+
+        private void _UpdateBuildTabsList()
+        {
+            buildTabsList.Clear();
+
+            int currentTab = buildTabs.CurrentTab;
+
+            bool noCurrentTab = currentTab < 0 || currentTab >= buildTabs.GetTabCount();
+
+            for (int i = 0; i < buildTabs.GetChildCount(); i++)
+            {
+                var tab = (MonoBuildTab) buildTabs.GetChild(i);
+
+                if (tab == null)
+                    continue;
+
+                string itemName = Path.GetFileNameWithoutExtension(tab.BuildInfo.Solution);
+                itemName += " [" + tab.BuildInfo.Configuration + "]";
+
+                buildTabsList.AddItem(itemName, tab.IconTexture);
+
+                string itemTooltip = "Solution: " + tab.BuildInfo.Solution;
+                itemTooltip += "\nConfiguration: " + tab.BuildInfo.Configuration;
+                itemTooltip += "\nStatus: ";
+
+                if (tab.BuildExited)
+                    itemTooltip += tab.BuildResult == MonoBuildTab.BuildResults.Success ? "Succeeded" : "Errored";
+                else
+                    itemTooltip += "Running";
+
+                if (!tab.BuildExited || tab.BuildResult == MonoBuildTab.BuildResults.Error)
+                    itemTooltip += $"\nErrors: {tab.ErrorCount}";
+
+                itemTooltip += $"\nWarnings: {tab.WarningCount}";
+
+                buildTabsList.SetItemTooltip(i, itemTooltip);
+
+                if (noCurrentTab || currentTab == i)
+                {
+                    buildTabsList.Select(i);
+                    _BuildTabsItemSelected(i);
+                }
+            }
+        }
+
+        public MonoBuildTab GetBuildTabFor(MonoBuildInfo buildInfo)
+        {
+            foreach (var buildTab in new Array<MonoBuildTab>(buildTabs.GetChildren()))
+            {
+                if (buildTab.BuildInfo.Equals(buildInfo))
+                    return buildTab;
+            }
+
+            var newBuildTab = new MonoBuildTab(buildInfo);
+            AddBuildTab(newBuildTab);
+
+            return newBuildTab;
+        }
+
+        private void _BuildTabsItemSelected(int idx)
+        {
+            if (idx < 0 || idx >= buildTabs.GetTabCount())
+                throw new IndexOutOfRangeException();
+
+            buildTabs.CurrentTab = idx;
+            if (!buildTabs.Visible)
+                buildTabs.Visible = true;
+
+            warningsBtn.Visible = true;
+            errorsBtn.Visible = true;
+            viewLogBtn.Visible = true;
+        }
+
+        private void _BuildTabsNothingSelected()
+        {
+            if (buildTabs.GetTabCount() != 0)
+            {
+                // just in case
+                buildTabs.Visible = false;
+
+                // This callback is called when clicking on the empty space of the list.
+                // ItemList won't deselect the items automatically, so we must do it ourselves.
+                buildTabsList.UnselectAll();
+            }
+
+            warningsBtn.Visible = false;
+            errorsBtn.Visible = false;
+            viewLogBtn.Visible = false;
+        }
+
+        private void _WarningsToggled(bool pressed)
+        {
+            int currentTab = buildTabs.CurrentTab;
+
+            if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
+                throw new InvalidOperationException("No tab selected");
+
+            var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab);
+            buildTab.WarningsVisible = pressed;
+            buildTab.UpdateIssuesList();
+        }
+
+        private void _ErrorsToggled(bool pressed)
+        {
+            int currentTab = buildTabs.CurrentTab;
+
+            if (currentTab < 0 || currentTab >= buildTabs.GetTabCount())
+                throw new InvalidOperationException("No tab selected");
+
+            var buildTab = (MonoBuildTab) buildTabs.GetChild(currentTab);
+            buildTab.ErrorsVisible = pressed;
+            buildTab.UpdateIssuesList();
+        }
+
+        public void BuildProjectPressed()
+        {
+            if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+                return; // No solution to build
+
+            string editorScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor");
+            string playerScriptsMetadataPath = Path.Combine(GodotSharpDirs.ResMetadataDir, "scripts_metadata.editor_player");
+
+            CSharpProject.GenerateScriptsMetadata(GodotSharpDirs.ProjectCsProjPath, editorScriptsMetadataPath);
+
+            if (File.Exists(editorScriptsMetadataPath))
+            {
+                try
+                {
+                    File.Copy(editorScriptsMetadataPath, playerScriptsMetadataPath);
+                }
+                catch (IOException e)
+                {
+                    GD.PushError($"Failed to copy scripts metadata file. Exception message: {e.Message}");
+                    return;
+                }
+            }
+
+            var godotDefines = new[]
+            {
+                OS.GetName(),
+                Internal.GodotIs32Bits() ? "32" : "64"
+            };
+
+            bool buildSuccess = GodotSharpBuilds.BuildProjectBlocking("Tools", godotDefines);
+
+            if (!buildSuccess)
+                return;
+
+            // Notify running game for hot-reload
+            Internal.ScriptEditorDebuggerReloadScripts();
+
+            // Hot-reload in the editor
+            GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
+
+            if (Internal.IsAssembliesReloadingNeeded())
+                Internal.ReloadAssemblies(softReload: false);
+        }
+
+        private void _ViewLogPressed()
+        {
+            if (!buildTabsList.IsAnythingSelected())
+                return;
+
+            var selectedItems = buildTabsList.GetSelectedItems();
+
+            if (selectedItems.Length != 1)
+                throw new InvalidOperationException($"Expected 1 selected item, got {selectedItems.Length}");
+
+            int selectedItem = selectedItems[0];
+
+            var buildTab = (MonoBuildTab) buildTabs.GetTabControl(selectedItem);
+
+            OS.ShellOpen(Path.Combine(buildTab.BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildLogFileName));
+        }
+
+        public override void _Notification(int what)
+        {
+            base._Notification(what);
+
+            if (what == EditorSettings.NotificationEditorSettingsChanged)
+            {
+                var editorBaseControl = editorInterface.GetBaseControl();
+                panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
+                panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
+                panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
+            }
+        }
+
+        public void AddBuildTab(MonoBuildTab buildTab)
+        {
+            buildTabs.AddChild(buildTab);
+            RaiseBuildTab(buildTab);
+        }
+
+        public void RaiseBuildTab(MonoBuildTab buildTab)
+        {
+            if (buildTab.GetParent() != buildTabs)
+                throw new InvalidOperationException("Build tab is not in the tabs list");
+
+            buildTabs.MoveChild(buildTab, 0);
+            _UpdateBuildTabsList();
+        }
+
+        public void ShowBuildTab()
+        {
+            for (int i = 0; i < panelTabs.GetTabCount(); i++)
+            {
+                if (panelTabs.GetTabControl(i) == panelBuildsTab)
+                {
+                    panelTabs.CurrentTab = i;
+                    GodotSharpEditor.Instance.MakeBottomPanelItemVisible(this);
+                    return;
+                }
+            }
+
+            GD.PushError("Builds tab not found");
+        }
+
+        public override void _Ready()
+        {
+            base._Ready();
+
+            editorInterface = GodotSharpEditor.Instance.GetEditorInterface();
+
+            var editorBaseControl = editorInterface.GetBaseControl();
+
+            SizeFlagsVertical = (int) SizeFlags.ExpandFill;
+            SetAnchorsAndMarginsPreset(LayoutPreset.Wide);
+
+            panelTabs = new TabContainer
+            {
+                TabAlign = TabContainer.TabAlignEnum.Left,
+                RectMinSize = new Vector2(0, 228) * Internal.EditorScale,
+                SizeFlagsVertical = (int) SizeFlags.ExpandFill
+            };
+            panelTabs.AddStyleboxOverride("panel", editorBaseControl.GetStylebox("DebuggerPanel", "EditorStyles"));
+            panelTabs.AddStyleboxOverride("tab_fg", editorBaseControl.GetStylebox("DebuggerTabFG", "EditorStyles"));
+            panelTabs.AddStyleboxOverride("tab_bg", editorBaseControl.GetStylebox("DebuggerTabBG", "EditorStyles"));
+            AddChild(panelTabs);
+
+            {
+                // Builds tab
+                panelBuildsTab = new VBoxContainer
+                {
+                    Name = "Builds", // TTR
+                    SizeFlagsHorizontal = (int) SizeFlags.ExpandFill
+                };
+                panelTabs.AddChild(panelBuildsTab);
+
+                var toolBarHBox = new HBoxContainer {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill};
+                panelBuildsTab.AddChild(toolBarHBox);
+
+                var buildProjectBtn = new Button
+                {
+                    Text = "Build Project", // TTR
+                    FocusMode = FocusModeEnum.None
+                };
+                buildProjectBtn.Connect("pressed", this, nameof(BuildProjectPressed));
+                toolBarHBox.AddChild(buildProjectBtn);
+
+                toolBarHBox.AddSpacer(begin: false);
+
+                warningsBtn = new ToolButton
+                {
+                    Text = "Warnings", // TTR
+                    ToggleMode = true,
+                    Pressed = true,
+                    Visible = false,
+                    FocusMode = FocusModeEnum.None
+                };
+                warningsBtn.Connect("toggled", this, nameof(_WarningsToggled));
+                toolBarHBox.AddChild(warningsBtn);
+
+                errorsBtn = new ToolButton
+                {
+                    Text = "Errors", // TTR
+                    ToggleMode = true,
+                    Pressed = true,
+                    Visible = false,
+                    FocusMode = FocusModeEnum.None
+                };
+                errorsBtn.Connect("toggled", this, nameof(_ErrorsToggled));
+                toolBarHBox.AddChild(errorsBtn);
+
+                toolBarHBox.AddSpacer(begin: false);
+
+                viewLogBtn = new Button
+                {
+                    Text = "View log", // TTR
+                    FocusMode = FocusModeEnum.None,
+                    Visible = false
+                };
+                viewLogBtn.Connect("pressed", this, nameof(_ViewLogPressed));
+                toolBarHBox.AddChild(viewLogBtn);
+
+                var hsc = new HSplitContainer
+                {
+                    SizeFlagsHorizontal = (int) SizeFlags.ExpandFill,
+                    SizeFlagsVertical = (int) SizeFlags.ExpandFill
+                };
+                panelBuildsTab.AddChild(hsc);
+
+                buildTabsList = new ItemList {SizeFlagsHorizontal = (int) SizeFlags.ExpandFill};
+                buildTabsList.Connect("item_selected", this, nameof(_BuildTabsItemSelected));
+                buildTabsList.Connect("nothing_selected", this, nameof(_BuildTabsNothingSelected));
+                hsc.AddChild(buildTabsList);
+
+                buildTabs = new TabContainer
+                {
+                    TabAlign = TabContainer.TabAlignEnum.Left,
+                    SizeFlagsHorizontal = (int) SizeFlags.ExpandFill,
+                    TabsVisible = false
+                };
+                hsc.AddChild(buildTabs);
+            }
+        }
+    }
+}

+ 47 - 0
modules/mono/editor/GodotTools/GodotTools/MonoBuildInfo.cs

@@ -0,0 +1,47 @@
+using System;
+using Godot;
+using Godot.Collections;
+using GodotTools.Internals;
+using Path = System.IO.Path;
+
+namespace GodotTools
+{
+    [Serializable]
+    public sealed class MonoBuildInfo : Reference // TODO Remove Reference once we have proper serialization
+    {
+        public string Solution { get; }
+        public string Configuration { get; }
+        public Array<string> CustomProperties { get; } = new Array<string>(); // TODO Use List once we have proper serialization
+
+        public string LogsDirPath => Path.Combine(GodotSharpDirs.BuildLogsDirs, $"{Solution.MD5Text()}_{Configuration}");
+
+        public override bool Equals(object obj)
+        {
+            if (obj is MonoBuildInfo other)
+                return other.Solution == Solution && other.Configuration == Configuration;
+
+            return false;
+        }
+
+        public override int GetHashCode()
+        {
+            unchecked
+            {
+                int hash = 17;
+                hash = hash * 29 + Solution.GetHashCode();
+                hash = hash * 29 + Configuration.GetHashCode();
+                return hash;
+            }
+        }
+
+        private MonoBuildInfo()
+        {
+        }
+
+        public MonoBuildInfo(string solution, string configuration)
+        {
+            Solution = solution;
+            Configuration = configuration;
+        }
+    }
+}

+ 260 - 0
modules/mono/editor/GodotTools/GodotTools/MonoBuildTab.cs

@@ -0,0 +1,260 @@
+using Godot;
+using System;
+using Godot.Collections;
+using GodotTools.Internals;
+using File = GodotTools.Utils.File;
+using Path = System.IO.Path;
+
+namespace GodotTools
+{
+    public class MonoBuildTab : VBoxContainer
+    {
+        public enum BuildResults
+        {
+            Error,
+            Success
+        }
+
+        [Serializable]
+        private class BuildIssue : Reference // TODO Remove Reference once we have proper serialization
+        {
+            public bool Warning { get; set; }
+            public string File { get; set; }
+            public int Line { get; set; }
+            public int Column { get; set; }
+            public string Code { get; set; }
+            public string Message { get; set; }
+            public string ProjectFile { get; set; }
+        }
+
+        private readonly Array<BuildIssue> issues = new Array<BuildIssue>(); // TODO Use List once we have proper serialization
+        private ItemList issuesList;
+
+        public bool BuildExited { get; private set; } = false;
+
+        public BuildResults? BuildResult { get; private set; } = null;
+
+        public int ErrorCount { get; private set; } = 0;
+
+        public int WarningCount { get; private set; } = 0;
+
+        public bool ErrorsVisible { get; set; } = true;
+        public bool WarningsVisible { get; set; } = true;
+
+        public Texture IconTexture
+        {
+            get
+            {
+                if (!BuildExited)
+                    return GetIcon("Stop", "EditorIcons");
+
+                if (BuildResult == BuildResults.Error)
+                    return GetIcon("StatusError", "EditorIcons");
+
+                return GetIcon("StatusSuccess", "EditorIcons");
+            }
+        }
+
+        public MonoBuildInfo BuildInfo { get; private set; }
+
+        private void _LoadIssuesFromFile(string csvFile)
+        {
+            using (var file = new Godot.File())
+            {
+                Error openError = file.Open(csvFile, Godot.File.ModeFlags.Read);
+
+                if (openError != Error.Ok)
+                    return;
+
+                while (!file.EofReached())
+                {
+                    string[] csvColumns = file.GetCsvLine();
+
+                    if (csvColumns.Length == 1 && csvColumns[0].Empty())
+                        return;
+
+                    if (csvColumns.Length != 7)
+                    {
+                        GD.PushError($"Expected 7 columns, got {csvColumns.Length}");
+                        continue;
+                    }
+
+                    var issue = new BuildIssue
+                    {
+                        Warning = csvColumns[0] == "warning",
+                        File = csvColumns[1],
+                        Line = int.Parse(csvColumns[2]),
+                        Column = int.Parse(csvColumns[3]),
+                        Code = csvColumns[4],
+                        Message = csvColumns[5],
+                        ProjectFile = csvColumns[6]
+                    };
+
+                    if (issue.Warning)
+                        WarningCount += 1;
+                    else
+                        ErrorCount += 1;
+
+                    issues.Add(issue);
+                }
+            }
+        }
+
+        private void _IssueActivated(int idx)
+        {
+            if (idx < 0 || idx >= issuesList.GetItemCount())
+                throw new IndexOutOfRangeException("Item list index out of range");
+
+            // Get correct issue idx from issue list
+            int issueIndex = (int) issuesList.GetItemMetadata(idx);
+
+            if (idx < 0 || idx >= issues.Count)
+                throw new IndexOutOfRangeException("Issue index out of range");
+
+            BuildIssue issue = issues[issueIndex];
+
+            if (issue.ProjectFile.Empty() && issue.File.Empty())
+                return;
+
+            string projectDir = issue.ProjectFile.Length > 0 ? issue.ProjectFile.GetBaseDir() : BuildInfo.Solution.GetBaseDir();
+
+            string file = Path.Combine(projectDir.SimplifyGodotPath(), issue.File.SimplifyGodotPath());
+
+            if (!File.Exists(file))
+                return;
+
+            file = ProjectSettings.LocalizePath(file);
+
+            if (file.StartsWith("res://"))
+            {
+                var script = (Script) ResourceLoader.Load(file, typeHint: Internal.CSharpLanguageType);
+
+                if (script != null && Internal.ScriptEditorEdit(script, issue.Line, issue.Column))
+                    Internal.EditorNodeShowScriptScreen();
+            }
+        }
+
+        public void UpdateIssuesList()
+        {
+            issuesList.Clear();
+
+            using (var warningIcon = GetIcon("Warning", "EditorIcons"))
+            using (var errorIcon = GetIcon("Error", "EditorIcons"))
+            {
+                for (int i = 0; i < issues.Count; i++)
+                {
+                    BuildIssue issue = issues[i];
+
+                    if (!(issue.Warning ? WarningsVisible : ErrorsVisible))
+                        continue;
+
+                    string tooltip = string.Empty;
+                    tooltip += $"Message: {issue.Message}";
+
+                    if (!issue.Code.Empty())
+                        tooltip += $"\nCode: {issue.Code}";
+
+                    tooltip += $"\nType: {(issue.Warning ? "warning" : "error")}";
+
+                    string text = string.Empty;
+
+                    if (!issue.File.Empty())
+                    {
+                        text += $"{issue.File}({issue.Line},{issue.Column}): ";
+
+                        tooltip += $"\nFile: {issue.File}";
+                        tooltip += $"\nLine: {issue.Line}";
+                        tooltip += $"\nColumn: {issue.Column}";
+                    }
+
+                    if (!issue.ProjectFile.Empty())
+                        tooltip += $"\nProject: {issue.ProjectFile}";
+
+                    text += issue.Message;
+
+                    int lineBreakIdx = text.IndexOf("\n", StringComparison.Ordinal);
+                    string itemText = lineBreakIdx == -1 ? text : text.Substring(0, lineBreakIdx);
+                    issuesList.AddItem(itemText, issue.Warning ? warningIcon : errorIcon);
+
+                    int index = issuesList.GetItemCount() - 1;
+                    issuesList.SetItemTooltip(index, tooltip);
+                    issuesList.SetItemMetadata(index, i);
+                }
+            }
+        }
+
+        public void OnBuildStart()
+        {
+            BuildExited = false;
+
+            issues.Clear();
+            WarningCount = 0;
+            ErrorCount = 0;
+            UpdateIssuesList();
+
+            GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
+        }
+
+        public void OnBuildExit(BuildResults result)
+        {
+            BuildExited = true;
+            BuildResult = result;
+
+            _LoadIssuesFromFile(Path.Combine(BuildInfo.LogsDirPath, GodotSharpBuilds.MsBuildIssuesFileName));
+            UpdateIssuesList();
+
+            GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
+        }
+
+        public void OnBuildExecFailed(string cause)
+        {
+            BuildExited = true;
+            BuildResult = BuildResults.Error;
+
+            issuesList.Clear();
+
+            var issue = new BuildIssue {Message = cause, Warning = false};
+
+            ErrorCount += 1;
+            issues.Add(issue);
+
+            UpdateIssuesList();
+
+            GodotSharpEditor.Instance.MonoBottomPanel.RaiseBuildTab(this);
+        }
+
+        public void RestartBuild()
+        {
+            if (!BuildExited)
+                throw new InvalidOperationException("Build already started");
+
+            GodotSharpBuilds.RestartBuild(this);
+        }
+
+        public void StopBuild()
+        {
+            if (!BuildExited)
+                throw new InvalidOperationException("Build is not in progress");
+
+            GodotSharpBuilds.StopBuild(this);
+        }
+
+        public override void _Ready()
+        {
+            base._Ready();
+
+            issuesList = new ItemList {SizeFlagsVertical = (int) SizeFlags.ExpandFill};
+            issuesList.Connect("item_activated", this, nameof(_IssueActivated));
+            AddChild(issuesList);
+        }
+
+        private MonoBuildTab()
+        {
+        }
+
+        public MonoBuildTab(MonoBuildInfo buildInfo)
+        {
+            BuildInfo = buildInfo;
+        }
+    }
+}

+ 26 - 29
modules/mono/editor/GodotSharpTools/Editor/MonoDevelopInstance.cs → modules/mono/editor/GodotTools/GodotTools/MonoDevelopInstance.cs

@@ -1,11 +1,11 @@
+using GodotTools.Core;
 using System;
 using System;
 using System.IO;
 using System.IO;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
-using System.Runtime.InteropServices;
-using System.Runtime.CompilerServices;
+using GodotTools.Internals;
 
 
-namespace GodotSharpTools.Editor
+namespace GodotTools
 {
 {
     public class MonoDevelopInstance
     public class MonoDevelopInstance
     {
     {
@@ -15,24 +15,24 @@ namespace GodotSharpTools.Editor
             VisualStudioForMac = 1
             VisualStudioForMac = 1
         }
         }
 
 
-        readonly string solutionFile;
-        readonly EditorId editorId;
+        private readonly string solutionFile;
+        private readonly EditorId editorId;
 
 
-        Process process;
+        private Process process;
 
 
-        public void Execute(string[] files)
+        public void Execute(params string[] files)
         {
         {
             bool newWindow = process == null || process.HasExited;
             bool newWindow = process == null || process.HasExited;
 
 
-            List<string> args = new List<string>();
+            var args = new List<string>();
 
 
             string command;
             string command;
 
 
             if (Utils.OS.IsOSX())
             if (Utils.OS.IsOSX())
             {
             {
-                string bundleId = codeEditorBundleIds[editorId];
+                string bundleId = CodeEditorBundleIds[editorId];
 
 
-                if (IsApplicationBundleInstalled(bundleId))
+                if (Internal.IsOsxAppBundleInstalled(bundleId))
                 {
                 {
                     command = "open";
                     command = "open";
 
 
@@ -47,12 +47,12 @@ namespace GodotSharpTools.Editor
                 }
                 }
                 else
                 else
                 {
                 {
-                    command = codeEditorPaths[editorId];
+                    command = CodeEditorPaths[editorId];
                 }
                 }
             }
             }
             else
             else
             {
             {
-                command = codeEditorPaths[editorId];
+                command = CodeEditorPaths[editorId];
             }
             }
 
 
             args.Add("--ipc-tcp");
             args.Add("--ipc-tcp");
@@ -72,7 +72,7 @@ namespace GodotSharpTools.Editor
 
 
             if (newWindow)
             if (newWindow)
             {
             {
-                process = Process.Start(new ProcessStartInfo()
+                process = Process.Start(new ProcessStartInfo
                 {
                 {
                     FileName = command,
                     FileName = command,
                     Arguments = string.Join(" ", args),
                     Arguments = string.Join(" ", args),
@@ -81,12 +81,12 @@ namespace GodotSharpTools.Editor
             }
             }
             else
             else
             {
             {
-                Process.Start(new ProcessStartInfo()
+                Process.Start(new ProcessStartInfo
                 {
                 {
                     FileName = command,
                     FileName = command,
                     Arguments = string.Join(" ", args),
                     Arguments = string.Join(" ", args),
                     UseShellExecute = false
                     UseShellExecute = false
-                });
+                })?.Dispose();
             }
             }
         }
         }
 
 
@@ -99,45 +99,42 @@ namespace GodotSharpTools.Editor
             this.editorId = editorId;
             this.editorId = editorId;
         }
         }
 
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private extern static bool IsApplicationBundleInstalled(string bundleId);
-
-        static readonly IReadOnlyDictionary<EditorId, string> codeEditorPaths;
-        static readonly IReadOnlyDictionary<EditorId, string> codeEditorBundleIds;
+        private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorPaths;
+        private static readonly IReadOnlyDictionary<EditorId, string> CodeEditorBundleIds;
 
 
         static MonoDevelopInstance()
         static MonoDevelopInstance()
         {
         {
             if (Utils.OS.IsOSX())
             if (Utils.OS.IsOSX())
             {
             {
-                codeEditorPaths = new Dictionary<EditorId, string>
+                CodeEditorPaths = new Dictionary<EditorId, string>
                 {
                 {
                     // Rely on PATH
                     // Rely on PATH
-                    { EditorId.MonoDevelop, "monodevelop" },
-                    { EditorId.VisualStudioForMac, "VisualStudio" }
+                    {EditorId.MonoDevelop, "monodevelop"},
+                    {EditorId.VisualStudioForMac, "VisualStudio"}
                 };
                 };
-                codeEditorBundleIds = new Dictionary<EditorId, string>
+                CodeEditorBundleIds = new Dictionary<EditorId, string>
                 {
                 {
                     // TODO EditorId.MonoDevelop
                     // TODO EditorId.MonoDevelop
-                    { EditorId.VisualStudioForMac, "com.microsoft.visual-studio" }
+                    {EditorId.VisualStudioForMac, "com.microsoft.visual-studio"}
                 };
                 };
             }
             }
             else if (Utils.OS.IsWindows())
             else if (Utils.OS.IsWindows())
             {
             {
-                codeEditorPaths = new Dictionary<EditorId, string>
+                CodeEditorPaths = new Dictionary<EditorId, string>
                 {
                 {
                     // XamarinStudio is no longer a thing, and the latest version is quite old
                     // XamarinStudio is no longer a thing, and the latest version is quite old
                     // MonoDevelop is available from source only on Windows. The recommendation
                     // MonoDevelop is available from source only on Windows. The recommendation
                     // is to use Visual Studio instead. Since there are no official builds, we
                     // is to use Visual Studio instead. Since there are no official builds, we
                     // will rely on custom MonoDevelop builds being added to PATH.
                     // will rely on custom MonoDevelop builds being added to PATH.
-                    { EditorId.MonoDevelop, "MonoDevelop.exe" }
+                    {EditorId.MonoDevelop, "MonoDevelop.exe"}
                 };
                 };
             }
             }
             else if (Utils.OS.IsUnix())
             else if (Utils.OS.IsUnix())
             {
             {
-                codeEditorPaths = new Dictionary<EditorId, string>
+                CodeEditorPaths = new Dictionary<EditorId, string>
                 {
                 {
                     // Rely on PATH
                     // Rely on PATH
-                    { EditorId.MonoDevelop, "monodevelop" }
+                    {EditorId.MonoDevelop, "monodevelop"}
                 };
                 };
             }
             }
         }
         }

+ 26 - 0
modules/mono/editor/GodotTools/GodotTools/Properties/AssemblyInfo.cs

@@ -0,0 +1,26 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("GodotTools")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("Godot Engine contributors")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]

+ 20 - 0
modules/mono/editor/GodotTools/GodotTools/Utils/CollectionExtensions.cs

@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+
+namespace GodotTools.Utils
+{
+    public static class CollectionExtensions
+    {
+        public static T SelectFirstNotNull<T>(this IEnumerable<T> enumerable, Func<T, T> predicate, T orElse = null)
+            where T : class
+        {
+            foreach (T elem in enumerable)
+            {
+                if (predicate(elem) != null)
+                    return elem;
+            }
+
+            return orElse;
+        }
+    }
+}

+ 40 - 0
modules/mono/editor/GodotTools/GodotTools/Utils/Directory.cs

@@ -0,0 +1,40 @@
+using System.IO;
+using Godot;
+
+namespace GodotTools.Utils
+{
+    public static class Directory
+    {
+        private static string GlobalizePath(this string path)
+        {
+            return ProjectSettings.GlobalizePath(path);
+        }
+
+        public static bool Exists(string path)
+        {
+            return System.IO.Directory.Exists(path.GlobalizePath());
+        }
+
+        /// Create directory recursively
+        public static DirectoryInfo CreateDirectory(string path)
+        {
+            return System.IO.Directory.CreateDirectory(path.GlobalizePath());
+        }
+
+        public static void Delete(string path, bool recursive)
+        {
+            System.IO.Directory.Delete(path.GlobalizePath(), recursive);
+        }
+
+
+        public static string[] GetDirectories(string path, string searchPattern, SearchOption searchOption)
+        {
+            return System.IO.Directory.GetDirectories(path.GlobalizePath(), searchPattern, searchOption);
+        }
+
+        public static string[] GetFiles(string path, string searchPattern, SearchOption searchOption)
+        {
+            return System.IO.Directory.GetFiles(path.GlobalizePath(), searchPattern, searchOption);
+        }
+    }
+}

+ 43 - 0
modules/mono/editor/GodotTools/GodotTools/Utils/File.cs

@@ -0,0 +1,43 @@
+using System;
+using Godot;
+
+namespace GodotTools.Utils
+{
+    public static class File
+    {
+        private static string GlobalizePath(this string path)
+        {
+            return ProjectSettings.GlobalizePath(path);
+        }
+
+        public static void WriteAllText(string path, string contents)
+        {
+            System.IO.File.WriteAllText(path.GlobalizePath(), contents);
+        }
+
+        public static bool Exists(string path)
+        {
+            return System.IO.File.Exists(path.GlobalizePath());
+        }
+
+        public static DateTime GetLastWriteTime(string path)
+        {
+            return System.IO.File.GetLastWriteTime(path.GlobalizePath());
+        }
+
+        public static void Delete(string path)
+        {
+            System.IO.File.Delete(path.GlobalizePath());
+        }
+
+        public static void Copy(string sourceFileName, string destFileName)
+        {
+            System.IO.File.Copy(sourceFileName.GlobalizePath(), destFileName.GlobalizePath(), overwrite: true);
+        }
+
+        public static byte[] ReadAllBytes(string path)
+        {
+            return System.IO.File.ReadAllBytes(path.GlobalizePath());
+        }
+    }
+}

+ 127 - 0
modules/mono/editor/GodotTools/GodotTools/Utils/OS.cs

@@ -0,0 +1,127 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+
+namespace GodotTools.Utils
+{
+    public static class OS
+    {
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        extern static string GetPlatformName();
+
+        const string HaikuName = "Haiku";
+        const string OSXName = "OSX";
+        const string ServerName = "Server";
+        const string UWPName = "UWP";
+        const string WindowsName = "Windows";
+        const string X11Name = "X11";
+
+        public static bool IsHaiku()
+        {
+            return HaikuName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsOSX()
+        {
+            return OSXName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsServer()
+        {
+            return ServerName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsUWP()
+        {
+            return UWPName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsWindows()
+        {
+            return WindowsName.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        public static bool IsX11()
+        {
+            return X11Name.Equals(GetPlatformName(), StringComparison.OrdinalIgnoreCase);
+        }
+
+        private static bool? _isUnixCache;
+        private static readonly string[] UnixPlatforms = {HaikuName, OSXName, ServerName, X11Name};
+
+        public static bool IsUnix()
+        {
+            if (_isUnixCache.HasValue)
+                return _isUnixCache.Value;
+
+            string osName = GetPlatformName();
+            _isUnixCache = UnixPlatforms.Any(p => p.Equals(osName, StringComparison.OrdinalIgnoreCase));
+            return _isUnixCache.Value;
+        }
+
+        public static char PathSep => IsWindows() ? ';' : ':';
+
+        public static string PathWhich(string name)
+        {
+            string[] windowsExts = IsWindows() ? Environment.GetEnvironmentVariable("PATHEXT")?.Split(PathSep) : null;
+            string[] pathDirs = Environment.GetEnvironmentVariable("PATH")?.Split(PathSep);
+
+            var searchDirs = new List<string>();
+
+            if (pathDirs != null)
+                searchDirs.AddRange(pathDirs);
+
+            searchDirs.Add(System.IO.Directory.GetCurrentDirectory()); // last in the list
+
+            foreach (var dir in searchDirs)
+            {
+                string path = Path.Combine(dir, name);
+
+                if (IsWindows() && windowsExts != null)
+                {
+                    foreach (var extension in windowsExts)
+                    {
+                        string pathWithExtension = path + extension;
+
+                        if (File.Exists(pathWithExtension))
+                            return pathWithExtension;
+                    }
+                }
+                else
+                {
+                    if (File.Exists(path))
+                        return path;
+                }
+            }
+
+            return null;
+        }
+
+        public static void RunProcess(string command, IEnumerable<string> arguments)
+        {
+            string CmdLineArgsToString(IEnumerable<string> args)
+            {
+                return string.Join(" ", args.Select(arg => arg.Contains(" ") ? $@"""{arg}""" : arg));
+            }
+
+            ProcessStartInfo startInfo = new ProcessStartInfo(command, CmdLineArgsToString(arguments))
+            {
+                RedirectStandardOutput = true,
+                RedirectStandardError = true,
+                UseShellExecute = false
+            };
+
+            using (Process process = Process.Start(startInfo))
+            {
+                if (process == null)
+                    throw new Exception("No process was started");
+
+                process.BeginOutputReadLine();
+                process.BeginErrorReadLine();
+            }
+        }
+    }
+}

+ 88 - 81
modules/mono/editor/bindings_generator.cpp

@@ -861,26 +861,22 @@ void BindingsGenerator::_generate_global_constants(StringBuilder &p_output) {
 	p_output.append("\n#pragma warning restore CS1591\n");
 	p_output.append("\n#pragma warning restore CS1591\n");
 }
 }
 
 
-Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution) {
-
-	String proj_dir = p_solution_dir.plus_file(CORE_API_ASSEMBLY_NAME);
+Error BindingsGenerator::generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_items) {
 
 
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 
 
-	if (!DirAccess::exists(proj_dir)) {
-		Error err = da->make_dir_recursive(proj_dir);
+	if (!DirAccess::exists(p_proj_dir)) {
+		Error err = da->make_dir_recursive(p_proj_dir);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 	}
 	}
 
 
-	da->change_dir(proj_dir);
+	da->change_dir(p_proj_dir);
 	da->make_dir("Core");
 	da->make_dir("Core");
 	da->make_dir("ObjectType");
 	da->make_dir("ObjectType");
 
 
-	String core_dir = path_join(proj_dir, "Core");
-	String obj_type_dir = path_join(proj_dir, "ObjectType");
-
-	Vector<String> compile_items;
+	String core_dir = path_join(p_proj_dir, "Core");
+	String obj_type_dir = path_join(p_proj_dir, "ObjectType");
 
 
 	// Generate source file for global scope constants and enums
 	// Generate source file for global scope constants and enums
 	{
 	{
@@ -891,7 +887,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
 		if (save_err != OK)
 		if (save_err != OK)
 			return save_err;
 			return save_err;
 
 
-		compile_items.push_back(output_file);
+		r_compile_items.push_back(output_file);
 	}
 	}
 
 
 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
@@ -909,7 +905,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
 		if (err != OK)
 		if (err != OK)
 			return err;
 			return err;
 
 
-		compile_items.push_back(output_file);
+		r_compile_items.push_back(output_file);
 	}
 	}
 
 
 	// Generate sources from compressed files
 	// Generate sources from compressed files
@@ -939,7 +935,7 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
 		file->store_buffer(data.ptr(), data.size());
 		file->store_buffer(data.ptr(), data.size());
 		file->close();
 		file->close();
 
 
-		compile_items.push_back(output_file);
+		r_compile_items.push_back(output_file);
 	}
 	}
 
 
 	StringBuilder cs_icalls_content;
 	StringBuilder cs_icalls_content;
@@ -981,43 +977,27 @@ Error BindingsGenerator::generate_cs_core_project(const String &p_solution_dir,
 	if (err != OK)
 	if (err != OK)
 		return err;
 		return err;
 
 
-	compile_items.push_back(internal_methods_file);
-
-	String guid = CSharpProject::generate_core_api_project(proj_dir, compile_items);
-
-	DotNetSolution::ProjectInfo proj_info;
-	proj_info.guid = guid;
-	proj_info.relpath = String(CORE_API_ASSEMBLY_NAME).plus_file(CORE_API_ASSEMBLY_NAME ".csproj");
-	proj_info.configs.push_back("Debug");
-	proj_info.configs.push_back("Release");
-
-	r_solution.add_new_project(CORE_API_ASSEMBLY_NAME, proj_info);
-
-	_log("The solution and C# project for the Core API was generated successfully\n");
+	r_compile_items.push_back(internal_methods_file);
 
 
 	return OK;
 	return OK;
 }
 }
 
 
-Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution) {
-
-	String proj_dir = p_solution_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
+Error BindingsGenerator::generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items) {
 
 
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 
 
-	if (!DirAccess::exists(proj_dir)) {
-		Error err = da->make_dir_recursive(proj_dir);
+	if (!DirAccess::exists(p_proj_dir)) {
+		Error err = da->make_dir_recursive(p_proj_dir);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 	}
 	}
 
 
-	da->change_dir(proj_dir);
+	da->change_dir(p_proj_dir);
 	da->make_dir("Core");
 	da->make_dir("Core");
 	da->make_dir("ObjectType");
 	da->make_dir("ObjectType");
 
 
-	String core_dir = path_join(proj_dir, "Core");
-	String obj_type_dir = path_join(proj_dir, "ObjectType");
-
-	Vector<String> compile_items;
+	String core_dir = path_join(p_proj_dir, "Core");
+	String obj_type_dir = path_join(p_proj_dir, "ObjectType");
 
 
 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
 	for (OrderedHashMap<StringName, TypeInterface>::Element E = obj_types.front(); E; E = E.next()) {
 		const TypeInterface &itype = E.get();
 		const TypeInterface &itype = E.get();
@@ -1034,7 +1014,7 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
 		if (err != OK)
 		if (err != OK)
 			return err;
 			return err;
 
 
-		compile_items.push_back(output_file);
+		r_compile_items.push_back(output_file);
 	}
 	}
 
 
 	StringBuilder cs_icalls_content;
 	StringBuilder cs_icalls_content;
@@ -1077,58 +1057,56 @@ Error BindingsGenerator::generate_cs_editor_project(const String &p_solution_dir
 	if (err != OK)
 	if (err != OK)
 		return err;
 		return err;
 
 
-	compile_items.push_back(internal_methods_file);
-
-	String guid = CSharpProject::generate_editor_api_project(proj_dir, "../" CORE_API_ASSEMBLY_NAME "/" CORE_API_ASSEMBLY_NAME ".csproj", compile_items);
-
-	DotNetSolution::ProjectInfo proj_info;
-	proj_info.guid = guid;
-	proj_info.relpath = String(EDITOR_API_ASSEMBLY_NAME).plus_file(EDITOR_API_ASSEMBLY_NAME ".csproj");
-	proj_info.configs.push_back("Debug");
-	proj_info.configs.push_back("Release");
-
-	r_solution.add_new_project(EDITOR_API_ASSEMBLY_NAME, proj_info);
-
-	_log("The solution and C# project for the Editor API was generated successfully\n");
+	r_compile_items.push_back(internal_methods_file);
 
 
 	return OK;
 	return OK;
 }
 }
 
 
 Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
 Error BindingsGenerator::generate_cs_api(const String &p_output_dir) {
 
 
+	String output_dir = DirAccess::get_full_path(p_output_dir, DirAccess::ACCESS_FILESYSTEM);
+
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 	ERR_FAIL_COND_V(!da, ERR_CANT_CREATE);
 
 
-	if (!DirAccess::exists(p_output_dir)) {
-		Error err = da->make_dir_recursive(p_output_dir);
+	if (!DirAccess::exists(output_dir)) {
+		Error err = da->make_dir_recursive(output_dir);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 		ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 	}
 	}
 
 
-	DotNetSolution solution(API_SOLUTION_NAME);
+	Error proj_err;
 
 
-	if (!solution.set_path(p_output_dir))
-		return ERR_FILE_NOT_FOUND;
+	// Generate GodotSharp source files
 
 
-	Error proj_err;
+	String core_proj_dir = output_dir.plus_file(CORE_API_ASSEMBLY_NAME);
+	Vector<String> core_compile_items;
 
 
-	proj_err = generate_cs_core_project(p_output_dir, solution);
+	proj_err = generate_cs_core_project(core_proj_dir, core_compile_items);
 	if (proj_err != OK) {
 	if (proj_err != OK) {
 		ERR_PRINT("Generation of the Core API C# project failed");
 		ERR_PRINT("Generation of the Core API C# project failed");
 		return proj_err;
 		return proj_err;
 	}
 	}
 
 
-	proj_err = generate_cs_editor_project(p_output_dir, solution);
+	// Generate GodotSharpEditor source files
+
+	String editor_proj_dir = output_dir.plus_file(EDITOR_API_ASSEMBLY_NAME);
+	Vector<String> editor_compile_items;
+
+	proj_err = generate_cs_editor_project(editor_proj_dir, editor_compile_items);
 	if (proj_err != OK) {
 	if (proj_err != OK) {
 		ERR_PRINT("Generation of the Editor API C# project failed");
 		ERR_PRINT("Generation of the Editor API C# project failed");
 		return proj_err;
 		return proj_err;
 	}
 	}
 
 
-	Error sln_error = solution.save();
-	if (sln_error != OK) {
-		ERR_PRINT("Failed to save API solution");
-		return sln_error;
+	// Generate solution
+
+	if (!CSharpProject::generate_api_solution(output_dir,
+				core_proj_dir, core_compile_items, editor_proj_dir, editor_compile_items)) {
+		return ERR_CANT_CREATE;
 	}
 	}
 
 
+	_log("The solution for the Godot API was generated successfully\n");
+
 	return OK;
 	return OK;
 }
 }
 
 
@@ -1311,8 +1289,9 @@ Error BindingsGenerator::_generate_cs_type(const TypeInterface &itype, const Str
 		output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
 		output.append(MEMBER_BEGIN "private static Godot.Object singleton;\n");
 		output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
 		output.append(MEMBER_BEGIN "public static Godot.Object Singleton\n" INDENT2 "{\n" INDENT3
 								   "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
 								   "get\n" INDENT3 "{\n" INDENT4 "if (singleton == null)\n" INDENT5
-								   "singleton = Engine.GetSingleton(" BINDINGS_NATIVE_NAME_FIELD ");\n" INDENT4
-								   "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
+								   "singleton = Engine.GetSingleton(typeof(");
+		output.append(itype.proxy_name);
+		output.append(").Name);\n" INDENT4 "return singleton;\n" INDENT3 "}\n" INDENT2 "}\n");
 
 
 		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
 		output.append(MEMBER_BEGIN "private const string " BINDINGS_NATIVE_NAME_FIELD " = \"");
 		output.append(itype.name);
 		output.append(itype.name);
@@ -2347,6 +2326,13 @@ void BindingsGenerator::_populate_object_type_interfaces() {
 				imethod.return_type.is_enum = true;
 				imethod.return_type.is_enum = true;
 			} else if (return_info.class_name != StringName()) {
 			} else if (return_info.class_name != StringName()) {
 				imethod.return_type.cname = return_info.class_name;
 				imethod.return_type.cname = return_info.class_name;
+				if (!imethod.is_virtual && ClassDB::is_parent_class(return_info.class_name, name_cache.type_Reference) && return_info.hint != PROPERTY_HINT_RESOURCE_TYPE) {
+					/* clang-format off */
+					ERR_PRINTS("Return type is reference but hint is not " _STR(PROPERTY_HINT_RESOURCE_TYPE) "."
+							" Are you returning a reference type by pointer? Method: " + itype.name + "." + imethod.name);
+					/* clang-format on */
+					ERR_FAIL();
+				}
 			} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 			} else if (return_info.hint == PROPERTY_HINT_RESOURCE_TYPE) {
 				imethod.return_type.cname = return_info.hint_string;
 				imethod.return_type.cname = return_info.hint_string;
 			} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
 			} else if (return_info.type == Variant::NIL && return_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT) {
@@ -3018,36 +3004,49 @@ void BindingsGenerator::_initialize() {
 void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
 void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args) {
 
 
 	const int NUM_OPTIONS = 2;
 	const int NUM_OPTIONS = 2;
-	String mono_glue_option = "--generate-mono-glue";
-	String cs_api_option = "--generate-cs-api";
+	String generate_all_glue_option = "--generate-mono-glue";
+	String generate_cs_glue_option = "--generate-mono-cs-glue";
+	String generate_cpp_glue_option = "--generate-mono-cpp-glue";
 
 
-	String mono_glue_path;
-	String cs_api_path;
+	String glue_dir_path;
+	String cs_dir_path;
+	String cpp_dir_path;
 
 
 	int options_left = NUM_OPTIONS;
 	int options_left = NUM_OPTIONS;
 
 
 	const List<String>::Element *elem = p_cmdline_args.front();
 	const List<String>::Element *elem = p_cmdline_args.front();
 
 
 	while (elem && options_left) {
 	while (elem && options_left) {
-		if (elem->get() == mono_glue_option) {
+		if (elem->get() == generate_all_glue_option) {
+			const List<String>::Element *path_elem = elem->next();
+
+			if (path_elem) {
+				glue_dir_path = path_elem->get();
+				elem = elem->next();
+			} else {
+				ERR_PRINTS(generate_all_glue_option + ": No output directory specified (expected path to {GODOT_ROOT}/modules/mono/glue)");
+			}
+
+			--options_left;
+		} else if (elem->get() == generate_cs_glue_option) {
 			const List<String>::Element *path_elem = elem->next();
 			const List<String>::Element *path_elem = elem->next();
 
 
 			if (path_elem) {
 			if (path_elem) {
-				mono_glue_path = path_elem->get();
+				cs_dir_path = path_elem->get();
 				elem = elem->next();
 				elem = elem->next();
 			} else {
 			} else {
-				ERR_PRINTS(mono_glue_option + ": No output directory specified");
+				ERR_PRINTS(generate_cs_glue_option + ": No output directory specified");
 			}
 			}
 
 
 			--options_left;
 			--options_left;
-		} else if (elem->get() == cs_api_option) {
+		} else if (elem->get() == generate_cpp_glue_option) {
 			const List<String>::Element *path_elem = elem->next();
 			const List<String>::Element *path_elem = elem->next();
 
 
 			if (path_elem) {
 			if (path_elem) {
-				cs_api_path = path_elem->get();
+				cpp_dir_path = path_elem->get();
 				elem = elem->next();
 				elem = elem->next();
 			} else {
 			} else {
-				ERR_PRINTS(cs_api_option + ": No output directory specified");
+				ERR_PRINTS(generate_cpp_glue_option + ": No output directory specified");
 			}
 			}
 
 
 			--options_left;
 			--options_left;
@@ -3056,18 +3055,26 @@ void BindingsGenerator::handle_cmdline_args(const List<String> &p_cmdline_args)
 		elem = elem->next();
 		elem = elem->next();
 	}
 	}
 
 
-	if (mono_glue_path.length() || cs_api_path.length()) {
+	if (glue_dir_path.length() || cs_dir_path.length() || cpp_dir_path.length()) {
 		BindingsGenerator bindings_generator;
 		BindingsGenerator bindings_generator;
 		bindings_generator.set_log_print_enabled(true);
 		bindings_generator.set_log_print_enabled(true);
 
 
-		if (mono_glue_path.length()) {
-			if (bindings_generator.generate_glue(mono_glue_path) != OK)
-				ERR_PRINTS(mono_glue_option + ": Failed to generate mono glue");
+		if (glue_dir_path.length()) {
+			if (bindings_generator.generate_glue(glue_dir_path) != OK)
+				ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C++ glue");
+
+			if (bindings_generator.generate_cs_api(glue_dir_path.plus_file("Managed/Generated")) != OK)
+				ERR_PRINTS(generate_all_glue_option + ": Failed to generate the C# API");
+		}
+
+		if (cs_dir_path.length()) {
+			if (bindings_generator.generate_cs_api(cs_dir_path) != OK)
+				ERR_PRINTS(generate_cs_glue_option + ": Failed to generate the C# API");
 		}
 		}
 
 
-		if (cs_api_path.length()) {
-			if (bindings_generator.generate_cs_api(cs_api_path) != OK)
-				ERR_PRINTS(cs_api_option + ": Failed to generate the C# API");
+		if (cpp_dir_path.length()) {
+			if (bindings_generator.generate_glue(cpp_dir_path) != OK)
+				ERR_PRINTS(generate_cpp_glue_option + ": Failed to generate the C++ glue");
 		}
 		}
 
 
 		// Exit once done
 		// Exit once done

+ 4 - 4
modules/mono/editor/bindings_generator.h

@@ -33,7 +33,6 @@
 
 
 #include "core/class_db.h"
 #include "core/class_db.h"
 #include "core/string_builder.h"
 #include "core/string_builder.h"
-#include "dotnet_solution.h"
 #include "editor/doc/doc_data.h"
 #include "editor/doc/doc_data.h"
 #include "editor/editor_help.h"
 #include "editor/editor_help.h"
 
 
@@ -614,12 +613,13 @@ class BindingsGenerator {
 	void _initialize();
 	void _initialize();
 
 
 public:
 public:
-	Error generate_cs_core_project(const String &p_solution_dir, DotNetSolution &r_solution);
-	Error generate_cs_editor_project(const String &p_solution_dir, DotNetSolution &r_solution);
+	Error generate_cs_core_project(const String &p_proj_dir, Vector<String> &r_compile_files);
+	Error generate_cs_editor_project(const String &p_proj_dir, Vector<String> &r_compile_items);
 	Error generate_cs_api(const String &p_output_dir);
 	Error generate_cs_api(const String &p_output_dir);
 	Error generate_glue(const String &p_output_dir);
 	Error generate_glue(const String &p_output_dir);
 
 
-	void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
+	_FORCE_INLINE_ bool is_log_print_enabled() { return log_print_enabled; }
+	_FORCE_INLINE_ void set_log_print_enabled(bool p_enabled) { log_print_enabled = p_enabled; }
 
 
 	static uint32_t get_version();
 	static uint32_t get_version();
 
 

+ 36 - 170
modules/mono/editor/csharp_project.cpp

@@ -44,66 +44,54 @@
 
 
 namespace CSharpProject {
 namespace CSharpProject {
 
 
-String generate_core_api_project(const String &p_dir, const Vector<String> &p_files) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
-
-	Variant dir = p_dir;
-	Variant compile_items = p_files;
-	const Variant *args[2] = { &dir, &compile_items };
+bool generate_api_solution_impl(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
+		const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items,
+		GDMonoAssembly *p_tools_project_editor_assembly) {
+
+	GDMonoClass *klass = p_tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ApiSolutionGenerator");
+
+	Variant solution_dir = p_solution_dir;
+	Variant core_proj_dir = p_core_proj_dir;
+	Variant core_compile_items = p_core_compile_items;
+	Variant editor_proj_dir = p_editor_proj_dir;
+	Variant editor_compile_items = p_editor_compile_items;
+	const Variant *args[5] = { &solution_dir, &core_proj_dir, &core_compile_items, &editor_proj_dir, &editor_compile_items };
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
-	MonoObject *ret = klass->get_method("GenCoreApiProject", 2)->invoke(NULL, args, &exc);
+	klass->get_method("GenerateApiSolution", 5)->invoke(NULL, args, &exc);
 
 
 	if (exc) {
 	if (exc) {
 		GDMonoUtils::debug_print_unhandled_exception(exc);
 		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL_V(String());
+		ERR_FAIL_V(false);
 	}
 	}
 
 
-	return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
+	return true;
 }
 }
 
 
-String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
-
-	Variant dir = p_dir;
-	Variant core_proj_path = p_core_proj_path;
-	Variant compile_items = p_files;
-	const Variant *args[3] = { &dir, &core_proj_path, &compile_items };
-	MonoException *exc = NULL;
-	MonoObject *ret = klass->get_method("GenEditorApiProject", 3)->invoke(NULL, args, &exc);
-
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL_V(String());
-	}
-
-	return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
-}
+bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
+		const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items) {
 
 
-String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files) {
+	if (GDMono::get_singleton()->get_tools_project_editor_assembly()) {
+		return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items,
+				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");
+		CRASH_COND(temp_domain == NULL);
+		_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(temp_domain);
 
 
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
+		_GDMONO_SCOPE_DOMAIN_(temp_domain);
 
 
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectGenerator");
+		GDMonoAssembly *tools_project_editor_assembly = NULL;
 
 
-	Variant dir = p_dir;
-	Variant name = p_name;
-	Variant compile_items = p_files;
-	const Variant *args[3] = { &dir, &name, &compile_items };
-	MonoException *exc = NULL;
-	MonoObject *ret = klass->get_method("GenGameProject", 3)->invoke(NULL, args, &exc);
+		if (!GDMono::get_singleton()->load_assembly("GodotTools.ProjectEditor", &tools_project_editor_assembly)) {
+			ERR_EXPLAIN("Failed to load assembly: 'GodotTools.ProjectEditor'");
+			ERR_FAIL_V(false);
+		}
 
 
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL_V(String());
+		return generate_api_solution_impl(p_solution_dir, p_core_proj_dir, p_core_compile_items,
+				p_editor_proj_dir, p_editor_compile_items,
+				tools_project_editor_assembly);
 	}
 	}
-
-	return ret ? GDMonoMarshal::mono_string_to_godot((MonoString *)ret) : String();
 }
 }
 
 
 void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
 void add_item(const String &p_project_path, const String &p_item_type, const String &p_include) {
@@ -111,9 +99,9 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
 	if (!GLOBAL_DEF("mono/project/auto_update_project", true))
 	if (!GLOBAL_DEF("mono/project/auto_update_project", true))
 		return;
 		return;
 
 
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
+	GDMonoAssembly *tools_project_editor_assembly = GDMono::get_singleton()->get_tools_project_editor_assembly();
 
 
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
+	GDMonoClass *klass = tools_project_editor_assembly->get_class("GodotTools.ProjectEditor", "ProjectUtils");
 
 
 	Variant project_path = p_project_path;
 	Variant project_path = p_project_path;
 	Variant item_type = p_item_type;
 	Variant item_type = p_item_type;
@@ -128,126 +116,4 @@ void add_item(const String &p_project_path, const String &p_item_type, const Str
 	}
 	}
 }
 }
 
 
-Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	if (FileAccess::exists(p_output_path)) {
-		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-		Error rm_err = da->remove(p_output_path);
-
-		ERR_EXPLAIN("Failed to remove old scripts metadata file");
-		ERR_FAIL_COND_V(rm_err != OK, rm_err);
-	}
-
-	GDMonoClass *project_utils = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Project", "ProjectUtils");
-
-	void *args[2] = {
-		GDMonoMarshal::mono_string_from_godot(p_project_path),
-		GDMonoMarshal::mono_string_from_godot("Compile")
-	};
-
-	MonoException *exc = NULL;
-	MonoArray *ret = (MonoArray *)project_utils->get_method("GetIncludeFiles", 2)->invoke_raw(NULL, args, &exc);
-
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL_V(FAILED);
-	}
-
-	PoolStringArray project_files = GDMonoMarshal::mono_array_to_PoolStringArray(ret);
-	PoolStringArray::Read r = project_files.read();
-
-	Dictionary old_dict = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
-	Dictionary new_dict;
-
-	for (int i = 0; i < project_files.size(); i++) {
-		const String &project_file = ("res://" + r[i]).simplify_path();
-
-		uint64_t modified_time = FileAccess::get_modified_time(project_file);
-
-		const Variant *old_file_var = old_dict.getptr(project_file);
-		if (old_file_var) {
-			Dictionary old_file_dict = old_file_var->operator Dictionary();
-
-			if (old_file_dict["modified_time"].operator uint64_t() == modified_time) {
-				// No changes so no need to parse again
-				new_dict[project_file] = old_file_dict;
-				continue;
-			}
-		}
-
-		ScriptClassParser scp;
-		Error err = scp.parse_file(project_file);
-		if (err != OK) {
-			ERR_PRINTS("Parse error: " + scp.get_error());
-			ERR_EXPLAIN("Failed to determine namespace and class for script: " + project_file);
-			ERR_FAIL_V(err);
-		}
-
-		Vector<ScriptClassParser::ClassDecl> classes = scp.get_classes();
-
-		bool found = false;
-		Dictionary class_dict;
-
-		String search_name = project_file.get_file().get_basename();
-
-		for (int j = 0; j < classes.size(); j++) {
-			const ScriptClassParser::ClassDecl &class_decl = classes[j];
-
-			if (class_decl.base.size() == 0)
-				continue; // Does not inherit nor implement anything, so it can't be a script class
-
-			String class_cmp;
-
-			if (class_decl.nested) {
-				class_cmp = class_decl.name.get_slice(".", class_decl.name.get_slice_count(".") - 1);
-			} else {
-				class_cmp = class_decl.name;
-			}
-
-			if (class_cmp != search_name)
-				continue;
-
-			class_dict["namespace"] = class_decl.namespace_;
-			class_dict["class_name"] = class_decl.name;
-			class_dict["nested"] = class_decl.nested;
-
-			found = true;
-			break;
-		}
-
-		if (found) {
-			Dictionary file_dict;
-			file_dict["modified_time"] = modified_time;
-			file_dict["class"] = class_dict;
-			new_dict[project_file] = file_dict;
-		}
-	}
-
-	if (new_dict.size()) {
-		String json = JSON::print(new_dict, "", false);
-
-		String base_dir = p_output_path.get_base_dir();
-
-		if (!DirAccess::exists(base_dir)) {
-			DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-
-			Error err = da->make_dir_recursive(base_dir);
-			ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
-		}
-
-		Error ferr;
-		FileAccess *f = FileAccess::open(p_output_path, FileAccess::WRITE, &ferr);
-		ERR_EXPLAIN("Cannot open file for writing: " + p_output_path);
-		ERR_FAIL_COND_V(ferr != OK, ferr);
-		f->store_string(json);
-		f->flush();
-		f->close();
-		memdelete(f);
-	}
-
-	return OK;
-}
-
 } // namespace CSharpProject
 } // namespace CSharpProject

+ 2 - 5
modules/mono/editor/csharp_project.h

@@ -35,14 +35,11 @@
 
 
 namespace CSharpProject {
 namespace CSharpProject {
 
 
-String generate_core_api_project(const String &p_dir, const Vector<String> &p_files = Vector<String>());
-String generate_editor_api_project(const String &p_dir, const String &p_core_proj_path, const Vector<String> &p_files = Vector<String>());
-String generate_game_project(const String &p_dir, const String &p_name, const Vector<String> &p_files = Vector<String>());
+bool generate_api_solution(const String &p_solution_dir, const String &p_core_proj_dir, const Vector<String> &p_core_compile_items,
+		const String &p_editor_proj_dir, const Vector<String> &p_editor_compile_items);
 
 
 void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
 void add_item(const String &p_project_path, const String &p_item_type, const String &p_include);
 
 
-Error generate_scripts_metadata(const String &p_project_path, const String &p_output_path);
-
 } // namespace CSharpProject
 } // namespace CSharpProject
 
 
 #endif // CSHARP_PROJECT_H
 #endif // CSHARP_PROJECT_H

+ 0 - 140
modules/mono/editor/dotnet_solution.cpp

@@ -1,140 +0,0 @@
-/*************************************************************************/
-/*  dotnet_solution.cpp                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "dotnet_solution.h"
-
-#include "core/os/dir_access.h"
-#include "core/os/file_access.h"
-
-#include "../utils/path_utils.h"
-#include "../utils/string_utils.h"
-#include "csharp_project.h"
-
-#define SOLUTION_TEMPLATE                                             \
-	"Microsoft Visual Studio Solution File, Format Version 12.00\n"   \
-	"# Visual Studio 2012\n"                                          \
-	"%0\n"                                                            \
-	"Global\n"                                                        \
-	"\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n" \
-	"%1\n"                                                            \
-	"\tEndGlobalSection\n"                                            \
-	"\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n" \
-	"%2\n"                                                            \
-	"\tEndGlobalSection\n"                                            \
-	"EndGlobal\n"
-
-#define PROJECT_DECLARATION "Project(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"%0\", \"%1\", \"{%2}\"\nEndProject"
-
-#define SOLUTION_PLATFORMS_CONFIG "\t%0|Any CPU = %0|Any CPU"
-
-#define PROJECT_PLATFORMS_CONFIG                   \
-	"\t\t{%0}.%1|Any CPU.ActiveCfg = %1|Any CPU\n" \
-	"\t\t{%0}.%1|Any CPU.Build.0 = %1|Any CPU"
-
-void DotNetSolution::add_new_project(const String &p_name, const ProjectInfo &p_project_info) {
-	projects[p_name] = p_project_info;
-}
-
-bool DotNetSolution::has_project(const String &p_name) const {
-	return projects.find(p_name) != NULL;
-}
-
-const DotNetSolution::ProjectInfo &DotNetSolution::get_project_info(const String &p_name) const {
-	return projects[p_name];
-}
-
-bool DotNetSolution::remove_project(const String &p_name) {
-	return projects.erase(p_name);
-}
-
-Error DotNetSolution::save() {
-	bool dir_exists = DirAccess::exists(path);
-	ERR_EXPLAIN("The directory does not exist.");
-	ERR_FAIL_COND_V(!dir_exists, ERR_FILE_NOT_FOUND);
-
-	String projs_decl;
-	String sln_platform_cfg;
-	String proj_platform_cfg;
-
-	for (Map<String, ProjectInfo>::Element *E = projects.front(); E; E = E->next()) {
-		const String &name = E->key();
-		const ProjectInfo &proj_info = E->value();
-
-		bool is_front = E == projects.front();
-
-		if (!is_front)
-			projs_decl += "\n";
-
-		projs_decl += sformat(PROJECT_DECLARATION, name, proj_info.relpath.replace("/", "\\"), proj_info.guid);
-
-		for (int i = 0; i < proj_info.configs.size(); i++) {
-			const String &config = proj_info.configs[i];
-
-			if (i != 0 || !is_front) {
-				sln_platform_cfg += "\n";
-				proj_platform_cfg += "\n";
-			}
-
-			sln_platform_cfg += sformat(SOLUTION_PLATFORMS_CONFIG, config);
-			proj_platform_cfg += sformat(PROJECT_PLATFORMS_CONFIG, proj_info.guid, config);
-		}
-	}
-
-	String content = sformat(SOLUTION_TEMPLATE, projs_decl, sln_platform_cfg, proj_platform_cfg);
-
-	FileAccess *file = FileAccess::open(path_join(path, name + ".sln"), FileAccess::WRITE);
-	ERR_FAIL_NULL_V(file, ERR_FILE_CANT_WRITE);
-	file->store_string(content);
-	file->close();
-	memdelete(file);
-
-	return OK;
-}
-
-bool DotNetSolution::set_path(const String &p_existing_path) {
-	if (p_existing_path.is_abs_path()) {
-		path = p_existing_path;
-	} else {
-		String abspath;
-		if (!rel_path_to_abs(p_existing_path, abspath))
-			return false;
-		path = abspath;
-	}
-
-	return true;
-}
-
-String DotNetSolution::get_path() {
-	return path;
-}
-
-DotNetSolution::DotNetSolution(const String &p_name) {
-	name = p_name;
-}

+ 0 - 63
modules/mono/editor/dotnet_solution.h

@@ -1,63 +0,0 @@
-/*************************************************************************/
-/*  dotnet_solution.h                                                    */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef NET_SOLUTION_H
-#define NET_SOLUTION_H
-
-#include "core/map.h"
-#include "core/ustring.h"
-
-struct DotNetSolution {
-	String name;
-
-	struct ProjectInfo {
-		String guid;
-		String relpath; // Must be relative to the solution directory
-		Vector<String> configs;
-	};
-
-	void add_new_project(const String &p_name, const ProjectInfo &p_project_info);
-	bool has_project(const String &p_name) const;
-	const ProjectInfo &get_project_info(const String &p_name) const;
-	bool remove_project(const String &p_name);
-
-	Error save();
-
-	bool set_path(const String &p_existing_path);
-	String get_path();
-
-	DotNetSolution(const String &p_name);
-
-private:
-	String path;
-	Map<String, ProjectInfo> projects;
-};
-
-#endif // NET_SOLUTION_H

+ 429 - 0
modules/mono/editor/editor_internal_calls.cpp

@@ -0,0 +1,429 @@
+/*************************************************************************/
+/*  editor_internal_calls.cpp                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "editor_internal_calls.h"
+
+#include "core/message_queue.h"
+#include "core/os/os.h"
+#include "core/version.h"
+#include "editor/editor_node.h"
+#include "editor/plugins/script_editor_plugin.h"
+#include "editor/script_editor_debugger.h"
+#include "main/main.h"
+
+#include "../csharp_script.h"
+#include "../glue/cs_glue_version.gen.h"
+#include "../godotsharp_dirs.h"
+#include "../mono_gd/gd_mono_marshal.h"
+#include "../utils/osx_utils.h"
+#include "bindings_generator.h"
+#include "godotsharp_export.h"
+#include "script_class_parser.h"
+
+MonoString *godot_icall_GodotSharpDirs_ResDataDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_data_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResMetadataDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_metadata_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResAssembliesBaseDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_base_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResAssembliesDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_assemblies_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResConfigDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_config_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResTempDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_base_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_ResTempAssembliesDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_res_temp_assemblies_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_MonoUserDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_user_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_MonoLogsDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_logs_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_MonoSolutionsDir() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_mono_solutions_dir());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_BuildLogsDirs() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_build_logs_dir());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_ProjectSlnPath() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_sln_path());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_ProjectCsProjPath() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_project_csproj_path());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_DataEditorToolsDir() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_tools_dir());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir() {
+#ifdef TOOLS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_editor_prebuilt_api_dir());
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_GodotSharpDirs_DataMonoEtcDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_etc_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_DataMonoLibDir() {
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_lib_dir());
+}
+
+MonoString *godot_icall_GodotSharpDirs_DataMonoBinDir() {
+#ifdef WINDOWS_ENABLED
+	return GDMonoMarshal::mono_string_from_godot(GodotSharpDirs::get_data_mono_bin_dir());
+#else
+	return NULL;
+#endif
+}
+
+void godot_icall_EditorProgress_Create(MonoString *p_task, MonoString *p_label, int32_t p_amount, MonoBoolean p_can_cancel) {
+	String task = GDMonoMarshal::mono_string_to_godot(p_task);
+	String label = GDMonoMarshal::mono_string_to_godot(p_label);
+	EditorNode::progress_add_task(task, label, p_amount, (bool)p_can_cancel);
+}
+
+void godot_icall_EditorProgress_Dispose(MonoString *p_task) {
+	String task = GDMonoMarshal::mono_string_to_godot(p_task);
+	EditorNode::progress_end_task(task);
+}
+
+MonoBoolean godot_icall_EditorProgress_Step(MonoString *p_task, MonoString *p_state, int32_t p_step, MonoBoolean p_force_refresh) {
+	String task = GDMonoMarshal::mono_string_to_godot(p_task);
+	String state = GDMonoMarshal::mono_string_to_godot(p_state);
+	return EditorNode::progress_task_step(task, state, p_step, (bool)p_force_refresh);
+}
+
+BindingsGenerator *godot_icall_BindingsGenerator_Ctor() {
+	return memnew(BindingsGenerator);
+}
+
+void godot_icall_BindingsGenerator_Dtor(BindingsGenerator *p_handle) {
+	memdelete(p_handle);
+}
+
+MonoBoolean godot_icall_BindingsGenerator_LogPrintEnabled(BindingsGenerator *p_handle) {
+	return p_handle->is_log_print_enabled();
+}
+
+void godot_icall_BindingsGenerator_SetLogPrintEnabled(BindingsGenerator p_handle, MonoBoolean p_enabled) {
+	p_handle.set_log_print_enabled(p_enabled);
+}
+
+int32_t godot_icall_BindingsGenerator_GenerateCsApi(BindingsGenerator *p_handle, MonoString *p_output_dir) {
+	String output_dir = GDMonoMarshal::mono_string_to_godot(p_output_dir);
+	return p_handle->generate_cs_api(output_dir);
+}
+
+uint32_t godot_icall_BindingsGenerator_Version() {
+	return BindingsGenerator::get_version();
+}
+
+uint32_t godot_icall_BindingsGenerator_CsGlueVersion() {
+	return CS_GLUE_VERSION;
+}
+
+int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObject *p_classes) {
+	String filepath = GDMonoMarshal::mono_string_to_godot(p_filepath);
+
+	ScriptClassParser scp;
+	Error err = scp.parse_file(filepath);
+	if (err == OK) {
+		Array classes = GDMonoMarshal::mono_object_to_variant(p_classes);
+		const Vector<ScriptClassParser::ClassDecl> &class_decls = scp.get_classes();
+
+		for (int i = 0; i < class_decls.size(); i++) {
+			const ScriptClassParser::ClassDecl &classDecl = class_decls[i];
+
+			Dictionary classDeclDict;
+			classDeclDict["name"] = classDecl.name;
+			classDeclDict["namespace"] = classDecl.namespace_;
+			classDeclDict["nested"] = classDecl.nested;
+			classDeclDict["base_count"] = classDecl.base.size();
+			classes.push_back(classDeclDict);
+		}
+	}
+	return err;
+}
+
+uint32_t godot_icall_GodotSharpExport_GetExportedAssemblyDependencies(MonoString *p_project_dll_name, MonoString *p_project_dll_src_path,
+		MonoString *p_build_config, MonoString *p_custom_lib_dir, MonoObject *r_dependencies) {
+	String project_dll_name = GDMonoMarshal::mono_string_to_godot(p_project_dll_name);
+	String project_dll_src_path = GDMonoMarshal::mono_string_to_godot(p_project_dll_src_path);
+	String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
+	String custom_lib_dir = GDMonoMarshal::mono_string_to_godot(p_custom_lib_dir);
+	Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
+
+	return GodotSharpExport::get_exported_assembly_dependencies(project_dll_name, project_dll_src_path, build_config, custom_lib_dir, dependencies);
+}
+
+float godot_icall_Internal_EditorScale() {
+	return EDSCALE;
+}
+
+MonoObject *godot_icall_Internal_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_Internal_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 = _GLOBAL_DEF(setting, default_value, (bool)p_restart_if_changed);
+	return GDMonoMarshal::variant_to_mono_object(result);
+}
+
+MonoString *godot_icall_Internal_FullTemplatesDir() {
+	String full_templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
+	return GDMonoMarshal::mono_string_from_godot(full_templates_dir);
+}
+
+MonoString *godot_icall_Internal_SimplifyGodotPath(MonoString *p_path) {
+	String path = GDMonoMarshal::mono_string_to_godot(p_path);
+	return GDMonoMarshal::mono_string_from_godot(path.simplify_path());
+}
+
+MonoBoolean godot_icall_Internal_IsOsxAppBundleInstalled(MonoString *p_bundle_id) {
+#ifdef OSX_ENABLED
+	String bundle_id = GDMonoMarshal::mono_string_to_godot(p_bundle_id);
+	return (MonoBoolean)osx_is_app_bundle_installed;
+#else
+	(void)p_bundle_id; // UNUSED
+	return (MonoBoolean) false;
+#endif
+}
+
+MonoBoolean godot_icall_Internal_MetadataIsApiAssemblyInvalidated(int32_t p_api_type) {
+	return GDMono::get_singleton()->metadata_is_api_assembly_invalidated((APIAssembly::Type)p_api_type);
+}
+
+void godot_icall_Internal_MetadataSetApiAssemblyInvalidated(int32_t p_api_type, MonoBoolean p_invalidated) {
+	GDMono::get_singleton()->metadata_set_api_assembly_invalidated((APIAssembly::Type)p_api_type, (bool)p_invalidated);
+}
+
+MonoBoolean godot_icall_Internal_IsMessageQueueFlushing() {
+	return (MonoBoolean)MessageQueue::get_singleton()->is_flushing();
+}
+
+MonoBoolean godot_icall_Internal_GodotIs32Bits() {
+	return sizeof(void *) == 4;
+}
+
+MonoBoolean godot_icall_Internal_GodotIsRealTDouble() {
+#ifdef REAL_T_IS_DOUBLE
+	return (MonoBoolean) true;
+#else
+	return (MonoBoolean) false;
+#endif
+}
+
+void godot_icall_Internal_GodotMainIteration() {
+	Main::iteration();
+}
+
+uint64_t godot_icall_Internal_GetCoreApiHash() {
+	return ClassDB::get_api_hash(ClassDB::API_CORE);
+}
+
+uint64_t godot_icall_Internal_GetEditorApiHash() {
+	return ClassDB::get_api_hash(ClassDB::API_EDITOR);
+}
+
+MonoBoolean godot_icall_Internal_IsAssembliesReloadingNeeded() {
+#ifdef GD_MONO_HOT_RELOAD
+	return (MonoBoolean)CSharpLanguage::get_singleton()->is_assembly_reloading_needed();
+#else
+	return (MonoBoolean) false;
+#endif
+}
+
+void godot_icall_Internal_ReloadAssemblies(MonoBoolean p_soft_reload) {
+#ifdef GD_MONO_HOT_RELOAD
+	_GodotSharp::get_singleton()->call_deferred("_reload_assemblies", (bool)p_soft_reload);
+#endif
+}
+
+void godot_icall_Internal_ScriptEditorDebuggerReloadScripts() {
+	ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
+}
+
+MonoBoolean godot_icall_Internal_ScriptEditorEdit(MonoObject *p_resource, int32_t p_line, int32_t p_col, MonoBoolean p_grab_focus) {
+	Ref<Resource> resource = GDMonoMarshal::mono_object_to_variant(p_resource);
+	return (MonoBoolean)ScriptEditor::get_singleton()->edit(resource, p_line, p_col, (bool)p_grab_focus);
+}
+
+void godot_icall_Internal_EditorNodeShowScriptScreen() {
+	EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
+}
+
+MonoObject *godot_icall_Internal_GetScriptsMetadataOrNothing(MonoReflectionType *p_dict_reftype) {
+	Dictionary maybe_metadata = CSharpLanguage::get_singleton()->get_scripts_metadata_or_nothing();
+
+	MonoType *dict_type = mono_reflection_type_get_type(p_dict_reftype);
+
+	uint32_t type_encoding = mono_type_get_type(dict_type);
+	MonoClass *type_class_raw = mono_class_from_mono_type(dict_type);
+	GDMonoClass *type_class = GDMono::get_singleton()->get_class(type_class_raw);
+
+	return GDMonoMarshal::variant_to_mono_object(maybe_metadata, ManagedType(type_encoding, type_class));
+}
+
+MonoString *godot_icall_Internal_MonoWindowsInstallRoot() {
+#ifdef WINDOWS_ENABLED
+	String install_root_dir = GDMono::get_singleton()->get_mono_reg_info().install_root_dir;
+	return GDMonoMarshal::mono_string_from_godot(install_root_dir);
+#else
+	return NULL;
+#endif
+}
+
+MonoString *godot_icall_Utils_OS_GetPlatformName() {
+	String os_name = OS::get_singleton()->get_name();
+	return GDMonoMarshal::mono_string_from_godot(os_name);
+}
+
+void register_editor_internal_calls() {
+
+	// GodotSharpDirs
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResDataDir", (void *)godot_icall_GodotSharpDirs_ResDataDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResMetadataDir", (void *)godot_icall_GodotSharpDirs_ResMetadataDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesBaseDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResAssembliesDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResConfigDir", (void *)godot_icall_GodotSharpDirs_ResConfigDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempDir", (void *)godot_icall_GodotSharpDirs_ResTempDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesBaseDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesBaseDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ResTempAssembliesDir", (void *)godot_icall_GodotSharpDirs_ResTempAssembliesDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoUserDir", (void *)godot_icall_GodotSharpDirs_MonoUserDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoLogsDir", (void *)godot_icall_GodotSharpDirs_MonoLogsDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_MonoSolutionsDir", (void *)godot_icall_GodotSharpDirs_MonoSolutionsDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_BuildLogsDirs", (void *)godot_icall_GodotSharpDirs_BuildLogsDirs);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectSlnPath", (void *)godot_icall_GodotSharpDirs_ProjectSlnPath);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_ProjectCsProjPath", (void *)godot_icall_GodotSharpDirs_ProjectCsProjPath);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorToolsDir", (void *)godot_icall_GodotSharpDirs_DataEditorToolsDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataEditorPrebuiltApiDir", (void *)godot_icall_GodotSharpDirs_DataEditorPrebuiltApiDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoEtcDir", (void *)godot_icall_GodotSharpDirs_DataMonoEtcDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoLibDir", (void *)godot_icall_GodotSharpDirs_DataMonoLibDir);
+	mono_add_internal_call("GodotTools.Internals.GodotSharpDirs::internal_DataMonoBinDir", (void *)godot_icall_GodotSharpDirs_DataMonoBinDir);
+
+	// EditorProgress
+	mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Create", (void *)godot_icall_EditorProgress_Create);
+	mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Dispose", (void *)godot_icall_EditorProgress_Dispose);
+	mono_add_internal_call("GodotTools.Internals.EditorProgress::internal_Step", (void *)godot_icall_EditorProgress_Step);
+
+	// BiningsGenerator
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Ctor", (void *)godot_icall_BindingsGenerator_Ctor);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Dtor", (void *)godot_icall_BindingsGenerator_Dtor);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_LogPrintEnabled", (void *)godot_icall_BindingsGenerator_LogPrintEnabled);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_SetLogPrintEnabled", (void *)godot_icall_BindingsGenerator_SetLogPrintEnabled);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_GenerateCsApi", (void *)godot_icall_BindingsGenerator_GenerateCsApi);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_Version", (void *)godot_icall_BindingsGenerator_Version);
+	mono_add_internal_call("GodotTools.Internals.BindingsGenerator::internal_CsGlueVersion", (void *)godot_icall_BindingsGenerator_CsGlueVersion);
+
+	// ScriptClassParser
+	mono_add_internal_call("GodotTools.Internals.ScriptClassParser::internal_ParseFile", (void *)godot_icall_ScriptClassParser_ParseFile);
+
+	// GodotSharpExport
+	mono_add_internal_call("GodotTools.GodotSharpExport::internal_GetExportedAssemblyDependencies", (void *)godot_icall_GodotSharpExport_GetExportedAssemblyDependencies);
+
+	// Internals
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorScale", (void *)godot_icall_Internal_EditorScale);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GlobalDef", (void *)godot_icall_Internal_GlobalDef);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorDef", (void *)godot_icall_Internal_EditorDef);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_FullTemplatesDir", (void *)godot_icall_Internal_FullTemplatesDir);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_SimplifyGodotPath", (void *)godot_icall_Internal_SimplifyGodotPath);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_IsOsxAppBundleInstalled", (void *)godot_icall_Internal_IsOsxAppBundleInstalled);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataIsApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataIsApiAssemblyInvalidated);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_MetadataSetApiAssemblyInvalidated", (void *)godot_icall_Internal_MetadataSetApiAssemblyInvalidated);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_IsMessageQueueFlushing", (void *)godot_icall_Internal_IsMessageQueueFlushing);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIs32Bits", (void *)godot_icall_Internal_GodotIs32Bits);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotIsRealTDouble", (void *)godot_icall_Internal_GodotIsRealTDouble);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GodotMainIteration", (void *)godot_icall_Internal_GodotMainIteration);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GetCoreApiHash", (void *)godot_icall_Internal_GetCoreApiHash);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GetEditorApiHash", (void *)godot_icall_Internal_GetEditorApiHash);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_IsAssembliesReloadingNeeded", (void *)godot_icall_Internal_IsAssembliesReloadingNeeded);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_ReloadAssemblies", (void *)godot_icall_Internal_ReloadAssemblies);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorDebuggerReloadScripts", (void *)godot_icall_Internal_ScriptEditorDebuggerReloadScripts);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_ScriptEditorEdit", (void *)godot_icall_Internal_ScriptEditorEdit);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_EditorNodeShowScriptScreen", (void *)godot_icall_Internal_EditorNodeShowScriptScreen);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_GetScriptsMetadataOrNothing", (void *)godot_icall_Internal_GetScriptsMetadataOrNothing);
+	mono_add_internal_call("GodotTools.Internals.Internal::internal_MonoWindowsInstallRoot", (void *)godot_icall_Internal_MonoWindowsInstallRoot);
+
+	// Utils.OS
+	mono_add_internal_call("GodotTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
+}

+ 5 - 24
modules/mono/editor/mono_build_info.h → modules/mono/editor/editor_internal_calls.h

@@ -1,5 +1,5 @@
 /*************************************************************************/
 /*************************************************************************/
-/*  mono_build_info.h                                                    */
+/*  editor_internal_calls.h                                              */
 /*************************************************************************/
 /*************************************************************************/
 /*                       This file is part of:                           */
 /*                       This file is part of:                           */
 /*                           GODOT ENGINE                                */
 /*                           GODOT ENGINE                                */
@@ -28,28 +28,9 @@
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
 /*************************************************************************/
 /*************************************************************************/
 
 
-#ifndef MONO_BUILD_INFO_H
-#define MONO_BUILD_INFO_H
+#ifndef EDITOR_INTERNAL_CALL_H
+#define EDITOR_INTERNAL_CALL_H
 
 
-#include "core/ustring.h"
-#include "core/vector.h"
+void register_editor_internal_calls();
 
 
-struct MonoBuildInfo {
-
-	struct Hasher {
-		static uint32_t hash(const MonoBuildInfo &p_key);
-	};
-
-	String solution;
-	String configuration;
-	Vector<String> custom_props;
-
-	bool operator==(const MonoBuildInfo &p_b) const;
-
-	String get_log_dirpath();
-
-	MonoBuildInfo();
-	MonoBuildInfo(const String &p_solution, const String &p_config);
-};
-
-#endif // MONO_BUILD_INFO_H
+#endif // EDITOR_INTERNAL_CALL_H

+ 0 - 632
modules/mono/editor/godotsharp_builds.cpp

@@ -1,632 +0,0 @@
-/*************************************************************************/
-/*  godotsharp_builds.cpp                                                */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "godotsharp_builds.h"
-
-#include "core/os/os.h"
-#include "core/vector.h"
-#include "main/main.h"
-
-#include "../glue/cs_glue_version.gen.h"
-#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../utils/path_utils.h"
-#include "bindings_generator.h"
-#include "csharp_project.h"
-#include "godotsharp_editor.h"
-
-#define PROP_NAME_MSBUILD_MONO "MSBuild (Mono)"
-#define PROP_NAME_MSBUILD_VS "MSBuild (VS Build Tools)"
-#define PROP_NAME_XBUILD "xbuild (Deprecated)"
-
-void godot_icall_BuildInstance_ExitCallback(MonoString *p_solution, MonoString *p_config, int p_exit_code) {
-
-	String solution = GDMonoMarshal::mono_string_to_godot(p_solution);
-	String config = GDMonoMarshal::mono_string_to_godot(p_config);
-	GodotSharpBuilds::get_singleton()->build_exit_callback(MonoBuildInfo(solution, config), p_exit_code);
-}
-
-static Vector<const char *> _get_msbuild_hint_dirs() {
-	Vector<const char *> ret;
-#ifdef OSX_ENABLED
-	ret.push_back("/Library/Frameworks/Mono.framework/Versions/Current/bin/");
-	ret.push_back("/usr/local/var/homebrew/linked/mono/bin/");
-#endif
-	ret.push_back("/opt/novell/mono/bin/");
-	return ret;
-}
-
-#ifdef UNIX_ENABLED
-String _find_build_engine_on_unix(const String &p_name) {
-	String ret = path_which(p_name);
-
-	if (ret.length())
-		return ret;
-
-	String ret_fallback = path_which(p_name + ".exe");
-	if (ret_fallback.length())
-		return ret_fallback;
-
-	static Vector<const char *> locations = _get_msbuild_hint_dirs();
-
-	for (int i = 0; i < locations.size(); i++) {
-		String hint_path = locations[i] + p_name;
-
-		if (FileAccess::exists(hint_path)) {
-			return hint_path;
-		}
-	}
-
-	return String();
-}
-#endif
-
-MonoString *godot_icall_BuildInstance_get_MSBuildPath() {
-
-	GodotSharpBuilds::BuildTool build_tool = GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool")));
-
-#if defined(WINDOWS_ENABLED)
-	switch (build_tool) {
-		case GodotSharpBuilds::MSBUILD_VS: {
-			static String msbuild_tools_path;
-
-			if (msbuild_tools_path.empty() || !FileAccess::exists(msbuild_tools_path)) {
-				// Try to search it again if it wasn't found last time or if it was removed from its location
-				msbuild_tools_path = MonoRegUtils::find_msbuild_tools_path();
-
-				if (msbuild_tools_path.empty()) {
-					ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_VS "'. Tried with path: " + msbuild_tools_path);
-					return NULL;
-				}
-			}
-
-			if (!msbuild_tools_path.ends_with("\\"))
-				msbuild_tools_path += "\\";
-
-			return GDMonoMarshal::mono_string_from_godot(msbuild_tools_path + "MSBuild.exe");
-		} break;
-		case GodotSharpBuilds::MSBUILD_MONO: {
-			String msbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("msbuild.bat");
-
-			if (!FileAccess::exists(msbuild_path)) {
-				ERR_PRINTS("Cannot find executable for '" PROP_NAME_MSBUILD_MONO "'. Tried with path: " + msbuild_path);
-				return NULL;
-			}
-
-			return GDMonoMarshal::mono_string_from_godot(msbuild_path);
-		} break;
-		case GodotSharpBuilds::XBUILD: {
-			String xbuild_path = GDMono::get_singleton()->get_mono_reg_info().bin_dir.plus_file("xbuild.bat");
-
-			if (!FileAccess::exists(xbuild_path)) {
-				ERR_PRINTS("Cannot find executable for '" PROP_NAME_XBUILD "'. Tried with path: " + xbuild_path);
-				return NULL;
-			}
-
-			return GDMonoMarshal::mono_string_from_godot(xbuild_path);
-		} break;
-		default:
-			ERR_EXPLAIN("You don't deserve to live");
-			CRASH_NOW();
-	}
-#elif defined(UNIX_ENABLED)
-	static String msbuild_path;
-	static String xbuild_path;
-
-	if (build_tool == GodotSharpBuilds::XBUILD) {
-		if (xbuild_path.empty() || !FileAccess::exists(xbuild_path)) {
-			// Try to search it again if it wasn't found last time or if it was removed from its location
-			xbuild_path = _find_build_engine_on_unix("msbuild");
-		}
-
-		if (xbuild_path.empty()) {
-			ERR_PRINT("Cannot find binary for '" PROP_NAME_XBUILD "'");
-			return NULL;
-		}
-	} else {
-		if (msbuild_path.empty() || !FileAccess::exists(msbuild_path)) {
-			// Try to search it again if it wasn't found last time or if it was removed from its location
-			msbuild_path = _find_build_engine_on_unix("msbuild");
-		}
-
-		if (msbuild_path.empty()) {
-			ERR_PRINT("Cannot find binary for '" PROP_NAME_MSBUILD_MONO "'");
-			return NULL;
-		}
-	}
-
-	return GDMonoMarshal::mono_string_from_godot(build_tool != GodotSharpBuilds::XBUILD ? msbuild_path : xbuild_path);
-#else
-	(void)build_tool; // UNUSED
-
-	ERR_EXPLAIN("Not implemented on this platform");
-	ERR_FAIL_V(NULL);
-#endif
-}
-
-MonoString *godot_icall_BuildInstance_get_MonoWindowsBinDir() {
-
-#if defined(WINDOWS_ENABLED)
-	const MonoRegInfo &mono_reg_info = GDMono::get_singleton()->get_mono_reg_info();
-	if (mono_reg_info.bin_dir.length()) {
-		return GDMonoMarshal::mono_string_from_godot(mono_reg_info.bin_dir);
-	}
-
-	ERR_EXPLAIN("Cannot find Mono's binaries directory in the registry");
-	ERR_FAIL_V(NULL);
-#else
-	return NULL;
-#endif
-}
-
-MonoBoolean godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows() {
-
-#if defined(WINDOWS_ENABLED)
-	return GodotSharpBuilds::BuildTool(int(EditorSettings::get_singleton()->get("mono/builds/build_tool"))) == GodotSharpBuilds::MSBUILD_MONO;
-#else
-	return false;
-#endif
-}
-
-MonoBoolean godot_icall_BuildInstance_get_PrintBuildOutput() {
-
-	return (bool)EDITOR_GET("mono/builds/print_build_output");
-}
-
-void GodotSharpBuilds::register_internal_calls() {
-
-	static bool registered = false;
-	ERR_FAIL_COND(registered);
-	registered = true;
-
-	mono_add_internal_call("GodotSharpTools.Build.BuildSystem::godot_icall_BuildInstance_ExitCallback", (void *)godot_icall_BuildInstance_ExitCallback);
-	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MSBuildPath", (void *)godot_icall_BuildInstance_get_MSBuildPath);
-	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_MonoWindowsBinDir", (void *)godot_icall_BuildInstance_get_MonoWindowsBinDir);
-	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows", (void *)godot_icall_BuildInstance_get_UsingMonoMSBuildOnWindows);
-	mono_add_internal_call("GodotSharpTools.Build.BuildInstance::godot_icall_BuildInstance_get_PrintBuildOutput", (void *)godot_icall_BuildInstance_get_PrintBuildOutput);
-}
-
-void GodotSharpBuilds::show_build_error_dialog(const String &p_message) {
-
-	GodotSharpEditor::get_singleton()->show_error_dialog(p_message, "Build error");
-	MonoBottomPanel::get_singleton()->show_build_tab();
-}
-
-bool GodotSharpBuilds::build_api_sln(const String &p_api_sln_dir, const String &p_config) {
-
-	String api_sln_file = p_api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
-
-	String core_api_assembly_dir = p_api_sln_dir.plus_file(CORE_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
-	String core_api_assembly_file = core_api_assembly_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
-
-	String editor_api_assembly_dir = p_api_sln_dir.plus_file(EDITOR_API_ASSEMBLY_NAME).plus_file("bin").plus_file(p_config);
-	String editor_api_assembly_file = editor_api_assembly_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
-
-	if (!FileAccess::exists(core_api_assembly_file) || !FileAccess::exists(editor_api_assembly_file)) {
-		MonoBuildInfo api_build_info(api_sln_file, p_config);
-		// TODO Replace this global NoWarn with '#pragma warning' directives on generated files,
-		// once we start to actively document manually maintained C# classes
-		api_build_info.custom_props.push_back("NoWarn=1591"); // Ignore missing documentation warnings
-
-		if (!GodotSharpBuilds::get_singleton()->build(api_build_info)) {
-			show_build_error_dialog("Failed to build " API_SOLUTION_NAME " solution.");
-			return false;
-		}
-	}
-
-	return true;
-}
-
-bool GodotSharpBuilds::copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
-
-	// Create destination directory if needed
-	if (!DirAccess::exists(p_dst_dir)) {
-		DirAccess *da = DirAccess::create_for_path(p_dst_dir);
-		Error err = da->make_dir_recursive(p_dst_dir);
-		memdelete(da);
-
-		if (err != OK) {
-			show_build_error_dialog("Failed to create destination directory for the API assemblies. Error: " + itos(err));
-			return false;
-		}
-	}
-
-	String assembly_file = p_assembly_name + ".dll";
-	String assembly_src = p_src_dir.plus_file(assembly_file);
-	String assembly_dst = p_dst_dir.plus_file(assembly_file);
-
-	if (!FileAccess::exists(assembly_dst) ||
-			FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
-			GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
-		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
-
-		String xml_file = p_assembly_name + ".xml";
-		if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
-			WARN_PRINTS("Failed to copy " + xml_file);
-
-		String pdb_file = p_assembly_name + ".pdb";
-		if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
-			WARN_PRINTS("Failed to copy " + pdb_file);
-
-		Error err = da->copy(assembly_src, assembly_dst);
-
-		if (err != OK) {
-			show_build_error_dialog("Failed to copy " + assembly_file);
-			return false;
-		}
-
-		GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
-	}
-
-	return true;
-}
-
-String GodotSharpBuilds::_api_folder_name(APIAssembly::Type p_api_type) {
-
-	uint64_t api_hash = p_api_type == APIAssembly::API_CORE ?
-								GDMono::get_singleton()->get_api_core_hash() :
-								GDMono::get_singleton()->get_api_editor_hash();
-	return String::num_uint64(api_hash) +
-		   "_" + String::num_uint64(BindingsGenerator::get_version()) +
-		   "_" + String::num_uint64(CS_GLUE_VERSION);
-}
-
-bool GodotSharpBuilds::make_api_assembly(APIAssembly::Type p_api_type) {
-
-	String api_name = p_api_type == APIAssembly::API_CORE ? CORE_API_ASSEMBLY_NAME : EDITOR_API_ASSEMBLY_NAME;
-
-	String editor_prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir();
-	String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
-
-	if (FileAccess::exists(editor_prebuilt_api_dir.plus_file(api_name + ".dll"))) {
-		EditorProgress pr("mono_copy_prebuilt_api_assembly", "Copying prebuilt " + api_name + " assembly...", 1);
-		pr.step("Copying " + api_name + " assembly", 0);
-		return GodotSharpBuilds::copy_api_assembly(editor_prebuilt_api_dir, res_assemblies_dir, api_name, p_api_type);
-	}
-
-	String api_build_config = "Release";
-
-	EditorProgress pr("mono_build_release_" API_SOLUTION_NAME, "Building " API_SOLUTION_NAME " solution...", 3);
-
-	pr.step("Generating " API_SOLUTION_NAME " solution", 0);
-
-	String api_sln_dir = GodotSharpDirs::get_mono_solutions_dir()
-								 .plus_file(_api_folder_name(APIAssembly::API_CORE));
-
-	String api_sln_file = api_sln_dir.plus_file(API_SOLUTION_NAME ".sln");
-
-	if (!DirAccess::exists(api_sln_dir) || !FileAccess::exists(api_sln_file)) {
-		BindingsGenerator bindings_generator;
-
-		if (!OS::get_singleton()->is_stdout_verbose()) {
-			bindings_generator.set_log_print_enabled(false);
-		}
-
-		Error err = bindings_generator.generate_cs_api(api_sln_dir);
-		if (err != OK) {
-			show_build_error_dialog("Failed to generate " API_SOLUTION_NAME " solution. Error: " + itos(err));
-			return false;
-		}
-	}
-
-	pr.step("Building " API_SOLUTION_NAME " solution", 1);
-
-	if (!GodotSharpBuilds::build_api_sln(api_sln_dir, api_build_config))
-		return false;
-
-	pr.step("Copying " + api_name + " assembly", 2);
-
-	// Copy the built assembly to the assemblies directory
-	String api_assembly_dir = api_sln_dir.plus_file(api_name).plus_file("bin").plus_file(api_build_config);
-	if (!GodotSharpBuilds::copy_api_assembly(api_assembly_dir, res_assemblies_dir, api_name, p_api_type))
-		return false;
-
-	return true;
-}
-
-bool GodotSharpBuilds::build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines) {
-
-	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
-		return true; // No solution to build
-
-	if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
-		return false;
-
-	if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
-		return false;
-
-	EditorProgress pr("mono_project_debug_build", "Building project solution...", 1);
-	pr.step("Building project solution", 0);
-
-	MonoBuildInfo build_info(GodotSharpDirs::get_project_sln_path(), p_config);
-
-	// Add Godot defines
-#ifdef WINDOWS_ENABLED
-	String constants = "GodotDefineConstants=\"";
-#else
-	String constants = "GodotDefineConstants=\\\"";
-#endif
-
-	for (int i = 0; i < p_godot_defines.size(); i++) {
-		constants += "GODOT_" + p_godot_defines[i].to_upper().replace("-", "_").replace(" ", "_").replace(";", "_") + ";";
-	}
-
-#ifdef REAL_T_IS_DOUBLE
-	constants += "GODOT_REAL_T_IS_DOUBLE;";
-#endif
-
-#ifdef WINDOWS_ENABLED
-	constants += "\"";
-#else
-	constants += "\\\"";
-#endif
-	build_info.custom_props.push_back(constants);
-
-	if (!GodotSharpBuilds::get_singleton()->build(build_info)) {
-		GodotSharpBuilds::show_build_error_dialog("Failed to build project solution");
-		return false;
-	}
-
-	return true;
-}
-
-bool GodotSharpBuilds::editor_build_callback() {
-
-	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
-		return true; // No solution to build
-
-	String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
-	String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
-
-	Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
-	ERR_FAIL_COND_V(metadata_err != OK, false);
-
-	if (FileAccess::exists(scripts_metadata_path_editor)) {
-		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-		Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
-
-		ERR_EXPLAIN("Failed to copy scripts metadata file");
-		ERR_FAIL_COND_V(copy_err != OK, false);
-	}
-
-	Vector<String> godot_defines;
-	godot_defines.push_back(OS::get_singleton()->get_name());
-	godot_defines.push_back(sizeof(void *) == 4 ? "32" : "64");
-	return build_project_blocking("Tools", godot_defines);
-}
-
-GodotSharpBuilds *GodotSharpBuilds::singleton = NULL;
-
-void GodotSharpBuilds::build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code) {
-
-	BuildProcess *match = builds.getptr(p_build_info);
-	ERR_FAIL_NULL(match);
-
-	BuildProcess &bp = *match;
-	bp.on_exit(p_exit_code);
-}
-
-void GodotSharpBuilds::restart_build(MonoBuildTab *p_build_tab) {
-}
-
-void GodotSharpBuilds::stop_build(MonoBuildTab *p_build_tab) {
-}
-
-bool GodotSharpBuilds::build(const MonoBuildInfo &p_build_info) {
-
-	BuildProcess *match = builds.getptr(p_build_info);
-
-	if (match) {
-		BuildProcess &bp = *match;
-		bp.start(true);
-		return bp.exit_code == 0;
-	} else {
-		BuildProcess bp = BuildProcess(p_build_info);
-		bp.start(true);
-		builds.set(p_build_info, bp);
-		return bp.exit_code == 0;
-	}
-}
-
-bool GodotSharpBuilds::build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) {
-
-	BuildProcess *match = builds.getptr(p_build_info);
-
-	if (match) {
-		BuildProcess &bp = *match;
-		bp.start();
-		return !bp.exited; // failed to start
-	} else {
-		BuildProcess bp = BuildProcess(p_build_info, p_callback);
-		bp.start();
-		builds.set(p_build_info, bp);
-		return !bp.exited; // failed to start
-	}
-}
-
-GodotSharpBuilds::GodotSharpBuilds() {
-
-	singleton = this;
-
-	EditorNode::get_singleton()->add_build_callback(&GodotSharpBuilds::editor_build_callback);
-
-	// Build tool settings
-	EditorSettings *ed_settings = EditorSettings::get_singleton();
-
-#ifdef WINDOWS_ENABLED
-	EDITOR_DEF("mono/builds/build_tool", MSBUILD_VS);
-#else
-	EDITOR_DEF("mono/builds/build_tool", MSBUILD_MONO);
-#endif
-
-	ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/builds/build_tool", PROPERTY_HINT_ENUM,
-			PROP_NAME_MSBUILD_MONO
-#ifdef WINDOWS_ENABLED
-			"," PROP_NAME_MSBUILD_VS
-#endif
-			"," PROP_NAME_XBUILD));
-
-	EDITOR_DEF("mono/builds/print_build_output", false);
-}
-
-GodotSharpBuilds::~GodotSharpBuilds() {
-
-	singleton = NULL;
-}
-
-void GodotSharpBuilds::BuildProcess::on_exit(int p_exit_code) {
-
-	exited = true;
-	exit_code = p_exit_code;
-	build_tab->on_build_exit(p_exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
-	build_instance.unref();
-
-	if (exit_callback)
-		exit_callback(exit_code);
-}
-
-void GodotSharpBuilds::BuildProcess::start(bool p_blocking) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	exit_code = -1;
-
-	String log_dirpath = build_info.get_log_dirpath();
-
-	if (build_tab) {
-		build_tab->on_build_start();
-	} else {
-		build_tab = memnew(MonoBuildTab(build_info, log_dirpath));
-		MonoBottomPanel::get_singleton()->add_build_tab(build_tab);
-	}
-
-	if (p_blocking) {
-		// Required in order to update the build tasks list
-		Main::iteration();
-	}
-
-	if (!exited) {
-		exited = true;
-		String message = "Tried to start build process, but it is already running";
-		build_tab->on_build_exec_failed(message);
-		ERR_EXPLAIN(message);
-		ERR_FAIL();
-	}
-
-	exited = false;
-
-	// Remove old issues file
-
-	String issues_file = get_msbuild_issues_filename();
-	DirAccessRef d = DirAccess::create_for_path(log_dirpath);
-	if (d->file_exists(issues_file)) {
-		Error err = d->remove(issues_file);
-		if (err != OK) {
-			exited = true;
-			String file_path = ProjectSettings::get_singleton()->localize_path(log_dirpath).plus_file(issues_file);
-			String message = "Cannot remove issues file: " + file_path;
-			build_tab->on_build_exec_failed(message);
-			ERR_EXPLAIN(message);
-			ERR_FAIL();
-		}
-	}
-
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Build", "BuildInstance");
-
-	MonoObject *mono_object = mono_object_new(mono_domain_get(), klass->get_mono_ptr());
-
-	// Construct
-
-	Variant solution = build_info.solution;
-	Variant config = build_info.configuration;
-
-	const Variant *ctor_args[2] = { &solution, &config };
-
-	MonoException *exc = NULL;
-	GDMonoMethod *ctor = klass->get_method(".ctor", 2);
-	ctor->invoke(mono_object, ctor_args, &exc);
-
-	if (exc) {
-		exited = true;
-		GDMonoUtils::debug_unhandled_exception(exc);
-		String message = "The build constructor threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
-		build_tab->on_build_exec_failed(message);
-		ERR_EXPLAIN(message);
-		ERR_FAIL();
-	}
-
-	// Call Build
-
-	String logger_assembly_path = GDMono::get_singleton()->get_editor_tools_assembly()->get_path();
-	Variant logger_assembly = ProjectSettings::get_singleton()->globalize_path(logger_assembly_path);
-	Variant logger_output_dir = log_dirpath;
-	Variant custom_props = build_info.custom_props;
-
-	const Variant *args[3] = { &logger_assembly, &logger_output_dir, &custom_props };
-
-	exc = NULL;
-	GDMonoMethod *build_method = klass->get_method(p_blocking ? "Build" : "BuildAsync", 3);
-	build_method->invoke(mono_object, args, &exc);
-
-	if (exc) {
-		exited = true;
-		GDMonoUtils::debug_unhandled_exception(exc);
-		String message = "The build method threw an exception.\n" + GDMonoUtils::get_exception_name_and_message(exc);
-		build_tab->on_build_exec_failed(message);
-		ERR_EXPLAIN(message);
-		ERR_FAIL();
-	}
-
-	// Build returned
-
-	if (p_blocking) {
-		exited = true;
-		exit_code = klass->get_field("exitCode")->get_int_value(mono_object);
-
-		if (exit_code != 0) {
-			String log_filepath = build_info.get_log_dirpath().plus_file(get_msbuild_log_filename());
-			print_verbose("MSBuild exited with code: " + itos(exit_code) + ". Log file: " + log_filepath);
-		}
-
-		build_tab->on_build_exit(exit_code == 0 ? MonoBuildTab::RESULT_SUCCESS : MonoBuildTab::RESULT_ERROR);
-	} else {
-		build_instance = MonoGCHandle::create_strong(mono_object);
-		exited = false;
-	}
-}
-
-GodotSharpBuilds::BuildProcess::BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback) :
-		build_info(p_build_info),
-		build_tab(NULL),
-		exit_callback(p_callback),
-		exited(true),
-		exit_code(-1) {
-}

+ 0 - 103
modules/mono/editor/godotsharp_builds.h

@@ -1,103 +0,0 @@
-/*************************************************************************/
-/*  godotsharp_builds.h                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef GODOTSHARP_BUILDS_H
-#define GODOTSHARP_BUILDS_H
-
-#include "../mono_gd/gd_mono.h"
-#include "mono_bottom_panel.h"
-#include "mono_build_info.h"
-
-typedef void (*GodotSharpBuild_ExitCallback)(int);
-
-class GodotSharpBuilds {
-
-private:
-	struct BuildProcess {
-		Ref<MonoGCHandle> build_instance;
-		MonoBuildInfo build_info;
-		MonoBuildTab *build_tab;
-		GodotSharpBuild_ExitCallback exit_callback;
-		bool exited;
-		int exit_code;
-
-		void on_exit(int p_exit_code);
-		void start(bool p_blocking = false);
-
-		BuildProcess() {}
-		BuildProcess(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
-	};
-
-	HashMap<MonoBuildInfo, BuildProcess, MonoBuildInfo::Hasher> builds;
-
-	static String _api_folder_name(APIAssembly::Type p_api_type);
-
-	static GodotSharpBuilds *singleton;
-
-public:
-	enum BuildTool {
-		MSBUILD_MONO,
-#ifdef WINDOWS_ENABLED
-		MSBUILD_VS,
-#endif
-		XBUILD // Deprecated
-	};
-
-	_FORCE_INLINE_ static GodotSharpBuilds *get_singleton() { return singleton; }
-
-	static void register_internal_calls();
-
-	static void show_build_error_dialog(const String &p_message);
-
-	static const char *get_msbuild_issues_filename() { return "msbuild_issues.csv"; }
-	static const char *get_msbuild_log_filename() { return "msbuild_log.txt"; }
-
-	void build_exit_callback(const MonoBuildInfo &p_build_info, int p_exit_code);
-
-	void restart_build(MonoBuildTab *p_build_tab);
-	void stop_build(MonoBuildTab *p_build_tab);
-
-	bool build(const MonoBuildInfo &p_build_info);
-	bool build_async(const MonoBuildInfo &p_build_info, GodotSharpBuild_ExitCallback p_callback = NULL);
-
-	static bool build_api_sln(const String &p_api_sln_dir, const String &p_config);
-	static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type);
-
-	static bool make_api_assembly(APIAssembly::Type p_api_type);
-
-	static bool build_project_blocking(const String &p_config, const Vector<String> &p_godot_defines);
-
-	static bool editor_build_callback();
-
-	GodotSharpBuilds();
-	~GodotSharpBuilds();
-};
-
-#endif // GODOTSHARP_BUILDS_H

+ 0 - 582
modules/mono/editor/godotsharp_editor.cpp

@@ -1,582 +0,0 @@
-/*************************************************************************/
-/*  godotsharp_editor.cpp                                                */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "godotsharp_editor.h"
-
-#include "core/message_queue.h"
-#include "core/os/os.h"
-#include "core/project_settings.h"
-#include "scene/gui/control.h"
-#include "scene/main/node.h"
-
-#include "../csharp_script.h"
-#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "../utils/path_utils.h"
-#include "bindings_generator.h"
-#include "csharp_project.h"
-#include "dotnet_solution.h"
-#include "godotsharp_export.h"
-
-#ifdef OSX_ENABLED
-#include "../utils/osx_utils.h"
-#endif
-
-#ifdef WINDOWS_ENABLED
-#include "../utils/mono_reg_utils.h"
-#endif
-
-GodotSharpEditor *GodotSharpEditor::singleton = NULL;
-
-bool GodotSharpEditor::_create_project_solution() {
-
-	EditorProgress pr("create_csharp_solution", TTR("Generating solution..."), 2);
-
-	pr.step(TTR("Generating C# project..."));
-
-	String path = OS::get_singleton()->get_resource_dir();
-	String appname = ProjectSettings::get_singleton()->get("application/config/name");
-	String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
-	if (appname_safe.empty()) {
-		appname_safe = "UnnamedProject";
-	}
-
-	String guid = CSharpProject::generate_game_project(path, appname_safe);
-
-	if (guid.length()) {
-
-		DotNetSolution solution(appname_safe);
-
-		if (!solution.set_path(path)) {
-			show_error_dialog(TTR("Failed to create solution."));
-			return false;
-		}
-
-		DotNetSolution::ProjectInfo proj_info;
-		proj_info.guid = guid;
-		proj_info.relpath = appname_safe + ".csproj";
-		proj_info.configs.push_back("Debug");
-		proj_info.configs.push_back("Release");
-		proj_info.configs.push_back("Tools");
-
-		solution.add_new_project(appname_safe, proj_info);
-
-		Error sln_error = solution.save();
-
-		if (sln_error != OK) {
-			show_error_dialog(TTR("Failed to save solution."));
-			return false;
-		}
-
-		if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
-			return false;
-
-		if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
-			return false;
-
-		pr.step(TTR("Done"));
-
-		// Here, after all calls to progress_task_step
-		call_deferred("_remove_create_sln_menu_option");
-
-	} else {
-		show_error_dialog(TTR("Failed to create C# project."));
-	}
-
-	return true;
-}
-
-void GodotSharpEditor::_make_api_solutions_if_needed() {
-	// I'm sick entirely of ProgressDialog
-
-	static int attempts_left = 100;
-
-	if (MessageQueue::get_singleton()->is_flushing() || !SceneTree::get_singleton()) {
-		ERR_FAIL_COND(attempts_left == 0); // You've got to be kidding
-
-		if (SceneTree::get_singleton()) {
-			SceneTree::get_singleton()->connect("idle_frame", this, "_make_api_solutions_if_needed", Vector<Variant>());
-		} else {
-			call_deferred("_make_api_solutions_if_needed");
-		}
-
-		attempts_left--;
-		return;
-	}
-
-	// Recursion guard needed because signals don't play well with ProgressDialog either, but unlike
-	// the message queue, with signals the collateral damage should be minimal in the worst case.
-	static bool recursion_guard = false;
-	if (!recursion_guard) {
-		recursion_guard = true;
-
-		// Oneshot signals don't play well with ProgressDialog either, so we do it this way instead
-		SceneTree::get_singleton()->disconnect("idle_frame", this, "_make_api_solutions_if_needed");
-
-		_make_api_solutions_if_needed_impl();
-
-		recursion_guard = false;
-	}
-}
-
-void GodotSharpEditor::_make_api_solutions_if_needed_impl() {
-	// If the project has a solution and C# project make sure the API assemblies are present and up to date
-	String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
-
-	if (!FileAccess::exists(res_assemblies_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll")) ||
-			GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
-		if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_CORE))
-			return;
-	}
-
-	if (!FileAccess::exists(res_assemblies_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll")) ||
-			GDMono::get_singleton()->metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
-		if (!GodotSharpBuilds::make_api_assembly(APIAssembly::API_EDITOR))
-			return; // Redundant? I don't think so
-	}
-}
-
-void GodotSharpEditor::_remove_create_sln_menu_option() {
-
-	menu_popup->remove_item(menu_popup->get_item_index(MENU_CREATE_SLN));
-
-	bottom_panel_btn->show();
-}
-
-void GodotSharpEditor::_show_about_dialog() {
-
-	bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start");
-	about_dialog_checkbox->set_pressed(show_on_start);
-	about_dialog->popup_centered_minsize();
-}
-
-void GodotSharpEditor::_toggle_about_dialog_on_start(bool p_enabled) {
-
-	bool show_on_start = EDITOR_GET("mono/editor/show_info_on_start");
-	if (show_on_start != p_enabled) {
-		EditorSettings::get_singleton()->set_setting("mono/editor/show_info_on_start", p_enabled);
-	}
-}
-
-void GodotSharpEditor::_build_solution_pressed() {
-
-	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path())) {
-		if (!_create_project_solution())
-			return; // Failed to create solution
-	}
-
-	MonoBottomPanel::get_singleton()->call("_build_project_pressed");
-}
-
-void GodotSharpEditor::_menu_option_pressed(int p_id) {
-
-	switch (p_id) {
-		case MENU_CREATE_SLN: {
-
-			_create_project_solution();
-		} break;
-		case MENU_ABOUT_CSHARP: {
-
-			_show_about_dialog();
-		} break;
-		default:
-			ERR_FAIL();
-	}
-}
-
-void GodotSharpEditor::_notification(int p_notification) {
-
-	switch (p_notification) {
-
-		case NOTIFICATION_READY: {
-
-			bool show_info_dialog = EDITOR_GET("mono/editor/show_info_on_start");
-			if (show_info_dialog) {
-				about_dialog->set_exclusive(true);
-				_show_about_dialog();
-				// Once shown a first time, it can be seen again via the Mono menu - it doesn't have to be exclusive then.
-				about_dialog->set_exclusive(false);
-			}
-		}
-	}
-}
-
-void GodotSharpEditor::_bind_methods() {
-
-	ClassDB::bind_method(D_METHOD("_build_solution_pressed"), &GodotSharpEditor::_build_solution_pressed);
-	ClassDB::bind_method(D_METHOD("_create_project_solution"), &GodotSharpEditor::_create_project_solution);
-	ClassDB::bind_method(D_METHOD("_make_api_solutions_if_needed"), &GodotSharpEditor::_make_api_solutions_if_needed);
-	ClassDB::bind_method(D_METHOD("_remove_create_sln_menu_option"), &GodotSharpEditor::_remove_create_sln_menu_option);
-	ClassDB::bind_method(D_METHOD("_toggle_about_dialog_on_start"), &GodotSharpEditor::_toggle_about_dialog_on_start);
-	ClassDB::bind_method(D_METHOD("_menu_option_pressed", "id"), &GodotSharpEditor::_menu_option_pressed);
-}
-
-MonoBoolean godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled(MonoString *p_bundle_id) {
-#ifdef OSX_ENABLED
-	return (MonoBoolean)osx_is_app_bundle_installed(GDMonoMarshal::mono_string_to_godot(p_bundle_id));
-#else
-	(void)p_bundle_id; // UNUSED
-	ERR_FAIL_V(false);
-#endif
-}
-
-MonoString *godot_icall_Utils_OS_GetPlatformName() {
-	return GDMonoMarshal::mono_string_from_godot(OS::get_singleton()->get_name());
-}
-
-void GodotSharpEditor::register_internal_calls() {
-
-	static bool registered = false;
-	ERR_FAIL_COND(registered);
-	registered = true;
-
-	mono_add_internal_call("GodotSharpTools.Editor.MonoDevelopInstance::IsApplicationBundleInstalled", (void *)godot_icall_MonoDevelopInstance_IsApplicationBundleInstalled);
-	mono_add_internal_call("GodotSharpTools.Utils.OS::GetPlatformName", (void *)godot_icall_Utils_OS_GetPlatformName);
-
-	GodotSharpBuilds::register_internal_calls();
-	GodotSharpExport::register_internal_calls();
-}
-
-void GodotSharpEditor::show_error_dialog(const String &p_message, const String &p_title) {
-
-	error_dialog->set_title(p_title);
-	error_dialog->set_text(p_message);
-	error_dialog->popup_centered_minsize();
-}
-
-Error GodotSharpEditor::open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) {
-
-	ExternalEditor editor = ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor")));
-
-	switch (editor) {
-		case EDITOR_VSCODE: {
-			static String vscode_path;
-
-			if (vscode_path.empty() || !FileAccess::exists(vscode_path)) {
-				// Try to search it again if it wasn't found last time or if it was removed from its location
-				bool found = false;
-
-				// TODO: Use initializer lists once C++11 is allowed
-
-				static Vector<String> vscode_names;
-				if (vscode_names.empty()) {
-					vscode_names.push_back("code");
-					vscode_names.push_back("code-oss");
-					vscode_names.push_back("vscode");
-					vscode_names.push_back("vscode-oss");
-					vscode_names.push_back("visual-studio-code");
-					vscode_names.push_back("visual-studio-code-oss");
-				}
-				for (int i = 0; i < vscode_names.size(); i++) {
-					vscode_path = path_which(vscode_names[i]);
-					if (!vscode_path.empty()) {
-						found = true;
-						break;
-					}
-				}
-
-				if (!found)
-					vscode_path.clear(); // Not found, clear so next time the empty() check is enough
-			}
-
-			List<String> args;
-
-#ifdef OSX_ENABLED
-			// The package path is '/Applications/Visual Studio Code.app'
-			static const String vscode_bundle_id = "com.microsoft.VSCode";
-			static bool osx_app_bundle_installed = osx_is_app_bundle_installed(vscode_bundle_id);
-
-			if (osx_app_bundle_installed) {
-				args.push_back("-b");
-				args.push_back(vscode_bundle_id);
-
-				// The reusing of existing windows made by the 'open' command might not choose a wubdiw that is
-				// editing our folder. It's better to ask for a new window and let VSCode do the window management.
-				args.push_back("-n");
-
-				// The open process must wait until the application finishes (which is instant in VSCode's case)
-				args.push_back("--wait-apps");
-
-				args.push_back("--args");
-			}
-#endif
-
-			args.push_back(ProjectSettings::get_singleton()->get_resource_path());
-
-			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
-
-			if (p_line >= 0) {
-				args.push_back("-g");
-				args.push_back(script_path + ":" + itos(p_line + 1) + ":" + itos(p_col));
-			} else {
-				args.push_back(script_path);
-			}
-
-#ifdef OSX_ENABLED
-			ERR_EXPLAIN("Cannot find code editor: VSCode");
-			ERR_FAIL_COND_V(!osx_app_bundle_installed && vscode_path.empty(), ERR_FILE_NOT_FOUND);
-
-			String command = osx_app_bundle_installed ? "/usr/bin/open" : vscode_path;
-#else
-			ERR_EXPLAIN("Cannot find code editor: VSCode");
-			ERR_FAIL_COND_V(vscode_path.empty(), ERR_FILE_NOT_FOUND);
-
-			String command = vscode_path;
-#endif
-
-			Error err = OS::get_singleton()->execute(command, args, false);
-
-			if (err != OK) {
-				ERR_PRINT("Error when trying to execute code editor: VSCode");
-				return err;
-			}
-		} break;
-#ifdef OSX_ENABLED
-		case EDITOR_VISUALSTUDIO_MAC:
-			// [[fallthrough]];
-#endif
-		case EDITOR_MONODEVELOP: {
-#ifdef OSX_ENABLED
-			bool is_visualstudio = editor == EDITOR_VISUALSTUDIO_MAC;
-
-			MonoDevelopInstance **instance = is_visualstudio ?
-													 &visualstudio_mac_instance :
-													 &monodevelop_instance;
-
-			MonoDevelopInstance::EditorId editor_id = is_visualstudio ?
-															  MonoDevelopInstance::VISUALSTUDIO_FOR_MAC :
-															  MonoDevelopInstance::MONODEVELOP;
-#else
-			MonoDevelopInstance **instance = &monodevelop_instance;
-			MonoDevelopInstance::EditorId editor_id = MonoDevelopInstance::MONODEVELOP;
-#endif
-
-			if (!*instance)
-				*instance = memnew(MonoDevelopInstance(GodotSharpDirs::get_project_sln_path(), editor_id));
-
-			String script_path = ProjectSettings::get_singleton()->globalize_path(p_script->get_path());
-
-			if (p_line >= 0) {
-				script_path += ";" + itos(p_line + 1) + ";" + itos(p_col);
-			}
-
-			(*instance)->execute(script_path);
-		} break;
-		default:
-			return ERR_UNAVAILABLE;
-	}
-
-	return OK;
-}
-
-bool GodotSharpEditor::overrides_external_editor() {
-
-	return ExternalEditor(int(EditorSettings::get_singleton()->get("mono/editor/external_editor"))) != EDITOR_NONE;
-}
-
-GodotSharpEditor::GodotSharpEditor(EditorNode *p_editor) {
-
-	singleton = this;
-
-	monodevelop_instance = NULL;
-#ifdef OSX_ENABLED
-	visualstudio_mac_instance = NULL;
-#endif
-
-	editor = p_editor;
-
-	error_dialog = memnew(AcceptDialog);
-	editor->get_gui_base()->add_child(error_dialog);
-
-	bottom_panel_btn = editor->add_bottom_panel_item(TTR("Mono"), memnew(MonoBottomPanel(editor)));
-
-	godotsharp_builds = memnew(GodotSharpBuilds);
-
-	editor->add_child(memnew(MonoReloadNode));
-
-	menu_popup = memnew(PopupMenu);
-	menu_popup->hide();
-	menu_popup->set_as_toplevel(true);
-	menu_popup->set_pass_on_modal_close_click(false);
-
-	editor->add_tool_submenu_item("Mono", menu_popup);
-
-	// TODO: Remove or edit this info dialog once Mono support is no longer in alpha
-	{
-		menu_popup->add_item(TTR("About C# support"), MENU_ABOUT_CSHARP);
-		about_dialog = memnew(AcceptDialog);
-		editor->get_gui_base()->add_child(about_dialog);
-		about_dialog->set_title("Important: C# support is not feature-complete");
-
-		// We don't use set_text() as the default AcceptDialog Label doesn't play well with the TextureRect and CheckBox
-		// we'll add. Instead we add containers and a new autowrapped Label inside.
-
-		// Main VBoxContainer (icon + label on top, checkbox at bottom)
-		VBoxContainer *about_vbc = memnew(VBoxContainer);
-		about_dialog->add_child(about_vbc);
-
-		// HBoxContainer for icon + label
-		HBoxContainer *about_hbc = memnew(HBoxContainer);
-		about_vbc->add_child(about_hbc);
-
-		TextureRect *about_icon = memnew(TextureRect);
-		about_hbc->add_child(about_icon);
-		Ref<Texture> about_icon_tex = about_icon->get_icon("NodeWarning", "EditorIcons");
-		about_icon->set_texture(about_icon_tex);
-
-		Label *about_label = memnew(Label);
-		about_hbc->add_child(about_label);
-		about_label->set_custom_minimum_size(Size2(600, 150) * EDSCALE);
-		about_label->set_v_size_flags(Control::SIZE_EXPAND_FILL);
-		about_label->set_autowrap(true);
-		String about_text =
-				String("C# support in Godot Engine is in late alpha stage and, while already usable, ") +
-				"it is not meant for use in production.\n\n" +
-				"Projects can be exported to Linux, macOS and Windows, but not yet to mobile or web platforms. " +
-				"Bugs and usability issues will be addressed gradually over future releases, " +
-				"potentially including compatibility breaking changes as new features are implemented for a better overall C# experience.\n\n" +
-				"If you experience issues with this Mono build, please report them on Godot's issue tracker with details about your system, MSBuild version, IDE, etc.:\n\n" +
-				"        https://github.com/godotengine/godot/issues\n\n" +
-				"Your critical feedback at this stage will play a great role in shaping the C# support in future releases, so thank you!";
-		about_label->set_text(about_text);
-
-		EDITOR_DEF("mono/editor/show_info_on_start", true);
-
-		// CheckBox in main container
-		about_dialog_checkbox = memnew(CheckBox);
-		about_vbc->add_child(about_dialog_checkbox);
-		about_dialog_checkbox->set_text("Show this warning when starting the editor");
-		about_dialog_checkbox->connect("toggled", this, "_toggle_about_dialog_on_start");
-	}
-
-	String sln_path = GodotSharpDirs::get_project_sln_path();
-	String csproj_path = GodotSharpDirs::get_project_csproj_path();
-
-	if (FileAccess::exists(sln_path) && FileAccess::exists(csproj_path)) {
-		// Defer this task because EditorProgress calls Main::iterarion() and the main loop is not yet initialized.
-		call_deferred("_make_api_solutions_if_needed");
-	} else {
-		bottom_panel_btn->hide();
-		menu_popup->add_item(TTR("Create C# solution"), MENU_CREATE_SLN);
-	}
-
-	menu_popup->connect("id_pressed", this, "_menu_option_pressed");
-
-	ToolButton *build_button = memnew(ToolButton);
-	build_button->set_text("Build");
-	build_button->set_tooltip("Build solution");
-	build_button->set_focus_mode(Control::FOCUS_NONE);
-	build_button->connect("pressed", this, "_build_solution_pressed");
-	editor->get_menu_hb()->add_child(build_button);
-
-	// External editor settings
-	EditorSettings *ed_settings = EditorSettings::get_singleton();
-	EDITOR_DEF("mono/editor/external_editor", EDITOR_NONE);
-
-	String settings_hint_str = "Disabled";
-
-#if defined(WINDOWS_ENABLED)
-	settings_hint_str += ",MonoDevelop,Visual Studio Code";
-#elif defined(OSX_ENABLED)
-	settings_hint_str += ",Visual Studio,MonoDevelop,Visual Studio Code";
-#elif defined(UNIX_ENABLED)
-	settings_hint_str += ",MonoDevelop,Visual Studio Code";
-#endif
-
-	ed_settings->add_property_hint(PropertyInfo(Variant::INT, "mono/editor/external_editor", PROPERTY_HINT_ENUM, settings_hint_str));
-
-	// Export plugin
-	Ref<GodotSharpExport> godotsharp_export;
-	godotsharp_export.instance();
-	EditorExport::get_singleton()->add_export_plugin(godotsharp_export);
-}
-
-GodotSharpEditor::~GodotSharpEditor() {
-
-	singleton = NULL;
-
-	memdelete(godotsharp_builds);
-
-	if (monodevelop_instance) {
-		memdelete(monodevelop_instance);
-		monodevelop_instance = NULL;
-	}
-}
-
-MonoReloadNode *MonoReloadNode::singleton = NULL;
-
-void MonoReloadNode::_reload_timer_timeout() {
-
-	if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
-		CSharpLanguage::get_singleton()->reload_assemblies(false);
-	}
-}
-
-void MonoReloadNode::restart_reload_timer() {
-
-	reload_timer->stop();
-	reload_timer->start();
-}
-
-void MonoReloadNode::_bind_methods() {
-
-	ClassDB::bind_method(D_METHOD("_reload_timer_timeout"), &MonoReloadNode::_reload_timer_timeout);
-}
-
-void MonoReloadNode::_notification(int p_what) {
-	switch (p_what) {
-		case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
-			restart_reload_timer();
-			if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
-				CSharpLanguage::get_singleton()->reload_assemblies(false);
-			}
-		} break;
-		default: {
-		} break;
-	};
-}
-
-MonoReloadNode::MonoReloadNode() {
-
-	singleton = this;
-
-	reload_timer = memnew(Timer);
-	add_child(reload_timer);
-	reload_timer->set_one_shot(false);
-	reload_timer->set_wait_time(EDITOR_DEF("mono/assembly_watch_interval_sec", 0.5));
-	reload_timer->connect("timeout", this, "_reload_timer_timeout");
-	reload_timer->start();
-}
-
-MonoReloadNode::~MonoReloadNode() {
-
-	singleton = NULL;
-}

+ 0 - 134
modules/mono/editor/godotsharp_editor.h

@@ -1,134 +0,0 @@
-/*************************************************************************/
-/*  godotsharp_editor.h                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef GODOTSHARP_EDITOR_H
-#define GODOTSHARP_EDITOR_H
-
-#include "godotsharp_builds.h"
-#include "monodevelop_instance.h"
-
-class GodotSharpEditor : public Node {
-	GDCLASS(GodotSharpEditor, Node);
-
-	EditorNode *editor;
-
-	MenuButton *menu_button;
-	PopupMenu *menu_popup;
-
-	AcceptDialog *error_dialog;
-	AcceptDialog *about_dialog;
-	CheckBox *about_dialog_checkbox;
-
-	ToolButton *bottom_panel_btn;
-
-	GodotSharpBuilds *godotsharp_builds;
-
-	MonoDevelopInstance *monodevelop_instance;
-#ifdef OSX_ENABLED
-	MonoDevelopInstance *visualstudio_mac_instance;
-#endif
-
-	bool _create_project_solution();
-	void _make_api_solutions_if_needed();
-	void _make_api_solutions_if_needed_impl();
-
-	void _remove_create_sln_menu_option();
-	void _show_about_dialog();
-	void _toggle_about_dialog_on_start(bool p_enabled);
-
-	void _menu_option_pressed(int p_id);
-
-	void _build_solution_pressed();
-
-	static GodotSharpEditor *singleton;
-
-protected:
-	void _notification(int p_notification);
-	static void _bind_methods();
-
-public:
-	enum MenuOptions {
-		MENU_CREATE_SLN,
-		MENU_ABOUT_CSHARP,
-	};
-
-	enum ExternalEditor {
-		EDITOR_NONE,
-#if defined(WINDOWS_ENABLED)
-		//EDITOR_VISUALSTUDIO, // TODO
-		EDITOR_MONODEVELOP,
-		EDITOR_VSCODE
-#elif defined(OSX_ENABLED)
-		EDITOR_VISUALSTUDIO_MAC,
-		EDITOR_MONODEVELOP,
-		EDITOR_VSCODE
-#elif defined(UNIX_ENABLED)
-		EDITOR_MONODEVELOP,
-		EDITOR_VSCODE
-#endif
-	};
-
-	_FORCE_INLINE_ static GodotSharpEditor *get_singleton() { return singleton; }
-
-	static void register_internal_calls();
-
-	void show_error_dialog(const String &p_message, const String &p_title = "Error");
-
-	Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col);
-	bool overrides_external_editor();
-
-	GodotSharpEditor(EditorNode *p_editor);
-	~GodotSharpEditor();
-};
-
-class MonoReloadNode : public Node {
-	GDCLASS(MonoReloadNode, Node);
-
-	Timer *reload_timer;
-
-	void _reload_timer_timeout();
-
-	static MonoReloadNode *singleton;
-
-protected:
-	static void _bind_methods();
-
-	void _notification(int p_what);
-
-public:
-	_FORCE_INLINE_ static MonoReloadNode *get_singleton() { return singleton; }
-
-	void restart_reload_timer();
-
-	MonoReloadNode();
-	~MonoReloadNode();
-};
-
-#endif // GODOTSHARP_EDITOR_H

+ 30 - 174
modules/mono/editor/godotsharp_export.cpp

@@ -30,180 +30,28 @@
 
 
 #include "godotsharp_export.h"
 #include "godotsharp_export.h"
 
 
-#include "core/version.h"
+#include <mono/metadata/image.h>
 
 
-#include "../csharp_script.h"
-#include "../godotsharp_defs.h"
-#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono_class.h"
-#include "../mono_gd/gd_mono_marshal.h"
-#include "csharp_project.h"
-#include "godotsharp_builds.h"
+#include "../mono_gd/gd_mono.h"
+#include "../mono_gd/gd_mono_assembly.h"
 
 
-static MonoString *godot_icall_GodotSharpExport_GetTemplatesDir() {
-	String current_version = VERSION_FULL_CONFIG;
-	String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(current_version);
-	return GDMonoMarshal::mono_string_from_godot(ProjectSettings::get_singleton()->globalize_path(templates_dir));
-}
-
-static MonoString *godot_icall_GodotSharpExport_GetDataDirName() {
-	String appname = ProjectSettings::get_singleton()->get("application/config/name");
-	String appname_safe = OS::get_singleton()->get_safe_dir_name(appname);
-	return GDMonoMarshal::mono_string_from_godot("data_" + appname_safe);
-}
-
-void GodotSharpExport::register_internal_calls() {
-	static bool registered = false;
-	ERR_FAIL_COND(registered);
-	registered = true;
-
-	mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetTemplatesDir", (void *)godot_icall_GodotSharpExport_GetTemplatesDir);
-	mono_add_internal_call("GodotSharpTools.Editor.GodotSharpExport::GetDataDirName", (void *)godot_icall_GodotSharpExport_GetDataDirName);
-}
-
-void GodotSharpExport::_export_file(const String &p_path, const String &p_type, const Set<String> &) {
-
-	if (p_type != CSharpLanguage::get_singleton()->get_type())
-		return;
-
-	ERR_FAIL_COND(p_path.get_extension() != CSharpLanguage::get_singleton()->get_extension());
-
-	// TODO what if the source file is not part of the game's C# project
-
-	if (!GLOBAL_GET("mono/export/include_scripts_content")) {
-		// We don't want to include the source code on exported games
-		add_file(p_path, Vector<uint8_t>(), false);
-		skip();
-	}
-}
-
-void GodotSharpExport::_export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags) {
-
-	// TODO right now there is no way to stop the export process with an error
-
-	ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
-	ERR_FAIL_NULL(TOOLS_DOMAIN);
-	ERR_FAIL_NULL(GDMono::get_singleton()->get_editor_tools_assembly());
-
-	if (FileAccess::exists(GodotSharpDirs::get_project_sln_path())) {
-		String build_config = p_debug ? "Debug" : "Release";
-
-		String scripts_metadata_path = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata." + String(p_debug ? "debug" : "release"));
-		Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path);
-		ERR_FAIL_COND(metadata_err != OK);
-
-		ERR_FAIL_COND(!_add_file(scripts_metadata_path, scripts_metadata_path));
-
-		// Turn export features into defines
-		Vector<String> godot_defines;
-		for (Set<String>::Element *E = p_features.front(); E; E = E->next()) {
-			godot_defines.push_back(E->get());
-		}
-		ERR_FAIL_COND(!GodotSharpBuilds::build_project_blocking(build_config, godot_defines));
-
-		// Add dependency assemblies
-
-		Map<String, String> dependencies;
-
-		String project_dll_name = ProjectSettings::get_singleton()->get("application/config/name");
-		String project_dll_name_safe = OS::get_singleton()->get_safe_dir_name(project_dll_name);
-		if (project_dll_name_safe.empty()) {
-			project_dll_name_safe = "UnnamedProject";
-		}
-
-		String project_dll_src_dir = GodotSharpDirs::get_res_temp_assemblies_base_dir().plus_file(build_config);
-		String project_dll_src_path = project_dll_src_dir.plus_file(project_dll_name_safe + ".dll");
-		dependencies.insert(project_dll_name_safe, project_dll_src_path);
-
-		{
-			MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.ProjectExportDomain");
-			ERR_FAIL_NULL(export_domain);
-			_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
-
-			_GDMONO_SCOPE_DOMAIN_(export_domain);
+String get_assemblyref_name(MonoImage *p_image, int index) {
+	const MonoTableInfo *table_info = mono_image_get_table_info(p_image, MONO_TABLE_ASSEMBLYREF);
 
 
-			GDMonoAssembly *scripts_assembly = NULL;
-			bool load_success = GDMono::get_singleton()->load_assembly_from(project_dll_name_safe,
-					project_dll_src_path, &scripts_assembly, /* refonly: */ true);
+	uint32_t cols[MONO_ASSEMBLYREF_SIZE];
 
 
-			ERR_EXPLAIN("Cannot load assembly (refonly): " + project_dll_name_safe);
-			ERR_FAIL_COND(!load_success);
+	mono_metadata_decode_row(table_info, index, cols, MONO_ASSEMBLYREF_SIZE);
 
 
-			Vector<String> search_dirs;
-			String templates_dir = EditorSettings::get_singleton()->get_templates_dir().plus_file(VERSION_FULL_CONFIG);
-			String android_bcl_dir = templates_dir.plus_file("android-bcl");
-
-			String custom_lib_dir;
-
-			if (p_features.find("Android") && DirAccess::exists(android_bcl_dir)) {
-				custom_lib_dir = android_bcl_dir;
-			}
-
-			GDMonoAssembly::fill_search_dirs(search_dirs, build_config, custom_lib_dir);
-
-			Error depend_error = _get_assembly_dependencies(scripts_assembly, search_dirs, dependencies);
-			ERR_FAIL_COND(depend_error != OK);
-		}
-
-		for (Map<String, String>::Element *E = dependencies.front(); E; E = E->next()) {
-			String depend_src_path = E->value();
-			String depend_dst_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(depend_src_path.get_file());
-			ERR_FAIL_COND(!_add_file(depend_src_path, depend_dst_path));
-		}
-	}
-
-	// Mono specific export template extras (data dir)
-
-	GDMonoClass *export_class = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "GodotSharpExport");
-	ERR_FAIL_NULL(export_class);
-	GDMonoMethod *export_begin_method = export_class->get_method("_ExportBegin", 4);
-	ERR_FAIL_NULL(export_begin_method);
-
-	MonoArray *features = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(String), p_features.size());
-	int i = 0;
-	for (const Set<String>::Element *E = p_features.front(); E; E = E->next()) {
-		MonoString *boxed = GDMonoMarshal::mono_string_from_godot(E->get());
-		mono_array_setref(features, i, boxed);
-		i++;
-	}
-
-	MonoBoolean debug = p_debug;
-	MonoString *path = GDMonoMarshal::mono_string_from_godot(p_path);
-	uint32_t flags = p_flags;
-	void *args[4] = { features, &debug, path, &flags };
-	MonoException *exc = NULL;
-	export_begin_method->invoke_raw(NULL, args, &exc);
-
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL();
-	}
-}
-
-bool GodotSharpExport::_add_file(const String &p_src_path, const String &p_dst_path, bool p_remap) {
-
-	FileAccessRef f = FileAccess::open(p_src_path, FileAccess::READ);
-	ERR_FAIL_COND_V(!f, false);
-
-	Vector<uint8_t> data;
-	data.resize(f->get_len());
-	f->get_buffer(data.ptrw(), data.size());
-
-	add_file(p_dst_path, data, p_remap);
-
-	return true;
+	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, Map<String, String> &r_dependencies) {
-
+Error GodotSharpExport::get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
 	MonoImage *image = p_assembly->get_image();
 	MonoImage *image = p_assembly->get_image();
 
 
 	for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
 	for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
-		MonoAssemblyName *ref_aname = aname_prealloc;
-		mono_assembly_get_assemblyref(image, i, ref_aname);
-		String ref_name = mono_assembly_name_get_name(ref_aname);
+		String ref_name = get_assemblyref_name(image, i);
 
 
-		if (r_dependencies.find(ref_name))
+		if (r_dependencies.has(ref_name))
 			continue;
 			continue;
 
 
 		GDMonoAssembly *ref_assembly = NULL;
 		GDMonoAssembly *ref_assembly = NULL;
@@ -242,9 +90,9 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
 			ERR_FAIL_V(ERR_CANT_RESOLVE);
 			ERR_FAIL_V(ERR_CANT_RESOLVE);
 		}
 		}
 
 
-		r_dependencies.insert(ref_name, ref_assembly->get_path());
+		r_dependencies[ref_name] = ref_assembly->get_path();
 
 
-		Error err = _get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
+		Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
 		if (err != OK) {
 		if (err != OK) {
 			ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name);
 			ERR_EXPLAIN("Cannot load one of the dependencies for the assembly: " + ref_name);
 			ERR_FAIL_V(err);
 			ERR_FAIL_V(err);
@@ -254,14 +102,22 @@ Error GodotSharpExport::_get_assembly_dependencies(GDMonoAssembly *p_assembly, c
 	return OK;
 	return OK;
 }
 }
 
 
-GodotSharpExport::GodotSharpExport() {
-	// MonoAssemblyName is an incomplete type (internal to mono), so we can't allocate it ourselves.
-	// There isn't any api to allocate an empty one either, so we need to do it this way.
-	aname_prealloc = mono_assembly_name_new("whatever");
-	mono_assembly_name_free(aname_prealloc); // "it does not frees the object itself, only the name members" (typo included)
-}
+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");
+	ERR_FAIL_NULL_V(export_domain, FAILED);
+	_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
+
+	_GDMONO_SCOPE_DOMAIN_(export_domain);
+
+	GDMonoAssembly *scripts_assembly = NULL;
+	bool load_success = GDMono::get_singleton()->load_assembly_from(p_project_dll_name,
+			p_project_dll_src_path, &scripts_assembly, /* refonly: */ true);
+
+	ERR_EXPLAIN("Cannot load assembly (refonly): " + p_project_dll_name);
+	ERR_FAIL_COND_V(!load_success, ERR_CANT_RESOLVE);
+
+	Vector<String> search_dirs;
+	GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_lib_dir);
 
 
-GodotSharpExport::~GodotSharpExport() {
-	if (aname_prealloc)
-		mono_free(aname_prealloc);
+	return get_assembly_dependencies(scripts_assembly, search_dirs, r_dependencies);
 }
 }

+ 9 - 19
modules/mono/editor/godotsharp_export.h

@@ -31,29 +31,19 @@
 #ifndef GODOTSHARP_EXPORT_H
 #ifndef GODOTSHARP_EXPORT_H
 #define GODOTSHARP_EXPORT_H
 #define GODOTSHARP_EXPORT_H
 
 
-#include <mono/metadata/image.h>
-
-#include "editor/editor_export.h"
+#include "core/dictionary.h"
+#include "core/error_list.h"
+#include "core/ustring.h"
 
 
 #include "../mono_gd/gd_mono_header.h"
 #include "../mono_gd/gd_mono_header.h"
 
 
-class GodotSharpExport : public EditorExportPlugin {
-
-	MonoAssemblyName *aname_prealloc;
-
-	bool _add_file(const String &p_src_path, const String &p_dst_path, bool p_remap = false);
-
-	Error _get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Map<String, String> &r_dependencies);
-
-protected:
-	virtual void _export_file(const String &p_path, const String &p_type, const Set<String> &p_features);
-	virtual void _export_begin(const Set<String> &p_features, bool p_debug, const String &p_path, int p_flags);
+namespace GodotSharpExport {
 
 
-public:
-	static void register_internal_calls();
+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);
 
 
-	GodotSharpExport();
-	~GodotSharpExport();
-};
+} // namespace GodotSharpExport
 
 
 #endif // GODOTSHARP_EXPORT_H
 #endif // GODOTSHARP_EXPORT_H

+ 0 - 528
modules/mono/editor/mono_bottom_panel.cpp

@@ -1,528 +0,0 @@
-/*************************************************************************/
-/*  mono_bottom_panel.cpp                                                */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "mono_bottom_panel.h"
-
-#include "editor/plugins/script_editor_plugin.h"
-#include "editor/script_editor_debugger.h"
-
-#include "../csharp_script.h"
-#include "../godotsharp_dirs.h"
-#include "csharp_project.h"
-#include "godotsharp_editor.h"
-
-MonoBottomPanel *MonoBottomPanel::singleton = NULL;
-
-void MonoBottomPanel::_update_build_tabs_list() {
-
-	build_tabs_list->clear();
-
-	int current_tab = build_tabs->get_current_tab();
-
-	bool no_current_tab = current_tab < 0 || current_tab >= build_tabs->get_tab_count();
-
-	for (int i = 0; i < build_tabs->get_child_count(); i++) {
-
-		MonoBuildTab *tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(i));
-
-		if (tab) {
-			String item_name = tab->build_info.solution.get_file().get_basename();
-			item_name += " [" + tab->build_info.configuration + "]";
-
-			build_tabs_list->add_item(item_name, tab->get_icon_texture());
-
-			String item_tooltip = "Solution: " + tab->build_info.solution;
-			item_tooltip += "\nConfiguration: " + tab->build_info.configuration;
-			item_tooltip += "\nStatus: ";
-
-			if (tab->build_exited) {
-				item_tooltip += tab->build_result == MonoBuildTab::RESULT_SUCCESS ? "Succeeded" : "Errored";
-			} else {
-				item_tooltip += "Running";
-			}
-
-			if (!tab->build_exited || tab->build_result == MonoBuildTab::RESULT_ERROR) {
-				item_tooltip += "\nErrors: " + itos(tab->error_count);
-			}
-
-			item_tooltip += "\nWarnings: " + itos(tab->warning_count);
-
-			build_tabs_list->set_item_tooltip(i, item_tooltip);
-
-			if (no_current_tab || current_tab == i) {
-				build_tabs_list->select(i);
-				_build_tabs_item_selected(i);
-			}
-		}
-	}
-}
-
-void MonoBottomPanel::add_build_tab(MonoBuildTab *p_build_tab) {
-
-	build_tabs->add_child(p_build_tab);
-	raise_build_tab(p_build_tab);
-}
-
-void MonoBottomPanel::raise_build_tab(MonoBuildTab *p_build_tab) {
-
-	ERR_FAIL_COND(p_build_tab->get_parent() != build_tabs);
-	build_tabs->move_child(p_build_tab, 0);
-	_update_build_tabs_list();
-}
-
-void MonoBottomPanel::show_build_tab() {
-
-	for (int i = 0; i < panel_tabs->get_tab_count(); i++) {
-		if (panel_tabs->get_tab_control(i) == panel_builds_tab) {
-			panel_tabs->set_current_tab(i);
-			editor->make_bottom_panel_item_visible(this);
-			return;
-		}
-	}
-
-	ERR_PRINT("Builds tab not found");
-}
-
-void MonoBottomPanel::_build_tabs_item_selected(int p_idx) {
-
-	ERR_FAIL_INDEX(p_idx, build_tabs->get_tab_count());
-
-	build_tabs->set_current_tab(p_idx);
-	if (!build_tabs->is_visible())
-		build_tabs->set_visible(true);
-
-	warnings_btn->set_visible(true);
-	errors_btn->set_visible(true);
-	view_log_btn->set_visible(true);
-}
-
-void MonoBottomPanel::_build_tabs_nothing_selected() {
-
-	if (build_tabs->get_tab_count() != 0) { // just in case
-		build_tabs->set_visible(false);
-
-		// This callback is called when clicking on the empty space of the list.
-		// ItemList won't deselect the items automatically, so we must do it ourselves.
-		build_tabs_list->unselect_all();
-	}
-
-	warnings_btn->set_visible(false);
-	errors_btn->set_visible(false);
-	view_log_btn->set_visible(false);
-}
-
-void MonoBottomPanel::_warnings_toggled(bool p_pressed) {
-
-	int current_tab = build_tabs->get_current_tab();
-	ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
-	MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
-	build_tab->warnings_visible = p_pressed;
-	build_tab->_update_issues_list();
-}
-
-void MonoBottomPanel::_errors_toggled(bool p_pressed) {
-
-	int current_tab = build_tabs->get_current_tab();
-	ERR_FAIL_INDEX(current_tab, build_tabs->get_tab_count());
-	MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_child(current_tab));
-	build_tab->errors_visible = p_pressed;
-	build_tab->_update_issues_list();
-}
-
-void MonoBottomPanel::_build_project_pressed() {
-
-	if (!FileAccess::exists(GodotSharpDirs::get_project_sln_path()))
-		return; // No solution to build
-
-	String scripts_metadata_path_editor = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor");
-	String scripts_metadata_path_player = GodotSharpDirs::get_res_metadata_dir().plus_file("scripts_metadata.editor_player");
-
-	Error metadata_err = CSharpProject::generate_scripts_metadata(GodotSharpDirs::get_project_csproj_path(), scripts_metadata_path_editor);
-	ERR_FAIL_COND(metadata_err != OK);
-
-	if (FileAccess::exists(scripts_metadata_path_editor)) {
-		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
-		Error copy_err = da->copy(scripts_metadata_path_editor, scripts_metadata_path_player);
-
-		ERR_EXPLAIN("Failed to copy scripts metadata file");
-		ERR_FAIL_COND(copy_err != OK);
-	}
-
-	Vector<String> godot_defines;
-	godot_defines.push_back(OS::get_singleton()->get_name());
-	godot_defines.push_back((sizeof(void *) == 4 ? "32" : "64"));
-	bool build_success = GodotSharpBuilds::get_singleton()->build_project_blocking("Tools", godot_defines);
-
-	if (build_success) {
-		// Notify running game for hot-reload
-		ScriptEditor::get_singleton()->get_debugger()->reload_scripts();
-
-		// Hot-reload in the editor
-		MonoReloadNode::get_singleton()->restart_reload_timer();
-
-		if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed()) {
-			CSharpLanguage::get_singleton()->reload_assemblies(false);
-		}
-	}
-}
-
-void MonoBottomPanel::_view_log_pressed() {
-
-	if (build_tabs_list->is_anything_selected()) {
-		Vector<int> selected_items = build_tabs_list->get_selected_items();
-		CRASH_COND(selected_items.size() != 1);
-		int selected_item = selected_items[0];
-
-		MonoBuildTab *build_tab = Object::cast_to<MonoBuildTab>(build_tabs->get_tab_control(selected_item));
-		ERR_FAIL_NULL(build_tab);
-
-		String log_dirpath = build_tab->get_build_info().get_log_dirpath();
-
-		OS::get_singleton()->shell_open(log_dirpath.plus_file(GodotSharpBuilds::get_msbuild_log_filename()));
-	}
-}
-
-void MonoBottomPanel::_notification(int p_what) {
-
-	switch (p_what) {
-
-		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
-			panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
-			panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
-			panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
-		} break;
-	}
-}
-
-void MonoBottomPanel::_bind_methods() {
-
-	ClassDB::bind_method(D_METHOD("_build_project_pressed"), &MonoBottomPanel::_build_project_pressed);
-	ClassDB::bind_method(D_METHOD("_view_log_pressed"), &MonoBottomPanel::_view_log_pressed);
-	ClassDB::bind_method(D_METHOD("_warnings_toggled", "pressed"), &MonoBottomPanel::_warnings_toggled);
-	ClassDB::bind_method(D_METHOD("_errors_toggled", "pressed"), &MonoBottomPanel::_errors_toggled);
-	ClassDB::bind_method(D_METHOD("_build_tabs_item_selected", "idx"), &MonoBottomPanel::_build_tabs_item_selected);
-	ClassDB::bind_method(D_METHOD("_build_tabs_nothing_selected"), &MonoBottomPanel::_build_tabs_nothing_selected);
-}
-
-MonoBottomPanel::MonoBottomPanel(EditorNode *p_editor) {
-
-	singleton = this;
-
-	editor = p_editor;
-
-	set_v_size_flags(SIZE_EXPAND_FILL);
-	set_anchors_and_margins_preset(Control::PRESET_WIDE);
-
-	panel_tabs = memnew(TabContainer);
-	panel_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
-	panel_tabs->add_style_override("panel", editor->get_gui_base()->get_stylebox("DebuggerPanel", "EditorStyles"));
-	panel_tabs->add_style_override("tab_fg", editor->get_gui_base()->get_stylebox("DebuggerTabFG", "EditorStyles"));
-	panel_tabs->add_style_override("tab_bg", editor->get_gui_base()->get_stylebox("DebuggerTabBG", "EditorStyles"));
-	panel_tabs->set_custom_minimum_size(Size2(0, 228) * EDSCALE);
-	panel_tabs->set_v_size_flags(SIZE_EXPAND_FILL);
-	add_child(panel_tabs);
-
-	{ // Builds
-		panel_builds_tab = memnew(VBoxContainer);
-		panel_builds_tab->set_name(TTR("Builds"));
-		panel_builds_tab->set_h_size_flags(SIZE_EXPAND_FILL);
-		panel_tabs->add_child(panel_builds_tab);
-
-		HBoxContainer *toolbar_hbc = memnew(HBoxContainer);
-		toolbar_hbc->set_h_size_flags(SIZE_EXPAND_FILL);
-		panel_builds_tab->add_child(toolbar_hbc);
-
-		Button *build_project_btn = memnew(Button);
-		build_project_btn->set_text(TTR("Build Project"));
-		build_project_btn->set_focus_mode(FOCUS_NONE);
-		build_project_btn->connect("pressed", this, "_build_project_pressed");
-		toolbar_hbc->add_child(build_project_btn);
-
-		toolbar_hbc->add_spacer();
-
-		warnings_btn = memnew(ToolButton);
-		warnings_btn->set_text(TTR("Warnings"));
-		warnings_btn->set_toggle_mode(true);
-		warnings_btn->set_pressed(true);
-		warnings_btn->set_visible(false);
-		warnings_btn->set_focus_mode(FOCUS_NONE);
-		warnings_btn->connect("toggled", this, "_warnings_toggled");
-		toolbar_hbc->add_child(warnings_btn);
-
-		errors_btn = memnew(ToolButton);
-		errors_btn->set_text(TTR("Errors"));
-		errors_btn->set_toggle_mode(true);
-		errors_btn->set_pressed(true);
-		errors_btn->set_visible(false);
-		errors_btn->set_focus_mode(FOCUS_NONE);
-		errors_btn->connect("toggled", this, "_errors_toggled");
-		toolbar_hbc->add_child(errors_btn);
-
-		toolbar_hbc->add_spacer();
-
-		view_log_btn = memnew(Button);
-		view_log_btn->set_text(TTR("View log"));
-		view_log_btn->set_focus_mode(FOCUS_NONE);
-		view_log_btn->set_visible(false);
-		view_log_btn->connect("pressed", this, "_view_log_pressed");
-		toolbar_hbc->add_child(view_log_btn);
-
-		HSplitContainer *hsc = memnew(HSplitContainer);
-		hsc->set_h_size_flags(SIZE_EXPAND_FILL);
-		hsc->set_v_size_flags(SIZE_EXPAND_FILL);
-		panel_builds_tab->add_child(hsc);
-
-		build_tabs_list = memnew(ItemList);
-		build_tabs_list->set_h_size_flags(SIZE_EXPAND_FILL);
-		build_tabs_list->connect("item_selected", this, "_build_tabs_item_selected");
-		build_tabs_list->connect("nothing_selected", this, "_build_tabs_nothing_selected");
-		hsc->add_child(build_tabs_list);
-
-		build_tabs = memnew(TabContainer);
-		build_tabs->set_tab_align(TabContainer::ALIGN_LEFT);
-		build_tabs->set_h_size_flags(SIZE_EXPAND_FILL);
-		build_tabs->set_tabs_visible(false);
-		hsc->add_child(build_tabs);
-	}
-}
-
-MonoBottomPanel::~MonoBottomPanel() {
-
-	singleton = NULL;
-}
-
-void MonoBuildTab::_load_issues_from_file(const String &p_csv_file) {
-
-	FileAccessRef f = FileAccess::open(p_csv_file, FileAccess::READ);
-
-	if (!f)
-		return;
-
-	while (!f->eof_reached()) {
-		Vector<String> csv_line = f->get_csv_line();
-
-		if (csv_line.size() == 1 && csv_line[0].empty())
-			return;
-
-		ERR_CONTINUE(csv_line.size() != 7);
-
-		BuildIssue issue;
-		issue.warning = csv_line[0] == "warning";
-		issue.file = csv_line[1];
-		issue.line = csv_line[2].to_int();
-		issue.column = csv_line[3].to_int();
-		issue.code = csv_line[4];
-		issue.message = csv_line[5];
-		issue.project_file = csv_line[6];
-
-		if (issue.warning)
-			warning_count += 1;
-		else
-			error_count += 1;
-
-		issues.push_back(issue);
-	}
-}
-
-void MonoBuildTab::_update_issues_list() {
-
-	issues_list->clear();
-
-	Ref<Texture> warning_icon = get_icon("Warning", "EditorIcons");
-	Ref<Texture> error_icon = get_icon("Error", "EditorIcons");
-
-	for (int i = 0; i < issues.size(); i++) {
-
-		const BuildIssue &issue = issues[i];
-
-		if (!(issue.warning ? warnings_visible : errors_visible))
-			continue;
-
-		String tooltip;
-		tooltip += String("Message: ") + issue.message;
-
-		if (issue.code.length()) {
-			tooltip += String("\nCode: ") + issue.code;
-		}
-
-		tooltip += String("\nType: ") + (issue.warning ? "warning" : "error");
-
-		String text;
-
-		if (issue.file.length()) {
-			String sline = String::num_int64(issue.line);
-			String scolumn = String::num_int64(issue.column);
-
-			text += issue.file + "(";
-			text += sline + ",";
-			text += scolumn + "): ";
-
-			tooltip += "\nFile: " + issue.file;
-			tooltip += "\nLine: " + sline;
-			tooltip += "\nColumn: " + scolumn;
-		}
-
-		if (issue.project_file.length()) {
-			tooltip += "\nProject: " + issue.project_file;
-		}
-
-		text += issue.message;
-
-		int line_break_idx = text.find("\n");
-		issues_list->add_item(line_break_idx == -1 ? text : text.substr(0, line_break_idx),
-				issue.warning ? warning_icon : error_icon);
-		int index = issues_list->get_item_count() - 1;
-		issues_list->set_item_tooltip(index, tooltip);
-		issues_list->set_item_metadata(index, i);
-	}
-}
-
-Ref<Texture> MonoBuildTab::get_icon_texture() const {
-
-	if (build_exited) {
-		if (build_result == RESULT_ERROR) {
-			return get_icon("StatusError", "EditorIcons");
-		} else {
-			return get_icon("StatusSuccess", "EditorIcons");
-		}
-	} else {
-		return get_icon("Stop", "EditorIcons");
-	}
-}
-
-MonoBuildInfo MonoBuildTab::get_build_info() {
-
-	return build_info;
-}
-
-void MonoBuildTab::on_build_start() {
-
-	build_exited = false;
-
-	issues.clear();
-	warning_count = 0;
-	error_count = 0;
-	_update_issues_list();
-
-	MonoBottomPanel::get_singleton()->raise_build_tab(this);
-}
-
-void MonoBuildTab::on_build_exit(BuildResult result) {
-
-	build_exited = true;
-	build_result = result;
-
-	_load_issues_from_file(logs_dir.plus_file(GodotSharpBuilds::get_msbuild_issues_filename()));
-	_update_issues_list();
-
-	MonoBottomPanel::get_singleton()->raise_build_tab(this);
-}
-
-void MonoBuildTab::on_build_exec_failed(const String &p_cause) {
-
-	build_exited = true;
-	build_result = RESULT_ERROR;
-
-	issues_list->clear();
-
-	BuildIssue issue;
-	issue.message = p_cause;
-	issue.warning = false;
-
-	error_count += 1;
-	issues.push_back(issue);
-
-	_update_issues_list();
-
-	MonoBottomPanel::get_singleton()->raise_build_tab(this);
-}
-
-void MonoBuildTab::restart_build() {
-
-	ERR_FAIL_COND(!build_exited);
-	GodotSharpBuilds::get_singleton()->restart_build(this);
-}
-
-void MonoBuildTab::stop_build() {
-
-	ERR_FAIL_COND(build_exited);
-	GodotSharpBuilds::get_singleton()->stop_build(this);
-}
-
-void MonoBuildTab::_issue_activated(int p_idx) {
-
-	ERR_FAIL_INDEX(p_idx, issues_list->get_item_count());
-
-	// Get correct issue idx from issue list
-	int issue_idx = this->issues_list->get_item_metadata(p_idx);
-
-	ERR_FAIL_INDEX(issue_idx, issues.size());
-
-	const BuildIssue &issue = issues[issue_idx];
-
-	if (issue.project_file.empty() && issue.file.empty())
-		return;
-
-	String project_dir = issue.project_file.length() ? issue.project_file.get_base_dir() : build_info.solution.get_base_dir();
-
-	String file = project_dir.simplify_path().plus_file(issue.file.simplify_path());
-
-	if (!FileAccess::exists(file))
-		return;
-
-	file = ProjectSettings::get_singleton()->localize_path(file);
-
-	if (file.begins_with("res://")) {
-		Ref<Script> script = ResourceLoader::load(file, CSharpLanguage::get_singleton()->get_type());
-
-		if (script.is_valid() && ScriptEditor::get_singleton()->edit(script, issue.line, issue.column)) {
-			EditorNode::get_singleton()->call("_editor_select", EditorNode::EDITOR_SCRIPT);
-		}
-	}
-}
-
-void MonoBuildTab::_bind_methods() {
-
-	ClassDB::bind_method("_issue_activated", &MonoBuildTab::_issue_activated);
-}
-
-MonoBuildTab::MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir) :
-		build_exited(false),
-		issues_list(memnew(ItemList)),
-		error_count(0),
-		warning_count(0),
-		errors_visible(true),
-		warnings_visible(true),
-		logs_dir(p_logs_dir),
-		build_info(p_build_info) {
-	issues_list->set_v_size_flags(SIZE_EXPAND_FILL);
-	issues_list->connect("item_activated", this, "_issue_activated");
-	add_child(issues_list);
-}

+ 0 - 150
modules/mono/editor/mono_bottom_panel.h

@@ -1,150 +0,0 @@
-/*************************************************************************/
-/*  mono_bottom_panel.h                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef MONO_BOTTOM_PANEL_H
-#define MONO_BOTTOM_PANEL_H
-
-#include "editor/editor_node.h"
-#include "scene/gui/control.h"
-
-#include "mono_build_info.h"
-
-class MonoBuildTab;
-
-class MonoBottomPanel : public VBoxContainer {
-
-	GDCLASS(MonoBottomPanel, VBoxContainer);
-
-	EditorNode *editor;
-
-	TabContainer *panel_tabs;
-
-	VBoxContainer *panel_builds_tab;
-
-	ItemList *build_tabs_list;
-	TabContainer *build_tabs;
-
-	ToolButton *warnings_btn;
-	ToolButton *errors_btn;
-	Button *view_log_btn;
-
-	void _update_build_tabs_list();
-
-	void _build_tabs_item_selected(int p_idx);
-	void _build_tabs_nothing_selected();
-
-	void _warnings_toggled(bool p_pressed);
-	void _errors_toggled(bool p_pressed);
-
-	void _build_project_pressed();
-	void _view_log_pressed();
-
-	static MonoBottomPanel *singleton;
-
-protected:
-	void _notification(int p_what);
-
-	static void _bind_methods();
-
-public:
-	_FORCE_INLINE_ static MonoBottomPanel *get_singleton() { return singleton; }
-
-	void add_build_tab(MonoBuildTab *p_build_tab);
-	void raise_build_tab(MonoBuildTab *p_build_tab);
-
-	void show_build_tab();
-
-	MonoBottomPanel(EditorNode *p_editor = NULL);
-	~MonoBottomPanel();
-};
-
-class MonoBuildTab : public VBoxContainer {
-
-	GDCLASS(MonoBuildTab, VBoxContainer);
-
-public:
-	enum BuildResult {
-		RESULT_ERROR,
-		RESULT_SUCCESS
-	};
-
-	struct BuildIssue {
-		bool warning;
-		String file;
-		int line;
-		int column;
-		String code;
-		String message;
-		String project_file;
-	};
-
-private:
-	friend class MonoBottomPanel;
-
-	bool build_exited;
-	BuildResult build_result;
-
-	Vector<BuildIssue> issues;
-	ItemList *issues_list;
-
-	int error_count;
-	int warning_count;
-
-	bool errors_visible;
-	bool warnings_visible;
-
-	String logs_dir;
-
-	MonoBuildInfo build_info;
-
-	void _load_issues_from_file(const String &p_csv_file);
-	void _update_issues_list();
-
-	void _issue_activated(int p_idx);
-
-protected:
-	static void _bind_methods();
-
-public:
-	Ref<Texture> get_icon_texture() const;
-
-	MonoBuildInfo get_build_info();
-
-	void on_build_start();
-	void on_build_exit(BuildResult result);
-	void on_build_exec_failed(const String &p_cause);
-
-	void restart_build();
-	void stop_build();
-
-	MonoBuildTab(const MonoBuildInfo &p_build_info, const String &p_logs_dir);
-};
-
-#endif // MONO_BOTTOM_PANEL_H

+ 0 - 62
modules/mono/editor/mono_build_info.cpp

@@ -1,62 +0,0 @@
-/*************************************************************************/
-/*  mono_build_info.cpp                                                  */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "mono_build_info.h"
-
-#include "../godotsharp_dirs.h"
-#include "../mono_gd/gd_mono_utils.h"
-
-uint32_t MonoBuildInfo::Hasher::hash(const MonoBuildInfo &p_key) {
-
-	uint32_t hash = 0;
-
-	GDMonoUtils::hash_combine(hash, p_key.solution.hash());
-	GDMonoUtils::hash_combine(hash, p_key.configuration.hash());
-
-	return hash;
-}
-
-bool MonoBuildInfo::operator==(const MonoBuildInfo &p_b) const {
-
-	return p_b.solution == solution && p_b.configuration == configuration;
-}
-
-String MonoBuildInfo::get_log_dirpath() {
-
-	return GodotSharpDirs::get_build_logs_dir().plus_file(solution.md5_text() + "_" + configuration);
-}
-
-MonoBuildInfo::MonoBuildInfo() {}
-
-MonoBuildInfo::MonoBuildInfo(const String &p_solution, const String &p_config) {
-
-	solution = p_solution;
-	configuration = p_config;
-}

+ 0 - 85
modules/mono/editor/monodevelop_instance.cpp

@@ -1,85 +0,0 @@
-/*************************************************************************/
-/*  monodevelop_instance.cpp                                             */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#include "monodevelop_instance.h"
-
-#include "../mono_gd/gd_mono.h"
-#include "../mono_gd/gd_mono_class.h"
-
-void MonoDevelopInstance::execute(const Vector<String> &p_files) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	ERR_FAIL_NULL(execute_method);
-	ERR_FAIL_COND(gc_handle.is_null());
-
-	MonoException *exc = NULL;
-
-	Variant files = p_files;
-	const Variant *args[1] = { &files };
-	execute_method->invoke(gc_handle->get_target(), args, &exc);
-
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL();
-	}
-}
-
-void MonoDevelopInstance::execute(const String &p_file) {
-
-	Vector<String> files;
-	files.push_back(p_file);
-	execute(files);
-}
-
-MonoDevelopInstance::MonoDevelopInstance(const String &p_solution, EditorId p_editor_id) {
-
-	_GDMONO_SCOPE_DOMAIN_(TOOLS_DOMAIN)
-
-	GDMonoClass *klass = GDMono::get_singleton()->get_editor_tools_assembly()->get_class("GodotSharpTools.Editor", "MonoDevelopInstance");
-
-	MonoObject *obj = mono_object_new(TOOLS_DOMAIN, klass->get_mono_ptr());
-
-	GDMonoMethod *ctor = klass->get_method(".ctor", 2);
-	MonoException *exc = NULL;
-
-	Variant solution = p_solution;
-	Variant editor_id = p_editor_id;
-	const Variant *args[2] = { &solution, &editor_id };
-	ctor->invoke(obj, args, &exc);
-
-	if (exc) {
-		GDMonoUtils::debug_print_unhandled_exception(exc);
-		ERR_FAIL();
-	}
-
-	gc_handle = MonoGCHandle::create_strong(obj);
-	execute_method = klass->get_method("Execute", 1);
-}

+ 0 - 56
modules/mono/editor/monodevelop_instance.h

@@ -1,56 +0,0 @@
-/*************************************************************************/
-/*  monodevelop_instance.h                                               */
-/*************************************************************************/
-/*                       This file is part of:                           */
-/*                           GODOT ENGINE                                */
-/*                      https://godotengine.org                          */
-/*************************************************************************/
-/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
-/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
-/*                                                                       */
-/* Permission is hereby granted, free of charge, to any person obtaining */
-/* a copy of this software and associated documentation files (the       */
-/* "Software"), to deal in the Software without restriction, including   */
-/* without limitation the rights to use, copy, modify, merge, publish,   */
-/* distribute, sublicense, and/or sell copies of the Software, and to    */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions:                                             */
-/*                                                                       */
-/* The above copyright notice and this permission notice shall be        */
-/* included in all copies or substantial portions of the Software.       */
-/*                                                                       */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
-/*************************************************************************/
-
-#ifndef MONODEVELOP_INSTANCE_H
-#define MONODEVELOP_INSTANCE_H
-
-#include "core/reference.h"
-
-#include "../mono_gc_handle.h"
-#include "../mono_gd/gd_mono_method.h"
-
-class MonoDevelopInstance {
-
-	Ref<MonoGCHandle> gc_handle;
-	GDMonoMethod *execute_method;
-
-public:
-	enum EditorId {
-		MONODEVELOP = 0,
-		VISUALSTUDIO_FOR_MAC = 1
-	};
-
-	void execute(const Vector<String> &p_files);
-	void execute(const String &p_file);
-
-	MonoDevelopInstance(const String &p_solution, EditorId p_editor_id);
-};
-
-#endif // MONODEVELOP_INSTANCE_H

+ 2 - 0
modules/mono/glue/Managed/.gitignore

@@ -0,0 +1,2 @@
+# Generated Godot API solution folder
+Generated

+ 8 - 8
modules/mono/glue/Managed/Files/Attributes/RPCAttributes.cs

@@ -2,27 +2,27 @@ using System;
 
 
 namespace Godot
 namespace Godot
 {
 {
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class RemoteAttribute : Attribute {}
     public class RemoteAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class SyncAttribute : Attribute {}
     public class SyncAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class MasterAttribute : Attribute {}
     public class MasterAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class PuppetAttribute : Attribute {}
     public class PuppetAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class SlaveAttribute : Attribute {}
     public class SlaveAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class RemoteSyncAttribute : Attribute {}
     public class RemoteSyncAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class MasterSyncAttribute : Attribute {}
     public class MasterSyncAttribute : Attribute {}
 
 
-    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field | AttributeTargets.Property)]
     public class PuppetSyncAttribute : Attribute {}
     public class PuppetSyncAttribute : Attribute {}
 }
 }

+ 8 - 0
modules/mono/glue/Managed/Files/Interfaces/ISerializationListener.cs

@@ -0,0 +1,8 @@
+namespace Godot
+{
+    public interface ISerializationListener
+    {
+        void OnBeforeSerialize();
+        void OnAfterDeserialize();
+    }
+}

+ 5 - 4
modules/mono/glue/Managed/Files/StringExtensions.cs

@@ -299,14 +299,14 @@ namespace Godot
             if (basepos != -1)
             if (basepos != -1)
             {
             {
                 var end = basepos + 3;
                 var end = basepos + 3;
-                rs = instance.Substring(end, instance.Length);
+                rs = instance.Substring(end);
                 @base = instance.Substring(0, end);
                 @base = instance.Substring(0, end);
             }
             }
             else
             else
             {
             {
                 if (instance.BeginsWith("/"))
                 if (instance.BeginsWith("/"))
                 {
                 {
-                    rs = instance.Substring(1, instance.Length);
+                    rs = instance.Substring(1);
                     @base = "/";
                     @base = "/";
                 }
                 }
                 else
                 else
@@ -333,7 +333,7 @@ namespace Godot
             if (sep == -1)
             if (sep == -1)
                 return instance;
                 return instance;
 
 
-            return instance.Substring(sep + 1, instance.Length);
+            return instance.Substring(sep + 1);
         }
         }
 
 
         // <summary>
         // <summary>
@@ -911,7 +911,8 @@ namespace Godot
         // </summary>
         // </summary>
         public static string Substr(this string instance, int from, int len)
         public static string Substr(this string instance, int from, int len)
         {
         {
-            return instance.Substring(from, len);
+            int max = instance.Length - from;
+            return instance.Substring(from, len > max ? max : len);
         }
         }
 
 
         // <summary>
         // <summary>

+ 6 - 6
modules/mono/glue/string_glue.cpp

@@ -68,12 +68,12 @@ MonoString *godot_icall_String_sha256_text(MonoString *p_str) {
 }
 }
 
 
 void godot_register_string_icalls() {
 void godot_register_string_icalls() {
-	mono_add_internal_call("Godot.String::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
-	mono_add_internal_call("Godot.String::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
-	mono_add_internal_call("Godot.String::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
-	mono_add_internal_call("Godot.String::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
-	mono_add_internal_call("Godot.String::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
-	mono_add_internal_call("Godot.String::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_buffer", (void *)godot_icall_String_md5_buffer);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_md5_text", (void *)godot_icall_String_md5_text);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfind", (void *)godot_icall_String_rfind);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_rfindn", (void *)godot_icall_String_rfindn);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_buffer", (void *)godot_icall_String_sha256_buffer);
+	mono_add_internal_call("Godot.StringExtensions::godot_icall_String_sha256_text", (void *)godot_icall_String_sha256_text);
 }
 }
 
 
 #endif // MONO_GLUE_ENABLED
 #endif // MONO_GLUE_ENABLED

+ 2 - 1
modules/mono/godotsharp_defs.h

@@ -39,7 +39,8 @@
 #define API_SOLUTION_NAME "GodotSharp"
 #define API_SOLUTION_NAME "GodotSharp"
 #define CORE_API_ASSEMBLY_NAME "GodotSharp"
 #define CORE_API_ASSEMBLY_NAME "GodotSharp"
 #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
 #define EDITOR_API_ASSEMBLY_NAME "GodotSharpEditor"
-#define EDITOR_TOOLS_ASSEMBLY_NAME "GodotSharpTools"
+#define TOOLS_ASSEMBLY_NAME "GodotTools"
+#define TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME "GodotTools.ProjectEditor"
 
 
 #define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
 #define BINDINGS_CLASS_NATIVECALLS "NativeCalls"
 #define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"
 #define BINDINGS_CLASS_NATIVECALLS_EDITOR "EditorNativeCalls"

+ 21 - 1
modules/mono/godotsharp_dirs.cpp

@@ -59,6 +59,20 @@ String _get_expected_build_config() {
 #endif
 #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() {
 String _get_mono_user_dir() {
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	if (EditorSettings::get_singleton()) {
 	if (EditorSettings::get_singleton()) {
@@ -88,6 +102,7 @@ class _GodotSharpDirs {
 public:
 public:
 	String res_data_dir;
 	String res_data_dir;
 	String res_metadata_dir;
 	String res_metadata_dir;
+	String res_assemblies_base_dir;
 	String res_assemblies_dir;
 	String res_assemblies_dir;
 	String res_config_dir;
 	String res_config_dir;
 	String res_temp_dir;
 	String res_temp_dir;
@@ -118,7 +133,8 @@ private:
 	_GodotSharpDirs() {
 	_GodotSharpDirs() {
 		res_data_dir = "res://.mono";
 		res_data_dir = "res://.mono";
 		res_metadata_dir = res_data_dir.plus_file("metadata");
 		res_metadata_dir = res_data_dir.plus_file("metadata");
-		res_assemblies_dir = res_data_dir.plus_file("assemblies");
+		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_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
 		res_config_dir = res_data_dir.plus_file("etc").plus_file("mono");
 
 
 		// TODO use paths from csproj
 		// TODO use paths from csproj
@@ -231,6 +247,10 @@ String get_res_metadata_dir() {
 	return _GodotSharpDirs::get_singleton().res_metadata_dir;
 	return _GodotSharpDirs::get_singleton().res_metadata_dir;
 }
 }
 
 
+String get_res_assemblies_base_dir() {
+	return _GodotSharpDirs::get_singleton().res_assemblies_base_dir;
+}
+
 String get_res_assemblies_dir() {
 String get_res_assemblies_dir() {
 	return _GodotSharpDirs::get_singleton().res_assemblies_dir;
 	return _GodotSharpDirs::get_singleton().res_assemblies_dir;
 }
 }

+ 1 - 0
modules/mono/godotsharp_dirs.h

@@ -37,6 +37,7 @@ namespace GodotSharpDirs {
 
 
 String get_res_data_dir();
 String get_res_data_dir();
 String get_res_metadata_dir();
 String get_res_metadata_dir();
+String get_res_assemblies_base_dir();
 String get_res_assemblies_dir();
 String get_res_assemblies_dir();
 String get_res_config_dir();
 String get_res_config_dir();
 String get_res_temp_dir();
 String get_res_temp_dir();

+ 150 - 83
modules/mono/mono_gd/gd_mono.cpp

@@ -52,7 +52,6 @@
 #include "gd_mono_utils.h"
 #include "gd_mono_utils.h"
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-#include "../editor/godotsharp_editor.h"
 #include "main/main.h"
 #include "main/main.h"
 #endif
 #endif
 
 
@@ -99,7 +98,7 @@ void gdmono_profiler_init() {
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 
 
-static bool _wait_for_debugger_msecs(uint32_t p_msecs) {
+bool _wait_for_debugger_msecs(uint32_t p_msecs) {
 
 
 	do {
 	do {
 		if (mono_is_debugger_attached())
 		if (mono_is_debugger_attached())
@@ -129,16 +128,17 @@ void gdmono_debug_init() {
 	bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
 	bool da_suspend = GLOBAL_DEF("mono/debugger_agent/wait_for_debugger", false);
 	int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
 	int da_timeout = GLOBAL_DEF("mono/debugger_agent/wait_timeout", 3000);
 
 
+	CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
+
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	if (Engine::get_singleton()->is_editor_hint() ||
 	if (Engine::get_singleton()->is_editor_hint() ||
 			ProjectSettings::get_singleton()->get_resource_path().empty() ||
 			ProjectSettings::get_singleton()->get_resource_path().empty() ||
 			Main::is_project_manager()) {
 			Main::is_project_manager()) {
-		return;
+		if (da_args.size() == 0)
+			return;
 	}
 	}
 #endif
 #endif
 
 
-	CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
-
 	if (da_args.length() == 0) {
 	if (da_args.length() == 0) {
 		da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
 		da_args = String("--debugger-agent=transport=dt_socket,address=127.0.0.1:" + itos(da_port) +
 						 ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
 						 ",embedding=1,server=y,suspend=" + (da_suspend ? "y,timeout=" + itos(da_timeout) : "n"))
@@ -207,6 +207,10 @@ void GDMono::initialize() {
 
 
 	print_verbose("Mono: Initializing module...");
 	print_verbose("Mono: Initializing module...");
 
 
+	char *runtime_build_info = mono_get_runtime_build_info();
+	print_verbose("Mono JIT compiler version " + String(runtime_build_info));
+	mono_free(runtime_build_info);
+
 #ifdef DEBUG_METHODS_ENABLED
 #ifdef DEBUG_METHODS_ENABLED
 	_initialize_and_check_api_hashes();
 	_initialize_and_check_api_hashes();
 #endif
 #endif
@@ -339,18 +343,6 @@ void GDMono::initialize() {
 	ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
 	ERR_EXPLAIN("Mono: Failed to load mscorlib assembly");
 	ERR_FAIL_COND(!_load_corlib_assembly());
 	ERR_FAIL_COND(!_load_corlib_assembly());
 
 
-#ifdef TOOLS_ENABLED
-	// The tools domain must be loaded here, before the scripts domain.
-	// Otherwise domain unload on the scripts domain will hang indefinitely.
-
-	ERR_EXPLAIN("Mono: Failed to load tools domain");
-	ERR_FAIL_COND(_load_tools_domain() != OK);
-
-	// TODO move to editor init callback, and do it lazily when required before editor init (e.g.: bindings generation)
-	ERR_EXPLAIN("Mono: Failed to load Editor Tools assembly");
-	ERR_FAIL_COND(!_load_editor_tools_assembly());
-#endif
-
 	ERR_EXPLAIN("Mono: Failed to load scripts domain");
 	ERR_EXPLAIN("Mono: Failed to load scripts domain");
 	ERR_FAIL_COND(_load_scripts_domain() != OK);
 	ERR_FAIL_COND(_load_scripts_domain() != OK);
 
 
@@ -365,8 +357,15 @@ void GDMono::initialize() {
 	// The following assemblies are not required at initialization
 	// The following assemblies are not required at initialization
 #ifdef MONO_GLUE_ENABLED
 #ifdef MONO_GLUE_ENABLED
 	if (_load_api_assemblies()) {
 	if (_load_api_assemblies()) {
-		// Everything is fine with the api assemblies, load the project assembly
+		// Everything is fine with the api assemblies, load the tools and project assemblies
+
+#if defined(TOOLS_ENABLED)
+		ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
+		ERR_FAIL_COND(!_load_tools_assemblies());
+#endif
+
 		_load_project_assembly();
 		_load_project_assembly();
+
 	} else {
 	} else {
 		if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
 		if ((core_api_assembly && (core_api_assembly_out_of_sync || !GDMonoUtils::mono_cache.godot_api_cache_updated))
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -427,10 +426,6 @@ void GDMono::_register_internal_calls() {
 #ifdef MONO_GLUE_ENABLED
 #ifdef MONO_GLUE_ENABLED
 	GodotSharpBindings::register_generated_icalls();
 	GodotSharpBindings::register_generated_icalls();
 #endif
 #endif
-
-#ifdef TOOLS_ENABLED
-	GodotSharpEditor::register_internal_calls();
-#endif
 }
 }
 
 
 void GDMono::_initialize_and_check_api_hashes() {
 void GDMono::_initialize_and_check_api_hashes() {
@@ -569,6 +564,50 @@ bool GDMono::_load_corlib_assembly() {
 	return success;
 	return success;
 }
 }
 
 
+static bool copy_api_assembly(const String &p_src_dir, const String &p_dst_dir, const String &p_assembly_name, APIAssembly::Type p_api_type) {
+
+	// Create destination directory if needed
+	if (!DirAccess::exists(p_dst_dir)) {
+		DirAccess *da = DirAccess::create_for_path(p_dst_dir);
+		Error err = da->make_dir_recursive(p_dst_dir);
+		memdelete(da);
+
+		if (err != OK) {
+			ERR_PRINTS("Failed to create destination directory for the API assemblies. Error: " + itos(err));
+			return false;
+		}
+	}
+
+	String assembly_file = p_assembly_name + ".dll";
+	String assembly_src = p_src_dir.plus_file(assembly_file);
+	String assembly_dst = p_dst_dir.plus_file(assembly_file);
+
+	if (!FileAccess::exists(assembly_dst) ||
+			FileAccess::get_modified_time(assembly_src) > FileAccess::get_modified_time(assembly_dst) ||
+			GDMono::get_singleton()->metadata_is_api_assembly_invalidated(p_api_type)) {
+		DirAccessRef da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
+
+		String xml_file = p_assembly_name + ".xml";
+		if (da->copy(p_src_dir.plus_file(xml_file), p_dst_dir.plus_file(xml_file)) != OK)
+			WARN_PRINTS("Failed to copy " + xml_file);
+
+		String pdb_file = p_assembly_name + ".pdb";
+		if (da->copy(p_src_dir.plus_file(pdb_file), p_dst_dir.plus_file(pdb_file)) != OK)
+			WARN_PRINTS("Failed to copy " + pdb_file);
+
+		Error err = da->copy(assembly_src, assembly_dst);
+
+		if (err != OK) {
+			ERR_PRINTS("Failed to copy " + assembly_file);
+			return false;
+		}
+
+		GDMono::get_singleton()->metadata_set_api_assembly_invalidated(p_api_type, false);
+	}
+
+	return true;
+}
+
 bool GDMono::_load_core_api_assembly() {
 bool GDMono::_load_core_api_assembly() {
 
 
 	if (core_api_assembly)
 	if (core_api_assembly)
@@ -576,19 +615,31 @@ bool GDMono::_load_core_api_assembly() {
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
 	if (metadata_is_api_assembly_invalidated(APIAssembly::API_CORE)) {
-		print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
-		return false;
+		String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+		String prebuilt_dll_path = prebuilt_api_dir.plus_file(CORE_API_ASSEMBLY_NAME ".dll");
+		String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_CORE);
+
+		if (!FileAccess::exists(prebuilt_dll_path) ||
+				FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
+			print_verbose("Mono: Skipping loading of Core API assembly because it was invalidated");
+			return false;
+		} else {
+			// Copy the prebuilt Api
+			String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
+			if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, CORE_API_ASSEMBLY_NAME, APIAssembly::API_CORE) ||
+					!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
+				print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Core API assembly because it was invalidated");
+				return false;
+			}
+		}
 	}
 	}
 #endif
 #endif
 
 
 	String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
 	String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(CORE_API_ASSEMBLY_NAME ".dll");
 
 
-	if (!FileAccess::exists(assembly_path))
-		return false;
-
-	bool success = load_assembly_from(CORE_API_ASSEMBLY_NAME,
-			assembly_path,
-			&core_api_assembly);
+	bool success = (FileAccess::exists(assembly_path) &&
+						   load_assembly_from(CORE_API_ASSEMBLY_NAME, assembly_path, &core_api_assembly)) ||
+				   load_assembly(CORE_API_ASSEMBLY_NAME, &core_api_assembly);
 
 
 	if (success) {
 	if (success) {
 #ifdef MONO_GLUE_ENABLED
 #ifdef MONO_GLUE_ENABLED
@@ -616,18 +667,29 @@ bool GDMono::_load_editor_api_assembly() {
 		return true;
 		return true;
 
 
 	if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
 	if (metadata_is_api_assembly_invalidated(APIAssembly::API_EDITOR)) {
-		print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
-		return false;
+		String prebuilt_api_dir = GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug");
+		String prebuilt_dll_path = prebuilt_api_dir.plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
+		String invalidated_dll_path = get_invalidated_api_assembly_path(APIAssembly::API_EDITOR);
+
+		if (!FileAccess::exists(prebuilt_dll_path) ||
+				FileAccess::get_modified_time(invalidated_dll_path) == FileAccess::get_modified_time(prebuilt_dll_path)) {
+			print_verbose("Mono: Skipping loading of Editor API assembly because it was invalidated");
+			return false;
+		} else {
+			// Copy the prebuilt editor Api (no need to copy the core api if we got to this point)
+			String res_assemblies_dir = GodotSharpDirs::get_res_assemblies_dir();
+			if (!copy_api_assembly(prebuilt_api_dir, res_assemblies_dir, EDITOR_API_ASSEMBLY_NAME, APIAssembly::API_EDITOR)) {
+				print_verbose("Mono: Failed to copy prebuilt API. Skipping loading of Editor API assembly because it was invalidated");
+				return false;
+			}
+		}
 	}
 	}
 
 
 	String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
 	String assembly_path = GodotSharpDirs::get_res_assemblies_dir().plus_file(EDITOR_API_ASSEMBLY_NAME ".dll");
 
 
-	if (!FileAccess::exists(assembly_path))
-		return false;
-
-	bool success = load_assembly_from(EDITOR_API_ASSEMBLY_NAME,
-			assembly_path,
-			&editor_api_assembly);
+	bool success = (FileAccess::exists(assembly_path) &&
+						   load_assembly_from(EDITOR_API_ASSEMBLY_NAME, assembly_path, &editor_api_assembly)) ||
+				   load_assembly(EDITOR_API_ASSEMBLY_NAME, &editor_api_assembly);
 
 
 	if (success) {
 	if (success) {
 #ifdef MONO_GLUE_ENABLED
 #ifdef MONO_GLUE_ENABLED
@@ -643,14 +705,15 @@ bool GDMono::_load_editor_api_assembly() {
 #endif
 #endif
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
-bool GDMono::_load_editor_tools_assembly() {
+bool GDMono::_load_tools_assemblies() {
 
 
-	if (editor_tools_assembly)
+	if (tools_assembly && tools_project_editor_assembly)
 		return true;
 		return true;
 
 
-	_GDMONO_SCOPE_DOMAIN_(tools_domain)
+	bool success = load_assembly(TOOLS_ASSEMBLY_NAME, &tools_assembly) &&
+				   load_assembly(TOOLS_PROJECT_EDITOR_ASSEMBLY_NAME, &tools_project_editor_assembly);
 
 
-	return load_assembly(EDITOR_TOOLS_ASSEMBLY_NAME, &editor_tools_assembly);
+	return success;
 }
 }
 #endif
 #endif
 
 
@@ -781,6 +844,14 @@ bool GDMono::metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type)
 
 
 	return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
 	return metadata->get_value(section, "invalidated", false) && modified_time <= stored_modified_time;
 }
 }
+
+String GDMono::get_invalidated_api_assembly_path(APIAssembly::Type p_api_type) {
+
+	return GodotSharpDirs::get_res_assemblies_dir()
+			.plus_file(p_api_type == APIAssembly::API_CORE ?
+							   CORE_API_ASSEMBLY_NAME ".dll" :
+							   EDITOR_API_ASSEMBLY_NAME ".dll");
+}
 #endif
 #endif
 
 
 Error GDMono::_load_scripts_domain() {
 Error GDMono::_load_scripts_domain() {
@@ -826,6 +897,8 @@ Error GDMono::_unload_scripts_domain() {
 	project_assembly = NULL;
 	project_assembly = NULL;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	editor_api_assembly = NULL;
 	editor_api_assembly = NULL;
+	tools_assembly = NULL;
+	tools_project_editor_assembly = NULL;
 #endif
 #endif
 
 
 	core_api_assembly_out_of_sync = false;
 	core_api_assembly_out_of_sync = false;
@@ -848,22 +921,6 @@ Error GDMono::_unload_scripts_domain() {
 	return OK;
 	return OK;
 }
 }
 
 
-#ifdef TOOLS_ENABLED
-Error GDMono::_load_tools_domain() {
-
-	ERR_FAIL_COND_V(tools_domain != NULL, ERR_BUG);
-
-	print_verbose("Mono: Loading tools domain...");
-
-	tools_domain = GDMonoUtils::create_domain("GodotEngine.ToolsDomain");
-
-	ERR_EXPLAIN("Mono: Could not create tools app domain");
-	ERR_FAIL_NULL_V(tools_domain, ERR_CANT_CREATE);
-
-	return OK;
-}
-#endif
-
 #ifdef GD_MONO_HOT_RELOAD
 #ifdef GD_MONO_HOT_RELOAD
 Error GDMono::reload_scripts_domain() {
 Error GDMono::reload_scripts_domain() {
 
 
@@ -925,6 +982,11 @@ Error GDMono::reload_scripts_domain() {
 		}
 		}
 	}
 	}
 
 
+#ifdef TOOLS_ENABLED
+	ERR_EXPLAIN("Mono: Failed to load GodotTools assemblies");
+	ERR_FAIL_COND_V(!_load_tools_assemblies(), ERR_CANT_OPEN);
+#endif
+
 	if (!_load_project_assembly()) {
 	if (!_load_project_assembly()) {
 		return ERR_CANT_OPEN;
 		return ERR_CANT_OPEN;
 	}
 	}
@@ -939,7 +1001,7 @@ Error GDMono::reload_scripts_domain() {
 Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 
 
 	CRASH_COND(p_domain == NULL);
 	CRASH_COND(p_domain == NULL);
-	CRASH_COND(p_domain == SCRIPTS_DOMAIN); // Should use _unload_scripts_domain() instead
+	CRASH_COND(p_domain == GDMono::get_singleton()->get_scripts_domain()); // Should use _unload_scripts_domain() instead
 
 
 	String domain_name = mono_domain_get_friendly_name(p_domain);
 	String domain_name = mono_domain_get_friendly_name(p_domain);
 
 
@@ -956,18 +1018,12 @@ Error GDMono::finalize_and_unload_domain(MonoDomain *p_domain) {
 
 
 	_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
 	_domain_assemblies_cleanup(mono_domain_get_id(p_domain));
 
 
-#ifdef TOOLS_ENABLED
-	if (p_domain == tools_domain) {
-		editor_tools_assembly = NULL;
-	}
-#endif
-
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	mono_domain_try_unload(p_domain, (MonoObject **)&exc);
 	mono_domain_try_unload(p_domain, (MonoObject **)&exc);
 
 
 	if (exc) {
 	if (exc) {
 		ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
 		ERR_PRINTS("Exception thrown when unloading domain `" + domain_name + "`");
-		GDMonoUtils::debug_unhandled_exception(exc);
+		GDMonoUtils::debug_print_unhandled_exception(exc);
 		return FAILED;
 		return FAILED;
 	}
 	}
 
 
@@ -998,6 +1054,22 @@ GDMonoClass *GDMono::get_class(MonoClass *p_raw_class) {
 	return NULL;
 	return NULL;
 }
 }
 
 
+GDMonoClass *GDMono::get_class(const StringName &p_namespace, const StringName &p_name) {
+
+	uint32_t domain_id = mono_domain_get_id(mono_domain_get());
+	HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[domain_id];
+
+	const String *k = NULL;
+	while ((k = domain_assemblies.next(k))) {
+		GDMonoAssembly *assembly = domain_assemblies.get(*k);
+		GDMonoClass *klass = assembly->get_class(p_namespace, p_name);
+		if (klass)
+			return klass;
+	}
+
+	return NULL;
+}
+
 void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
 void GDMono::_domain_assemblies_cleanup(uint32_t p_domain_id) {
 
 
 	HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
 	HashMap<String, GDMonoAssembly *> &domain_assemblies = assemblies[p_domain_id];
@@ -1038,9 +1110,6 @@ GDMono::GDMono() {
 
 
 	root_domain = NULL;
 	root_domain = NULL;
 	scripts_domain = NULL;
 	scripts_domain = NULL;
-#ifdef TOOLS_ENABLED
-	tools_domain = NULL;
-#endif
 
 
 	core_api_assembly_out_of_sync = false;
 	core_api_assembly_out_of_sync = false;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -1052,7 +1121,8 @@ GDMono::GDMono() {
 	project_assembly = NULL;
 	project_assembly = NULL;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	editor_api_assembly = NULL;
 	editor_api_assembly = NULL;
-	editor_tools_assembly = NULL;
+	tools_assembly = NULL;
+	tools_project_editor_assembly = NULL;
 #endif
 #endif
 
 
 	api_core_hash = 0;
 	api_core_hash = 0;
@@ -1064,16 +1134,6 @@ GDMono::GDMono() {
 GDMono::~GDMono() {
 GDMono::~GDMono() {
 
 
 	if (is_runtime_initialized()) {
 	if (is_runtime_initialized()) {
-
-#ifdef TOOLS_ENABLED
-		if (tools_domain) {
-			Error err = finalize_and_unload_domain(tools_domain);
-			if (err != OK) {
-				ERR_PRINT("Mono: Failed to unload tools domain");
-			}
-		}
-#endif
-
 		if (scripts_domain) {
 		if (scripts_domain) {
 			Error err = _unload_scripts_domain();
 			Error err = _unload_scripts_domain();
 			if (err != OK) {
 			if (err != OK) {
@@ -1128,14 +1188,14 @@ int32_t _GodotSharp::get_domain_id() {
 
 
 int32_t _GodotSharp::get_scripts_domain_id() {
 int32_t _GodotSharp::get_scripts_domain_id() {
 
 
-	MonoDomain *domain = SCRIPTS_DOMAIN;
+	MonoDomain *domain = GDMono::get_singleton()->get_scripts_domain();
 	CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method
 	CRASH_COND(!domain); // User must check if scripts domain is loaded before calling this method
 	return mono_domain_get_id(domain);
 	return mono_domain_get_id(domain);
 }
 }
 
 
 bool _GodotSharp::is_scripts_domain_loaded() {
 bool _GodotSharp::is_scripts_domain_loaded() {
 
 
-	return GDMono::get_singleton()->is_runtime_initialized() && SCRIPTS_DOMAIN != NULL;
+	return GDMono::get_singleton()->is_runtime_initialized() && GDMono::get_singleton()->get_scripts_domain() != NULL;
 }
 }
 
 
 bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
 bool _GodotSharp::_is_domain_finalizing_for_unload(int32_t p_domain_id) {
@@ -1157,7 +1217,7 @@ bool _GodotSharp::is_domain_finalizing_for_unload(MonoDomain *p_domain) {
 
 
 	if (!p_domain)
 	if (!p_domain)
 		return true;
 		return true;
-	if (p_domain == SCRIPTS_DOMAIN && GDMono::get_singleton()->is_finalizing_scripts_domain())
+	if (p_domain == GDMono::get_singleton()->get_scripts_domain() && GDMono::get_singleton()->is_finalizing_scripts_domain())
 		return true;
 		return true;
 	return mono_domain_is_unloading(p_domain);
 	return mono_domain_is_unloading(p_domain);
 }
 }
@@ -1172,6 +1232,12 @@ bool _GodotSharp::is_runtime_initialized() {
 	return GDMono::get_singleton()->is_runtime_initialized();
 	return GDMono::get_singleton()->is_runtime_initialized();
 }
 }
 
 
+void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
+#ifdef GD_MONO_HOT_RELOAD
+	CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
+#endif
+}
+
 void _GodotSharp::_bind_methods() {
 void _GodotSharp::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
 	ClassDB::bind_method(D_METHOD("attach_thread"), &_GodotSharp::attach_thread);
@@ -1184,6 +1250,7 @@ void _GodotSharp::_bind_methods() {
 
 
 	ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
 	ClassDB::bind_method(D_METHOD("is_runtime_shutting_down"), &_GodotSharp::is_runtime_shutting_down);
 	ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
 	ClassDB::bind_method(D_METHOD("is_runtime_initialized"), &_GodotSharp::is_runtime_initialized);
+	ClassDB::bind_method(D_METHOD("_reload_assemblies"), &_GodotSharp::_reload_assemblies);
 }
 }
 
 
 _GodotSharp::_GodotSharp() {
 _GodotSharp::_GodotSharp() {

+ 9 - 18
modules/mono/mono_gd/gd_mono.h

@@ -78,11 +78,6 @@ struct Version {
 String to_string(Type p_type);
 String to_string(Type p_type);
 } // namespace APIAssembly
 } // namespace APIAssembly
 
 
-#define SCRIPTS_DOMAIN GDMono::get_singleton()->get_scripts_domain()
-#ifdef TOOLS_ENABLED
-#define TOOLS_DOMAIN GDMono::get_singleton()->get_tools_domain()
-#endif
-
 class GDMono {
 class GDMono {
 
 
 	bool runtime_initialized;
 	bool runtime_initialized;
@@ -90,9 +85,6 @@ class GDMono {
 
 
 	MonoDomain *root_domain;
 	MonoDomain *root_domain;
 	MonoDomain *scripts_domain;
 	MonoDomain *scripts_domain;
-#ifdef TOOLS_ENABLED
-	MonoDomain *tools_domain;
-#endif
 
 
 	bool core_api_assembly_out_of_sync;
 	bool core_api_assembly_out_of_sync;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
@@ -104,7 +96,8 @@ class GDMono {
 	GDMonoAssembly *project_assembly;
 	GDMonoAssembly *project_assembly;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	GDMonoAssembly *editor_api_assembly;
 	GDMonoAssembly *editor_api_assembly;
-	GDMonoAssembly *editor_tools_assembly;
+	GDMonoAssembly *tools_assembly;
+	GDMonoAssembly *tools_project_editor_assembly;
 #endif
 #endif
 
 
 	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
 	HashMap<uint32_t, HashMap<String, GDMonoAssembly *> > assemblies;
@@ -115,7 +108,7 @@ class GDMono {
 	bool _load_core_api_assembly();
 	bool _load_core_api_assembly();
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	bool _load_editor_api_assembly();
 	bool _load_editor_api_assembly();
-	bool _load_editor_tools_assembly();
+	bool _load_tools_assemblies();
 #endif
 #endif
 	bool _load_project_assembly();
 	bool _load_project_assembly();
 
 
@@ -132,10 +125,6 @@ class GDMono {
 	Error _load_scripts_domain();
 	Error _load_scripts_domain();
 	Error _unload_scripts_domain();
 	Error _unload_scripts_domain();
 
 
-#ifdef TOOLS_ENABLED
-	Error _load_tools_domain();
-#endif
-
 	uint64_t api_core_hash;
 	uint64_t api_core_hash;
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	uint64_t api_editor_hash;
 	uint64_t api_editor_hash;
@@ -170,6 +159,7 @@ public:
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
 	void metadata_set_api_assembly_invalidated(APIAssembly::Type p_api_type, bool p_invalidated);
 	bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
 	bool metadata_is_api_assembly_invalidated(APIAssembly::Type p_api_type);
+	String get_invalidated_api_assembly_path(APIAssembly::Type p_api_type);
 #endif
 #endif
 
 
 	static GDMono *get_singleton() { return singleton; }
 	static GDMono *get_singleton() { return singleton; }
@@ -185,16 +175,14 @@ public:
 	_FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
 	_FORCE_INLINE_ bool is_finalizing_scripts_domain() { return finalizing_scripts_domain; }
 
 
 	_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
 	_FORCE_INLINE_ MonoDomain *get_scripts_domain() { return scripts_domain; }
-#ifdef TOOLS_ENABLED
-	_FORCE_INLINE_ MonoDomain *get_tools_domain() { return tools_domain; }
-#endif
 
 
 	_FORCE_INLINE_ GDMonoAssembly *get_corlib_assembly() const { return corlib_assembly; }
 	_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; }
 	_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
 	_FORCE_INLINE_ GDMonoAssembly *get_project_assembly() const { return project_assembly; }
 #ifdef TOOLS_ENABLED
 #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; }
-	_FORCE_INLINE_ GDMonoAssembly *get_editor_tools_assembly() const { return editor_tools_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
 #endif
 
 
 #if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
 #if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
@@ -202,6 +190,7 @@ public:
 #endif
 #endif
 
 
 	GDMonoClass *get_class(MonoClass *p_raw_class);
 	GDMonoClass *get_class(MonoClass *p_raw_class);
+	GDMonoClass *get_class(const StringName &p_namespace, const StringName &p_name);
 
 
 #ifdef GD_MONO_HOT_RELOAD
 #ifdef GD_MONO_HOT_RELOAD
 	Error reload_scripts_domain();
 	Error reload_scripts_domain();
@@ -276,6 +265,8 @@ class _GodotSharp : public Object {
 	List<NodePath *> np_delete_queue;
 	List<NodePath *> np_delete_queue;
 	List<RID *> rid_delete_queue;
 	List<RID *> rid_delete_queue;
 
 
+	void _reload_assemblies(bool p_soft_reload);
+
 protected:
 protected:
 	static _GodotSharp *singleton;
 	static _GodotSharp *singleton;
 	static void _bind_methods();
 	static void _bind_methods();

+ 22 - 0
modules/mono/mono_gd/gd_mono_assembly.cpp

@@ -46,6 +46,20 @@ bool GDMonoAssembly::in_preload = false;
 
 
 Vector<String> GDMonoAssembly::search_dirs;
 Vector<String> GDMonoAssembly::search_dirs;
 
 
+static String _get_expected_api_build_config() {
+#ifdef TOOLS_ENABLED
+	return "Debug";
+#else
+
+#ifdef DEBUG_ENABLED
+	return "Debug";
+#else
+	return "Release";
+#endif
+
+#endif
+}
+
 void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
 void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const String &p_custom_config, const String &p_custom_bcl_dir) {
 
 
 	String framework_dir;
 	String framework_dir;
@@ -67,11 +81,19 @@ void GDMonoAssembly::fill_search_dirs(Vector<String> &r_search_dirs, const Strin
 		r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
 		r_search_dirs.push_back(GodotSharpDirs::get_res_temp_assemblies_dir());
 	}
 	}
 
 
+	String api_config = p_custom_config.empty() ? _get_expected_api_build_config() :
+												  (p_custom_config == "Release" ? "Release" : "Debug");
+	r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_base_dir().plus_file(api_config));
+
 	r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
 	r_search_dirs.push_back(GodotSharpDirs::get_res_assemblies_dir());
 	r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
 	r_search_dirs.push_back(OS::get_singleton()->get_resource_dir());
 	r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
 	r_search_dirs.push_back(OS::get_singleton()->get_executable_path().get_base_dir());
+
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
 	r_search_dirs.push_back(GodotSharpDirs::get_data_editor_tools_dir());
+
+	// For GodotTools to find the api assemblies
+	r_search_dirs.push_back(GodotSharpDirs::get_data_editor_prebuilt_api_dir().plus_file("Debug"));
 #endif
 #endif
 }
 }
 
 

+ 7 - 10
modules/mono/mono_gd/gd_mono_class.cpp

@@ -41,7 +41,7 @@ String GDMonoClass::get_full_name(MonoClass *p_mono_class) {
 
 
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
 	MonoString *str = GDMonoUtils::object_to_string((MonoObject *)type_obj, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 
 
 	return GDMonoMarshal::mono_string_to_godot(str);
 	return GDMonoMarshal::mono_string_to_godot(str);
 }
 }
@@ -74,16 +74,13 @@ bool GDMonoClass::is_assignable_from(GDMonoClass *p_from) const {
 }
 }
 
 
 GDMonoClass *GDMonoClass::get_parent_class() {
 GDMonoClass *GDMonoClass::get_parent_class() {
+	MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
+	return parent_mono_class ? GDMono::get_singleton()->get_class(parent_mono_class) : NULL;
+}
 
 
-	if (assembly) {
-		MonoClass *parent_mono_class = mono_class_get_parent(mono_class);
-
-		if (parent_mono_class) {
-			return GDMono::get_singleton()->get_class(parent_mono_class);
-		}
-	}
-
-	return NULL;
+GDMonoClass *GDMonoClass::get_nesting_class() {
+	MonoClass *nesting_type = mono_class_get_nesting_type(mono_class);
+	return nesting_type ? GDMono::get_singleton()->get_class(nesting_type) : NULL;
 }
 }
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED

+ 1 - 0
modules/mono/mono_gd/gd_mono_class.h

@@ -121,6 +121,7 @@ public:
 	_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
 	_FORCE_INLINE_ const GDMonoAssembly *get_assembly() const { return assembly; }
 
 
 	GDMonoClass *get_parent_class();
 	GDMonoClass *get_parent_class();
+	GDMonoClass *get_nesting_class();
 
 
 #ifdef TOOLS_ENABLED
 #ifdef TOOLS_ENABLED
 	Vector<MonoClassField *> get_enum_fields();
 	Vector<MonoClassField *> get_enum_fields();

+ 20 - 8
modules/mono/mono_gd/gd_mono_field.cpp

@@ -315,7 +315,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 
 
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
 
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
 
 
 			MonoReflectionType *key_reftype, *value_reftype;
 			MonoReflectionType *key_reftype, *value_reftype;
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
@@ -340,9 +340,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 			}
 			}
 
 
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
-				mono_field_set_value(p_object, mono_field, managed);
-				break;
+				if (GDMonoUtils::tools_godot_api_check()) {
+					MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+					mono_field_set_value(p_object, mono_field, managed);
+					break;
+				} else {
+					MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
+					mono_field_set_value(p_object, mono_field, managed);
+					break;
+				}
 			}
 			}
 
 
 			ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
 			ERR_EXPLAIN(String() + "Attempted to set the value of a field of unmarshallable type: " + type_class->get_name());
@@ -450,7 +456,7 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 		} break;
 		} break;
 
 
 		case MONO_TYPE_GENERICINST: {
 		case MONO_TYPE_GENERICINST: {
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
 				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Dictionary(), type.type_class);
@@ -489,9 +495,15 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 			}
 			}
 
 
 			if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 			if (type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
-				mono_field_set_value(p_object, mono_field, managed);
-				break;
+				if (GDMonoUtils::tools_godot_api_check()) {
+					MonoObject *managed = GDMonoUtils::create_managed_from(p_value.operator Array(), CACHED_CLASS(Array));
+					mono_field_set_value(p_object, mono_field, managed);
+					break;
+				} else {
+					MonoObject *managed = (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_value.operator Array());
+					mono_field_set_value(p_object, mono_field, managed);
+					break;
+				}
 			}
 			}
 		} break;
 		} break;
 
 

+ 3 - 4
modules/mono/mono_gd/gd_mono_internals.cpp

@@ -74,15 +74,14 @@ void tie_managed_to_unmanaged(MonoObject *managed, Object *unmanaged) {
 		script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
 		script_binding.type_name = NATIVE_GDMONOCLASS_NAME(klass);
 		script_binding.wrapper_class = klass;
 		script_binding.wrapper_class = klass;
 		script_binding.gchandle = MonoGCHandle::create_strong(managed);
 		script_binding.gchandle = MonoGCHandle::create_strong(managed);
+		script_binding.owner = unmanaged;
 
 
-		Reference *kref = Object::cast_to<Reference>(unmanaged);
-		if (kref) {
+		if (ref) {
 			// Unsafe refcount increment. The managed instance also counts as a reference.
 			// Unsafe refcount increment. The managed instance also counts as a reference.
 			// This way if the unmanaged world has no references to our owner
 			// This way if the unmanaged world has no references to our owner
 			// but the managed instance is alive, the refcount will be 1 instead of 0.
 			// but the managed instance is alive, the refcount will be 1 instead of 0.
 			// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
 			// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
-
-			kref->reference();
+			ref->reference();
 		}
 		}
 
 
 		// The object was just created, no script instance binding should have been attached
 		// The object was just created, no script instance binding should have been attached

+ 22 - 14
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -159,7 +159,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 
 
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
 
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
 				return Variant::DICTIONARY;
 				return Variant::DICTIONARY;
@@ -179,7 +179,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 		} break;
 		} break;
 
 
 		case MONO_TYPE_GENERICINST: {
 		case MONO_TYPE_GENERICINST: {
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 				return Variant::DICTIONARY;
 				return Variant::DICTIONARY;
@@ -217,7 +217,7 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
 bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_elem_type) {
 	switch (p_array_type.type_encoding) {
 	switch (p_array_type.type_encoding) {
 		case MONO_TYPE_GENERICINST: {
 		case MONO_TYPE_GENERICINST: {
-			MonoReflectionType *array_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_array_type.type_class->get_mono_type());
+			MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_array(array_reftype)) {
 				MonoReflectionType *elem_reftype;
 				MonoReflectionType *elem_reftype;
@@ -244,7 +244,7 @@ bool try_get_array_element_type(const ManagedType &p_array_type, ManagedType &r_
 bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
 bool try_get_dictionary_key_value_types(const ManagedType &p_dictionary_type, ManagedType &r_key_type, ManagedType &r_value_type) {
 	switch (p_dictionary_type.type_encoding) {
 	switch (p_dictionary_type.type_encoding) {
 		case MONO_TYPE_GENERICINST: {
 		case MONO_TYPE_GENERICINST: {
-			MonoReflectionType *dict_reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_dictionary_type.type_class->get_mono_type());
+			MonoReflectionType *dict_reftype = mono_type_get_object(mono_domain_get(), p_dictionary_type.type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(dict_reftype)) {
 				MonoReflectionType *key_reftype;
 				MonoReflectionType *key_reftype;
@@ -539,7 +539,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 
 
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
 
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
 
 
 			MonoReflectionType *key_reftype, *value_reftype;
 			MonoReflectionType *key_reftype, *value_reftype;
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype, &key_reftype, &value_reftype)) {
@@ -558,7 +558,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 			}
 			}
 
 
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 			if (type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-				return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+				if (GDMonoUtils::tools_godot_api_check()) {
+					return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+				} else {
+					return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
+				}
 			}
 			}
 		} break;
 		} break;
 		case MONO_TYPE_OBJECT: {
 		case MONO_TYPE_OBJECT: {
@@ -652,7 +656,7 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 			}
 			}
 			break;
 			break;
 			case MONO_TYPE_GENERICINST: {
 			case MONO_TYPE_GENERICINST: {
-				MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, p_type.type_class->get_mono_type());
+				MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), p_type.type_class->get_mono_type());
 
 
 				if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 				if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
 					return GDMonoUtils::create_managed_from(p_var->operator Dictionary(), p_type.type_class);
@@ -681,7 +685,11 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 				}
 				}
 
 
 				if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
 				if (p_type.type_class->implements_interface(CACHED_CLASS(System_Collections_IEnumerable))) {
-					return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+					if (GDMonoUtils::tools_godot_api_check()) {
+						return GDMonoUtils::create_managed_from(p_var->operator Array(), CACHED_CLASS(Array));
+					} else {
+						return (MonoObject *)GDMonoMarshal::Array_to_mono_array(p_var->operator Array());
+					}
 				}
 				}
 			} break;
 			} break;
 		} break;
 		} break;
@@ -831,20 +839,20 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
 			if (CACHED_CLASS(Array) == type_class) {
 			if (CACHED_CLASS(Array) == type_class) {
 				MonoException *exc = NULL;
 				MonoException *exc = NULL;
 				Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
 				Array *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Array, GetPtr), p_obj, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
+				UNHANDLED_EXCEPTION(exc);
 				return ptr ? Variant(*ptr) : Variant();
 				return ptr ? Variant(*ptr) : Variant();
 			}
 			}
 
 
 			if (CACHED_CLASS(Dictionary) == type_class) {
 			if (CACHED_CLASS(Dictionary) == type_class) {
 				MonoException *exc = NULL;
 				MonoException *exc = NULL;
 				Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
 				Dictionary *ptr = invoke_method_thunk(CACHED_METHOD_THUNK(Dictionary, GetPtr), p_obj, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
+				UNHANDLED_EXCEPTION(exc);
 				return ptr ? Variant(*ptr) : Variant();
 				return ptr ? Variant(*ptr) : Variant();
 			}
 			}
 
 
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 			// The order in which we check the following interfaces is very important (dictionaries and generics first)
 
 
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
 			if (GDMonoUtils::Marshal::generic_idictionary_is_assignable_from(reftype)) {
 				return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
 				return GDMonoUtils::Marshal::generic_idictionary_to_dictionary(p_obj);
@@ -864,19 +872,19 @@ Variant mono_object_to_variant(MonoObject *p_obj) {
 		} break;
 		} break;
 
 
 		case MONO_TYPE_GENERICINST: {
 		case MONO_TYPE_GENERICINST: {
-			MonoReflectionType *reftype = mono_type_get_object(SCRIPTS_DOMAIN, type.type_class->get_mono_type());
+			MonoReflectionType *reftype = mono_type_get_object(mono_domain_get(), type.type_class->get_mono_type());
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_dictionary(reftype)) {
 				MonoException *exc = NULL;
 				MonoException *exc = NULL;
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
+				UNHANDLED_EXCEPTION(exc);
 				return *unbox<Dictionary *>(ret);
 				return *unbox<Dictionary *>(ret);
 			}
 			}
 
 
 			if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
 			if (GDMonoUtils::Marshal::type_is_generic_array(reftype)) {
 				MonoException *exc = NULL;
 				MonoException *exc = NULL;
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
 				MonoObject *ret = type.type_class->get_method("GetPtr")->invoke(p_obj, &exc);
-				UNLIKELY_UNHANDLED_EXCEPTION(exc);
+				UNHANDLED_EXCEPTION(exc);
 				return *unbox<Array *>(ret);
 				return *unbox<Array *>(ret);
 			}
 			}
 
 

+ 67 - 47
modules/mono/mono_gd/gd_mono_utils.cpp

@@ -125,6 +125,7 @@ void MonoCache::clear_godot_api_cache() {
 	class_Array = NULL;
 	class_Array = NULL;
 	class_Dictionary = NULL;
 	class_Dictionary = NULL;
 	class_MarshalUtils = NULL;
 	class_MarshalUtils = NULL;
+	class_ISerializationListener = NULL;
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	class_DebuggingUtils = NULL;
 	class_DebuggingUtils = NULL;
@@ -242,6 +243,7 @@ void update_godot_api_cache() {
 	CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
 	CACHE_CLASS_AND_CHECK(Array, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Array));
 	CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
 	CACHE_CLASS_AND_CHECK(Dictionary, GODOT_API_NS_CLAS(BINDINGS_NAMESPACE_COLLECTIONS, Dictionary));
 	CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
 	CACHE_CLASS_AND_CHECK(MarshalUtils, GODOT_API_CLASS(MarshalUtils));
+	CACHE_CLASS_AND_CHECK(ISerializationListener, GODOT_API_CLASS(ISerializationListener));
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
 	CACHE_CLASS_AND_CHECK(DebuggingUtils, GODOT_API_CLASS(DebuggingUtils));
@@ -302,7 +304,7 @@ void update_godot_api_cache() {
 #endif
 #endif
 
 
 	// TODO Move to CSharpLanguage::init() and do handle disposal
 	// TODO Move to CSharpLanguage::init() and do handle disposal
-	MonoObject *task_scheduler = mono_object_new(SCRIPTS_DOMAIN, GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
+	MonoObject *task_scheduler = mono_object_new(mono_domain_get(), GODOT_API_CLASS(GodotTaskScheduler)->get_mono_ptr());
 	GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
 	GDMonoUtils::runtime_object_init(task_scheduler, GODOT_API_CLASS(GodotTaskScheduler));
 	mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
 	mono_cache.task_scheduler_handle = MonoGCHandle::create_strong(task_scheduler);
 
 
@@ -371,7 +373,6 @@ MonoObject *unmanaged_get_managed(Object *unmanaged) {
 		// This way if the unmanaged world has no references to our owner
 		// This way if the unmanaged world has no references to our owner
 		// but the managed instance is alive, the refcount will be 1 instead of 0.
 		// but the managed instance is alive, the refcount will be 1 instead of 0.
 		// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
 		// See: godot_icall_Reference_Dtor(MonoObject *p_obj, Object *p_ptr)
-
 		ref->reference();
 		ref->reference();
 	}
 	}
 
 
@@ -384,7 +385,7 @@ void set_main_thread(MonoThread *p_thread) {
 
 
 void attach_current_thread() {
 void attach_current_thread() {
 	ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
 	ERR_FAIL_COND(!GDMono::get_singleton()->is_runtime_initialized());
-	MonoThread *mono_thread = mono_thread_attach(SCRIPTS_DOMAIN);
+	MonoThread *mono_thread = mono_thread_attach(mono_domain_get());
 	ERR_FAIL_NULL(mono_thread);
 	ERR_FAIL_NULL(mono_thread);
 }
 }
 
 
@@ -448,17 +449,12 @@ GDMonoClass *get_class_native_base(GDMonoClass *p_class) {
 }
 }
 
 
 MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
 MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringName &p_native, Object *p_object) {
-	String object_type = p_object->get_class_name();
-
-	if (object_type[0] == '_')
-		object_type = object_type.substr(1, object_type.length());
-
-	if (!ClassDB::is_parent_class(object_type, p_native)) {
+	if (!ClassDB::is_parent_class(p_object->get_class_name(), p_native)) {
 		ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'");
 		ERR_EXPLAIN("Type inherits from native type '" + p_native + "', so it can't be instanced in object of type: '" + p_object->get_class() + "'");
 		ERR_FAIL_V(NULL);
 		ERR_FAIL_V(NULL);
 	}
 	}
 
 
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
 	ERR_FAIL_NULL_V(mono_object, NULL);
 	ERR_FAIL_NULL_V(mono_object, NULL);
 
 
 	CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
 	CACHED_FIELD(GodotObject, ptr)->set_value_raw(mono_object, p_object);
@@ -470,7 +466,7 @@ MonoObject *create_managed_for_godot_object(GDMonoClass *p_class, const StringNa
 }
 }
 
 
 MonoObject *create_managed_from(const NodePath &p_from) {
 MonoObject *create_managed_from(const NodePath &p_from) {
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(NodePath));
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(NodePath));
 	ERR_FAIL_NULL_V(mono_object, NULL);
 	ERR_FAIL_NULL_V(mono_object, NULL);
 
 
 	// Construct
 	// Construct
@@ -482,7 +478,7 @@ MonoObject *create_managed_from(const NodePath &p_from) {
 }
 }
 
 
 MonoObject *create_managed_from(const RID &p_from) {
 MonoObject *create_managed_from(const RID &p_from) {
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(RID));
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), CACHED_CLASS_RAW(RID));
 	ERR_FAIL_NULL_V(mono_object, NULL);
 	ERR_FAIL_NULL_V(mono_object, NULL);
 
 
 	// Construct
 	// Construct
@@ -494,7 +490,7 @@ MonoObject *create_managed_from(const RID &p_from) {
 }
 }
 
 
 MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
 MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
 	ERR_FAIL_NULL_V(mono_object, NULL);
 	ERR_FAIL_NULL_V(mono_object, NULL);
 
 
 	// Search constructor that takes a pointer as parameter
 	// Search constructor that takes a pointer as parameter
@@ -518,13 +514,13 @@ MonoObject *create_managed_from(const Array &p_from, GDMonoClass *p_class) {
 
 
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
 	GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 
 
 	return mono_object;
 	return mono_object;
 }
 }
 
 
 MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
 MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class) {
-	MonoObject *mono_object = mono_object_new(SCRIPTS_DOMAIN, p_class->get_mono_ptr());
+	MonoObject *mono_object = mono_object_new(mono_domain_get(), p_class->get_mono_ptr());
 	ERR_FAIL_NULL_V(mono_object, NULL);
 	ERR_FAIL_NULL_V(mono_object, NULL);
 
 
 	// Search constructor that takes a pointer as parameter
 	// Search constructor that takes a pointer as parameter
@@ -548,7 +544,7 @@ MonoObject *create_managed_from(const Dictionary &p_from, GDMonoClass *p_class)
 
 
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
 	GDMonoUtils::runtime_invoke(m, mono_object, args, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 
 
 	return mono_object;
 	return mono_object;
 }
 }
@@ -667,7 +663,10 @@ void print_unhandled_exception(MonoException *p_exc) {
 }
 }
 
 
 void set_pending_exception(MonoException *p_exc) {
 void set_pending_exception(MonoException *p_exc) {
-#ifdef HAS_PENDING_EXCEPTIONS
+#ifdef NO_PENDING_EXCEPTIONS
+	debug_unhandled_exception(p_exc);
+	GD_UNREACHABLE();
+#else
 	if (get_runtime_invoke_count() == 0) {
 	if (get_runtime_invoke_count() == 0) {
 		debug_unhandled_exception(p_exc);
 		debug_unhandled_exception(p_exc);
 		GD_UNREACHABLE();
 		GD_UNREACHABLE();
@@ -677,9 +676,6 @@ void set_pending_exception(MonoException *p_exc) {
 		ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
 		ERR_PRINTS("Exception thrown from managed code, but it could not be set as pending:");
 		GDMonoUtils::debug_print_unhandled_exception(p_exc);
 		GDMonoUtils::debug_print_unhandled_exception(p_exc);
 	}
 	}
-#else
-	debug_unhandled_exception(p_exc);
-	GD_UNREACHABLE();
 #endif
 #endif
 }
 }
 
 
@@ -755,113 +751,137 @@ void dispose(MonoObject *p_mono_object, MonoException **r_exc) {
 
 
 namespace Marshal {
 namespace Marshal {
 
 
-MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype) {
+#ifdef MONO_GLUE_ENABLED
+#ifdef TOOLS_ENABLED
+#define NO_GLUE_RET(m_ret)                                     \
+	{                                                          \
+		if (!mono_cache.godot_api_cache_updated) return m_ret; \
+	}
+#else
+#define NO_GLUE_RET(m_ret) \
+	{}
+#endif
+#else
+#define NO_GLUE_RET(m_ret) \
+	{ return m_ret; }
+#endif
+
+bool type_is_generic_array(MonoReflectionType *p_reftype) {
+	NO_GLUE_RET(false);
 	TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
 	TypeIsGenericArray thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericArray);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
-MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype) {
+bool type_is_generic_dictionary(MonoReflectionType *p_reftype) {
+	NO_GLUE_RET(false);
 	TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
 	TypeIsGenericDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, TypeIsGenericDictionary);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
 void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
 void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype) {
 	ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
 	ArrayGetElementType thunk = CACHED_METHOD_THUNK(MarshalUtils, ArrayGetElementType);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
 	invoke_method_thunk(thunk, p_array_reftype, r_elem_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 }
 }
 
 
 void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
 void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
 	DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
 	DictionaryGetKeyValueTypes thunk = CACHED_METHOD_THUNK(MarshalUtils, DictionaryGetKeyValueTypes);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
 	invoke_method_thunk(thunk, p_dict_reftype, r_key_reftype, r_value_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 }
 }
 
 
-MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
+bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype) {
+	NO_GLUE_RET(false);
 	GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
 	GenericIEnumerableIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
-MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
+bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype) {
+	NO_GLUE_RET(false);
 	GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
 	GenericIDictionaryIsAssignableFromType thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
-MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype) {
+	NO_GLUE_RET(false);
 	GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
 	GenericIEnumerableIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIEnumerableIsAssignableFromType_with_info);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_elem_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
-MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype) {
+	NO_GLUE_RET(false);
 	GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
 	GenericIDictionaryIsAssignableFromType_with_info thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryIsAssignableFromType_with_info);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
 	MonoBoolean res = invoke_method_thunk(thunk, p_reftype, r_key_reftype, r_value_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
-	return res;
+	UNHANDLED_EXCEPTION(exc);
+	return (bool)res;
 }
 }
 
 
 Array enumerable_to_array(MonoObject *p_enumerable) {
 Array enumerable_to_array(MonoObject *p_enumerable) {
+	NO_GLUE_RET(Array());
 	Array result;
 	Array result;
 	EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
 	EnumerableToArray thunk = CACHED_METHOD_THUNK(MarshalUtils, EnumerableToArray);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	invoke_method_thunk(thunk, p_enumerable, &result, &exc);
 	invoke_method_thunk(thunk, p_enumerable, &result, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 	return result;
 	return result;
 }
 }
 
 
 Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
 Dictionary idictionary_to_dictionary(MonoObject *p_idictionary) {
+	NO_GLUE_RET(Dictionary());
 	Dictionary result;
 	Dictionary result;
 	IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
 	IDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, IDictionaryToDictionary);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	invoke_method_thunk(thunk, p_idictionary, &result, &exc);
 	invoke_method_thunk(thunk, p_idictionary, &result, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 	return result;
 	return result;
 }
 }
 
 
 Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
 Dictionary generic_idictionary_to_dictionary(MonoObject *p_generic_idictionary) {
+	NO_GLUE_RET(Dictionary());
 	Dictionary result;
 	Dictionary result;
 	GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
 	GenericIDictionaryToDictionary thunk = CACHED_METHOD_THUNK(MarshalUtils, GenericIDictionaryToDictionary);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
 	invoke_method_thunk(thunk, p_generic_idictionary, &result, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 	return result;
 	return result;
 }
 }
 
 
 GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
 GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype) {
+	NO_GLUE_RET(NULL);
 	MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
 	MakeGenericArrayType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericArrayType);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
 	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_elem_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
 	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
 }
 }
 
 
 GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
 GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype) {
+	NO_GLUE_RET(NULL);
 	MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
 	MakeGenericDictionaryType thunk = CACHED_METHOD_THUNK(MarshalUtils, MakeGenericDictionaryType);
 	MonoException *exc = NULL;
 	MonoException *exc = NULL;
 	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
 	MonoReflectionType *reftype = invoke_method_thunk(thunk, p_key_reftype, p_value_reftype, &exc);
-	UNLIKELY_UNHANDLED_EXCEPTION(exc);
+	UNHANDLED_EXCEPTION(exc);
 	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
 	return GDMono::get_singleton()->get_class(mono_class_from_mono_type(mono_reflection_type_get_type(reftype)));
 }
 }
 
 
 } // namespace Marshal
 } // namespace Marshal
 
 
-// namespace Marshal
-
 } // namespace GDMonoUtils
 } // namespace GDMonoUtils

+ 17 - 7
modules/mono/mono_gd/gd_mono_utils.h

@@ -41,7 +41,7 @@
 #include "core/object.h"
 #include "core/object.h"
 #include "core/reference.h"
 #include "core/reference.h"
 
 
-#define UNLIKELY_UNHANDLED_EXCEPTION(m_exc)            \
+#define UNHANDLED_EXCEPTION(m_exc)                     \
 	if (unlikely(m_exc != NULL)) {                     \
 	if (unlikely(m_exc != NULL)) {                     \
 		GDMonoUtils::debug_unhandled_exception(m_exc); \
 		GDMonoUtils::debug_unhandled_exception(m_exc); \
 		GD_UNREACHABLE();                              \
 		GD_UNREACHABLE();                              \
@@ -78,16 +78,16 @@ typedef void (*GenericIDictionaryToDictionary)(MonoObject *, Dictionary *, MonoE
 
 
 namespace Marshal {
 namespace Marshal {
 
 
-MonoBoolean type_is_generic_array(MonoReflectionType *p_reftype);
-MonoBoolean type_is_generic_dictionary(MonoReflectionType *p_reftype);
+bool type_is_generic_array(MonoReflectionType *p_reftype);
+bool type_is_generic_dictionary(MonoReflectionType *p_reftype);
 
 
 void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
 void array_get_element_type(MonoReflectionType *p_array_reftype, MonoReflectionType **r_elem_reftype);
 void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
 void dictionary_get_key_value_types(MonoReflectionType *p_dict_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
 
 
-MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
-MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
-MonoBoolean generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
-MonoBoolean generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
+bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype);
+bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype);
+bool generic_ienumerable_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_elem_reftype);
+bool generic_idictionary_is_assignable_from(MonoReflectionType *p_reftype, MonoReflectionType **r_key_reftype, MonoReflectionType **r_value_reftype);
 
 
 GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
 GDMonoClass *make_generic_array_type(MonoReflectionType *p_elem_reftype);
 GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
 GDMonoClass *make_generic_dictionary_type(MonoReflectionType *p_key_reftype, MonoReflectionType *p_value_reftype);
@@ -157,6 +157,7 @@ struct MonoCache {
 	GDMonoClass *class_Array;
 	GDMonoClass *class_Array;
 	GDMonoClass *class_Dictionary;
 	GDMonoClass *class_Dictionary;
 	GDMonoClass *class_MarshalUtils;
 	GDMonoClass *class_MarshalUtils;
+	GDMonoClass *class_ISerializationListener;
 
 
 #ifdef DEBUG_ENABLED
 #ifdef DEBUG_ENABLED
 	GDMonoClass *class_DebuggingUtils;
 	GDMonoClass *class_DebuggingUtils;
@@ -235,10 +236,19 @@ void update_godot_api_cache();
 inline void clear_corlib_cache() {
 inline void clear_corlib_cache() {
 	mono_cache.clear_corlib_cache();
 	mono_cache.clear_corlib_cache();
 }
 }
+
 inline void clear_godot_api_cache() {
 inline void clear_godot_api_cache() {
 	mono_cache.clear_godot_api_cache();
 	mono_cache.clear_godot_api_cache();
 }
 }
 
 
+_FORCE_INLINE_ bool tools_godot_api_check() {
+#ifdef TOOLS_ENABLED
+	return mono_cache.godot_api_cache_updated;
+#else
+	return true; // Assume it's updated if this was called, otherwise it's a bug
+#endif
+}
+
 _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
 _FORCE_INLINE_ void hash_combine(uint32_t &p_hash, const uint32_t &p_with_hash) {
 	p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
 	p_hash ^= p_with_hash + 0x9e3779b9 + (p_hash << 6) + (p_hash >> 2);
 }
 }

+ 1 - 1
modules/mono/signal_awaiter_utils.cpp

@@ -91,7 +91,7 @@ Variant SignalAwaiterHandle::_signal_callback(const Variant **p_args, int p_argc
 	set_completed(true);
 	set_completed(true);
 
 
 	int signal_argc = p_argcount - 1;
 	int signal_argc = p_argcount - 1;
-	MonoArray *signal_args = mono_array_new(SCRIPTS_DOMAIN, CACHED_CLASS_RAW(MonoObject), signal_argc);
+	MonoArray *signal_args = mono_array_new(mono_domain_get(), CACHED_CLASS_RAW(MonoObject), signal_argc);
 
 
 	for (int i = 0; i < signal_argc; i++) {
 	for (int i = 0; i < signal_argc; i++) {
 		MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);
 		MonoObject *boxed = GDMonoMarshal::variant_to_mono_object(*p_args[i]);