Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET: Failed to unload assemblies. Please check <this issue> for more information. #78513

Open
RedworkDE opened this issue Jun 21, 2023 · 113 comments

Comments

@RedworkDE
Copy link
Member

RedworkDE commented Jun 21, 2023

Godot version

Any 4.x version

Issue description

Assembly reloading can fail for various reasons, usually because a library used in tools code is not compatible with assembly unloading.

After unloading has failed, C# scripts will be unavailable until the editor is restarted (in rare cases it may be possible to complete the unloading by re-building assemblies after some time).

If assembly unloading fails for your project check Microsoft's troubleshooting instructions and ensure that you are not using one of the libraries known to be incompatible:

If you know of additional libraries that cause issues, please leave a comment.
If your code doesn't use any libraries, doesn't violate any guidelines and you believe unloading is blocked by godot, please open a new issue. Already reported causes are:

Minimal reproduction project & Cleanup example

using Godot;
using System;

[Tool]
public partial class UnloadingIssuesSample : Node
{
    public override void _Ready()
    {
        // block unloading with a strong handle
        var handle = System.Runtime.InteropServices.GCHandle.Alloc(this);

        // register cleanup code to prevent unloading issues
        System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly()).Unloading += alc =>
        {
            // handle.Free();
        };
    }
}

Footnotes

  1. Bugsquad edit 2

@wp2000x
Copy link

wp2000x commented Jul 6, 2023

Something was wrong with one of my [Tool] class in code after upgrade from 4.0 to 4.1 ("This class does not inherit from Node") and got this error. I just changed it to Node3D and back, and then the "cache" bug got fixed magicly?

@Quinn-L
Copy link

Quinn-L commented Jul 7, 2023

System.Text.Json also has this issue where serializing your classes will be held internally by the Json library.

The workaround for this using this library is also copied below:

var assembly = typeof(JsonSerializerOptions).Assembly;
var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
clearCacheMethod?.Invoke(null, new object?[] { null }); 

As far as I'm aware Godot doesn't provide an event for signalling when your dll/plugin is about to be unloaded, so you essentially need to call the above solution after every serialization/deserialization.

@RedworkDE
Copy link
Member Author

RedworkDE commented Jul 7, 2023

As far as I'm aware Godot doesn't provide an event for signalling when your dll/plugin is about to be unloaded, so you essentially need to call the above solution after every serialization/deserialization.

You can use the normal AssemblyLoadContext.Unloading event to trigger such cleanup code. I unfolded the code example in the initial post demonstrating its usage.

@Quinn-L
Copy link

Quinn-L commented Jul 7, 2023

You can use the normal AssemblyLoadContext.Unloading event to trigger such cleanup code. I unfolded the code example in the initial post demonstrating its usage.

Thanks for the info, I somehow missed that.

As an FYI, I played around with your solution trying it on a EditorPlugin derived class, but didn't find it reliable in on _Ready. As far as I could tell (or maybe I tested it wrong), rebuilding would reload the C# project but not re-invoke _Ready causing the error on subsequent rebuilds since the 'new' Unloading event is not registered (I assume because the node itself is not removed and re-added).

My solution was to place the code within a [ModuleInitializer] method, ie:

internal class AppModule
{
    [System.Runtime.CompilerServices.ModuleInitializer]
    public static void Initialize()
    {
        System.Runtime.Loader.AssemblyLoadContext.GetLoadContext(System.Reflection.Assembly.GetExecutingAssembly()).Unloading += alc =>
        {
            var assembly = typeof(JsonSerializerOptions).Assembly;
            var updateHandlerType = assembly.GetType("System.Text.Json.JsonSerializerOptionsUpdateHandler");
            var clearCacheMethod = updateHandlerType?.GetMethod("ClearCache", BindingFlags.Static | BindingFlags.Public);
            clearCacheMethod?.Invoke(null, new object?[] { null });

            // Unload any other unloadable references
        };
    }
}

This ensures it is not dependent on any node(s) and is always registered only once, and re-registered upon reloading the assembly.

@taylorhadden
Copy link

I am not using Json.NET or System.Text.Json, and I am experiencing this issue. In the worst case, it results in data loss as serialized properties from C# Node scripts are reset to default.

The only library I am referencing is one I have authored. My .csproj file looks like this:

<Project Sdk="Godot.NET.Sdk/4.1.0">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <EnableDynamicLoading>true</EnableDynamicLoading>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\Lib\Hawthorn\Hawthorn\Hawthorn.csproj" />
  </ItemGroup>
</Project>

That project's csproj is dead simple, with no other project references.

@RedworkDE, you mentioned "does not violate any guidelines"; what are these guidelines? Is there a documentation page? Something else? Thanks.

@RedworkDE
Copy link
Member Author

It refers to Microsoft's docs page that was linked a bit further up: https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#troubleshoot-unloadability-issues

Also all these really also have to apply to the library, but for most libraries there isn't too much you can do about it (except not use the library)

@taylorhadden
Copy link

I am hitting this issue and I'm fairly confident it is not my (direct) fault.

I can reliably cause this error to appear in the console by running a C# rebuild while an offending Scene is loaded in the editor:

modules/mono/glue/runtime_interop.cpp:1324 - System.ArgumentException: An item with the same key has already been added. Key: BehaviorActorView`1[LeshonkiView]
     at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
     at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
     at Godot.Bridge.ScriptManagerBridge.ScriptTypeBiMap.Add(IntPtr scriptPtr, Type scriptType) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs:line 23
     at Godot.Bridge.ScriptManagerBridge.TryReloadRegisteredScriptWithClass(IntPtr scriptPtr) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 579

If I build again, I will get the "Failed to unload assemblies" error. If I close the offending scene and run the rebuild again, I rebuild without any issues and everything is fine.

I've been trying to figure out precisely what's needed to get this problem to repro. It seems connected to the new [GlobalClass] attribute and resources, but not necessarily on a one-to-one basis.

@RedworkDE
Copy link
Member Author

You also seem to be using generic Nodes which also cause issues in some cases, see #79007

@taylorhadden
Copy link

taylorhadden commented Jul 15, 2023

Yeah, it seems that's the root cause. That would be very unfortunate if true. I have a micro reproduction project and I'll write up another issue. It runs fine but for the occasional compilation issue.

@taylorhadden
Copy link

I have created that reproduction project: #79519

@Pilvinen
Copy link

I don't know what is going on or why but I started getting this error today in Godot 4.1.1 .NET version. It's completely blocking me from doing any development. I've spent all day trying to hunt down weird behaviors. It seems that when this issue happens it's corrupting properties in the editor - or possibly inherited scenes are breaking.
Restarting the editor is good for one run. But I keep constantly having to restart.
This is 100% blocking all game development for me.

@Pilvinen
Copy link

Pilvinen commented Jul 23, 2023

Slight update. I'm just guessing here, but I suspect the issue might be somehow related to the new GlobalClass. That's all I can think of. I got rid of the errors by deleting a node which had either been added via GlobalClass node or it was a regular node with the same script attached to it. Either way, I deleted it and the error was gone. I added it back in as GlobalClass node - the error stayed gone.
Maybe this is helpful or maybe it is not, but I thought I'd mention it. It was a nightmare to track down.

@swissdude15
Copy link

I can absolutely understand that. The problems since 4.1 are unfortunately unexpected, varied and sometimes difficult to analyse. The new GlobalClass attribute does not work correctly for me so far, so I will not use this great feature for the time being. But I still got the error message above.

As described above, some problems are related to tool code whose assemblies cannot be unloaded during build. This certainly has side effects on GlobalClass classes. In principle, this is a problem of .net and poorly implemented libs (which cannot be unloaded) and not an engine bug. But it is important that a solution is found, because it makes the use of .net, especially as tool-code in the editor, much more difficult.

The simplest workaround is probably to close the problematic scene tabs (mostly [tool] code) before each build. Of course, this is annoying and tedious.

Otherwise, try the code listed at the top. Surprisingly, the following worked for me:

`[Tool]
...
            AssemblyLoadContext.GetLoadContext(Assembly.GetExecutingAssembly()).Unloading += alc =>
            {

               // trigger unload 
                AssemblyLoadContext.GetLoadContext(typeof(JsonSerializerOptions).Assembly).Unload();
                AssemblyLoadContext.GetLoadContext(typeof(DataContractJsonSerializer).Assembly).Unload();

                // Unload any other unloadable references					

            };   

With this code, the unload works after an incorrect attempt.

@Ryan-000
Copy link
Contributor

Yeah, it seems that's the root cause. That would be very unfortunate if true. I have a micro reproduction project and I'll write up another issue. It runs fine but for the occasional compilation issue.

I added generic nodes today, and I started getting this issue.

@ashelleyPurdue
Copy link

I'm getting this issue in 4.1, and I'm not using any third-party libraries, nor am I using generic nodes, nor am I using the [GlobalClass] or [Tool] attributes. It just seems to be happening at random whenever I recompile.

Well, OK. I used to have a C# script that had both of those attributes, but I've since rewritten it in GDScript, so it shouldn't be relevant anymore...right? Is there any chance that the ghost of the C# version of that script still lives on? Maybe in some cache somewhere?

@Ryan-000
Copy link
Contributor

Ryan-000 commented Jul 29, 2023

Also happens whenever I enable a C# editor plugin, and only goes away once I disable the plugin and restart the editor.

@swissdude15
Copy link

The new [GlobalClass] attribute also causes this problem because these classes make it act like [Tool] code. The basic problem probably lies in the System.Collection.Generic class. :

  modules/mono/glue/runtime_interop.cpp:1324 - System.ArgumentException: An item with the same key has already been added. Key: Game.WeaponAbilityData
     at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
     at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
     at Godot.Bridge.ScriptManagerBridge.ScriptTypeBiMap.Add(IntPtr scriptPtr, Type scriptType) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.types.cs:line 23
     at Godot.Bridge.ScriptManagerBridge.AddScriptBridge(IntPtr scriptPtr, godot_string* scriptPath) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 419

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

@swissdude15
Copy link

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

Nope. We decided not to waste so much time with this problem and rewrite certain parts of the code in GDScript to work around the trouble spots with C#. We are not happy with this. This will especially affect [GlobalClass] and [Tool] code, probably also System.Collection.Generic will cause trouble.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

I think I figured out what was causing it for me. It wasn't editor plugins, global classes, or tool scripts. No, it's because I had more than one Node class in a single script file.

namespace FlightSpeedway
{
    public partial class Player : CharacterBody3D
    {
        private PlayerState _currentState;
        private Vector3 _spawnPoint;
        private Vector3 _spawnRotation;

        public void ChangeState<TState>() where TState : PlayerState
        {
            GD.Print($"Changing state to {typeof(TState).Name}");
            _currentState?.OnStateExited();

            foreach (var state in States())
            {
                state.ProcessMode = ProcessModeEnum.Disabled;
            }

            _currentState = States().First(s => s is TState);
            _currentState.ProcessMode = ProcessModeEnum.Inherit;
            _currentState.OnStateEntered();
        }

        private IEnumerable<PlayerState> States()
        {
            for (int i = 0; i < GetChildCount(); i++)
            {
                var child = GetChild<Node>(i);

                if (child is PlayerState state)
                    yield return state;
            }
        }
    }

    public partial class PlayerState : Node
    {
        protected Player _player => GetParent<Player>();
        protected Node3D _model => GetNode<Node3D>("%Model");

        public virtual void OnStateEntered() {}
        public virtual void OnStateExited() {}
    }
}

Once I moved PlayerState to a separate file, the issue stopped happening.

In case this is relevant, there are other node classes(such as PlayerFlyState) that inherit PlayerState. Those nodes exist as children of the player node, and they get enabled/disabled as the player changes states.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

I will try today the method mentioned above by Quinn-L and put the unload handler in an EditorPlugin. But I have rather little hope that it will work cleanly.

Nope. We decided not to waste so much time with this problem and rewrite certain parts of the code in GDScript to work around the trouble spots with C#. We are not happy with this. This will especially affect [GlobalClass] and [Tool] code, probably also System.Collection.Generic will cause trouble.

System.Collections.Generic definitely does not cause trouble. I've been using it extensively in a different Godot 4.1 project, and have never seen this problem in that project.

@swissdude15
Copy link

No, it's because I had more than one Node class in a single script file.

I can't confirm, we have all classes in separate files and as soon as [Tool] or [GlobalClass] is used, there are the problems described above. With [Tool] code you can work around the problem by closing these scene tabs before compiling. With [GlobalClass] you can't, because the editor keeps instances of these classes persistently in the background and the binding of some libs prevents unloading. And as I see it, System.Collection.Generic is used by the Godot code generator and this might cause trouble, because maybe one of the libraries in this namespace can't be unloaded correctly. I.e. even if you don't use a Generic in your [GlobalClass] script, it causes problems in the editor. At least this is always the case with us.

@ashelleyPurdue
Copy link

ashelleyPurdue commented Aug 12, 2023

No, it's because I had more than one Node class in a single script file.

I can't confirm, we have all classes in separate files and as soon as [Tool] or [GlobalClass] is used, there are the problems described above.

I'm not saying those scripts don't trigger the problem; I'm saying that multiple nodes in one file also triggers this problem.

@taylorhadden
Copy link

There are multiple causes of this problem. From the comments, there are at least three ways you can run into the issue..

@swissdude15

This comment was marked as off-topic.

@DevMimas
Copy link

Hello, I received the same error message in version 4.2.2. When changing an EditorPlugin written in C#. More precisely, when a script was attached to the [Tool] attribute to use it in the EditorPlugin.

Godot Engine v4.2.2.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.

modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:513 - .NET: Giving up on assembly reloading. Please restart the editor if unloading was failing. (User)

Good idea to write this in an error message.

@sebialex
Copy link

sebialex commented Jun 12, 2024

I thought I was done with these kind of issues (4.3.dev6 looked like it was good), but I'm getting this after upgrading to 4.3.beta1 (Windows 10, .NET).

 Assertion failed: Script path can't be empty.
    Details: 
     at Godot.GodotTraceListener.Fail(String message, String detailMessage) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/GodotTraceListener.cs:line 24
     at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage)
     at System.Diagnostics.Debug.Fail(String message, String detailMessage)
     at Godot.Bridge.ScriptManagerBridge.GetGlobalClassName(godot_string* scriptPath, godot_string* outBaseType, godot_string* outIconPath, godot_string* outClassName) in /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ScriptManagerBridge.cs:line 232

No idea which of my source files is causing this, it doesn't say. I'm not sure if this is related, but it feels like it.

@Delsin-Yu
Copy link
Contributor

Hello, I received the same error message in version 4.2.2. When changing an EditorPlugin written in C#. More precisely, when a script was attached to the [Tool] attribute to use it in the EditorPlugin.

Godot Engine v4.2.2.stable.mono.official (c) 2007-present Juan Linietsky, Ariel Manzur & Godot Contributors.

modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:529 - .NET: Failed to unload assemblies. Please check https://github.com/godotengine/godot/issues/78513 for more information. (User)
modules/mono/mono_gd/gd_mono.cpp:513 - .NET: Giving up on assembly reloading. Please restart the editor if unloading was failing. (User)

Good idea to write this in an error message.

#78513 (comment)
#78513 (comment)
https://learn.microsoft.com/en-us/dotnet/standard/assembly/unloadability#troubleshoot-unloadability-issues

@hhyyrylainen
Copy link

Does anyone of those links explain the behaviour that the assembly unload stops working after a couple of reloads? I'm still on 4.2.2 but that's what I'm seeing: assembly reloading works a couple of times usually after which it stops working. I don't think any new code from my side that uses any of the things that are said to cause assembly unloading issues would run only after like the seconds or third time an assembly reload happens.

@Delsin-Yu
Copy link
Contributor

Delsin-Yu commented Jun 12, 2024

Does anyone of those links explain the behaviour that the assembly unload stops working after a couple of reloads? I'm still on 4.2.2 but that's what I'm seeing: assembly reloading works a couple of times usually after which it stops working. I don't think any new code from my side that uses any of the things that are said to cause assembly unloading issues would run only after like the seconds or third time an assembly reload happens.

4.2.x is suffering from all four major issues. These are bugs within the Godot Engine / .Net Module that a user will encounter regardless of whether they are creating Editor Tools or not. These issues are fixed in 4.3 but not (yet?) cherry-picked to 4.2 (or maybe never for #81903 because of this).
#79519
#81903
#80175
#90837
But things are different for EditorPlugins. Failed unloading can happen by user error, especially during editor script development where exceptions can break unloading functionalities, or as the link explains, certain libraries or use cases are not unloadable by C# design, there is nothing for us language consumer can do other than rebooting the editor.

@Odaimoko
Copy link

Odaimoko commented Aug 10, 2024

I encounter this after compiling/running scene with another godot instance, mostly from command line after compiling new csharp scripts (IDE: Jetbrains Rider).

godot --path ".../my project path" "res://Scenes/GameMain.tscn"

And then I go back to the editor, it pops up this thread.

@maxime4000
Copy link

I would like to share my Theory.

TL;DR; Multiple Resource classes that are "GlobalClass" and located in the same file are problematic and when you refactor all your resource into separated files, this issue disappear.

The long version:

I was having this issues through 4.2 until 4.3-RC1. Just before RC1 release, I had refactor a lot of my code and split files from multiple classes inside a single file to each class has it's own separated file. When RC1 release, I just finished that refactor and I was happy that the Engine was now working. I've though the issue was solve within RC1, while it wasn't mention though the changelog.

RC1, RC2, RC3 and 4.3-stable were all improving my experience until I've start designing a new systems for my game. This issues that was uncleared to me but only allowed me to build my project once per "Godot execution" was back and worst. Then I though maybe I have found the culprit.

Right now I can't confirm but I highly think I've found a reproductible pattern. When I had the issue, I had multiple Resource classes found in the same file at multiple location.

I had this issues where my scenes where corrupted after running into this issues and saving my scene. (AKA: Running your game will save any opened and edited scene which were edited not by me, but by the compilation issue process. Resulting that after any build that has failed, the following build will corrupt your opened scene if this issues has happen. the C# meta data is corrupted and Godot will try using this new meta data, will find any node with a "Resource" [Export] property to be incorrect and will corrupt it with a null value...). All my tscn files changed and now having exported variable inserted to null instead of what it was before.

Signal were corrupted and to have my build working again, I had to "reset" each exported variable that was corrupted. I've start connecting signals through my C# code due to that issues...

But one of the things I notice is that all my "resource" exported variable where the Script value in the Inspector was "corrupted". (By memory) It look like: "res://src/SomethingComponent.cs::Resource_4go2v" the UUID at the end. I had to manually connect the Script to the Right C# file to fix the issues and it was reccuring like "Moe's kicking Barney out of his bar" meme.

One thing I made sure to fix in the refactoring I have done prior to RC1 was that I had one file with like 10 Resource "GlobalClass" and one day I've decide to refactor all those classes into separated file for each Resource class. After fixing my .tres files, it didn't happen for like 3 weeks. Until I start working on my next systems, which I guess I start creating new Resource classes and put multiple resource classes inside the same file again. Now I am stuck with this issues which disappear for a short period.

Pretty sure it's related to something like this. When I will tackle the refactoring, I will make sure to do a follow up about this theory. Until then, I encourage anybody to test this solution if it solved the issue. At least, if it doesn't solve the issues, it will reduced the Tech Debt in their game. Lol

Those are example of visual distinction found when the bug happen.

image
image

@hhyyrylainen
Copy link

hhyyrylainen commented Aug 26, 2024

I don't think I've used any Godot resources at all. Other than the built-in ones of course. So I think my project contains zero resources with a C# script related to them. And I still see this issue (though I haven't updated to 4.3 yet as I've been busy the past week) so I don't think that can be the whole picture.

Edit: I've now tried Godot 4.3 and I still got the same issue within about 5 minutes.

@DSOE1024
Copy link

DSOE1024 commented Sep 5, 2024

Is it possible that the resourceformatsaver and resourceformatloader that you wrote yourself could cause this problem?

@Delsin-Yu
Copy link
Contributor

Delsin-Yu commented Oct 11, 2024

@maxime4000 If possible, can you try if #98097 fixes your issue? No, I will continue to search for the reason

Update

@MDuRieu
Copy link

MDuRieu commented Oct 18, 2024

I had this error caused by using [Export] on an enum and assigning it to the MultiplayerSynchroniser. Not sure which part was the exact cause.

@hunterloftis
Copy link
Contributor

Multiple Resource classes that are "GlobalClass" and located in the same file are problematic and when you refactor all your resource into separated files, this issue disappear.

This is not the case, at least not universally. I'm consistently seeing this issue in a project in which there are no shared GlobalClass files.

@ilexl
Copy link

ilexl commented Nov 12, 2024

Most of the time when I get this error a quick restart and build works just fine. Is there anyway to get the editor to try effectively restart and build again once if it encounters this error?

@Whitecl4ws
Copy link

These two lines were both causing unload issues in the same file for me:
Image
Create your exported variables properly otherwise you might get unloading issues.

@ilexl
Copy link

ilexl commented Nov 21, 2024

These two lines were both causing unload issues in the same file for me: Image Create your exported variables properly otherwise you might get unloading issues.

@Whitecl4ws What would be the correct way to create Exported variables then?

@Whitecl4ws
Copy link

@ilexl Well for the top line, I'm exporting a non Godot.Collections.Array object and as for the bottom, I'm exporting a variable with no getters/setters. Neither of those exported variables show up in the inspector for me.

I haven't tested it but the way you would do both would be:

[Export]
public Godot.Collections.Array<SchedulerCycleModel> Cycles { get; set; } = new Godot.Collections.Array<SchedulerCycleModel> { new() };

[Export]
public bool RunActionOnStart { get; set; } = false;

Note that my personal use case was very specific wherein I was using the GoDotTest test runner to run tests ontop of coverlet and it was failing to unload in the editor every other run. But I spent way too much time hunting these two lines down not to share my findings with unfortunate people that might deal with this issue in the future. :P

@Tichau
Copy link

Tichau commented Nov 28, 2024

For information, I reproduced the issue by modifying the listeners (from System.Diagnostics.Trace) in the static constructor of a Node. If the scene is open in the editor, Godot fails to unload the assembly (probably because the listener registered keep being used).

public partial class App : Node
{
     static App()
     {
         Trace.Listeners.Clear();
         Trace.Listeners.Add(new Core.TraceListener(hideInternalCalls: true));
     }
}

@ghtyrant
Copy link

ghtyrant commented Dec 1, 2024

Can anyone give me a direction on how to debug this? I'm currently facing this issue on every project rebuild. I'm not using any [Tool], nor GlobalClasses, nor external libraries.

I'm quite stumped what could cause this, so I read through Microsoft's documentation on how to debug this. As far as I understood, you create a helper program that loads the assemblies, unloads them and then checks for errors. So far so good, but I don't know how to apply this to Godot now, since I'm not in control of loading my game's assembly. Also, just loading the assemblies from an external program does not cause any unloading issues, but I guess this is just because nothing's happening when I simply load the assemblies but don't call any functions.

edit: Just to make sure, I've stopped using Rider, made a clean build of the project using Godot but was able to reproduce the issue.

@Delsin-Yu
Copy link
Contributor

@ghtyrant You may check if you are using any unload-incompatible featrues (System.Text.Json, or the Example above) in editor reachable code ([ModuleInitializer], static constructor, instance constructer, [Export]ed properties).

@ghtyrant
Copy link

ghtyrant commented Dec 2, 2024

@Delsin-Yu Thanks a lot. I went through all of my code again and noticed I missed two exported variables that were not properties. Now I can rebuild my project without restarting Godot.

Any plans on adding a warning/error messages for such cases?

@Delsin-Yu
Copy link
Contributor

@ghtyrant That sounds like a relatively heavy task that requires a dedicated Roslyn analyzer to go through all code paths that the editor may touch and perform syntax tree analysis; you are welcome to open up a proposal in the proposal repo and see if any community contributor is interested.

@godotengine godotengine deleted a comment from Anasbaltar Dec 2, 2024
@godotengine godotengine deleted a comment from Anasbaltar Dec 2, 2024
@godotengine godotengine deleted a comment from Anasbaltar Dec 2, 2024
@4everalone
Copy link

Folks,
In my case the issue was with my code and not knowing how Godot works. I thought pre-allocating objects via packagedScene.Instantiate in separate thread and shoving them into a list is a way to go. But now I realized that C#'s ThreadPool and the list were holding references that prevented it from being unloaded. In my case threadpool was holding threads. I guess i will have to go with more complex object pooling

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests