diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 2904e54b223..bcd305a13e1 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -518,6 +518,12 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_ ClassDB::bind_method_custom(class_name, method); } + +void GDExtension::_register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info) { + StringName class_name = *reinterpret_cast(p_class_name); + ClassDB::add_extension_class_virtual_method(class_name, p_method_info); +} + void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield) { GDExtension *self = reinterpret_cast(p_library); @@ -834,6 +840,7 @@ void GDExtension::initialize_gdextensions() { #endif // DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); + register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property); register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 588efed0177..dbb39acb2e4 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -77,6 +77,7 @@ class GDExtension : public Resource { static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); + static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 88572f24f0d..c6d7779473a 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -1194,6 +1194,33 @@ static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObj return (GDObjectInstanceID)o->get_instance_id(); } +static GDExtensionBool gdextension_object_has_script_method(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method) { + Object *o = (Object *)p_object; + const StringName method = *reinterpret_cast(p_method); + + ScriptInstance *script_instance = o->get_script_instance(); + if (script_instance) { + return script_instance->has_method(method); + } + return false; +} + +static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { + Object *o = (Object *)p_object; + const StringName method = *reinterpret_cast(p_method); + const Variant **args = (const Variant **)p_args; + + Callable::CallError error; + memnew_placement(r_return, Variant); + *(Variant *)r_return = o->callp(method, args, p_argument_count, error); + + if (r_error) { + r_error->error = (GDExtensionCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + static GDExtensionObjectPtr gdextension_ref_get_object(GDExtensionConstRefPtr p_ref) { const Ref *ref = (const Ref *)p_ref; if (ref == nullptr || ref->is_null()) { @@ -1515,6 +1542,8 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(object_cast_to); REGISTER_INTERFACE_FUNC(object_get_instance_from_id); REGISTER_INTERFACE_FUNC(object_get_instance_id); + REGISTER_INTERFACE_FUNC(object_has_script_method); + REGISTER_INTERFACE_FUNC(object_call_script_method); REGISTER_INTERFACE_FUNC(ref_get_object); REGISTER_INTERFACE_FUNC(ref_set_object); #ifndef DISABLE_DEPRECATED diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 8fda11c6513..48a66c9faef 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -364,13 +364,18 @@ typedef struct { GDExtensionClassMethodPtrCall ptrcall_func; uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. - /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */ + /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. + * + * @todo Consider dropping `has_return_value` and making the other two properties match `GDExtensionMethodInfo` and `GDExtensionClassVirtualMethod` for consistency in future version of this struct. + */ GDExtensionBool has_return_value; GDExtensionPropertyInfo *return_value_info; GDExtensionClassMethodArgumentMetadata return_value_metadata; /* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`. * Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. + * + * @todo Consider renaming `arguments_info` to `arguments` for consistency in future version of this struct. */ uint32_t argument_count; GDExtensionPropertyInfo *arguments_info; @@ -381,6 +386,18 @@ typedef struct { GDExtensionVariantPtr *default_arguments; } GDExtensionClassMethodInfo; +typedef struct { + GDExtensionStringNamePtr name; + uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. + + GDExtensionPropertyInfo return_value; + GDExtensionClassMethodArgumentMetadata return_value_metadata; + + uint32_t argument_count; + GDExtensionPropertyInfo *arguments; + GDExtensionClassMethodArgumentMetadata *arguments_metadata; +} GDExtensionClassVirtualMethodInfo; + typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata); typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata); @@ -2268,6 +2285,34 @@ typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDOb */ typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object); +/** + * @name object_has_script_method + * @since 4.3 + * + * Checks if this object has a script with the given method. + * + * @param p_object A pointer to the Object. + * @param p_method A pointer to a StringName identifying the method. + * + * @returns true if the object has a script and that script has a method with the given name. Returns false if the object has no script. + */ +typedef GDExtensionBool (*GDExtensionInterfaceObjectHasScriptMethod)(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method); + +/** + * @name object_call_script_method + * @since 4.3 + * + * Call the given script method on this object. + * + * @param p_object A pointer to the Object. + * @param p_method A pointer to a StringName identifying the method. + * @param p_args A pointer to a C array of Variant. + * @param p_argument_count The number of arguments. + * @param r_return A pointer a Variant which will be assigned the return value. + * @param r_error A pointer the structure which will hold error information. + */ +typedef void (*GDExtensionInterfaceObjectCallScriptMethod)(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error); + /* INTERFACE: Reference */ /** @@ -2483,6 +2528,20 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl */ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); +/** + * @name classdb_register_extension_class_virtual_method + * @since 4.3 + * + * Registers a virtual method on an extension class in ClassDB, that can be implemented by scripts or other extensions. + * + * 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_method_info A pointer to a GDExtensionClassMethodInfo struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); + /** * @name classdb_register_extension_class_integer_constant * @since 4.1 diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index 45fbb19f88b..f2a9a68d081 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -1615,6 +1615,28 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List *p #endif } +void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) { + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + +#ifdef DEBUG_METHODS_ENABLED + PackedStringArray arg_names; + + MethodInfo mi; + mi.name = *reinterpret_cast(p_method_info->name); + mi.return_val = PropertyInfo(p_method_info->return_value); + mi.return_val_metadata = p_method_info->return_value_metadata; + mi.flags = p_method_info->method_flags; + for (int i = 0; i < (int)p_method_info->argument_count; i++) { + PropertyInfo arg(p_method_info->arguments[i]); + mi.arguments.push_back(arg); + mi.arguments_metadata.push_back(p_method_info->arguments_metadata[i]); + arg_names.push_back(arg.name); + } + + add_virtual_method(p_class, mi, true, arg_names); +#endif +} + void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { OBJTYPE_WLOCK; diff --git a/core/object/class_db.h b/core/object/class_db.h index 7a4ee1afa45..7697af98e1f 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -410,6 +410,7 @@ public: static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector &p_arg_names = Vector(), bool p_object_core = false); static void get_virtual_methods(const StringName &p_class, List *p_methods, bool p_no_inheritance = false); + static void add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info); static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false); static void get_integer_constant_list(const StringName &p_class, List *p_constants, bool p_no_inheritance = false);