C# Action/CallDeferred headache


I have this Actions:

public Action<Case> OnCaseCompleted;

which is subscriped like this:

_gameManager.OnCaseCompleted += CaseCompleted;

And leads to this function:

private async void CaseCompleted(Case _case)
    var completedCase = _openCasesContainer.FindChild(_case.Id.ToString());

which results results in the message

Caller thread can't call this function in this node (xyz) Use call_deferred() or call_thread_group() instead.

And a null-reference exception, becaus completedCase is null.

The problem is: _openCasesContainer.CallDeferred() does not return any object. So whats the way to go?

If Node-based operations aren’t called on the main thread, you will tend to run into issues like this. Godot is recommending you use call-deferred so that it can essentially call your method at the end of frame on the main thread. This is the reason it does not return an object as well, because it’s not actually calling it yet where you invoke it, but later.

A couple things you could do:
A) Create a Method that does the FindChild and QueueFree like you have shown, but call that method deferred from CaseCompleted.

B) Create a singleton node with a queue that you can fill and process your queued actions on the main thread. It’s essentially similar to CallDeferred, but you have more control over the behavior. (This is what I use so I can just avoid these headaches in general)

public void InvokeOnMainThread( Action action )
    if( action != null )
        deferredActionQueue.Enqueue( action );

public override void _Process( double deltaTime )
    sinceLastProcessing += deltaTime;

    if( sinceLastProcessing >= ProcessIntervalSeconds ) 
        int numProcessed = 0;
        while( deferredActionQueue.TryDequeue( out var actionToRun ) )

            if( numProcessed >= MaxToProcessForFrame )

        sinceLastProcessing = 0.0;

Also use the built in Godot Signals where possible, as its thread safe.

Not saying C# actions wouldn’t be, but Godot cant ‘see’ them thats the issue, and therefore isnt taking them into account.

Autload an ‘Event Manager’ with signals instead. More work, has some limitations, but is far safer.