summaryrefslogtreecommitdiffstats
path: root/modules/mono
diff options
context:
space:
mode:
Diffstat (limited to 'modules/mono')
-rwxr-xr-xmodules/mono/build_scripts/build_assemblies.py16
-rw-r--r--modules/mono/csharp_script.cpp49
-rw-r--r--modules/mono/csharp_script.h1
-rw-r--r--modules/mono/doc_classes/CSharpScript.xml4
-rw-r--r--modules/mono/doc_classes/GodotSharp.xml2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props13
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs60
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs18
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs42
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj2
-rw-r--r--modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs4
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs22
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs54
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs12
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj3
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs7
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs51
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs474
-rw-r--r--modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs8
-rw-r--r--modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotPlugins/Main.cs44
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs26
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs53
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs5
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs21
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs32
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj2
-rw-r--r--modules/mono/mono_gd/gd_mono.cpp30
-rw-r--r--modules/mono/mono_gd/gd_mono.h2
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
35 files changed, 470 insertions, 619 deletions
diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py
index 0b91cda9b8..580f51c973 100755
--- a/modules/mono/build_scripts/build_assemblies.py
+++ b/modules/mono/build_scripts/build_assemblies.py
@@ -286,15 +286,29 @@ def generate_sdk_package_versions():
version_status = version_status[:pos] + "." + version_status[pos:]
version_str += "-" + version_status
+ import version
+
+ version_defines = (
+ [
+ f"GODOT{version.major}",
+ f"GODOT{version.major}_{version.minor}",
+ f"GODOT{version.major}_{version.minor}_{version.patch}",
+ ]
+ + [f"GODOT{v}_OR_GREATER" for v in range(4, version.major + 1)]
+ + [f"GODOT{version.major}_{v}_OR_GREATER" for v in range(0, version.minor + 1)]
+ + [f"GODOT{version.major}_{version.minor}_{v}_OR_GREATER" for v in range(0, version.patch + 1)]
+ )
+
props = """<Project>
<PropertyGroup>
<PackageVersion_GodotSharp>{0}</PackageVersion_GodotSharp>
<PackageVersion_Godot_NET_Sdk>{0}</PackageVersion_Godot_NET_Sdk>
<PackageVersion_Godot_SourceGenerators>{0}</PackageVersion_Godot_SourceGenerators>
+ <GodotVersionConstants>{1}</GodotVersionConstants>
</PropertyGroup>
</Project>
""".format(
- version_str
+ version_str, ";".join(version_defines)
)
# We write in ../SdkPackageVersions.props.
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index bc26352e9c..1ed495943f 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -120,6 +120,7 @@ void CSharpLanguage::init() {
GLOBAL_DEF("dotnet/project/assembly_name", "");
#ifdef TOOLS_ENABLED
GLOBAL_DEF("dotnet/project/solution_directory", "");
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "dotnet/project/assembly_reload_attempts", PROPERTY_HINT_RANGE, "1,16,1,or_greater"), 3);
#endif
gdmono = memnew(GDMono);
@@ -770,10 +771,6 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
return;
}
- // TODO:
- // Currently, this reloads all scripts, including those whose class is not part of the
- // assembly load context being unloaded. As such, we unnecessarily reload GodotTools.
-
print_verbose(".NET: Reloading assemblies...");
// There is no soft reloading with Mono. It's always hard reloading.
@@ -784,8 +781,20 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
MutexLock lock(script_instances_mutex);
for (SelfList<CSharpScript> *elem = script_list.first(); elem; elem = elem->next()) {
- // Cast to CSharpScript to avoid being erased by accident
- scripts.push_back(Ref<CSharpScript>(elem->self()));
+ // Do not reload scripts with only non-collectible instances to avoid disrupting event subscriptions and such.
+ bool is_reloadable = elem->self()->instances.size() == 0;
+ for (Object *obj : elem->self()->instances) {
+ ERR_CONTINUE(!obj->get_script_instance());
+ CSharpInstance *csi = static_cast<CSharpInstance *>(obj->get_script_instance());
+ if (GDMonoCache::managed_callbacks.GCHandleBridge_GCHandleIsTargetCollectible(csi->get_gchandle_intptr())) {
+ is_reloadable = true;
+ break;
+ }
+ }
+ if (is_reloadable) {
+ // Cast to CSharpScript to avoid being erased by accident.
+ scripts.push_back(Ref<CSharpScript>(elem->self()));
+ }
}
}
@@ -800,6 +809,10 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
ERR_CONTINUE(managed_callable->delegate_handle.value == nullptr);
+ if (!GDMonoCache::managed_callbacks.GCHandleBridge_GCHandleIsTargetCollectible(managed_callable->delegate_handle)) {
+ continue;
+ }
+
Array serialized_data;
bool success = GDMonoCache::managed_callbacks.DelegateUtils_TrySerializeDelegateWithGCHandle(
@@ -907,6 +920,15 @@ void CSharpLanguage::reload_assemblies(bool p_soft_reload) {
scr->_clear();
}
+ // Release the delegates that were serialized earlier.
+ {
+ MutexLock lock(ManagedCallable::instances_mutex);
+
+ for (KeyValue<ManagedCallable *, Array> &kv : ManagedCallable::instances_pending_reload) {
+ kv.key->release_delegate_handle();
+ }
+ }
+
// Do domain reload
if (gdmono->reload_project_assemblies() != OK) {
// Failed to reload the scripts domain
@@ -1158,19 +1180,6 @@ bool CSharpLanguage::debug_break(const String &p_error, bool p_allow_continue) {
}
}
-void CSharpLanguage::_on_scripts_domain_about_to_unload() {
-#ifdef GD_MONO_HOT_RELOAD
- {
- MutexLock lock(ManagedCallable::instances_mutex);
-
- for (SelfList<ManagedCallable> *elem = ManagedCallable::instances.first(); elem; elem = elem->next()) {
- ManagedCallable *managed_callable = elem->self();
- managed_callable->release_delegate_handle();
- }
- }
-#endif
-}
-
#ifdef TOOLS_ENABLED
void CSharpLanguage::_editor_init_callback() {
// Load GodotTools and initialize GodotSharpEditor
@@ -2263,7 +2272,7 @@ void CSharpScript::reload_registered_script(Ref<CSharpScript> p_script) {
// If the EditorFileSystem singleton is available, update the file;
// otherwise, the file will be updated when the singleton becomes available.
EditorFileSystem *efs = EditorFileSystem::get_singleton();
- if (efs) {
+ if (efs && !p_script->get_path().is_empty()) {
efs->update_file(p_script->get_path());
}
#endif
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index cfdf8ae6f8..9802067b46 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -347,7 +347,6 @@ class CSharpLanguage : public ScriptLanguage {
String _debug_error;
friend class GDMono;
- void _on_scripts_domain_about_to_unload();
#ifdef TOOLS_ENABLED
EditorPlugin *godotsharp_editor = nullptr;
diff --git a/modules/mono/doc_classes/CSharpScript.xml b/modules/mono/doc_classes/CSharpScript.xml
index e8da9d8465..b559ca20b2 100644
--- a/modules/mono/doc_classes/CSharpScript.xml
+++ b/modules/mono/doc_classes/CSharpScript.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="CSharpScript" inherits="Script" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="CSharpScript" inherits="Script" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- A script implemented in the C# programming language (Mono-enabled builds only).
+ A script implemented in the C# programming language, saved with the [code].cs[/code] extension (Mono-enabled builds only).
</brief_description>
<description>
This class represents a C# script. It is the C# equivalent of the [GDScript] class and is only available in Mono-enabled Godot builds.
diff --git a/modules/mono/doc_classes/GodotSharp.xml b/modules/mono/doc_classes/GodotSharp.xml
index 76eefc4925..969ca14350 100644
--- a/modules/mono/doc_classes/GodotSharp.xml
+++ b/modules/mono/doc_classes/GodotSharp.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
-<class name="GodotSharp" inherits="Object" version="4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
+<class name="GodotSharp" inherits="Object" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
Bridge between Godot and the Mono runtime (Mono-enabled builds only).
</brief_description>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
index e8ad6a77ea..663eb14f07 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Godot.NET.Sdk.csproj
@@ -7,7 +7,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.NET.Sdk</PackageId>
- <Version>4.1.0</Version>
+ <Version>4.2.0</Version>
<PackageVersion>$(PackageVersion_Godot_NET_Sdk)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
index 45f930fdf7..b0bee795f8 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.NET.Sdk/Sdk/Sdk.props
@@ -74,15 +74,8 @@
<!-- Godot DefineConstants. -->
<PropertyGroup>
- <!-- Define constants to identify Godot builds and versions. -->
- <GodotDefineConstants>
- GODOT;
- GODOT4;GODOT4_OR_GREATER;
- GODOT4_1;GODOT4_1_OR_GREATER;GODOT4_0_OR_GREATER;
- GODOT4_1_0;GODOT4_1_0_OR_GREATER;
- </GodotDefineConstants>
- <!-- Ensure the define constants don't contain whitespace (see https://github.com/dotnet/roslyn/issues/58391). -->
- <GodotDefineConstants>$(GodotDefineConstants.Replace('%0A','').Replace('%0D','').Replace('%09','').Replace(' ',''))</GodotDefineConstants>
+ <!-- Define constants to identify Godot builds. -->
+ <GodotDefineConstants>GODOT</GodotDefineConstants>
<!--
Define constant to determine the target Godot platform. This includes the
@@ -97,7 +90,7 @@
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'ios' ">GODOT_IPHONE;GODOT_IOS;GODOT_MOBILE</GodotPlatformConstants>
<GodotPlatformConstants Condition=" '$(GodotTargetPlatform)' == 'web' ">GODOT_JAVASCRIPT;GODOT_HTML5;GODOT_WASM;GODOT_WEB</GodotPlatformConstants>
- <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants)</GodotDefineConstants>
+ <GodotDefineConstants>$(GodotDefineConstants);$(GodotPlatformConstants);$(GodotVersionConstants)</GodotDefineConstants>
</PropertyGroup>
<PropertyGroup>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
index 8be1151142..72614dd7e0 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs
@@ -384,5 +384,65 @@ namespace Godot.SourceGenerators
typeArgumentSyntax.GetLocation(),
typeArgumentSyntax.SyntaxTree.FilePath));
}
+
+ public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule =
+ new DiagnosticDescriptor(id: "GD0401",
+ title: "The class must derive from GodotObject or a derived class",
+ messageFormat: "The class '{0}' must derive from GodotObject or a derived class.",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustDeriveFromGodotObject(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must derive from GodotObject or a derived class";
+
+ string description = $"{message}. Change the base class or remove the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0401",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
+
+ public static readonly DiagnosticDescriptor GlobalClassMustNotBeGenericRule =
+ new DiagnosticDescriptor(id: "GD0402",
+ title: "The class must not contain generic arguments",
+ messageFormat: "The class '{0}' must not contain generic arguments",
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.");
+
+ public static void ReportGlobalClassMustNotBeGeneric(
+ SyntaxNodeAnalysisContext context,
+ SyntaxNode classSyntax,
+ ISymbol typeSymbol)
+ {
+ string message = $"The class '{typeSymbol.ToDisplayString()}' must not contain generic arguments";
+
+ string description = $"{message}. Remove the generic arguments or the '[GlobalClass]' attribute.";
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ new DiagnosticDescriptor(id: "GD0402",
+ title: message,
+ messageFormat: message,
+ category: "Usage",
+ DiagnosticSeverity.Error,
+ isEnabledByDefault: true,
+ description),
+ classSyntax.GetLocation(),
+ classSyntax.SyntaxTree.FilePath));
+ }
}
}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
index 38af1cbade..b6ea4b8e88 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ExtensionMethods.cs
@@ -37,7 +37,7 @@ namespace Godot.SourceGenerators
while (symbol != null)
{
if (symbol.ContainingAssembly?.Name == assemblyName &&
- symbol.ToString() == typeFullName)
+ symbol.FullQualifiedNameOmitGlobal() == typeFullName)
{
return true;
}
@@ -81,7 +81,7 @@ namespace Godot.SourceGenerators
return godotClassName ?? nativeType.Name;
}
- private static bool IsGodotScriptClass(
+ private static bool TryGetGodotScriptClass(
this ClassDeclarationSyntax cds, Compilation compilation,
out INamedTypeSymbol? symbol
)
@@ -108,7 +108,7 @@ namespace Godot.SourceGenerators
{
foreach (var cds in source)
{
- if (cds.IsGodotScriptClass(compilation, out var symbol))
+ if (cds.TryGetGodotScriptClass(compilation, out var symbol))
yield return (cds, symbol!);
}
}
@@ -230,22 +230,22 @@ namespace Godot.SourceGenerators
.Replace(">", ")");
public static bool IsGodotExportAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.ExportAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.ExportAttr;
public static bool IsGodotSignalAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.SignalAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SignalAttr;
public static bool IsGodotMustBeVariantAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.MustBeVariantAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.MustBeVariantAttr;
public static bool IsGodotClassNameAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.GodotClassNameAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GodotClassNameAttr;
public static bool IsGodotGlobalClassAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.GlobalClassAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.GlobalClassAttr;
public static bool IsSystemFlagsAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GodotClasses.SystemFlagsAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GodotClasses.SystemFlagsAttr;
public static GodotMethodData? HasGodotCompatibleSignature(
this IMethodSymbol method,
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
new file mode 100644
index 0000000000..bcb35dae8a
--- /dev/null
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/GlobalClassAnalyzer.cs
@@ -0,0 +1,42 @@
+using System.Collections.Immutable;
+using System.Linq;
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+
+namespace Godot.SourceGenerators
+{
+ [DiagnosticAnalyzer(LanguageNames.CSharp)]
+ public class GlobalClassAnalyzer : DiagnosticAnalyzer
+ {
+ public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics
+ => ImmutableArray.Create(
+ Common.GlobalClassMustDeriveFromGodotObjectRule,
+ Common.GlobalClassMustNotBeGenericRule);
+
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+ context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.ClassDeclaration);
+ }
+
+ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
+ {
+ var typeClassDecl = (ClassDeclarationSyntax)context.Node;
+
+ // Return if not a type symbol or the type is not a global class.
+ if (context.ContainingSymbol is not INamedTypeSymbol typeSymbol ||
+ !typeSymbol.GetAttributes().Any(a => a.AttributeClass?.IsGodotGlobalClassAttribute() ?? false))
+ return;
+
+ if (typeSymbol.IsGenericType)
+ Common.ReportGlobalClassMustNotBeGeneric(context, typeClassDecl, typeSymbol);
+
+ if (!typeSymbol.InheritsFrom("GodotSharp", GodotClasses.GodotObject))
+ Common.ReportGlobalClassMustDeriveFromGodotObject(context, typeClassDecl, typeSymbol);
+ }
+ }
+}
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
index 2557b70e75..a03c9bc06c 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Godot.SourceGenerators.csproj
@@ -9,7 +9,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>Godot.SourceGenerators</PackageId>
- <Version>4.1.0</Version>
+ <Version>4.2.0</Version>
<PackageVersion>$(PackageVersion_Godot_SourceGenerators)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
index e856ad5c13..09a4ab538f 100644
--- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
+++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs
@@ -362,7 +362,7 @@ namespace Godot.SourceGenerators
{
foreach (var attr in memberSymbol.GetAttributes())
{
- PropertyUsageFlags? propertyUsage = attr.AttributeClass?.ToString() switch
+ PropertyUsageFlags? propertyUsage = attr.AttributeClass?.FullQualifiedNameOmitGlobal() switch
{
GodotClasses.ExportCategoryAttr => PropertyUsageFlags.Category,
GodotClasses.ExportGroupAttr => PropertyUsageFlags.Group,
@@ -620,7 +620,7 @@ namespace Godot.SourceGenerators
bool isPresetHint = false;
- if (elementVariantType == VariantType.String)
+ if (elementVariantType == VariantType.String || elementVariantType == VariantType.StringName)
isPresetHint = GetStringArrayEnumHint(elementVariantType, exportAttr, out hintString);
if (!isPresetHint)
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
index 27963be00f..312c65e364 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/BuildManager.cs
@@ -206,16 +206,16 @@ namespace GodotTools.Build
private static bool BuildProjectBlocking(BuildInfo buildInfo)
{
- if (!File.Exists(buildInfo.Solution))
- return true; // No solution to build
+ if (!File.Exists(buildInfo.Project))
+ return true; // No project to build.
using var pr = new EditorProgress("dotnet_build_project", "Building .NET project...", 1);
- pr.Step("Building project solution", 0);
+ pr.Step("Building project", 0);
if (!Build(buildInfo))
{
- ShowBuildErrorDialog("Failed to build project solution");
+ ShowBuildErrorDialog("Failed to build project");
return false;
}
@@ -224,16 +224,16 @@ namespace GodotTools.Build
private static bool CleanProjectBlocking(BuildInfo buildInfo)
{
- if (!File.Exists(buildInfo.Solution))
- return true; // No solution to clean
+ if (!File.Exists(buildInfo.Project))
+ return true; // No project to clean.
using var pr = new EditorProgress("dotnet_clean_project", "Cleaning .NET project...", 1);
- pr.Step("Cleaning project solution", 0);
+ pr.Step("Cleaning project", 0);
if (!Build(buildInfo))
{
- ShowBuildErrorDialog("Failed to clean project solution");
+ ShowBuildErrorDialog("Failed to clean project");
return false;
}
@@ -322,11 +322,11 @@ namespace GodotTools.Build
public static bool EditorBuildCallback()
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return true; // No solution to build
+ if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ return true; // No project to build.
if (GodotSharpEditor.Instance.SkipBuildBeforePlaying)
- return true; // Requested play from an external editor/IDE which already built the project
+ return true; // Requested play from an external editor/IDE which already built the project.
return BuildProjectBlocking("Debug");
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
index 8fe7d3c2d7..1bb1b3227e 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Build/MSBuildPanel.cs
@@ -29,46 +29,46 @@ namespace GodotTools.Build
BuildOutputView.UpdateIssuesList();
}
- public void BuildSolution()
+ public void BuildProject()
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
+ if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ return; // No project to build.
if (!BuildManager.BuildProjectBlocking("Debug"))
- return; // Build failed
+ return; // Build failed.
- // Notify running game for hot-reload
+ // Notify running game for hot-reload.
Internal.EditorDebuggerNodeReloadScripts();
- // Hot-reload in the editor
+ // Hot-reload in the editor.
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
if (Internal.IsAssembliesReloadingNeeded())
Internal.ReloadAssemblies(softReload: false);
}
- private void RebuildSolution()
+ private void RebuildProject()
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
+ if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ return; // No project to build.
if (!BuildManager.BuildProjectBlocking("Debug", rebuild: true))
- return; // Build failed
+ return; // Build failed.
- // Notify running game for hot-reload
+ // Notify running game for hot-reload.
Internal.EditorDebuggerNodeReloadScripts();
- // Hot-reload in the editor
+ // Hot-reload in the editor.
GodotSharpEditor.Instance.GetNode<HotReloadAssemblyWatcher>("HotReloadAssemblyWatcher").RestartTimer();
if (Internal.IsAssembliesReloadingNeeded())
Internal.ReloadAssemblies(softReload: false);
}
- private void CleanSolution()
+ private void CleanProject()
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
- return; // No solution to build
+ if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ return; // No project to build.
_ = BuildManager.CleanProjectBlocking("Debug");
}
@@ -83,14 +83,14 @@ namespace GodotTools.Build
{
switch ((BuildMenuOptions)id)
{
- case BuildMenuOptions.BuildSolution:
- BuildSolution();
+ case BuildMenuOptions.BuildProject:
+ BuildProject();
break;
- case BuildMenuOptions.RebuildSolution:
- RebuildSolution();
+ case BuildMenuOptions.RebuildProject:
+ RebuildProject();
break;
- case BuildMenuOptions.CleanSolution:
- CleanSolution();
+ case BuildMenuOptions.CleanProject:
+ CleanProject();
break;
default:
throw new ArgumentOutOfRangeException(nameof(id), id, "Invalid build menu option");
@@ -99,9 +99,9 @@ namespace GodotTools.Build
private enum BuildMenuOptions
{
- BuildSolution,
- RebuildSolution,
- CleanSolution
+ BuildProject,
+ RebuildProject,
+ CleanProject
}
public override void _Ready()
@@ -118,9 +118,9 @@ namespace GodotTools.Build
toolBarHBox.AddChild(_buildMenuBtn);
var buildMenu = _buildMenuBtn.GetPopup();
- buildMenu.AddItem("Build Solution".TTR(), (int)BuildMenuOptions.BuildSolution);
- buildMenu.AddItem("Rebuild Solution".TTR(), (int)BuildMenuOptions.RebuildSolution);
- buildMenu.AddItem("Clean Solution".TTR(), (int)BuildMenuOptions.CleanSolution);
+ buildMenu.AddItem("Build Project".TTR(), (int)BuildMenuOptions.BuildProject);
+ buildMenu.AddItem("Rebuild Project".TTR(), (int)BuildMenuOptions.RebuildProject);
+ buildMenu.AddItem("Clean Project".TTR(), (int)BuildMenuOptions.CleanProject);
buildMenu.IdPressed += BuildMenuOptionPressed;
_errorsBtn = new Button
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
index 4e33b38ac2..622a155d37 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotSharpEditor.cs
@@ -140,15 +140,15 @@ namespace GodotTools
}
}
- private void BuildSolutionPressed()
+ private void BuildProjectPressed()
{
- if (!File.Exists(GodotSharpDirs.ProjectSlnPath))
+ if (!File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
if (!CreateProjectSolution())
- return; // Failed to create solution
+ return; // Failed to create project.
}
- Instance.MSBuildPanel.BuildSolution();
+ Instance.MSBuildPanel.BuildProject();
}
private enum MenuOptions
@@ -507,10 +507,10 @@ namespace GodotTools
Shortcut = buildSolutionShortcut,
ShortcutInTooltip = true
};
- _toolBarBuildButton.Pressed += BuildSolutionPressed;
+ _toolBarBuildButton.Pressed += BuildProjectPressed;
AddControlToContainer(CustomControlContainer.Toolbar, _toolBarBuildButton);
- if (File.Exists(GodotSharpDirs.ProjectSlnPath) && File.Exists(GodotSharpDirs.ProjectCsProjPath))
+ if (File.Exists(GodotSharpDirs.ProjectCsProjPath))
{
ApplyNecessaryChangesToSolution();
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
index 30525ba04a..4a0b7f9bed 100644
--- a/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
+++ b/modules/mono/editor/GodotTools/GodotTools/GodotTools.csproj
@@ -28,10 +28,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2019.1.3.0" ExcludeAssets="runtime" PrivateAssets="all" />
+ <PackageReference Include="JetBrains.Rider.PathLocator" Version="1.0.1" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="All" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
- <!-- For RiderPathLocator -->
- <PackageReference Include="Microsoft.Win32.Registry" Version="5.0.0" />
<Reference Include="GodotSharp">
<HintPath>$(GodotApiAssembliesDir)/GodotSharp.dll</HintPath>
<Private>False</Private>
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
index 62db6e3af5..51c7a8aa22 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/MessagingServer.cs
@@ -385,9 +385,12 @@ namespace GodotTools.Ides
// However, it doesn't fix resource loading if the rest of the path is also case insensitive.
string scriptFileLocalized = FsPathUtils.LocalizePathWithCaseChecked(request.ScriptFile);
+ // The node API can only be called from the main thread.
+ await Godot.Engine.GetMainLoop().ToSignal(Godot.Engine.GetMainLoop(), "process_frame");
+
var response = new CodeCompletionResponse { Kind = request.Kind, ScriptFile = request.ScriptFile };
- response.Suggestions = await Task.Run(() =>
- Internal.CodeCompletionRequest(response.Kind, scriptFileLocalized ?? request.ScriptFile));
+ response.Suggestions = Internal.CodeCompletionRequest(response.Kind,
+ scriptFileLocalized ?? request.ScriptFile);
return response;
}
}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs
new file mode 100644
index 0000000000..7e08d8c01d
--- /dev/null
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderLocatorEnvironment.cs
@@ -0,0 +1,51 @@
+using System;
+using Godot;
+using JetBrains.Rider.PathLocator;
+using Newtonsoft.Json;
+using OS = GodotTools.Utils.OS;
+
+namespace GodotTools.Ides.Rider;
+
+public class RiderLocatorEnvironment : IRiderLocatorEnvironment
+{
+ public JetBrains.Rider.PathLocator.OS CurrentOS
+ {
+ get
+ {
+ if (OS.IsWindows)
+ return JetBrains.Rider.PathLocator.OS.Windows;
+ if (OS.IsMacOS) return JetBrains.Rider.PathLocator.OS.MacOSX;
+ if (OS.IsUnixLike) return JetBrains.Rider.PathLocator.OS.Linux;
+ return JetBrains.Rider.PathLocator.OS.Other;
+ }
+ }
+
+ public T FromJson<T>(string json)
+ {
+ return JsonConvert.DeserializeObject<T>(json);
+ }
+
+ public void Info(string message, Exception e = null)
+ {
+ if (e == null)
+ GD.Print(message);
+ else
+ GD.Print(message, e);
+ }
+
+ public void Warn(string message, Exception e = null)
+ {
+ if (e == null)
+ GD.PushWarning(message);
+ else
+ GD.PushWarning(message, e);
+ }
+
+ public void Error(string message, Exception e = null)
+ {
+ if (e == null)
+ GD.PushError(message);
+ else
+ GD.PushError(message, e);
+ }
+}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
deleted file mode 100644
index dad6e35344..0000000000
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathLocator.cs
+++ /dev/null
@@ -1,474 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.IO;
-using System.Linq;
-using System.Runtime.Versioning;
-using Godot;
-using Microsoft.Win32;
-using Newtonsoft.Json;
-using Directory = System.IO.Directory;
-using Environment = System.Environment;
-using File = System.IO.File;
-using Path = System.IO.Path;
-using OS = GodotTools.Utils.OS;
-
-// ReSharper disable UnassignedField.Local
-// ReSharper disable InconsistentNaming
-// ReSharper disable UnassignedField.Global
-// ReSharper disable MemberHidesStaticFromOuterClass
-
-namespace GodotTools.Ides.Rider
-{
- /// <summary>
- /// This code is a modified version of the JetBrains resharper-unity plugin listed under Apache License 2.0 license:
- /// https://github.com/JetBrains/resharper-unity/blob/master/unity/JetBrains.Rider.Unity.Editor/EditorPlugin/RiderPathLocator.cs
- /// </summary>
- public static class RiderPathLocator
- {
- public static RiderInfo[] GetAllRiderPaths()
- {
- try
- {
- if (OS.IsWindows)
- {
- return CollectRiderInfosWindows();
- }
- if (OS.IsMacOS)
- {
- return CollectRiderInfosMac();
- }
- if (OS.IsUnixLike)
- {
- return CollectAllRiderPathsLinux();
- }
- throw new InvalidOperationException("Unexpected OS.");
- }
- catch (Exception e)
- {
- GD.PushWarning(e.Message);
- }
-
- return Array.Empty<RiderInfo>();
- }
-
- private static RiderInfo[] CollectAllRiderPathsLinux()
- {
- var installInfos = new List<RiderInfo>();
- string home = Environment.GetEnvironmentVariable("HOME");
- if (!string.IsNullOrEmpty(home))
- {
- string toolboxRiderRootPath = GetToolboxBaseDir();
- installInfos.AddRange(CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider.sh", false)
- .Select(a => new RiderInfo(a, true)).ToList());
-
- //$Home/.local/share/applications/jetbrains-rider.desktop
- var shortcut = new FileInfo(Path.Combine(home, @".local/share/applications/jetbrains-rider.desktop"));
-
- if (shortcut.Exists)
- {
- string[] lines = File.ReadAllLines(shortcut.FullName);
- foreach (string line in lines)
- {
- if (!line.StartsWith("Exec=\""))
- continue;
- string path = line.Split('"').Where((item, index) => index == 1).SingleOrDefault();
- if (string.IsNullOrEmpty(path))
- continue;
-
- if (installInfos.Any(a => a.Path == path)) // avoid adding similar build as from toolbox
- continue;
- installInfos.Add(new RiderInfo(path, false));
- }
- }
- }
-
- // snap install
- string snapInstallPath = "/snap/rider/current/bin/rider.sh";
- if (new FileInfo(snapInstallPath).Exists)
- installInfos.Add(new RiderInfo(snapInstallPath, false));
-
- return installInfos.ToArray();
- }
-
- private static RiderInfo[] CollectRiderInfosMac()
- {
- var installInfos = new List<RiderInfo>();
- // "/Applications/*Rider*.app"
- // should be combined with "Contents/MacOS/rider"
- var folder = new DirectoryInfo("/Applications");
- if (folder.Exists)
- {
- installInfos.AddRange(folder.GetDirectories("*Rider*.app")
- .Select(a => new RiderInfo(Path.Combine(a.FullName, "Contents/MacOS/rider"), false))
- .ToList());
- }
-
- // /Users/user/Library/Application Support/JetBrains/Toolbox/apps/Rider/ch-1/181.3870.267/Rider EAP.app
- // should be combined with "Contents/MacOS/rider"
- string toolboxRiderRootPath = GetToolboxBaseDir();
- var paths = CollectPathsFromToolbox(toolboxRiderRootPath, "", "Rider*.app", true)
- .Select(a => new RiderInfo(Path.Combine(a, "Contents/MacOS/rider"), true));
- installInfos.AddRange(paths);
-
- return installInfos.ToArray();
- }
-
- [SupportedOSPlatform("windows")]
- private static RiderInfo[] CollectRiderInfosWindows()
- {
- var installInfos = new List<RiderInfo>();
- var toolboxRiderRootPath = GetToolboxBaseDir();
- var installPathsToolbox = CollectPathsFromToolbox(toolboxRiderRootPath, "bin", "rider64.exe", false).ToList();
- installInfos.AddRange(installPathsToolbox.Select(a => new RiderInfo(a, true)).ToList());
-
- var installPaths = new List<string>();
- const string registryKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
- CollectPathsFromRegistry(registryKey, installPaths);
- const string wowRegistryKey = @"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
- CollectPathsFromRegistry(wowRegistryKey, installPaths);
-
- installInfos.AddRange(installPaths.Select(a => new RiderInfo(a, false)).ToList());
-
- return installInfos.ToArray();
- }
-
- private static string GetToolboxBaseDir()
- {
- if (OS.IsWindows)
- {
- string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
- return GetToolboxRiderRootPath(localAppData);
- }
-
- if (OS.IsMacOS)
- {
- var home = Environment.GetEnvironmentVariable("HOME");
- if (string.IsNullOrEmpty(home))
- return string.Empty;
- var localAppData = Path.Combine(home, @"Library/Application Support");
- return GetToolboxRiderRootPath(localAppData);
- }
-
- if (OS.IsUnixLike)
- {
- var home = Environment.GetEnvironmentVariable("HOME");
- if (string.IsNullOrEmpty(home))
- return string.Empty;
- var localAppData = Path.Combine(home, @".local/share");
- return GetToolboxRiderRootPath(localAppData);
- }
-
- return string.Empty;
- }
-
-
- private static string GetToolboxRiderRootPath(string localAppData)
- {
- var toolboxPath = Path.Combine(localAppData, @"JetBrains/Toolbox");
- var settingsJson = Path.Combine(toolboxPath, ".settings.json");
-
- if (File.Exists(settingsJson))
- {
- var path = SettingsJson.GetInstallLocationFromJson(File.ReadAllText(settingsJson));
- if (!string.IsNullOrEmpty(path))
- toolboxPath = path;
- }
-
- var toolboxRiderRootPath = Path.Combine(toolboxPath, @"apps/Rider");
- return toolboxRiderRootPath;
- }
-
- internal static ProductInfo GetBuildVersion(string path)
- {
- var buildTxtFileInfo = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
- var dir = buildTxtFileInfo.DirectoryName;
- if (!Directory.Exists(dir))
- return null;
- var buildVersionFile = new FileInfo(Path.Combine(dir, "product-info.json"));
- if (!buildVersionFile.Exists)
- return null;
- var json = File.ReadAllText(buildVersionFile.FullName);
- return ProductInfo.GetProductInfo(json);
- }
-
- internal static Version GetBuildNumber(string path)
- {
- var file = new FileInfo(Path.Combine(path, GetRelativePathToBuildTxt()));
- if (!file.Exists)
- return null;
- var text = File.ReadAllText(file.FullName);
- if (text.Length <= 3)
- return null;
-
- var versionText = text.Substring(3);
- return Version.TryParse(versionText, out var v) ? v : null;
- }
-
- internal static bool IsToolbox(string path)
- {
- return path.StartsWith(GetToolboxBaseDir());
- }
-
- private static string GetRelativePathToBuildTxt()
- {
- if (OS.IsWindows || OS.IsUnixLike)
- return "../../build.txt";
- if (OS.IsMacOS)
- return "Contents/Resources/build.txt";
- throw new InvalidOperationException("Unknown OS.");
- }
-
- [SupportedOSPlatform("windows")]
- private static void CollectPathsFromRegistry(string registryKey, List<string> installPaths)
- {
- using (var key = Registry.CurrentUser.OpenSubKey(registryKey))
- {
- CollectPathsFromRegistry(installPaths, key);
- }
- using (var key = Registry.LocalMachine.OpenSubKey(registryKey))
- {
- CollectPathsFromRegistry(installPaths, key);
- }
- }
-
- [SupportedOSPlatform("windows")]
- private static void CollectPathsFromRegistry(List<string> installPaths, RegistryKey key)
- {
- if (key == null) return;
- foreach (var subkeyName in key.GetSubKeyNames().Where(a => a.Contains("Rider")))
- {
- using (var subkey = key.OpenSubKey(subkeyName))
- {
- var folderObject = subkey?.GetValue("InstallLocation");
- if (folderObject == null) continue;
- var folder = folderObject.ToString();
- var possiblePath = Path.Combine(folder, @"bin\rider64.exe");
- if (File.Exists(possiblePath))
- installPaths.Add(possiblePath);
- }
- }
- }
-
- private static string[] CollectPathsFromToolbox(string toolboxRiderRootPath, string dirName, string searchPattern,
- bool isMac)
- {
- if (!Directory.Exists(toolboxRiderRootPath))
- return Array.Empty<string>();
-
- var channelDirs = Directory.GetDirectories(toolboxRiderRootPath);
- var paths = channelDirs.SelectMany(channelDir =>
- {
- try
- {
- // use history.json - last entry stands for the active build https://jetbrains.slack.com/archives/C07KNP99D/p1547807024066500?thread_ts=1547731708.057700&cid=C07KNP99D
- var historyFile = Path.Combine(channelDir, ".history.json");
- if (File.Exists(historyFile))
- {
- var json = File.ReadAllText(historyFile);
- var build = ToolboxHistory.GetLatestBuildFromJson(json);
- if (build != null)
- {
- var buildDir = Path.Combine(channelDir, build);
- var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
- if (executablePaths.Any())
- return executablePaths;
- }
- }
-
- var channelFile = Path.Combine(channelDir, ".channel.settings.json");
- if (File.Exists(channelFile))
- {
- var json = File.ReadAllText(channelFile).Replace("active-application", "active_application");
- var build = ToolboxInstallData.GetLatestBuildFromJson(json);
- if (build != null)
- {
- var buildDir = Path.Combine(channelDir, build);
- var executablePaths = GetExecutablePaths(dirName, searchPattern, isMac, buildDir);
- if (executablePaths.Any())
- return executablePaths;
- }
- }
-
- // changes in toolbox json files format may brake the logic above, so return all found Rider installations
- return Directory.GetDirectories(channelDir)
- .SelectMany(buildDir => GetExecutablePaths(dirName, searchPattern, isMac, buildDir));
- }
- catch (Exception e)
- {
- // do not write to Debug.Log, just log it.
- Logger.Warn($"Failed to get RiderPath from {channelDir}", e);
- }
-
- return Array.Empty<string>();
- })
- .Where(c => !string.IsNullOrEmpty(c))
- .ToArray();
- return paths;
- }
-
- private static string[] GetExecutablePaths(string dirName, string searchPattern, bool isMac, string buildDir)
- {
- var folder = new DirectoryInfo(Path.Combine(buildDir, dirName));
- if (!folder.Exists)
- return Array.Empty<string>();
-
- if (!isMac)
- return new[] { Path.Combine(folder.FullName, searchPattern) }.Where(File.Exists).ToArray();
- return folder.GetDirectories(searchPattern).Select(f => f.FullName)
- .Where(Directory.Exists).ToArray();
- }
-
- // Disable the "field is never assigned" compiler warning. We never assign it, but Unity does.
- // Note that Unity disable this warning in the generated C# projects
-#pragma warning disable 0649
-
- [Serializable]
- class SettingsJson
- {
- public string install_location;
-
- [return: MaybeNull]
- public static string GetInstallLocationFromJson(string json)
- {
- try
- {
- return JsonConvert.DeserializeObject<SettingsJson>(json).install_location;
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get install_location from json {json}");
- }
-
- return null;
- }
- }
-
- [Serializable]
- class ToolboxHistory
- {
- public List<ItemNode> history;
-
- public static string GetLatestBuildFromJson(string json)
- {
- try
- {
- return JsonConvert.DeserializeObject<ToolboxHistory>(json).history.LastOrDefault()?.item.build;
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get latest build from json {json}");
- }
-
- return null;
- }
- }
-
- [Serializable]
- class ItemNode
- {
- public BuildNode item;
- }
-
- [Serializable]
- class BuildNode
- {
- public string build;
- }
-
- [Serializable]
- public class ProductInfo
- {
- public string version;
- public string versionSuffix;
-
- [return: MaybeNull]
- internal static ProductInfo GetProductInfo(string json)
- {
- try
- {
- var productInfo = JsonConvert.DeserializeObject<ProductInfo>(json);
- return productInfo;
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get version from json {json}");
- }
-
- return null;
- }
- }
-
- // ReSharper disable once ClassNeverInstantiated.Global
- [Serializable]
- class ToolboxInstallData
- {
- // ReSharper disable once InconsistentNaming
- public ActiveApplication active_application;
-
- [return: MaybeNull]
- public static string GetLatestBuildFromJson(string json)
- {
- try
- {
- var toolbox = JsonConvert.DeserializeObject<ToolboxInstallData>(json);
- var builds = toolbox.active_application.builds;
- if (builds != null && builds.Any())
- return builds.First();
- }
- catch (Exception)
- {
- Logger.Warn($"Failed to get latest build from json {json}");
- }
-
- return null;
- }
- }
-
- [Serializable]
- class ActiveApplication
- {
- public List<string> builds;
- }
-
-#pragma warning restore 0649
-
- public struct RiderInfo
- {
- // ReSharper disable once NotAccessedField.Global
- public bool IsToolbox;
- public string Presentation;
- public Version BuildNumber;
- public ProductInfo ProductInfo;
- public string Path;
-
- public RiderInfo(string path, bool isToolbox)
- {
- BuildNumber = GetBuildNumber(path);
- ProductInfo = GetBuildVersion(path);
- Path = new FileInfo(path).FullName; // normalize separators
- var presentation = $"Rider {BuildNumber}";
-
- if (ProductInfo != null && !string.IsNullOrEmpty(ProductInfo.version))
- {
- var suffix = string.IsNullOrEmpty(ProductInfo.versionSuffix) ? "" : $" {ProductInfo.versionSuffix}";
- presentation = $"Rider {ProductInfo.version}{suffix}";
- }
-
- if (isToolbox)
- presentation += " (JetBrains Toolbox)";
-
- Presentation = presentation;
- IsToolbox = isToolbox;
- }
- }
-
- private static class Logger
- {
- internal static void Warn(string message, Exception e = null)
- {
- throw new Exception(message, e);
- }
- }
- }
-}
diff --git a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
index f55ca4c7d7..5c09f1f83a 100644
--- a/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
+++ b/modules/mono/editor/GodotTools/GodotTools/Ides/Rider/RiderPathManager.cs
@@ -4,11 +4,19 @@ using System.IO;
using System.Linq;
using Godot;
using GodotTools.Internals;
+using JetBrains.Rider.PathLocator;
namespace GodotTools.Ides.Rider
{
public static class RiderPathManager
{
+ private static readonly RiderPathLocator RiderPathLocator;
+
+ static RiderPathManager()
+ {
+ RiderPathLocator = new RiderPathLocator(new RiderLocatorEnvironment());
+ }
+
public static readonly string EditorPathSettingName = "dotnet/editor/editor_path_optional";
private static string GetRiderPathFromSettings()
diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
index 37f7005d01..a0bd96412a 100644
--- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
+++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/ExtensionMethods.cs
@@ -38,7 +38,7 @@ internal static class ExtensionMethods
}
private static bool IsGenerateUnmanagedCallbacksAttribute(this INamedTypeSymbol symbol)
- => symbol.ToString() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
+ => symbol.FullQualifiedNameOmitGlobal() == GeneratorClasses.GenerateUnmanagedCallbacksAttr;
public static IEnumerable<(ClassDeclarationSyntax cds, INamedTypeSymbol symbol)> SelectUnmanagedCallbacksClasses(
this IEnumerable<ClassDeclarationSyntax> source,
diff --git a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
index 2a72b7c53e..6117ae17ea 100644
--- a/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
+++ b/modules/mono/glue/GodotSharp/GodotPlugins/Main.cs
@@ -21,6 +21,13 @@ namespace GodotPlugins
private sealed class PluginLoadContextWrapper
{
private PluginLoadContext? _pluginLoadContext;
+ private readonly WeakReference _weakReference;
+
+ private PluginLoadContextWrapper(PluginLoadContext pluginLoadContext, WeakReference weakReference)
+ {
+ _pluginLoadContext = pluginLoadContext;
+ _weakReference = weakReference;
+ }
public string? AssemblyLoadedPath
{
@@ -31,7 +38,14 @@ namespace GodotPlugins
public bool IsCollectible
{
[MethodImpl(MethodImplOptions.NoInlining)]
- get => _pluginLoadContext?.IsCollectible ?? false;
+ // if _pluginLoadContext is null we already started unloading, so it was collectible
+ get => _pluginLoadContext?.IsCollectible ?? true;
+ }
+
+ public bool IsAlive
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ get => _weakReference.IsAlive;
}
[MethodImpl(MethodImplOptions.NoInlining)]
@@ -43,20 +57,14 @@ namespace GodotPlugins
bool isCollectible
)
{
- var wrapper = new PluginLoadContextWrapper();
- wrapper._pluginLoadContext = new PluginLoadContext(
- pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
- var assembly = wrapper._pluginLoadContext.LoadFromAssemblyName(assemblyName);
+ var context = new PluginLoadContext(pluginPath, sharedAssemblies, mainLoadContext, isCollectible);
+ var reference = new WeakReference(context, trackResurrection: true);
+ var wrapper = new PluginLoadContextWrapper(context, reference);
+ var assembly = context.LoadFromAssemblyName(assemblyName);
return (assembly, wrapper);
}
[MethodImpl(MethodImplOptions.NoInlining)]
- public WeakReference CreateWeakReference()
- {
- return new WeakReference(_pluginLoadContext, trackResurrection: true);
- }
-
- [MethodImpl(MethodImplOptions.NoInlining)]
internal void Unload()
{
_pluginLoadContext?.Unload();
@@ -165,7 +173,7 @@ namespace GodotPlugins
if (_editorApiAssembly == null)
throw new InvalidOperationException("The Godot editor API assembly is not loaded.");
- var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: _editorHint);
+ var (assembly, _) = LoadPlugin(assemblyPath, isCollectible: false);
NativeLibrary.SetDllImportResolver(assembly, _dllImportResolver!);
@@ -236,32 +244,29 @@ namespace GodotPlugins
Console.WriteLine("Unloading assembly load context...");
- var alcWeakReference = pluginLoadContext.CreateWeakReference();
-
pluginLoadContext.Unload();
- pluginLoadContext = null;
int startTimeMs = Environment.TickCount;
bool takingTooLong = false;
- while (alcWeakReference.IsAlive)
+ while (pluginLoadContext.IsAlive)
{
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
- if (!alcWeakReference.IsAlive)
+ if (!pluginLoadContext.IsAlive)
break;
int elapsedTimeMs = Environment.TickCount - startTimeMs;
- if (!takingTooLong && elapsedTimeMs >= 2000)
+ if (!takingTooLong && elapsedTimeMs >= 200)
{
takingTooLong = true;
// TODO: How to log from GodotPlugins? (delegate pointer?)
Console.Error.WriteLine("Assembly unloading is taking longer than expected...");
}
- else if (elapsedTimeMs >= 5000)
+ else if (elapsedTimeMs >= 1000)
{
// TODO: How to log from GodotPlugins? (delegate pointer?)
Console.Error.WriteLine(
@@ -273,6 +278,7 @@ namespace GodotPlugins
Console.WriteLine("Assembly load context unloaded successfully.");
+ pluginLoadContext = null;
return true;
}
catch (Exception e)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
index 36f5d8e2ab..74425c9835 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using System.ComponentModel;
namespace Godot
{
@@ -623,21 +624,31 @@ namespace Godot
/// </summary>
/// <param name="target">The position to look at.</param>
/// <param name="up">The relative up direction.</param>
+ /// <param name="useModelFront">
+ /// If true, then the model is oriented in reverse,
+ /// towards the model front axis (+Z, Vector3.ModelFront),
+ /// which is more useful for orienting 3D models.
+ /// </param>
/// <returns>The resulting basis matrix.</returns>
- public static Basis LookingAt(Vector3 target, Vector3 up)
+ public static Basis LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false)
{
+ up ??= Vector3.Up;
#if DEBUG
if (target.IsZeroApprox())
{
throw new ArgumentException("The vector can't be zero.", nameof(target));
}
- if (up.IsZeroApprox())
+ if (up.Value.IsZeroApprox())
{
throw new ArgumentException("The vector can't be zero.", nameof(up));
}
#endif
- Vector3 column2 = -target.Normalized();
- Vector3 column0 = up.Cross(column2);
+ Vector3 column2 = target.Normalized();
+ if (!useModelFront)
+ {
+ column2 = -column2;
+ }
+ Vector3 column0 = up.Value.Cross(column2);
#if DEBUG
if (column0.IsZeroApprox())
{
@@ -649,6 +660,13 @@ namespace Godot
return new Basis(column0, column1, column2);
}
+ /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public static Basis LookingAt(Vector3 target, Vector3 up)
+ {
+ return LookingAt(target, up, false);
+ }
+
/// <summary>
/// Returns the orthonormalized version of the basis matrix (useful to
/// call occasionally to avoid rounding errors for orthogonal matrices).
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
index 456a118b90..8217572648 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/GCHandleBridge.cs
@@ -18,5 +18,26 @@ namespace Godot.Bridge
ExceptionUtils.LogException(e);
}
}
+
+ // Returns true, if releasing the provided handle is necessary for assembly unloading to succeed.
+ // This check is not perfect and only intended to prevent things in GodotTools from being reloaded.
+ [UnmanagedCallersOnly]
+ internal static godot_bool GCHandleIsTargetCollectible(IntPtr gcHandlePtr)
+ {
+ try
+ {
+ var target = GCHandle.FromIntPtr(gcHandlePtr).Target;
+
+ if (target is Delegate @delegate)
+ return DelegateUtils.IsDelegateCollectible(@delegate).ToGodotBool();
+
+ return target.GetType().IsCollectible.ToGodotBool();
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ return godot_bool.True;
+ }
+ }
}
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 0571515e61..109643c2d4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -38,6 +38,7 @@ namespace Godot.Bridge
public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_SerializeState;
public delegate* unmanaged<IntPtr, godot_dictionary*, godot_dictionary*, void> CSharpInstanceBridge_DeserializeState;
public delegate* unmanaged<IntPtr, void> GCHandleBridge_FreeGCHandle;
+ public delegate* unmanaged<IntPtr, godot_bool> GCHandleBridge_GCHandleIsTargetCollectible;
public delegate* unmanaged<void*, void> DebuggingUtils_GetCurrentStackInfo;
public delegate* unmanaged<void> DisposablesTracker_OnGodotShuttingDown;
public delegate* unmanaged<godot_bool, void> GD_OnCoreApiAssemblyLoaded;
@@ -78,6 +79,7 @@ namespace Godot.Bridge
CSharpInstanceBridge_SerializeState = &CSharpInstanceBridge.SerializeState,
CSharpInstanceBridge_DeserializeState = &CSharpInstanceBridge.DeserializeState,
GCHandleBridge_FreeGCHandle = &GCHandleBridge.FreeGCHandle,
+ GCHandleBridge_GCHandleIsTargetCollectible = &GCHandleBridge.GCHandleIsTargetCollectible,
DebuggingUtils_GetCurrentStackInfo = &DebuggingUtils.GetCurrentStackInfo,
DisposablesTracker_OnGodotShuttingDown = &DisposablesTracker.OnGodotShuttingDown,
GD_OnCoreApiAssemblyLoaded = &GD.OnCoreApiAssemblyLoaded,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index 279dadf425..6c2fb7374c 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -500,24 +500,17 @@ namespace Godot
Type? returnType = hasReturn ? DeserializeType(reader) : typeof(void);
int parametersCount = reader.ReadInt32();
+ var parameterTypes = parametersCount == 0 ? Type.EmptyTypes : new Type[parametersCount];
- if (parametersCount > 0)
+ for (int i = 0; i < parametersCount; i++)
{
- var parameterTypes = new Type[parametersCount];
-
- for (int i = 0; i < parametersCount; i++)
- {
- Type? parameterType = DeserializeType(reader);
- if (parameterType == null)
- return false;
- parameterTypes[i] = parameterType;
- }
-
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
- return methodInfo != null && methodInfo.ReturnType == returnType;
+ Type? parameterType = DeserializeType(reader);
+ if (parameterType == null)
+ return false;
+ parameterTypes[i] = parameterType;
}
- methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags);
+ methodInfo = declaringType.GetMethod(methodName, (BindingFlags)flags, null, parameterTypes, null);
return methodInfo != null && methodInfo.ReturnType == returnType;
}
@@ -560,6 +553,38 @@ namespace Godot
return type;
}
+ // Returns true, if unloading the delegate is necessary for assembly unloading to succeed.
+ // This check is not perfect and only intended to prevent things in GodotTools from being reloaded.
+ internal static bool IsDelegateCollectible(Delegate @delegate)
+ {
+ if (@delegate.GetType().IsCollectible)
+ return true;
+
+ if (@delegate is MulticastDelegate multicastDelegate)
+ {
+ Delegate[] invocationList = multicastDelegate.GetInvocationList();
+
+ if (invocationList.Length > 1)
+ {
+ foreach (Delegate oneDelegate in invocationList)
+ if (IsDelegateCollectible(oneDelegate))
+ return true;
+
+ return false;
+ }
+ }
+
+ if (@delegate.Method.IsCollectible)
+ return true;
+
+ object? target = @delegate.Target;
+
+ if (target is not null && target.GetType().IsCollectible)
+ return true;
+
+ return false;
+ }
+
internal static class RuntimeTypeConversionHelper
{
[SuppressMessage("ReSharper", "RedundantNameQualifier")]
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
index b9a5ac82d1..12e8a638d3 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotObject.base.cs
@@ -125,7 +125,10 @@ namespace Godot
NativePtr = IntPtr.Zero;
}
- DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
+ if (_weakReferenceToSelf != null)
+ {
+ DisposablesTracker.UnregisterGodotObject(this, _weakReferenceToSelf);
+ }
}
/// <summary>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
index 1e2aaa299f..ae2c025137 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs
@@ -1,5 +1,6 @@
using System;
using System.Runtime.InteropServices;
+using System.ComponentModel;
namespace Godot
{
@@ -175,14 +176,26 @@ namespace Godot
/// </summary>
/// <param name="target">The object to look at.</param>
/// <param name="up">The relative up direction.</param>
+ /// <param name="useModelFront">
+ /// If true, then the model is oriented in reverse,
+ /// towards the model front axis (+Z, Vector3.ModelFront),
+ /// which is more useful for orienting 3D models.
+ /// </param>
/// <returns>The resulting transform.</returns>
- public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
+ public readonly Transform3D LookingAt(Vector3 target, Vector3? up = null, bool useModelFront = false)
{
Transform3D t = this;
- t.SetLookAt(Origin, target, up);
+ t.SetLookAt(Origin, target, up ?? Vector3.Up, useModelFront);
return t;
}
+ /// <inheritdoc cref="LookingAt(Vector3, Nullable{Vector3}, bool)"/>
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public readonly Transform3D LookingAt(Vector3 target, Vector3 up)
+ {
+ return LookingAt(target, up, false);
+ }
+
/// <summary>
/// Returns the transform with the basis orthogonal (90 degrees),
/// and normalized axis vectors (scale of 1 or -1).
@@ -247,9 +260,9 @@ namespace Godot
return new Transform3D(Basis * tmpBasis, Origin);
}
- private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up)
+ private void SetLookAt(Vector3 eye, Vector3 target, Vector3 up, bool useModelFront = false)
{
- Basis = Basis.LookingAt(target - eye, up);
+ Basis = Basis.LookingAt(target - eye, up, useModelFront);
Origin = eye;
}
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
index c773c0fda6..d929b5c6ab 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector3.cs
@@ -660,6 +660,13 @@ namespace Godot
private static readonly Vector3 _forward = new Vector3(0, 0, -1);
private static readonly Vector3 _back = new Vector3(0, 0, 1);
+ private static readonly Vector3 _modelLeft = new Vector3(1, 0, 0);
+ private static readonly Vector3 _modelRight = new Vector3(-1, 0, 0);
+ private static readonly Vector3 _modelTop = new Vector3(0, 1, 0);
+ private static readonly Vector3 _modelBottom = new Vector3(0, -1, 0);
+ private static readonly Vector3 _modelFront = new Vector3(0, 0, 1);
+ private static readonly Vector3 _modelRear = new Vector3(0, 0, -1);
+
/// <summary>
/// Zero vector, a vector with all components set to <c>0</c>.
/// </summary>
@@ -712,6 +719,31 @@ namespace Godot
public static Vector3 Back { get { return _back; } }
/// <summary>
+ /// Unit vector pointing towards the left side of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelLeft { get { return _modelLeft; } }
+ /// <summary>
+ /// Unit vector pointing towards the right side of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelRight { get { return _modelRight; } }
+ /// <summary>
+ /// Unit vector pointing towards the top side (up) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelTop { get { return _modelTop; } }
+ /// <summary>
+ /// Unit vector pointing towards the bottom side (down) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelBottom { get { return _modelBottom; } }
+ /// <summary>
+ /// Unit vector pointing towards the front side (facing forward) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelFront { get { return _modelFront; } }
+ /// <summary>
+ /// Unit vector pointing towards the rear side (back) of imported 3D assets.
+ /// </summary>
+ public static Vector3 ModelRear { get { return _modelRear; } }
+
+ /// <summary>
/// Constructs a new <see cref="Vector3"/> with the given components.
/// </summary>
/// <param name="x">The vector's X component.</param>
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
index 16820e363a..8a36b3e514 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharp/GodotSharp.csproj
@@ -20,7 +20,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>GodotSharp</PackageId>
- <Version>4.1.0</Version>
+ <Version>4.2.0</Version>
<PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharp</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
diff --git a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
index c249ebf804..db9337d4eb 100644
--- a/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
+++ b/modules/mono/glue/GodotSharp/GodotSharpEditor/GodotSharpEditor.csproj
@@ -15,7 +15,7 @@
<Authors>Godot Engine contributors</Authors>
<PackageId>GodotSharpEditor</PackageId>
- <Version>4.1.0</Version>
+ <Version>4.2.0</Version>
<PackageVersion>$(PackageVersion_GodotSharp)</PackageVersion>
<RepositoryUrl>https://github.com/godotengine/godot/tree/master/modules/mono/glue/GodotSharp/GodotSharpEditor</RepositoryUrl>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
diff --git a/modules/mono/mono_gd/gd_mono.cpp b/modules/mono/mono_gd/gd_mono.cpp
index 92fa30e5e8..4337478370 100644
--- a/modules/mono/mono_gd/gd_mono.cpp
+++ b/modules/mono/mono_gd/gd_mono.cpp
@@ -488,15 +488,31 @@ bool GDMono::_load_project_assembly() {
#endif
#ifdef GD_MONO_HOT_RELOAD
+void GDMono::reload_failure() {
+ if (++project_load_failure_count >= (int)GLOBAL_GET("dotnet/project/assembly_reload_attempts")) {
+ // After reloading a project has failed n times in a row, update the path and modification time
+ // to stop any further attempts at loading this assembly, which probably is never going to work anyways.
+ project_load_failure_count = 0;
+
+ ERR_PRINT_ED(".NET: Giving up on assembly reloading. Please restart the editor if unloading was failing.");
+
+ String assembly_name = path::get_csharp_project_name();
+ String assembly_path = GodotSharpDirs::get_res_temp_assemblies_dir().path_join(assembly_name + ".dll");
+ assembly_path = ProjectSettings::get_singleton()->globalize_path(assembly_path);
+ project_assembly_path = assembly_path.simplify_path();
+ project_assembly_modified_time = FileAccess::get_modified_time(assembly_path);
+ }
+}
+
Error GDMono::reload_project_assemblies() {
ERR_FAIL_COND_V(!runtime_initialized, ERR_BUG);
finalizing_scripts_domain = true;
- CSharpLanguage::get_singleton()->_on_scripts_domain_about_to_unload();
-
if (!get_plugin_callbacks().UnloadProjectPluginCallback()) {
- ERR_FAIL_V_MSG(Error::FAILED, ".NET: Failed to unload assemblies.");
+ ERR_PRINT_ED(".NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information.");
+ reload_failure();
+ return FAILED;
}
finalizing_scripts_domain = false;
@@ -504,10 +520,16 @@ Error GDMono::reload_project_assemblies() {
// Load the project's main assembly. Here, during hot-reloading, we do
// consider failing to load the project's main assembly to be an error.
if (!_load_project_assembly()) {
- print_error(".NET: Failed to load project assembly.");
+ ERR_PRINT_ED(".NET: Failed to load project assembly.");
+ reload_failure();
return ERR_CANT_OPEN;
}
+ if (project_load_failure_count > 0) {
+ project_load_failure_count = 0;
+ ERR_PRINT_ED(".NET: Assembly reloading succeeded after failures.");
+ }
+
return OK;
}
#endif
diff --git a/modules/mono/mono_gd/gd_mono.h b/modules/mono/mono_gd/gd_mono.h
index 398f94d924..c629ab2eff 100644
--- a/modules/mono/mono_gd/gd_mono.h
+++ b/modules/mono/mono_gd/gd_mono.h
@@ -68,6 +68,7 @@ class GDMono {
String project_assembly_path;
uint64_t project_assembly_modified_time = 0;
+ int project_load_failure_count = 0;
#ifdef TOOLS_ENABLED
bool _load_project_assembly();
@@ -144,6 +145,7 @@ public:
#endif
#ifdef GD_MONO_HOT_RELOAD
+ void reload_failure();
Error reload_project_assemblies();
#endif
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index e254484df9..8fdf163b26 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -79,6 +79,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, SerializeState);
CHECK_CALLBACK_NOT_NULL(CSharpInstanceBridge, DeserializeState);
CHECK_CALLBACK_NOT_NULL(GCHandleBridge, FreeGCHandle);
+ CHECK_CALLBACK_NOT_NULL(GCHandleBridge, GCHandleIsTargetCollectible);
CHECK_CALLBACK_NOT_NULL(DebuggingUtils, GetCurrentStackInfo);
CHECK_CALLBACK_NOT_NULL(DisposablesTracker, OnGodotShuttingDown);
CHECK_CALLBACK_NOT_NULL(GD, OnCoreApiAssemblyLoaded);
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 9201da7cae..f604e4d681 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -104,6 +104,7 @@ struct ManagedCallbacks {
using FuncCSharpInstanceBridge_SerializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
using FuncCSharpInstanceBridge_DeserializeState = void(GD_CLR_STDCALL *)(GCHandleIntPtr, const Dictionary *, const Dictionary *);
using FuncGCHandleBridge_FreeGCHandle = void(GD_CLR_STDCALL *)(GCHandleIntPtr);
+ using FuncGCHandleBridge_GCHandleIsTargetCollectible = bool(GD_CLR_STDCALL *)(GCHandleIntPtr);
using FuncDebuggingUtils_GetCurrentStackInfo = void(GD_CLR_STDCALL *)(Vector<ScriptLanguage::StackInfo> *);
using FuncDisposablesTracker_OnGodotShuttingDown = void(GD_CLR_STDCALL *)();
using FuncGD_OnCoreApiAssemblyLoaded = void(GD_CLR_STDCALL *)(bool);
@@ -138,6 +139,7 @@ struct ManagedCallbacks {
FuncCSharpInstanceBridge_SerializeState CSharpInstanceBridge_SerializeState;
FuncCSharpInstanceBridge_DeserializeState CSharpInstanceBridge_DeserializeState;
FuncGCHandleBridge_FreeGCHandle GCHandleBridge_FreeGCHandle;
+ FuncGCHandleBridge_GCHandleIsTargetCollectible GCHandleBridge_GCHandleIsTargetCollectible;
FuncDebuggingUtils_GetCurrentStackInfo DebuggingUtils_GetCurrentStackInfo;
FuncDisposablesTracker_OnGodotShuttingDown DisposablesTracker_OnGodotShuttingDown;
FuncGD_OnCoreApiAssemblyLoaded GD_OnCoreApiAssemblyLoaded;