diff --git a/binding_generator.py b/binding_generator.py index 972cbf95b8..6b37f32320 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -1733,6 +1733,7 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us result.append(f"#include ") result.append("") + result.append("#include ") result.append("#include ") result.append("#include ") result.append("") @@ -1763,6 +1764,9 @@ 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, nullptr);") result.append("#endif // DEBUG_ENABLED") + result.append("\t\tif (likely(singleton)) {") + result.append("\t\t\tClassDB::_register_engine_singleton(singleton);") + result.append("\t\t}") result.append("\t}") result.append("\treturn singleton;") result.append("}") diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 31ebddc75f..e7e1e333ed 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -104,6 +104,7 @@ class ClassDB { static std::unordered_map instance_binding_callbacks; // Used to remember the custom class registration order. static std::vector class_register_order; + static std::vector engine_singletons; static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount); static void initialize_class(const ClassInfo &cl); @@ -153,6 +154,10 @@ class ClassDB { instance_binding_callbacks[p_name] = p_callbacks; } + _FORCE_INLINE_ static void _register_engine_singleton(Object *p_singleton) { + engine_singletons.push_back(p_singleton); + } + template static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args); diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 5a6293027e..11877458b7 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -160,6 +160,7 @@ extern "C" GDExtensionInterfaceObjectDestroy gdextension_interface_object_destro extern "C" GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton; extern "C" GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding; extern "C" GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding; +extern "C" GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding; extern "C" GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance; extern "C" GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name; extern "C" GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to; diff --git a/src/core/class_db.cpp b/src/core/class_db.cpp index acead8bfa4..e9dca42d0a 100644 --- a/src/core/class_db.cpp +++ b/src/core/class_db.cpp @@ -43,6 +43,7 @@ namespace godot { std::unordered_map ClassDB::classes; std::unordered_map ClassDB::instance_binding_callbacks; std::vector ClassDB::class_register_order; +std::vector ClassDB::engine_singletons; GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE; MethodDefinition D_METHOD(StringName p_name) { @@ -419,6 +420,13 @@ void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) { }); class_register_order.erase(it, class_register_order.end()); } + + if (p_level == GDEXTENSION_INITIALIZATION_CORE) { + for (std::vector::iterator i = engine_singletons.begin(); i != engine_singletons.end(); i++) { + internal::gdextension_interface_object_free_instance_binding((*i)->_owner, internal::token); + } + engine_singletons.clear(); + } } } // namespace godot diff --git a/src/godot.cpp b/src/godot.cpp index 8a031be23b..68d682fbe4 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -166,6 +166,7 @@ GDExtensionInterfaceObjectDestroy gdextension_interface_object_destroy = nullptr GDExtensionInterfaceGlobalGetSingleton gdextension_interface_global_get_singleton = nullptr; GDExtensionInterfaceObjectGetInstanceBinding gdextension_interface_object_get_instance_binding = nullptr; GDExtensionInterfaceObjectSetInstanceBinding gdextension_interface_object_set_instance_binding = nullptr; +GDExtensionInterfaceObjectFreeInstanceBinding gdextension_interface_object_free_instance_binding = nullptr; GDExtensionInterfaceObjectSetInstance gdextension_interface_object_set_instance = nullptr; GDExtensionInterfaceObjectGetClassName gdextension_interface_object_get_class_name = nullptr; GDExtensionInterfaceObjectCastTo gdextension_interface_object_cast_to = nullptr; @@ -406,6 +407,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(global_get_singleton, GDExtensionInterfaceGlobalGetSingleton); LOAD_PROC_ADDRESS(object_get_instance_binding, GDExtensionInterfaceObjectGetInstanceBinding); LOAD_PROC_ADDRESS(object_set_instance_binding, GDExtensionInterfaceObjectSetInstanceBinding); + LOAD_PROC_ADDRESS(object_free_instance_binding, GDExtensionInterfaceObjectFreeInstanceBinding); LOAD_PROC_ADDRESS(object_set_instance, GDExtensionInterfaceObjectSetInstance); LOAD_PROC_ADDRESS(object_get_class_name, GDExtensionInterfaceObjectGetClassName); LOAD_PROC_ADDRESS(object_cast_to, GDExtensionInterfaceObjectCastTo); diff --git a/test/project/main.gd b/test/project/main.gd index 0cfba196fa..665582fd5c 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -256,6 +256,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 notifications happen on both parent and child classes. var example_child = $ExampleChild assert_equal(example_child.get_value1(), 11) diff --git a/test/src/example.cpp b/test/src/example.cpp index 3ec8bca076..78d706225f 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include using namespace godot; @@ -239,6 +240,8 @@ void Example::_bind_methods() { GDVIRTUAL_BIND(_do_something_virtual, "name", "value"); ClassDB::bind_method(D_METHOD("test_virtual_implemented_in_script"), &Example::test_virtual_implemented_in_script); + ClassDB::bind_method(D_METHOD("test_use_engine_singleton"), &Example::test_use_engine_singleton); + ClassDB::bind_static_method("Example", D_METHOD("test_static", "a", "b"), &Example::test_static); ClassDB::bind_static_method("Example", D_METHOD("test_static2"), &Example::test_static2); @@ -671,6 +674,10 @@ String Example::test_virtual_implemented_in_script(const String &p_name, int p_v return "Unimplemented"; } +String Example::test_use_engine_singleton() const { + return OS::get_singleton()->get_name(); +} + void ExampleRuntime::_bind_methods() { ClassDB::bind_method(D_METHOD("set_prop_value", "value"), &ExampleRuntime::set_prop_value); ClassDB::bind_method(D_METHOD("get_prop_value"), &ExampleRuntime::get_prop_value); diff --git a/test/src/example.h b/test/src/example.h index 0ad949328a..1af4e5faaa 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -186,6 +186,8 @@ class Example : public Control { GDVIRTUAL2R(String, _do_something_virtual, String, int); String test_virtual_implemented_in_script(const String &p_name, int p_value); + + String test_use_engine_singleton() const; }; VARIANT_ENUM_CAST(Example::Constants);