Browse Source

Merge pull request #38131 from akien-mga/3.2-cherrypicks

Cherry-picks for the 3.2 branch (future 3.2.2) - 3rd batch
Rémi Verschelde 5 years ago
parent
commit
ed27b7e6b9

+ 1 - 2
editor/plugins/spatial_editor_plugin.h

@@ -828,12 +828,11 @@ public:
 	static const int HIDDEN = 1;
 	static const int ON_TOP = 2;
 
-private:
+protected:
 	int current_state;
 	List<EditorSpatialGizmo *> current_gizmos;
 	HashMap<String, Vector<Ref<SpatialMaterial> > > materials;
 
-protected:
 	static void _bind_methods();
 	virtual bool has_gizmo(Spatial *p_spatial);
 	virtual Ref<EditorSpatialGizmo> create_gizmo(Spatial *p_spatial);

+ 6 - 5
editor/plugins/tile_map_editor_plugin.cpp

@@ -57,17 +57,18 @@ void TileMapEditor::_notification(int p_what) {
 
 		} break;
 
+		case NOTIFICATION_ENTER_TREE: {
+
+			get_tree()->connect("node_removed", this, "_node_removed");
+			FALLTHROUGH;
+		}
+
 		case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: {
 
 			if (is_visible_in_tree()) {
 				_update_palette();
 			}
-			FALLTHROUGH;
-		}
 
-		case NOTIFICATION_ENTER_TREE: {
-
-			get_tree()->connect("node_removed", this, "_node_removed");
 			paint_button->set_icon(get_icon("Edit", "EditorIcons"));
 			bucket_fill_button->set_icon(get_icon("Bucket", "EditorIcons"));
 			picker_button->set_icon(get_icon("ColorPick", "EditorIcons"));

+ 19 - 0
editor/spatial_editor_gizmos.cpp

@@ -4075,6 +4075,25 @@ JointSpatialGizmoPlugin::JointSpatialGizmoPlugin() {
 	create_material("joint_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint", Color(0.5, 0.8, 1)));
 	create_material("joint_body_a_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_a", Color(0.6, 0.8, 1)));
 	create_material("joint_body_b_material", EDITOR_DEF("editors/3d_gizmos/gizmo_colors/joint_body_b", Color(0.6, 0.9, 1)));
+
+	update_timer = memnew(Timer);
+	update_timer->set_name("JointGizmoUpdateTimer");
+	update_timer->set_wait_time(1.0 / 120.0);
+	update_timer->connect("timeout", this, "incremental_update_gizmos");
+	update_timer->set_autostart(true);
+	EditorNode::get_singleton()->call_deferred("add_child", update_timer);
+}
+
+void JointSpatialGizmoPlugin::_bind_methods() {
+	ClassDB::bind_method(D_METHOD("incremental_update_gizmos"), &JointSpatialGizmoPlugin::incremental_update_gizmos);
+}
+
+void JointSpatialGizmoPlugin::incremental_update_gizmos() {
+	if (!current_gizmos.empty()) {
+		update_idx++;
+		update_idx = update_idx % current_gizmos.size();
+		redraw(current_gizmos[update_idx]);
+	}
 }
 
 bool JointSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {

+ 8 - 0
editor/spatial_editor_gizmos.h

@@ -390,6 +390,14 @@ class JointSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
 
 	GDCLASS(JointSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
 
+	Timer *update_timer;
+	uint64_t update_idx = 0;
+
+	void incremental_update_gizmos();
+
+protected:
+	static void _bind_methods();
+
 public:
 	bool has_gizmo(Spatial *p_spatial);
 	String get_name() const;

+ 8 - 0
modules/gdscript/gdscript_parser.cpp

@@ -2683,6 +2683,7 @@ void GDScriptParser::_transform_match_statment(MatchNode *p_match_statement) {
 			LocalVarNode *local_var = branch->body->variables[e->key()];
 			local_var->assign = e->value();
 			local_var->set_datatype(local_var->assign->get_datatype());
+			local_var->assignments++;
 
 			IdentifierNode *id2 = alloc_node<IdentifierNode>();
 			id2->name = local_var->name;
@@ -3673,6 +3674,12 @@ void GDScriptParser::_parse_class(ClassNode *p_class) {
 						_set_error("A constant named \"" + String(name) + "\" already exists in the outer class scope (at line" + itos(outer_class->constant_expressions[name].expression->line) + ").");
 						return;
 					}
+					for (int i = 0; i < outer_class->variables.size(); i++) {
+						if (outer_class->variables[i].identifier == name) {
+							_set_error("A variable named \"" + String(name) + "\" already exists in the outer class scope (at line " + itos(outer_class->variables[i].line) + ").");
+							return;
+						}
+					}
 
 					outer_class = outer_class->owner;
 				}
@@ -6568,6 +6575,7 @@ GDScriptParser::DataType GDScriptParser::_reduce_node_type(Node *p_node) {
 							node_type = _reduce_identifier_type(&base_type, member_id->name, op->line, true);
 #ifdef DEBUG_ENABLED
 							if (!node_type.has_type) {
+								_mark_line_as_unsafe(op->line);
 								_add_warning(GDScriptWarning::UNSAFE_PROPERTY_ACCESS, op->line, member_id->name.operator String(), base_type.to_string());
 							}
 #endif // DEBUG_ENABLED

+ 27 - 0
modules/mono/editor/GodotTools/GodotTools.Core/FileUtils.cs

@@ -0,0 +1,27 @@
+using System.IO;
+
+namespace GodotTools.Core
+{
+    public static class FileUtils
+    {
+        public static void SaveBackupCopy(string filePath)
+        {
+            string backupPathBase = filePath + ".old";
+            string backupPath = backupPathBase;
+
+            const int maxAttempts = 5;
+            int attempt = 1;
+
+            while (File.Exists(backupPath) && attempt <= maxAttempts)
+            {
+                backupPath = backupPathBase + "." + (attempt);
+                attempt++;
+            }
+
+            if (attempt > maxAttempts + 1)
+                return;
+
+            File.Copy(filePath, backupPath, overwrite: true);
+        }
+    }
+}

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

@@ -31,6 +31,7 @@
     <Reference Include="System" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="FileUtils.cs" />
     <Compile Include="ProcessExtensions.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="StringExtensions.cs" />

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

@@ -153,7 +153,12 @@ EndProject";
             var result = regex.Replace(input,m => dict[m.Value]);
 
             if (result != input)
+            {
+                // Save a copy of the solution before replacing it
+                FileUtils.SaveBackupCopy(slnPath);
+
                 File.WriteAllText(slnPath, result);
+            }
         }
     }
 }

+ 25 - 40
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs

@@ -9,8 +9,28 @@ using Microsoft.Build.Construction;
 
 namespace GodotTools.ProjectEditor
 {
+    public sealed class MSBuildProject
+    {
+        public ProjectRootElement Root { get; }
+
+        public bool HasUnsavedChanges => Root.HasUnsavedChanges;
+
+        public void Save() => Root.Save();
+
+        public MSBuildProject(ProjectRootElement root)
+        {
+            Root = root;
+        }
+    }
+
     public static class ProjectUtils
     {
+        public static MSBuildProject Open(string path)
+        {
+            var root = ProjectRootElement.Open(path);
+            return root != null ? new MSBuildProject(root) : null;
+        }
+
         public static void AddItemToProjectChecked(string projectPath, string itemType, string include)
         {
             var dir = Directory.GetParent(projectPath).FullName;
@@ -43,7 +63,6 @@ namespace GodotTools.ProjectEditor
 
         public static void RemoveItemFromProjectChecked(string projectPath, string itemType, string include)
         {
-            var dir = Directory.GetParent(projectPath).FullName;
             var root = ProjectRootElement.Open(projectPath);
             Debug.Assert(root != null);
 
@@ -59,8 +78,6 @@ namespace GodotTools.ProjectEditor
             var root = ProjectRootElement.Open(projectPath);
             Debug.Assert(root != null);
 
-            bool dirty = false;
-
             var oldFolderNormalized = oldFolder.NormalizePath();
             var newFolderNormalized = newFolder.NormalizePath();
             string absOldFolderNormalized = Path.GetFullPath(oldFolderNormalized).NormalizePath();
@@ -71,10 +88,9 @@ namespace GodotTools.ProjectEditor
                 string absPathNormalized = Path.GetFullPath(item.Include).NormalizePath();
                 string absNewIncludeNormalized = absNewFolderNormalized + absPathNormalized.Substring(absOldFolderNormalized.Length);
                 item.Include = absNewIncludeNormalized.RelativeToPath(dir).Replace("/", "\\");
-                dirty = true;
             }
 
-            if (dirty)
+            if (root.HasUnsavedChanges)
                 root.Save();
         }
 
@@ -150,12 +166,9 @@ namespace GodotTools.ProjectEditor
         }
 
         ///  Simple function to make sure the Api assembly references are configured correctly
-        public static void FixApiHintPath(string projectPath)
+        public static void FixApiHintPath(MSBuildProject project)
         {
-            var root = ProjectRootElement.Open(projectPath);
-            Debug.Assert(root != null);
-
-            bool dirty = false;
+            var root = project.Root;
 
             void AddPropertyIfNotPresent(string name, string condition, string value)
             {
@@ -170,7 +183,6 @@ namespace GodotTools.ProjectEditor
                 }
 
                 root.AddProperty(name, value).Condition = " " + condition + " ";
-                dirty = true;
             }
 
             AddPropertyIfNotPresent(name: "ApiConfiguration",
@@ -212,7 +224,6 @@ namespace GodotTools.ProjectEditor
                         }
 
                         referenceWithHintPath.AddMetadata("HintPath", hintPath);
-                        dirty = true;
                         return;
                     }
 
@@ -221,14 +232,12 @@ namespace GodotTools.ProjectEditor
                     {
                         // Found a Reference item without a HintPath
                         referenceWithoutHintPath.AddMetadata("HintPath", hintPath);
-                        dirty = true;
                         return;
                     }
                 }
 
                 // Found no Reference item at all. Add it.
                 root.AddItem("Reference", referenceName).Condition = " " + condition + " ";
-                dirty = true;
             }
 
             const string coreProjectName = "GodotSharp";
@@ -242,17 +251,11 @@ namespace GodotTools.ProjectEditor
 
             SetReferenceHintPath(coreProjectName, coreCondition, coreHintPath);
             SetReferenceHintPath(editorProjectName, editorCondition, editorHintPath);
-
-            if (dirty)
-                root.Save();
         }
 
-        public static void MigrateFromOldConfigNames(string projectPath)
+        public static void MigrateFromOldConfigNames(MSBuildProject project)
         {
-            var root = ProjectRootElement.Open(projectPath);
-            Debug.Assert(root != null);
-
-            bool dirty = false;
+            var root = project.Root;
 
             bool hasGodotProjectGeneratorVersion = false;
             bool foundOldConfiguration = false;
@@ -267,7 +270,6 @@ namespace GodotTools.ProjectEditor
                 {
                     configItem.Value = "Debug";
                     foundOldConfiguration = true;
-                    dirty = true;
                 }
             }
 
@@ -275,7 +277,6 @@ namespace GodotTools.ProjectEditor
             {
                 root.PropertyGroups.First(g => g.Condition == string.Empty)?
                     .AddProperty("GodotProjectGeneratorVersion", Assembly.GetExecutingAssembly().GetName().Version.ToString());
-                dirty = true;
             }
 
             if (!foundOldConfiguration)
@@ -299,33 +300,21 @@ namespace GodotTools.ProjectEditor
                     void MigrateConditions(string oldCondition, string newCondition)
                     {
                         foreach (var propertyGroup in root.PropertyGroups.Where(g => g.Condition.Trim() == oldCondition))
-                        {
                             propertyGroup.Condition = " " + newCondition + " ";
-                            dirty = true;
-                        }
 
                         foreach (var propertyGroup in root.PropertyGroups)
                         {
                             foreach (var prop in propertyGroup.Properties.Where(p => p.Condition.Trim() == oldCondition))
-                            {
                                 prop.Condition = " " + newCondition + " ";
-                                dirty = true;
-                            }
                         }
 
                         foreach (var itemGroup in root.ItemGroups.Where(g => g.Condition.Trim() == oldCondition))
-                        {
                             itemGroup.Condition = " " + newCondition + " ";
-                            dirty = true;
-                        }
 
                         foreach (var itemGroup in root.ItemGroups)
                         {
                             foreach (var item in itemGroup.Items.Where(item => item.Condition.Trim() == oldCondition))
-                            {
                                 item.Condition = " " + newCondition + " ";
-                                dirty = true;
-                            }
                         }
                     }
 
@@ -340,10 +329,6 @@ namespace GodotTools.ProjectEditor
                 MigrateConfigurationConditions("Release", "ExportRelease");
                 MigrateConfigurationConditions("Tools", "Debug"); // Must be last
             }
-
-
-            if (dirty)
-                root.Save();
         }
     }
 }

+ 33 - 21
modules/mono/editor/GodotTools/GodotTools/Export/ExportPlugin.cs

@@ -168,13 +168,13 @@ namespace GodotTools.Export
 
             // Add dependency assemblies
 
-            var dependencies = new Godot.Collections.Dictionary<string, string>();
+            var assemblies = new Godot.Collections.Dictionary<string, string>();
 
             string projectDllName = GodotSharpEditor.ProjectAssemblyName;
             string projectDllSrcDir = Path.Combine(GodotSharpDirs.ResTempAssembliesBaseDir, buildConfig);
             string projectDllSrcPath = Path.Combine(projectDllSrcDir, $"{projectDllName}.dll");
 
-            dependencies[projectDllName] = projectDllSrcPath;
+            assemblies[projectDllName] = projectDllSrcPath;
 
             if (platform == OS.Platforms.Android)
             {
@@ -184,15 +184,15 @@ namespace GodotTools.Export
                 if (!File.Exists(monoAndroidAssemblyPath))
                     throw new FileNotFoundException("Assembly not found: 'Mono.Android'", monoAndroidAssemblyPath);
 
-                dependencies["Mono.Android"] = monoAndroidAssemblyPath;
+                assemblies["Mono.Android"] = monoAndroidAssemblyPath;
             }
 
             string bclDir = DeterminePlatformBclDir(platform);
 
-            var initialDependencies = dependencies.Duplicate();
-            internal_GetExportedAssemblyDependencies(initialDependencies, buildConfig, bclDir, dependencies);
+            var initialAssemblies = assemblies.Duplicate();
+            internal_GetExportedAssemblyDependencies(initialAssemblies, buildConfig, bclDir, assemblies);
 
-            AddI18NAssemblies(dependencies, bclDir);
+            AddI18NAssemblies(assemblies, bclDir);
 
             string outputDataDir = null;
 
@@ -211,20 +211,32 @@ namespace GodotTools.Export
                     Directory.CreateDirectory(outputDataGameAssembliesDir);
             }
 
-            foreach (var dependency in dependencies)
+            foreach (var assembly in assemblies)
             {
-                string dependSrcPath = dependency.Value;
-
-                if (assembliesInsidePck)
-                {
-                    string dependDstPath = Path.Combine(resAssembliesDir, dependSrcPath.GetFile());
-                    AddFile(dependSrcPath, dependDstPath);
-                }
-                else
+                void AddToAssembliesDir(string fileSrcPath)
                 {
-                    string dependDstPath = Path.Combine(outputDataDir, "Assemblies", dependSrcPath.GetFile());
-                    File.Copy(dependSrcPath, dependDstPath);
+                    if (assembliesInsidePck)
+                    {
+                        string fileDstPath = Path.Combine(resAssembliesDir, fileSrcPath.GetFile());
+                        AddFile(fileSrcPath, fileDstPath);
+                    }
+                    else
+                    {
+                        Debug.Assert(outputDataDir != null);
+                        string fileDstPath = Path.Combine(outputDataDir, "Assemblies", fileSrcPath.GetFile());
+                        File.Copy(fileSrcPath, fileDstPath);
+                    }
                 }
+
+                string assemblySrcPath = assembly.Value;
+
+                string assemblyPathWithoutExtension = Path.ChangeExtension(assemblySrcPath, null);
+                string pdbSrcPath = assemblyPathWithoutExtension + ".pdb";
+
+                AddToAssembliesDir(assemblySrcPath);
+
+                if (File.Exists(pdbSrcPath))
+                    AddToAssembliesDir(pdbSrcPath);
             }
 
             // AOT compilation
@@ -254,7 +266,7 @@ namespace GodotTools.Export
                     ToolchainPath = aotToolchainPath
                 };
 
-                AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, dependencies);
+                AotBuilder.CompileAssemblies(this, aotOpts, features, platform, isDebug, bclDir, outputDir, outputDataDir, assemblies);
             }
         }
 
@@ -366,7 +378,7 @@ namespace GodotTools.Export
                     if (PlatformRequiresCustomBcl(platform))
                         throw new FileNotFoundException($"Missing BCL (Base Class Library) for platform: {platform}");
 
-                    platformBclDir = typeof(object).Assembly.Location; // Use the one we're running on
+                    platformBclDir = typeof(object).Assembly.Location.GetBaseDir(); // Use the one we're running on
                 }
             }
 
@@ -425,7 +437,7 @@ namespace GodotTools.Export
         }
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialDependencies,
-            string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencies);
+        private static extern void internal_GetExportedAssemblyDependencies(Godot.Collections.Dictionary<string, string> initialAssemblies,
+            string buildConfig, string customBclDir, Godot.Collections.Dictionary<string, string> dependencyAssemblies);
     }
 }

+ 18 - 3
modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs

@@ -1,4 +1,5 @@
 using Godot;
+using GodotTools.Core;
 using GodotTools.Export;
 using GodotTools.Utils;
 using System;
@@ -450,13 +451,27 @@ namespace GodotTools
                 {
                     // Migrate solution from old configuration names to: Debug, ExportDebug and ExportRelease
                     DotNetSolution.MigrateFromOldConfigNames(GodotSharpDirs.ProjectSlnPath);
+
+                    var msbuildProject = ProjectUtils.Open(GodotSharpDirs.ProjectCsProjPath)
+                                         ?? throw new Exception("Cannot open C# project");
+
+                    // NOTE: The order in which changes are made to the project is important
+
                     // Migrate csproj from old configuration names to: Debug, ExportDebug and ExportRelease
-                    ProjectUtils.MigrateFromOldConfigNames(GodotSharpDirs.ProjectCsProjPath);
+                    ProjectUtils.MigrateFromOldConfigNames(msbuildProject);
 
-                    // Apply the other fixes after configurations are migrated
+                    // Apply the other fixes only after configurations have been migrated
 
                     // Make sure the existing project has Api assembly references configured correctly
-                    ProjectUtils.FixApiHintPath(GodotSharpDirs.ProjectCsProjPath);
+                    ProjectUtils.FixApiHintPath(msbuildProject);
+
+                    if (msbuildProject.HasUnsavedChanges)
+                    {
+                        // Save a copy of the project before replacing it
+                        FileUtils.SaveBackupCopy(GodotSharpDirs.ProjectCsProjPath);
+
+                        msbuildProject.Save();
+                    }
                 }
                 catch (Exception e)
                 {

+ 5 - 5
modules/mono/editor/editor_internal_calls.cpp

@@ -231,14 +231,14 @@ int32_t godot_icall_ScriptClassParser_ParseFile(MonoString *p_filepath, MonoObje
 	return err;
 }
 
-uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_dependencies,
-		MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_dependencies) {
-	Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_dependencies);
+uint32_t godot_icall_ExportPlugin_GetExportedAssemblyDependencies(MonoObject *p_initial_assemblies,
+		MonoString *p_build_config, MonoString *p_custom_bcl_dir, MonoObject *r_assembly_dependencies) {
+	Dictionary initial_dependencies = GDMonoMarshal::mono_object_to_variant(p_initial_assemblies);
 	String build_config = GDMonoMarshal::mono_string_to_godot(p_build_config);
 	String custom_bcl_dir = GDMonoMarshal::mono_string_to_godot(p_custom_bcl_dir);
-	Dictionary dependencies = GDMonoMarshal::mono_object_to_variant(r_dependencies);
+	Dictionary assembly_dependencies = GDMonoMarshal::mono_object_to_variant(r_assembly_dependencies);
 
-	return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, dependencies);
+	return GodotSharpExport::get_exported_assembly_dependencies(initial_dependencies, build_config, custom_bcl_dir, assembly_dependencies);
 }
 
 MonoString *godot_icall_Internal_UpdateApiAssembliesFromPrebuilt(MonoString *p_config) {

+ 9 - 9
modules/mono/editor/godotsharp_export.cpp

@@ -50,13 +50,13 @@ String get_assemblyref_name(MonoImage *p_image, int index) {
 	return String::utf8(mono_metadata_string_heap(p_image, cols[MONO_ASSEMBLYREF_NAME]));
 }
 
-Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies) {
+Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_assembly_dependencies) {
 	MonoImage *image = p_assembly->get_image();
 
 	for (int i = 0; i < mono_image_get_table_rows(image, MONO_TABLE_ASSEMBLYREF); i++) {
 		String ref_name = get_assemblyref_name(image, i);
 
-		if (r_dependencies.has(ref_name))
+		if (r_assembly_dependencies.has(ref_name))
 			continue;
 
 		GDMonoAssembly *ref_assembly = NULL;
@@ -93,17 +93,17 @@ Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String>
 		ERR_FAIL_COND_V_MSG(!ref_assembly, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + ref_name + "'.");
 
 		// Use the path we got from the search. Don't try to get the path from the loaded assembly as we can't trust it will be from the selected BCL dir.
-		r_dependencies[ref_name] = path;
+		r_assembly_dependencies[ref_name] = path;
 
-		Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_dependencies);
+		Error err = get_assembly_dependencies(ref_assembly, p_search_dirs, r_assembly_dependencies);
 		ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot load one of the dependencies for the assembly: '" + ref_name + "'.");
 	}
 
 	return OK;
 }
 
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
-		const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_dependencies) {
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
+		const String &p_build_config, const String &p_custom_bcl_dir, Dictionary &r_assembly_dependencies) {
 	MonoDomain *export_domain = GDMonoUtils::create_domain("GodotEngine.Domain.ProjectExport");
 	ERR_FAIL_NULL_V(export_domain, FAILED);
 	_GDMONO_SCOPE_EXIT_DOMAIN_UNLOAD_(export_domain);
@@ -113,16 +113,16 @@ Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencie
 	Vector<String> search_dirs;
 	GDMonoAssembly::fill_search_dirs(search_dirs, p_build_config, p_custom_bcl_dir);
 
-	for (const Variant *key = p_initial_dependencies.next(); key; key = p_initial_dependencies.next(key)) {
+	for (const Variant *key = p_initial_assemblies.next(); key; key = p_initial_assemblies.next(key)) {
 		String assembly_name = *key;
-		String assembly_path = p_initial_dependencies[*key];
+		String assembly_path = p_initial_assemblies[*key];
 
 		GDMonoAssembly *assembly = NULL;
 		bool load_success = GDMono::get_singleton()->load_assembly_from(assembly_name, assembly_path, &assembly, /* refonly: */ true);
 
 		ERR_FAIL_COND_V_MSG(!load_success, ERR_CANT_RESOLVE, "Cannot load assembly (refonly): '" + assembly_name + "'.");
 
-		Error err = get_assembly_dependencies(assembly, search_dirs, r_dependencies);
+		Error err = get_assembly_dependencies(assembly, search_dirs, r_assembly_dependencies);
 		if (err != OK)
 			return err;
 	}

+ 2 - 2
modules/mono/editor/godotsharp_export.h

@@ -41,8 +41,8 @@ namespace GodotSharpExport {
 
 Error get_assembly_dependencies(GDMonoAssembly *p_assembly, const Vector<String> &p_search_dirs, Dictionary &r_dependencies);
 
-Error get_exported_assembly_dependencies(const Dictionary &p_initial_dependencies,
-		const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_dependencies);
+Error get_exported_assembly_dependencies(const Dictionary &p_initial_assemblies,
+		const String &p_build_config, const String &p_custom_lib_dir, Dictionary &r_assembly_dependencies);
 
 } // namespace GodotSharpExport
 

+ 9 - 8
modules/mono/mono_gd/gd_mono.cpp

@@ -128,12 +128,8 @@ void gd_mono_profiler_init() {
 	}
 }
 
-#if defined(DEBUG_ENABLED)
-
 void gd_mono_debug_init() {
 
-	mono_debug_init(MONO_DEBUG_FORMAT_MONO);
-
 	CharString da_args = OS::get_singleton()->get_environment("GODOT_MONO_DEBUGGER_AGENT").utf8();
 
 #ifdef TOOLS_ENABLED
@@ -158,6 +154,10 @@ void gd_mono_debug_init() {
 		return; // Exported games don't use the project settings to setup the debugger agent
 #endif
 
+	// Debugging enabled
+
+	mono_debug_init(MONO_DEBUG_FORMAT_MONO);
+
 	// --debugger-agent=help
 	const char *options[] = {
 		"--soft-breakpoints",
@@ -166,7 +166,6 @@ void gd_mono_debug_init() {
 	mono_jit_parse_options(2, (char **)options);
 }
 
-#endif // defined(DEBUG_ENABLED)
 #endif // !defined(JAVASCRIPT_ENABLED)
 
 #if defined(JAVASCRIPT_ENABLED)
@@ -174,6 +173,7 @@ MonoDomain *gd_initialize_mono_runtime() {
 	const char *vfs_prefix = "managed";
 	int enable_debugging = 0;
 
+	// TODO: Provide a way to enable debugging on WASM release builds.
 #ifdef DEBUG_ENABLED
 	enable_debugging = 1;
 #endif
@@ -184,9 +184,7 @@ MonoDomain *gd_initialize_mono_runtime() {
 }
 #else
 MonoDomain *gd_initialize_mono_runtime() {
-#ifdef DEBUG_ENABLED
 	gd_mono_debug_init();
-#endif
 
 #if defined(IPHONE_ENABLED) || defined(ANDROID_ENABLED)
 	// I don't know whether this actually matters or not
@@ -1388,7 +1386,10 @@ bool _GodotSharp::is_runtime_initialized() {
 
 void _GodotSharp::_reload_assemblies(bool p_soft_reload) {
 #ifdef GD_MONO_HOT_RELOAD
-	CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
+	// This method may be called more than once with `call_deferred`, so we need to check
+	// again if reloading is needed to avoid reloading multiple times unnecessarily.
+	if (CSharpLanguage::get_singleton()->is_assembly_reloading_needed())
+		CSharpLanguage::get_singleton()->reload_assemblies(p_soft_reload);
 #endif
 }
 

+ 7 - 0
modules/mono/mono_gd/gd_mono_field.cpp

@@ -281,6 +281,13 @@ void GDMonoField::set_value_from_variant(MonoObject *p_object, const Variant &p_
 				break;
 			}
 
+			GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+			if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class)) {
+				MonoArray *managed = GDMonoMarshal::Array_to_mono_array(p_value.operator ::Array(), array_type_class);
+				mono_field_set_value(p_object, mono_field, managed);
+				break;
+			}
+
 			ERR_FAIL_MSG("Attempted to convert Variant to a managed array of unmarshallable element type.");
 		} break;
 

+ 31 - 0
modules/mono/mono_gd/gd_mono_marshal.cpp

@@ -132,6 +132,10 @@ Variant::Type managed_to_variant_type(const ManagedType &p_type) {
 
 			if (array_type->eklass == CACHED_CLASS_RAW(Color))
 				return Variant::POOL_COLOR_ARRAY;
+
+			GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+			if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+				return Variant::ARRAY;
 		} break;
 
 		case MONO_TYPE_CLASS: {
@@ -217,6 +221,13 @@ 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) {
 	switch (p_array_type.type_encoding) {
+		case MONO_TYPE_ARRAY:
+		case MONO_TYPE_SZARRAY: {
+			MonoArrayType *array_type = mono_type_get_array_type(p_array_type.type_class->get_mono_type());
+			GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+			r_elem_type = ManagedType::from_class(array_type_class);
+			return true;
+		} break;
 		case MONO_TYPE_GENERICINST: {
 			MonoReflectionType *array_reftype = mono_type_get_object(mono_domain_get(), p_array_type.type_class->get_mono_type());
 
@@ -511,6 +522,10 @@ MonoObject *variant_to_mono_object(const Variant *p_var, const ManagedType &p_ty
 			if (array_type->eklass == CACHED_CLASS_RAW(Color))
 				return (MonoObject *)PoolColorArray_to_mono_array(p_var->operator PoolColorArray());
 
+			GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+			if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+				return (MonoObject *)Array_to_mono_array(p_var->operator Array(), array_type_class);
+
 			ERR_FAIL_V_MSG(NULL, "Attempted to convert Variant to a managed array of unmarshallable element type.");
 		} break;
 
@@ -805,6 +820,10 @@ Variant mono_object_to_variant_impl(MonoObject *p_obj, const ManagedType &p_type
 			if (array_type->eklass == CACHED_CLASS_RAW(Color))
 				return mono_array_to_PoolColorArray((MonoArray *)p_obj);
 
+			GDMonoClass *array_type_class = GDMono::get_singleton()->get_class(array_type->eklass);
+			if (CACHED_CLASS(GodotObject)->is_assignable_from(array_type_class))
+				return mono_array_to_Array((MonoArray *)p_obj);
+
 			if (p_fail_with_err) {
 				ERR_FAIL_V_MSG(Variant(), "Attempted to convert a managed array of unmarshallable element type to Variant.");
 			} else {
@@ -970,6 +989,18 @@ MonoArray *Array_to_mono_array(const Array &p_array) {
 	return ret;
 }
 
+MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class) {
+	int length = p_array.size();
+	MonoArray *ret = mono_array_new(mono_domain_get(), p_array_type_class->get_mono_ptr(), length);
+
+	for (int i = 0; i < length; i++) {
+		MonoObject *boxed = variant_to_mono_object(p_array[i]);
+		mono_array_setref(ret, i, boxed);
+	}
+
+	return ret;
+}
+
 Array mono_array_to_Array(MonoArray *p_array) {
 	Array ret;
 	if (!p_array)

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

@@ -125,6 +125,7 @@ String mono_object_to_variant_string(MonoObject *p_obj, MonoException **r_exc);
 // Array
 
 MonoArray *Array_to_mono_array(const Array &p_array);
+MonoArray *Array_to_mono_array(const Array &p_array, GDMonoClass *p_array_type_class);
 Array mono_array_to_Array(MonoArray *p_array);
 
 // PoolIntArray

+ 3 - 1
platform/android/java/app/build.gradle

@@ -90,7 +90,9 @@ android {
     packagingOptions {
         exclude 'META-INF/LICENSE'
         exclude 'META-INF/NOTICE'
-        doNotStrip '**/*.so'
+
+        // Should be uncommented for development purpose within Android Studio
+        // doNotStrip '**/*.so'
     }
 
     // Both signing and zip-aligning will be done at export time

+ 3 - 1
platform/android/java/lib/build.gradle

@@ -26,7 +26,9 @@ android {
     packagingOptions {
         exclude 'META-INF/LICENSE'
         exclude 'META-INF/NOTICE'
-        doNotStrip '**/*.so'
+
+        // Should be uncommented for development purpose within Android Studio
+        // doNotStrip '**/*.so'
     }
 
     sourceSets {

+ 2 - 2
scene/animation/animation_player.cpp

@@ -950,13 +950,13 @@ void AnimationPlayer::_animation_process(float p_delta) {
 				play(queued.front()->get());
 				String new_name = playback.assigned;
 				queued.pop_front();
-				if (end_notify)
+				if (end_notify || playback.seeked)
 					emit_signal(SceneStringNames::get_singleton()->animation_changed, old, new_name);
 			} else {
 				//stop();
 				playing = false;
 				_set_process(false);
-				if (end_notify)
+				if (end_notify || playback.seeked)
 					emit_signal(SceneStringNames::get_singleton()->animation_finished, playback.assigned);
 			}
 			end_reached = false;

+ 2 - 0
thirdparty/README.md

@@ -151,6 +151,8 @@ Files extracted from upstream source:
 
 - `jpgd*.{c,h}`
 
+Patches in the `patches` directory should be re-applied after updates.
+
 
 ## libogg
 

+ 3 - 3
thirdparty/jpeg-compressor/jpgd.cpp

@@ -2126,7 +2126,7 @@ namespace jpgd {
 
 	int jpeg_decoder::decode_next_mcu_row()
 	{
-		if (setjmp(m_jmp_state))
+		if (::setjmp(m_jmp_state))
 			return JPGD_FAILED;
 
 		const bool chroma_y_filtering = ((m_flags & cFlagBoxChromaFiltering) == 0) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2));
@@ -3042,7 +3042,7 @@ namespace jpgd {
 
 	jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags)
 	{
-		if (setjmp(m_jmp_state))
+		if (::setjmp(m_jmp_state))
 			return;
 		decode_init(pStream, flags);
 	}
@@ -3055,7 +3055,7 @@ namespace jpgd {
 		if (m_error_code)
 			return JPGD_FAILED;
 
-		if (setjmp(m_jmp_state))
+		if (::setjmp(m_jmp_state))
 			return JPGD_FAILED;
 
 		decode_start();

+ 31 - 0
thirdparty/jpeg-compressor/patches/fix-msvc2017-build.patch

@@ -0,0 +1,31 @@
+diff --git a/thirdparty/jpeg-compressor/jpgd.cpp b/thirdparty/jpeg-compressor/jpgd.cpp
+index a0c494db61..257d0b7574 100644
+--- a/thirdparty/jpeg-compressor/jpgd.cpp
++++ b/thirdparty/jpeg-compressor/jpgd.cpp
+@@ -2126,7 +2126,7 @@ namespace jpgd {
+ 
+ 	int jpeg_decoder::decode_next_mcu_row()
+ 	{
+-		if (setjmp(m_jmp_state))
++		if (::setjmp(m_jmp_state))
+ 			return JPGD_FAILED;
+ 
+ 		const bool chroma_y_filtering = ((m_flags & cFlagBoxChromaFiltering) == 0) && ((m_scan_type == JPGD_YH2V2) || (m_scan_type == JPGD_YH1V2));
+@@ -3042,7 +3042,7 @@ namespace jpgd {
+ 
+ 	jpeg_decoder::jpeg_decoder(jpeg_decoder_stream* pStream, uint32_t flags)
+ 	{
+-		if (setjmp(m_jmp_state))
++		if (::setjmp(m_jmp_state))
+ 			return;
+ 		decode_init(pStream, flags);
+ 	}
+@@ -3055,7 +3055,7 @@ namespace jpgd {
+ 		if (m_error_code)
+ 			return JPGD_FAILED;
+ 
+-		if (setjmp(m_jmp_state))
++		if (::setjmp(m_jmp_state))
+ 			return JPGD_FAILED;
+ 
+ 		decode_start();