From fb884573ea1a0ec36d1968bf12a667cd17023d38 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Fri, 29 Sep 2023 16:30:59 -0500 Subject: [PATCH] Allow registering "runtime classes" --- gdextension/gdextension_interface.h | 53 +++++++++++++++++++++++++++-- include/godot_cpp/core/class_db.hpp | 17 ++++++--- include/godot_cpp/godot.hpp | 2 +- src/godot.cpp | 4 +-- test/src/example.cpp | 20 +++++++++++ test/src/example.h | 16 +++++++++ test/src/register_types.cpp | 1 + 7 files changed, 104 insertions(+), 9 deletions(-) diff --git a/gdextension/gdextension_interface.h b/gdextension/gdextension_interface.h index 765952cf..7a8c6714 100644 --- a/gdextension/gdextension_interface.h +++ b/gdextension/gdextension_interface.h @@ -290,7 +290,7 @@ typedef struct { GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead. +} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead. typedef struct { GDExtensionBool is_virtual; @@ -323,7 +323,41 @@ typedef struct { GDExtensionClassCallVirtualWithData call_virtual_with_data_func; GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo2; +} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead. + +typedef struct { + GDExtensionBool is_virtual; + GDExtensionBool is_abstract; + GDExtensionBool is_exposed; + GDExtensionBool is_runtime; + GDExtensionClassSet set_func; + GDExtensionClassGet get_func; + GDExtensionClassGetPropertyList get_property_list_func; + GDExtensionClassFreePropertyList free_property_list_func; + GDExtensionClassPropertyCanRevert property_can_revert_func; + GDExtensionClassPropertyGetRevert property_get_revert_func; + GDExtensionClassValidateProperty validate_property_func; + GDExtensionClassNotification2 notification_func; + GDExtensionClassToString to_string_func; + GDExtensionClassReference reference_func; + GDExtensionClassUnreference unreference_func; + GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. + GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. + GDExtensionClassRecreateInstance recreate_instance_func; + // Queries a virtual function by name and returns a callback to invoke the requested virtual function. + GDExtensionClassGetVirtual get_virtual_func; + // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that + // need or benefit from extra data when calling virtual functions. + // Returns user data that will be passed to `call_virtual_with_data_func`. + // Returning `NULL` from this function signals to Godot that the virtual function is not overridden. + // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized. + // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`. + GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + // Used to call virtual functions when `get_virtual_call_data_func` is not null. + GDExtensionClassCallVirtualWithData call_virtual_with_data_func; + GDExtensionClassGetRID get_rid_func; + void *class_userdata; // Per-class user data, later accessible in instance bindings. +} GDExtensionClassCreationInfo3; typedef void *GDExtensionClassLibraryPtr; @@ -2514,6 +2548,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla */ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); +/** + * @name classdb_register_extension_class3 + * @since 4.3 + * + * Registers an extension class in the ClassDB. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_parent_class_name A pointer to a StringName with the parent class name. + * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); + /** * @name classdb_register_extension_class_method * @since 4.1 diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index e5bccf3d..91748e65 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -110,7 +110,7 @@ private: static void bind_method_godot(const StringName &p_class_name, MethodBind *p_method); template - static void _register_class(bool p_virtual = false, bool p_exposed = true); + static void _register_class(bool p_virtual = false, bool p_exposed = true, bool p_runtime = false); template static GDExtensionObjectPtr _create_instance_func(void *data) { @@ -146,6 +146,8 @@ public: static void register_abstract_class(); template static void register_internal_class(); + template + static void register_runtime_class(); _FORCE_INLINE_ static void _register_engine_class(const StringName &p_name, const GDExtensionInstanceBindingCallbacks *p_callbacks) { instance_binding_callbacks[p_name] = p_callbacks; @@ -199,7 +201,7 @@ public: } template -void ClassDB::_register_class(bool p_virtual, bool p_exposed) { +void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) { static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks; @@ -217,10 +219,11 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed) { class_register_order.push_back(cl.name); // Register this class with Godot - GDExtensionClassCreationInfo2 class_info = { + GDExtensionClassCreationInfo3 class_info = { p_virtual, // GDExtensionBool is_virtual; is_abstract, // GDExtensionBool is_abstract; p_exposed, // GDExtensionBool is_exposed; + p_runtime, // GDExtensionBool is_runtime; T::set_bind, // GDExtensionClassSet set_func; T::get_bind, // GDExtensionClassGet get_func; T::has_get_property_list() ? T::get_property_list_bind : nullptr, // GDExtensionClassGetPropertyList get_property_list_func; @@ -242,7 +245,7 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed) { (void *)&T::get_class_static(), // void *class_userdata; }; - internal::gdextension_interface_classdb_register_extension_class2(internal::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info); + internal::gdextension_interface_classdb_register_extension_class3(internal::library, cl.name._native_ptr(), cl.parent_name._native_ptr(), &class_info); // call bind_methods etc. to register all members of the class T::initialize_class(); @@ -266,6 +269,11 @@ void ClassDB::register_internal_class() { ClassDB::_register_class(false, false); } +template +void ClassDB::register_runtime_class() { + ClassDB::_register_class(false, true, true); +} + template MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. @@ -325,6 +333,7 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p #define GDREGISTER_VIRTUAL_CLASS(m_class) ClassDB::register_class(true); #define GDREGISTER_ABSTRACT_CLASS(m_class) ClassDB::register_abstract_class(); #define GDREGISTER_INTERNAL_CLASS(m_class) ClassDB::register_internal_class(); +#define GDREGISTER_RUNTIME_CLASS(m_class) ClassDB::register_runtime_class(); } // namespace godot diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 1b420d38..c4e5d838 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -177,7 +177,7 @@ extern "C" GDExtensionInterfacePlaceHolderScriptInstanceUpdate gdextension_inter extern "C" GDExtensionInterfaceClassdbConstructObject gdextension_interface_classdb_construct_object; extern "C" GDExtensionInterfaceClassdbGetMethodBind gdextension_interface_classdb_get_method_bind; extern "C" GDExtensionInterfaceClassdbGetClassTag gdextension_interface_classdb_get_class_tag; -extern "C" GDExtensionInterfaceClassdbRegisterExtensionClass2 gdextension_interface_classdb_register_extension_class2; +extern "C" GDExtensionInterfaceClassdbRegisterExtensionClass3 gdextension_interface_classdb_register_extension_class3; extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassMethod gdextension_interface_classdb_register_extension_class_method; extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod gdextension_interface_classdb_register_extension_class_virtual_method; extern "C" GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant gdextension_interface_classdb_register_extension_class_integer_constant; diff --git a/src/godot.cpp b/src/godot.cpp index a80ad90d..dd8ace3c 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -183,7 +183,7 @@ GDExtensionInterfacePlaceHolderScriptInstanceUpdate gdextension_interface_placeh GDExtensionInterfaceClassdbConstructObject gdextension_interface_classdb_construct_object = nullptr; GDExtensionInterfaceClassdbGetMethodBind gdextension_interface_classdb_get_method_bind = nullptr; GDExtensionInterfaceClassdbGetClassTag gdextension_interface_classdb_get_class_tag = nullptr; -GDExtensionInterfaceClassdbRegisterExtensionClass2 gdextension_interface_classdb_register_extension_class2 = nullptr; +GDExtensionInterfaceClassdbRegisterExtensionClass3 gdextension_interface_classdb_register_extension_class3 = nullptr; GDExtensionInterfaceClassdbRegisterExtensionClassMethod gdextension_interface_classdb_register_extension_class_method = nullptr; GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod gdextension_interface_classdb_register_extension_class_virtual_method = nullptr; GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant gdextension_interface_classdb_register_extension_class_integer_constant = nullptr; @@ -423,7 +423,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(classdb_construct_object, GDExtensionInterfaceClassdbConstructObject); LOAD_PROC_ADDRESS(classdb_get_method_bind, GDExtensionInterfaceClassdbGetMethodBind); LOAD_PROC_ADDRESS(classdb_get_class_tag, GDExtensionInterfaceClassdbGetClassTag); - LOAD_PROC_ADDRESS(classdb_register_extension_class2, GDExtensionInterfaceClassdbRegisterExtensionClass2); + LOAD_PROC_ADDRESS(classdb_register_extension_class3, GDExtensionInterfaceClassdbRegisterExtensionClass3); LOAD_PROC_ADDRESS(classdb_register_extension_class_method, GDExtensionInterfaceClassdbRegisterExtensionClassMethod); LOAD_PROC_ADDRESS(classdb_register_extension_class_virtual_method, GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod); LOAD_PROC_ADDRESS(classdb_register_extension_class_integer_constant, GDExtensionInterfaceClassdbRegisterExtensionClassIntegerConstant); diff --git a/test/src/example.cpp b/test/src/example.cpp index 53d11f4a..136ac94c 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -637,3 +637,23 @@ String Example::test_virtual_implemented_in_script(const String &p_name, int p_v } return "Unimplemented"; } + +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); + ADD_PROPERTY(PropertyInfo(Variant::INT, "prop_value"), "set_prop_value", "get_prop_value"); +} + +void ExampleRuntime::set_prop_value(int p_prop_value) { + prop_value = p_prop_value; +} + +int ExampleRuntime::get_prop_value() const { + return prop_value; +} + +ExampleRuntime::ExampleRuntime() { +} + +ExampleRuntime::~ExampleRuntime() { +} diff --git a/test/src/example.h b/test/src/example.h index 1c577203..7da7b08a 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -220,4 +220,20 @@ protected: virtual int test_function() override { return 25; } }; +class ExampleRuntime : public Node { + GDCLASS(ExampleRuntime, Node); + + int prop_value = 12; + +protected: + static void _bind_methods(); + +public: + void set_prop_value(int p_prop_value); + int get_prop_value() const; + + ExampleRuntime(); + ~ExampleRuntime(); +}; + #endif // EXAMPLE_CLASS_H diff --git a/test/src/register_types.cpp b/test/src/register_types.cpp index 58080d20..5c38241f 100644 --- a/test/src/register_types.cpp +++ b/test/src/register_types.cpp @@ -27,6 +27,7 @@ void initialize_example_module(ModuleInitializationLevel p_level) { ClassDB::register_class(true); ClassDB::register_abstract_class(); ClassDB::register_class(); + ClassDB::register_runtime_class(); } void uninitialize_example_module(ModuleInitializationLevel p_level) {