godot-rust / gdext

Rust bindings for Godot 4
https://mastodon.gamedev.place/@GodotRust
Mozilla Public License 2.0
2.91k stars 178 forks source link

Gd<XXX>.bind() cause crash, with `api-custom` feature in custom build godot. #755

Open tommyZZM opened 2 months ago

tommyZZM commented 2 months ago

I m trying to write a custom plugin for godot in double precision build

Gd.bind() cause crash, with double-precision feature in double precision build godot. (Latest godot master branch)

Update the title... this issue is cause by the api-custom feature..

even i only just add api-custom the case crash too

Gd.bind() cause crash, with api-custom feature in custom build godot...

But if same codes run in common build version it works fine

Maybe a minial reproduce case for this issue is here

https://github.com/tommyZZM/issue-cast-gd-pointer-crash-in-double-build/blob/main/rust/src/lib.rs#L95

if let Ok(mesh) = object.try_cast::<MyCustomMesh>() {
     // mesh.bind().size // crash by this
     godot_print!("size is {}", mesh.bind().size) // <-- crash this line
}

logs


thread '<unnamed>' panicked at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj:
Class issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh -- null instance; does the class have a Godot creator f?
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\panicking.rs:645
   1: core::panicking::panic_fmt
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\core\src\panicking.rs:72
   2: godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::resolve_instance_ptr<issu>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj\raw.rs:428
   3: godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::storage_unbounded<issue_c>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj\raw.rs:413
   4: godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::storage<issue_cast_gd_poi>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj\raw.rs:389
   5: godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::bind<issue_cast_gd_pointe>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj\raw.rs:370
   6: godot_core::obj::gd::Gd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::bind<issue_cast_gd_pointer_cr>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\obj\gd.rs:160
   7: issue_cast_gd_pointer_crash_in_double_build::impl$40::parse_begin
             at H:\WorkShop\_TEST\issue-cast-gd-pointer-crash-in-double-build\rust\src\lib.rs:95
   8: issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virtual_fn::closure$0
             at H:\WorkShop\_TEST\issue-cast-gd-pointer-crash-in-double-build\rust\src\lib.rs:83
   9: core::ops::function::FnOnce::call_once<issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virt:
             at /rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6\library\core\src\ops\function.rs:250
  10: godot_core::meta::signature::impl$18::in_ptrcall<tuple$<>,godot_core::obj::gd::Gd<godot_core::gen::classes::objec>
             at C:\Users\Mehere\.cargo\git\checkouts\gdext-76630c89719e160c\7eec09c\godot-core\src\meta\signature.rs:9
  11: issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virtual_fn
             at H:\WorkShop\_TEST\issue-cast-gd-pointer-crash-in-double-build\rust\src\lib.rs:83
  12: embree::TaskScheduler::wait
  13: embree::TaskScheduler::wait
  14: embree::TaskScheduler::wait
  15: embree::TaskScheduler::wait
  16: embree::TaskScheduler::wait
  17: embree::TaskScheduler::wait
  18: embree::TaskScheduler::wait
  19: embree::TaskScheduler::wait
  20: embree::TaskScheduler::wait
  21: embree::TaskScheduler::wait
  22: embree::TaskScheduler::wait
  23: <unknown>
  24: embree::TaskScheduler::wait
  25: embree::TaskScheduler::wait
  26: embree::TaskScheduler::wait
  27: embree::TaskScheduler::wait
  28: embree::TaskScheduler::wait
  29: <unknown>
  30: embree::TaskScheduler::wait
  31: embree::TaskScheduler::wait
  32: embree::TaskScheduler::wait
  33: embree::TaskScheduler::wait
  34: embree::TaskScheduler::wait
  35: embree::TaskScheduler::wait
  36: embree::TaskScheduler::wait
  37: embree::TaskScheduler::wait
  38: <unknown>
  39: <unknown>
  40: embree::TaskScheduler::wait
  41: embree::TaskScheduler::wait
  42: <unknown>
  43: <unknown>
  44: <unknown>
  45: <unknown>
  46: <unknown>
  47: embree::TaskScheduler::wait
  48: BaseThreadInitThunk
  49: RtlUserThreadStart
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

================================================================
CrashHandlerException: Program crashed
Engine version: Godot Engine v4.3.beta.custom_build (5833f597865c773fae3ee09fc4e31d4a243f812d)
Dumping the backtrace. Please include this when reporting the bug to the project developer.
[0] <couldn't map PC to fn name>
[1] <couldn't map PC to fn name>
[2] <couldn't map PC to fn name>
[3] panic_unwind::__rust_start_panic (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\panic_unwind\src\lib.rs:1)
[4] std::panicking::rust_panic (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\panicking.rs:831)
[5] std::panicking::rust_panic_with_hook (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\panicking.rs:)
[6] std::panicking::begin_panic_handler::closure$0 (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\pan)
[7] std::sys_common::backtrace::__rust_end_short_backtrace<std::panicking::begin_panic_handler::closure_env$0,never$> ()
[8] std::panicking::begin_panic_handler (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\std\src\panicking.rs:6)
[9] core::panicking::panic_fmt (/rustc/9b00956e56009bab2aa15d7bff10916599e3d6d6/library\core\src\panicking.rs:72)
[10] godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::resolve_instance_ptr<issue)
[11] godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::storage_unbounded<issue_ca)
[12] godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::storage<issue_cast_gd_poin)
[13] godot_core::obj::raw::RawGd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::bind<issue_cast_gd_pointer)
[14] godot_core::obj::gd::Gd<issue_cast_gd_pointer_crash_in_double_build::MyCustomMesh>::bind<issue_cast_gd_pointer_cra)
[15] issue_cast_gd_pointer_crash_in_double_build::impl$40::parse_begin (H:\WorkShop\_TEST\issue-cast-gd-pointer-crash-i)
[16] issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virtual_fn::closure$0 (H:\WorkShop\_TEST\iss)
[17] core::ops::function::FnOnce::call_once<issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virtu)
[18] godot_core::meta::signature::impl$18::in_ptrcall<tuple$<>,godot_core::obj::gd::Gd<godot_core::gen::classes::object)
[19] issue_cast_gd_pointer_crash_in_double_build::impl$42::__virtual_call::virtual_fn (H:\WorkShop\_TEST\issue-cast-gd-)
[20] embree::TaskScheduler::wait
[21] embree::TaskScheduler::wait
[22] embree::TaskScheduler::wait
[23] embree::TaskScheduler::wait
[24] embree::TaskScheduler::wait
[25] embree::TaskScheduler::wait
[26] embree::TaskScheduler::wait
[27] embree::TaskScheduler::wait
[28] embree::TaskScheduler::wait
[29] embree::TaskScheduler::wait
[30] embree::TaskScheduler::wait
[31] <couldn't map PC to fn name>
[32] embree::TaskScheduler::wait
[33] embree::TaskScheduler::wait
[34] embree::TaskScheduler::wait
[35] embree::TaskScheduler::wait
[36] embree::TaskScheduler::wait
[37] <couldn't map PC to fn name>
[38] embree::TaskScheduler::wait
[39] embree::TaskScheduler::wait
[40] embree::TaskScheduler::wait
[41] embree::TaskScheduler::wait
[42] embree::TaskScheduler::wait
[43] embree::TaskScheduler::wait
[44] embree::TaskScheduler::wait
[45] embree::TaskScheduler::wait
[46] <couldn't map PC to fn name>
[47] <couldn't map PC to fn name>
[48] embree::TaskScheduler::wait
[49] embree::TaskScheduler::wait
[50] <couldn't map PC to fn name>
[51] <couldn't map PC to fn name>
[52] <couldn't map PC to fn name>
[53] <couldn't map PC to fn name>
[54] <couldn't map PC to fn name>
[55] embree::TaskScheduler::wait
[56] BaseThreadInitThunk
-- END OF BACKTRACE --
lilizoey commented 2 months ago

exactly what godot version are you using, and what platform are you running on? i also notice you've enabled experimental-wasm, is this necessary?

tommyZZM commented 2 months ago

godot version

Latest godot master branch these days. recently tried 475248d99df89fc29032a42f1d29ad4cef49c8b5

i also notice you've enabled experimental-wasm, is this necessary

It is not necessary.


i was removed experimental-wasm in the case. same issue happen.

tommyZZM commented 2 months ago

https://github.com/godot-rust/gdext/blob/master/godot-core/src/registry/callbacks.rs#L67

fn create_rust_part_for_existing_godot_part<T, F>(
    make_user_instance: F,
    base_ptr: sys::GDExtensionObjectPtr,
) -> sys::GDExtensionClassInstancePtr
where
    T: GodotClass,
    F: FnOnce(Base<T::Base>) -> T,
{
    let class_name = T::class_name();

    //out!("create callback: {}", class_name.backing);

    let base = unsafe { Base::from_sys(base_ptr) }; // <--- base_ptr

https://github.com/godot-rust/gdext/blob/master/godot-core/src/obj/raw.rs#L426C66-L426C80

    unsafe fn resolve_instance_ptr(&self) -> sys::GDExtensionClassInstancePtr {
        if self.is_null() {
            return ptr::null_mut();
        }

        let callbacks = crate::storage::nop_instance_callbacks();
        let token = sys::get_library() as *mut std::ffi::c_void;
        let binding = interface_fn!(object_get_instance_binding)(self.obj_sys(), token, &callbacks); // self.obj_sys()

base_ptr and self.obj_sys() has a same address on common build godot.

but they have a different address in double-precision case

tommyZZM commented 2 months ago

Update the title... this issue is cause by the api-custom feature..

even i just add api-custom the case crash too

lilizoey commented 2 months ago

That's weird, we build against Godot master in CI, so this should be tested. What os are you on?

tommyZZM commented 2 months ago

Windows 10

Bromeon commented 2 months ago

even i only just add api-custom the case crash too

[...]

Maybe a minial reproduce case for this issue is here

You mentioned just having the feature enabled reproduces it, however your example includes EditorPlugin etc.

Can you come up with a truly minimal, reproducible example? If possible, post it inline here.

Also, make sure you create a fresh Godot 4.2.2 project (with GODOT4_BIN pointing to it).

tommyZZM commented 2 months ago

Godot 4.2.2

I tried to build a 4.2.2 rev of godot (15073afe3856abd2aa1622492fe50026c7d63dc1). But gdext master prompts error

[print_error] [panic]
  gdext was compiled against newer Godot version: v4.3.beta.custom_build
  but loaded by older Godot binary, with version: v4.2.2.stable.custom_build

But when I build the latest master branch(8a6c1e8f5232ff3b3a5eac024d590e7479b29e90), gdext currently doesn't seem to load correctly.

This issues is probably caused by some version difference.

I think it might have something related to prebuilt? what can I do to fix this? can I make custom prebuilts?

Bromeon commented 2 months ago

Well, you cannot use a newer Godot version in api-custom than the binary in which you run the extension.

See https://godot-rust.github.io/book/toolchain/godot-version.html and https://godot-rust.github.io/book/toolchain/compatibility.html.

tommyZZM commented 2 months ago

When I want to build a double-precision godot, which commit rev should I build?

I tried adding the api-4-2-2 feature, features = ["api-4-2-2", "api-custom", "double-precision"]

godot 4.2.2 15073afe3856abd2aa1622492fe50026c7d63dc1 and latest gdext master rev cd31a83d80edc7529ab2ef450d4c59d983913c19

But after building and running, it still prompts error

[print_error] Rust function panicked at godot-ffi\src\compat\compat_4_1plus.rs:97.
Context: error when loading GDExtension library
[print_error] [panic]
gdext was compiled against newer Godot version: v4.3.beta.custom_build
but loaded by older Godot binary, with version: v4.2.2.stable.custom_build
Bromeon commented 2 months ago

Did you read my links? :wink:

You need api-custom when you use double-precision. You must make sure that the Godot binary you have in GODOT4_BIN is compatible with the one you use to run the extension.

Bromeon commented 1 month ago

@tommyZZM any update?

tommyZZM commented 1 month ago

@tommyZZM any update?

i tried to build a godot 4.3 beta2

https://godotengine.org/article/dev-snapshot-godot-4-3-beta-2/#changelog

(https://github.com/godotengine/godot/commit/b75f0485ba15951b87f1d9a2d8dd0fcd55e178e4)

scons platform=windows

same crash still happen. what is latest compatible godot version or revision for api-custom and double-precision build?

Bromeon commented 1 month ago

Can you come up with a truly minimal, reproducible example? If possible, post it inline here.

Please do this. To avoid further back-and-forth, what I'm looking for is:

Sorry if this sounds annoying, but the reason why I ask is that we have a CI job that runs on Windows with a Godot double configuration (from nightly master), and it just passed a few minutes ago. We definitely use Gd::bind() (in fact we run the whole integration test suite), and it doesn't crash.

I need a way to reproduce this reliably and with as minimal setup as possible for the issue to stay open. Thanks a lot!

tommyZZM commented 1 month ago

Can you come up with a truly minimal, reproducible example? If possible, post it inline here.

Please do this. To avoid further back-and-forth, what I'm looking for is:

  • A single lib.rs file that is self-contained and triggers the problem
  • The exact Cargo commands you use to build
  • The exact gdext and Godot versions (ideally both master, but maybe post commit SHA hashes)
  • If there is any configuration or project/scene setup on Godot side necessary to trigger that, elaborate that.

Sorry if this sounds annoying, but the reason why I ask is that we have a CI job that runs on Windows with a Godot double configuration (from nightly master), and it just passed a few minutes ago. We definitely use Gd::bind() (in fact we run the whole integration test suite), and it doesn't crash.

I need a way to reproduce this reliably and with as minimal setup as possible for the issue to stay open. Thanks a lot!

Hello,

I have tried some examples, and I attempted to run the try_case and bind methods in the enter_tree of both Parent and Child nodes. It seems to work during runtime.

However, the same issue still appears in the example I initially provided. It might be that this problem only occurs with EditorInspectorPlugin, or it could be related to the editor itself.


use godot::prelude::*;
use godot::classes::{
    Node,
    INode,
    EditorInspectorPlugin, 
    EditorPlugin,
    EditorProperty, 
    IEditorInspectorPlugin, 
    IEditorPlugin
};

struct IssueReproduceExtensionLibrary;

#[gdextension]
unsafe impl ExtensionLibrary for IssueReproduceExtensionLibrary {}

#[derive(GodotClass)]
#[class(base=Node)]
pub struct MyNodeParent {
    pub base: Base<Node>,
    pub count: i32,
}

#[godot_api]
impl INode for MyNodeParent {
    // in editor created
    fn init(base: Base<Node>) -> Self {
        // godot_print!("init.. {}", !randInt!());
        godot_print!("init..");

        MyNodeParent {
            base, 
            count: 1
        }
    }

    fn enter_tree(&mut self) {
        godot_print!("enter_tree..");
        let child = Gd::from_init_fn(|base| {
            MyNodeChild { base }
        });
        self.base_mut().add_child(child.upcast());
    }
}

#[godot_api]
impl MyNodeParent {
    #[func]
    fn add(&mut self) {
        godot_print!("add...");
        self.count = self.count + 1
    }
}

#[derive(GodotClass)]
#[class(base=Node)]
pub struct MyNodeChild {
    pub base: Base<Node>
}

#[godot_api]
impl INode for MyNodeChild {
    // in editor created
    fn init(base: Base<Node>) -> Self {
        // godot_print!("init.. {}", !randInt!());
        godot_print!("init..");

        MyNodeChild {
            base
        }
    }

    fn enter_tree(&mut self) {
        let p = self.base_mut().get_parent().unwrap();
        let p_class: String = p.clone().get_class().to_string();
        godot_print!("[MyNodeChild]enter_tree.. parent is {}", p_class);

        if p_class == "MyNodeParent" {
            if let Ok(mut p_casted) = p.clone().try_cast::<MyNodeParent>() { // <-- this is OK
                let mut b = p_casted.bind_mut();
                b.add();
                godot_print!("size is {}", b.count)
            }
        } else {

        }
    }
}

#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
pub struct MyEditorPlugin {
  base: Base<EditorPlugin>,
  inspector_: Option<Gd<EditorInspectorPlugin>>
}

#[godot_api]
impl IEditorPlugin for MyEditorPlugin {
  fn enter_tree(&mut self) {
      godot_print!("enter_tree");
      // Perform typical plugin operations here.
      let inspector_ = Gd::from_init_fn(|base| {
            MyEdtiorInspectorPlugin { 
                base
            }
      }).clone();

      self.inspector_ = Some(inspector_.clone().upcast());

      self.base_mut().add_inspector_plugin(inspector_.clone().upcast());
  }

  fn exit_tree(&mut self) {
      // Perform typical plugin operations here.
      if let Some(ins) = self.inspector_.clone() {
          self.base_mut().remove_inspector_plugin(ins.clone())
      } else {
      }
  }
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorInspectorPlugin)]
pub struct MyEdtiorInspectorPlugin{
    base: Base<EditorInspectorPlugin>,
}

#[godot_api]
impl IEditorInspectorPlugin for MyEdtiorInspectorPlugin {
    fn can_handle(&self, object: Gd<godot::classes::Object>) -> bool {
        let a: String = object.clone().get_class().to_string();

        let bool = a == "MyNodeParent";

        bool
    }

    fn parse_begin(&mut self, object: Gd<godot::classes::Object>,) {
        let a: String = object.clone().get_class().to_string();
        godot_print!("get_calss is {}", a);
        if let Ok(mut my_node_parent) = object.clone().try_cast::<MyNodeParent>() {
            godot_print!("cast ok {}", a);
            let my_node_parent_bind_mut = my_node_parent.bind(); // <-- error this line
            // ERROR: godot-rust function call failed: MyEdtiorInspectorPlugin::parse_begin()
            // Reason: [panic]  Class test_case::MyNodeParent -- null instance; does the class h?
            godot_print!("size is {}", my_node_parent_bind_mut.count) // <-- crash this line
        }
    }
}
tommyZZM commented 1 month ago

In the parse_begin method of EditorInspectorPlugin, when using custom-api, the object parameter seems unable to be correctly cast to the original object, resulting in a panic when calling bind. I tried printing the class name of the object, and it appears to be correct.


ERROR: godot-rust function call failed: MyEdtiorInspectorPlugin::parse_begin()
Reason: [panic]  Class test_case::MyNode -- null instance; does the class have a Godot creator function?
at: godot_core::private::report_call_error (godot-core\src\private.rs:313)

this should be the minial case yet

It doesn’t crash like the Mesh object does, but the console outputs a panic when the bind method is executed during the parse_begin.

To reproduce the panic, you have to select the MyNode in godot editor to trigger it.

use godot::prelude::*;
use godot::classes::{
    Node,
    INode,
    EditorInspectorPlugin, 
    EditorPlugin,
    EditorProperty, 
    IEditorInspectorPlugin, 
    IEditorPlugin
};

struct IssueReproduceExtensionLibrary;

#[gdextension]
unsafe impl ExtensionLibrary for IssueReproduceExtensionLibrary {}

#[derive(GodotClass)]
#[class(base=Node)]
pub struct MyNode {
    pub base: Base<Node>,

    #[export]
    size: i32,
}

#[godot_api]
impl INode for MyNode {
    // in editor created
    fn init(base: Base<Node>) -> Self {
        // godot_print!("init.. {}", !randInt!());
        godot_print!("init..");

        MyNode {
            base,
            size: 1024
        }
    }
}

#[derive(GodotClass)]
#[class(tool, init, editor_plugin, base=EditorPlugin)]
pub struct MyEditorPlugin {
    base: Base<EditorPlugin>,
    inspector_1: Option<Gd<EditorInspectorPlugin>>
}

#[godot_api]
impl IEditorPlugin for MyEditorPlugin {
    fn enter_tree(&mut self) {
        // Perform typical plugin operations here.
        let inspector_1 = Gd::from_init_fn(|base| {
            MyEdtiorInspectorPlugin { 
                base
            }
        }).clone();

        self.inspector_1 = Some(inspector_1.clone().upcast());

        self.base_mut().add_inspector_plugin(inspector_1.clone().upcast());
    }

    fn exit_tree(&mut self) {
        // Perform typical plugin operations here.
        if let Some(ins) = self.inspector_1.clone() {
            self.base_mut().remove_inspector_plugin(ins.clone())
        } else {
        }
    }
}

#[derive(GodotClass)]
#[class(tool, init, base=EditorInspectorPlugin)]
pub struct MyEdtiorInspectorPlugin{
    base: Base<EditorInspectorPlugin>,
}

#[godot_api]
impl IEditorInspectorPlugin for MyEdtiorInspectorPlugin {
    fn can_handle(&self, object: Gd<godot::classes::Object>) -> bool {
        let a: String = object.get_class().to_string();

        let bool = a == "MyNode";

        bool
    }

    // panic when you select `MyNode` in editor
    fn parse_begin(&mut self, object: Gd<godot::classes::Object>,) {
        if let Ok(mesh) = object.try_cast::<MyNode>() {
            // mesh.bind().size
            // ERROR: godot-rust function call failed: MyEdtiorInspectorPlugin::parse_begin()
            // Reason: [panic]  Class test_case::MyNode -- null instance; does the class have a Godot creator function?
            // at: godot_core::private::report_call_error (godot-core\src\private.rs:313)
            godot_print!("size is {}", mesh.bind().size) // <-- crash this line
        }
    }
}

To reproduce the issue:

  1. Build from the Godot master branch. (e25f3c0d38d457b15a63720240736f564ce0501b)
  2. Add ["api-custom"] to gdext (dbbd59c53efe77e1d355468d9618dba889ec7f65) (everything works fine if not using api-custom or a custom build).
  3. Create a Godot project and run the Godot editor.
  4. Create a new MyNode and add it to the scene.
  5. Select this MyNode (you must manually select the MyNode in the Godot editor, which will immediately trigger the panic).
  6. Panic!

You can use the repository I initially provided to follow these steps. https://github.com/tommyZZM/issue-cast-gd-pointer-crash-in-double-build


case yet find in EditorInspectorPlugin interacts with try_cast and bind in its callbacks

Bromeon commented 1 month ago

Thanks a lot. Since this is a bit more involved, it will take me some time until I can get around to try and reproduce it. In case you find a simpler way in the meantime (e.g. code only), let me know :slightly_smiling_face: