godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.56k stars 20.28k forks source link

[.Net / GDScript Interop] Cannot Call GDScript-Lambda-Backed `Callable` in C# #97358

Open Delsin-Yu opened 5 days ago

Delsin-Yu commented 5 days ago

Tested versions

v4.3.stable.mono.official [77dcf97d8]

System information

Godot v4.3.stable.mono - Windows 10.0.17763 - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4060 Ti (NVIDIA; 32.0.15.6109) - AMD Ryzen 9 5900X 12-Core Processor (24 Threads)

Issue description

This is a niche use case, but we should document it if it's not supported.

In C# script, when trying to Call() a Callable that is backed by a GDScript Lambda Expression, Godot will produce the following error instead of actually making the call.

E 0:00:00:0894   main.gd:4 @ _ready(): Attempt to call callable 'null::null' on a null instance.
  <C# Source>    /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:160 @ void Godot.NativeInterop.ExceptionUtils.DebugCheckCallError(Godot.NativeInterop.godot_callable&, Godot.NativeInterop.godot_variant**, int, Godot.NativeInterop.godot_variant_call_error)
  <Stack Trace>  main.gd:4 @ _ready()

Steps to reproduce

  1. Create a C# Godot Project.
  2. Create main.gd.
  3. Create a scene, attach the main.gd to the root node, and save it to a file.
  4. Create Helper.cs.
  5. Run the project.

Minimal reproduction project (MRP)

main.gd

extends Node

func _ready():
    Helper.new().CallCallable(func(): print("Hello World"));

Helper.cs

using Godot;

[GlobalClass]
public partial class Helper : Node
{
    public void CallCallable(Callable callable) => callable.Call();
}
dalexeev commented 5 days ago

Does the error occur if you store the lambda callable in a local or class variable? Maybe GDScript/C#/core loses the last reference and therefore frees the lambda before the callable is called.

Delsin-Yu commented 5 days ago

@dalexeev

No, neither works.

extends Node

var lambda_field := func(): print("Hello World");

func _ready():
    var helper := Helper.new();
    var lambda_local := func(): print("Hello World");
    helper.CallCallable(lambda_local);
    helper.CallCallable(lambda_field);
E 0:00:00:0814   main.gd:8 @ _ready(): Attempt to call callable 'null::null' on a null instance.
  <C# Source>    /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:160 @ void Godot.NativeInterop.ExceptionUtils.DebugCheckCallError(Godot.NativeInterop.godot_callable&, Godot.NativeInterop.godot_variant**, int, Godot.NativeInterop.godot_variant_call_error)
  <Stack Trace>  main.gd:8 @ _ready()
E 0:00:00:0815   main.gd:9 @ _ready(): Attempt to call callable 'null::null' on a null instance.
  <C# Source>    /root/godot/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/ExceptionUtils.cs:160 @ void Godot.NativeInterop.ExceptionUtils.DebugCheckCallError(Godot.NativeInterop.godot_callable&, Godot.NativeInterop.godot_variant**, int, Godot.NativeInterop.godot_variant_call_error)
  <Stack Trace>  main.gd:9 @ _ready()
zaevi commented 5 days ago

GodotSharp does not yet support some CallableCustom, such as GDScriptLambdaCallable, CallableCustomBind/Unbind.

shabbywu commented 3 days ago

same error but happen at calling the CallableCustom return from GDExtension.


By the way, if i use Variant but not Callable to define the variable, csharp can call the CallableCustom::get_as_text correctly when i print the callable to screen. So i guess the problem happen at csharp Callable stub.

shabbywu commented 3 days ago

A tiny GDExtension to make csharp can work well with CallableCustom.

have tested in windows.