From edf02ae94b7a90e48bb0c7861a44c391b6fb7ce2 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Mon, 5 Jan 2026 11:25:19 -0600 Subject: [PATCH] Apply singleton instance binding clean-up fix no matter how it is created --- binding_generator.py | 19 +++++++++++++++---- include/godot_cpp/classes/wrapped.hpp | 2 +- test/build_profile.json | 4 +++- test/project/main.gd | 5 +++-- test/src/example.cpp | 12 ++++++++++-- test/src/example.h | 3 ++- 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/binding_generator.py b/binding_generator.py index 4c7184e2f..0254cf8f2 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -2062,6 +2062,7 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us result = [] class_name = class_api["name"] + inherits = class_api["inherits"] if "inherits" in class_api else "Wrapped" snake_class_name = camel_to_snake(class_name) is_singleton = class_name in singletons @@ -2101,20 +2102,26 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us result.append("#ifdef DEBUG_ENABLED") result.append("\t\tERR_FAIL_NULL_V(singleton_obj, nullptr);") result.append("#endif // DEBUG_ENABLED") + # This will lead to the constructor which will set `singleton`. result.append( - f"\t\tsingleton = reinterpret_cast<{class_name} *>(::godot::gdextension_interface::object_get_instance_binding(singleton_obj, ::godot::gdextension_interface::token, &{class_name}::_gde_binding_callbacks));" + f"\t\t::godot::gdextension_interface::object_get_instance_binding(singleton_obj, ::godot::gdextension_interface::token, &{class_name}::_gde_binding_callbacks);" ) result.append("#ifdef DEBUG_ENABLED") result.append("\t\tERR_FAIL_NULL_V(singleton, nullptr);") result.append("#endif // DEBUG_ENABLED") - result.append("\t\tif (likely(singleton)) {") - result.append(f"\t\t\tClassDB::_register_engine_singleton({class_name}::get_class_static(), singleton);") - result.append("\t\t}") result.append("\t}") result.append("\treturn singleton;") result.append("}") result.append("") + result.append(f"{class_name}::{class_name}(GodotObject *p_godot_object) : {inherits}(p_godot_object) {{") + result.append("\tif (singleton == nullptr) {") + result.append("\t\tsingleton = this;") + result.append(f"\t\tClassDB::_register_engine_singleton({class_name}::get_class_static(), singleton);") + result.append("\t}") + result.append("}") + result.append("") + result.append(f"{class_name}::~{class_name}() {{") result.append("\tif (singleton == this) {") result.append(f"\t\tClassDB::_unregister_engine_singleton({class_name}::get_class_static());") @@ -2122,6 +2129,10 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us result.append("\t}") result.append("}") result.append("") + else: + result.append(f"{class_name}::{class_name}(GodotObject *p_godot_object) : {inherits}(p_godot_object) {{") + result.append("}") + result.append("") if "methods" in class_api: for method in class_api["methods"]: diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index 65d9d17dd..0a9cc3e80 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -416,7 +416,7 @@ private: \ protected: \ m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \ - m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \ + m_class(GodotObject *p_godot_object); \ \ static void _bind_methods() {} \ \ diff --git a/test/build_profile.json b/test/build_profile.json index b4de43479..91c3166cb 100644 --- a/test/build_profile.json +++ b/test/build_profile.json @@ -1,6 +1,7 @@ { "enabled_classes": [ "Control", + "Engine", "InputEventKey", "Label", "MultiplayerAPI", @@ -9,6 +10,7 @@ "TileMap", "TileSet", "Tween", - "Viewport" + "Viewport", + "XRServer" ] } diff --git a/test/project/main.gd b/test/project/main.gd index 064876217..b5f432bab 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -274,8 +274,9 @@ func _ready(): assert_equal(example.test_virtual_implemented_in_script("Virtual", 939), "Implemented") assert_equal(custom_signal_emitted, ["Virtual", 939]) - # Test that we can access an engine singleton. - assert_equal(example.test_use_engine_singleton(), OS.get_name()) + # Test that we can access an engine singletons. + assert_equal(example.test_use_engine_singleton1(), OS.get_name()) + assert_equal(example.test_use_engine_singleton2(), true) if godot_target_version["minor"] >= 4: assert_equal(example.test_get_internal(1), 1) diff --git a/test/src/example.cpp b/test/src/example.cpp index f1bc687e5..2c5097534 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -7,11 +7,13 @@ #include +#include #include #include #include #include #include +#include #include using namespace godot; @@ -265,7 +267,8 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("test_virtual_implemented_in_script"), &Example::test_virtual_implemented_in_script); GDVIRTUAL_BIND(_do_something_virtual_with_control, "control"); - ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton); + ClassDB::bind_method(D_METHOD("test_use_engine_singleton1"), &Example::test_use_engine_singleton1); + ClassDB::bind_method(D_METHOD("test_use_engine_singleton2"), &Example::test_use_engine_singleton2); ClassDB::bind_method(D_METHOD("test_get_internal_class"), &Example::test_get_internal_class); @@ -758,10 +761,15 @@ String Example::test_virtual_implemented_in_script(const String &p_name, int p_v return "Unimplemented"; } -String Example::test_use_engine_singleton() const { +String Example::test_use_engine_singleton1() const { return OS::get_singleton()->get_name(); } +bool Example::test_use_engine_singleton2() const { + XRServer *xr_server = Object::cast_to(Engine::get_singleton()->get_singleton("XRServer")); + return xr_server != nullptr; +} + String Example::test_library_path() { String library_path; ::godot::gdextension_interface::get_library_path(::godot::gdextension_interface::library, library_path._native_ptr()); diff --git a/test/src/example.h b/test/src/example.h index 4e6208361..48a31737a 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -213,7 +213,8 @@ class Example : public Control { String test_virtual_implemented_in_script(const String &p_name, int p_value); GDVIRTUAL1(_do_something_virtual_with_control, Control *); - String test_use_engine_singleton() const; + String test_use_engine_singleton1() const; + bool test_use_engine_singleton2() const; static String test_library_path();