Skip to content

Commit 306dd5b

Browse files
committed
Merge pull request #87758 from dsnopek/gdextension-register-virtual-method
Allow GDExtensions to register virtual methods and call them on scripts
2 parents 3be3d50 + be11002 commit 306dd5b

File tree

6 files changed

+120
-1
lines changed

6 files changed

+120
-1
lines changed

core/extension/gdextension.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,12 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_
518518

519519
ClassDB::bind_method_custom(class_name, method);
520520
}
521+
522+
void GDExtension::_register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info) {
523+
StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
524+
ClassDB::add_extension_class_virtual_method(class_name, p_method_info);
525+
}
526+
521527
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) {
522528
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
523529

@@ -837,6 +843,7 @@ void GDExtension::initialize_gdextensions() {
837843
#endif // DISABLE_DEPRECATED
838844
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
839845
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
846+
register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
840847
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
841848
register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
842849
register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed);

core/extension/gdextension.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ class GDExtension : public Resource {
7777
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
7878
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);
7979
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
80+
static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
8081
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);
8182
static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
8283
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);

core/extension/gdextension_interface.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,6 +1194,33 @@ static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObj
11941194
return (GDObjectInstanceID)o->get_instance_id();
11951195
}
11961196

1197+
static GDExtensionBool gdextension_object_has_script_method(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method) {
1198+
Object *o = (Object *)p_object;
1199+
const StringName method = *reinterpret_cast<const StringName *>(p_method);
1200+
1201+
ScriptInstance *script_instance = o->get_script_instance();
1202+
if (script_instance) {
1203+
return script_instance->has_method(method);
1204+
}
1205+
return false;
1206+
}
1207+
1208+
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) {
1209+
Object *o = (Object *)p_object;
1210+
const StringName method = *reinterpret_cast<const StringName *>(p_method);
1211+
const Variant **args = (const Variant **)p_args;
1212+
1213+
Callable::CallError error;
1214+
memnew_placement(r_return, Variant);
1215+
*(Variant *)r_return = o->callp(method, args, p_argument_count, error);
1216+
1217+
if (r_error) {
1218+
r_error->error = (GDExtensionCallErrorType)(error.error);
1219+
r_error->argument = error.argument;
1220+
r_error->expected = error.expected;
1221+
}
1222+
}
1223+
11971224
static GDExtensionObjectPtr gdextension_ref_get_object(GDExtensionConstRefPtr p_ref) {
11981225
const Ref<RefCounted> *ref = (const Ref<RefCounted> *)p_ref;
11991226
if (ref == nullptr || ref->is_null()) {
@@ -1515,6 +1542,8 @@ void gdextension_setup_interface() {
15151542
REGISTER_INTERFACE_FUNC(object_cast_to);
15161543
REGISTER_INTERFACE_FUNC(object_get_instance_from_id);
15171544
REGISTER_INTERFACE_FUNC(object_get_instance_id);
1545+
REGISTER_INTERFACE_FUNC(object_has_script_method);
1546+
REGISTER_INTERFACE_FUNC(object_call_script_method);
15181547
REGISTER_INTERFACE_FUNC(ref_get_object);
15191548
REGISTER_INTERFACE_FUNC(ref_set_object);
15201549
#ifndef DISABLE_DEPRECATED

core/extension/gdextension_interface.h

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -364,13 +364,18 @@ typedef struct {
364364
GDExtensionClassMethodPtrCall ptrcall_func;
365365
uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`.
366366

367-
/* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */
367+
/* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored.
368+
*
369+
* @todo Consider dropping `has_return_value` and making the other two properties match `GDExtensionMethodInfo` and `GDExtensionClassVirtualMethod` for consistency in future version of this struct.
370+
*/
368371
GDExtensionBool has_return_value;
369372
GDExtensionPropertyInfo *return_value_info;
370373
GDExtensionClassMethodArgumentMetadata return_value_metadata;
371374

372375
/* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`.
373376
* Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies.
377+
*
378+
* @todo Consider renaming `arguments_info` to `arguments` for consistency in future version of this struct.
374379
*/
375380
uint32_t argument_count;
376381
GDExtensionPropertyInfo *arguments_info;
@@ -381,6 +386,18 @@ typedef struct {
381386
GDExtensionVariantPtr *default_arguments;
382387
} GDExtensionClassMethodInfo;
383388

389+
typedef struct {
390+
GDExtensionStringNamePtr name;
391+
uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`.
392+
393+
GDExtensionPropertyInfo return_value;
394+
GDExtensionClassMethodArgumentMetadata return_value_metadata;
395+
396+
uint32_t argument_count;
397+
GDExtensionPropertyInfo *arguments;
398+
GDExtensionClassMethodArgumentMetadata *arguments_metadata;
399+
} GDExtensionClassVirtualMethodInfo;
400+
384401
typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
385402
typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata);
386403
typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata);
@@ -2268,6 +2285,34 @@ typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDOb
22682285
*/
22692286
typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object);
22702287

2288+
/**
2289+
* @name object_has_script_method
2290+
* @since 4.3
2291+
*
2292+
* Checks if this object has a script with the given method.
2293+
*
2294+
* @param p_object A pointer to the Object.
2295+
* @param p_method A pointer to a StringName identifying the method.
2296+
*
2297+
* @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.
2298+
*/
2299+
typedef GDExtensionBool (*GDExtensionInterfaceObjectHasScriptMethod)(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method);
2300+
2301+
/**
2302+
* @name object_call_script_method
2303+
* @since 4.3
2304+
*
2305+
* Call the given script method on this object.
2306+
*
2307+
* @param p_object A pointer to the Object.
2308+
* @param p_method A pointer to a StringName identifying the method.
2309+
* @param p_args A pointer to a C array of Variant.
2310+
* @param p_argument_count The number of arguments.
2311+
* @param r_return A pointer a Variant which will be assigned the return value.
2312+
* @param r_error A pointer the structure which will hold error information.
2313+
*/
2314+
typedef void (*GDExtensionInterfaceObjectCallScriptMethod)(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error);
2315+
22712316
/* INTERFACE: Reference */
22722317

22732318
/**
@@ -2483,6 +2528,20 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
24832528
*/
24842529
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
24852530

2531+
/**
2532+
* @name classdb_register_extension_class_virtual_method
2533+
* @since 4.3
2534+
*
2535+
* Registers a virtual method on an extension class in ClassDB, that can be implemented by scripts or other extensions.
2536+
*
2537+
* Provided struct can be safely freed once the function returns.
2538+
*
2539+
* @param p_library A pointer the library received by the GDExtension's entry point function.
2540+
* @param p_class_name A pointer to a StringName with the class name.
2541+
* @param p_method_info A pointer to a GDExtensionClassMethodInfo struct.
2542+
*/
2543+
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
2544+
24862545
/**
24872546
* @name classdb_register_extension_class_integer_constant
24882547
* @since 4.1

core/object/class_db.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,28 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p
16151615
#endif
16161616
}
16171617

1618+
void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) {
1619+
ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
1620+
1621+
#ifdef DEBUG_METHODS_ENABLED
1622+
PackedStringArray arg_names;
1623+
1624+
MethodInfo mi;
1625+
mi.name = *reinterpret_cast<StringName *>(p_method_info->name);
1626+
mi.return_val = PropertyInfo(p_method_info->return_value);
1627+
mi.return_val_metadata = p_method_info->return_value_metadata;
1628+
mi.flags = p_method_info->method_flags;
1629+
for (int i = 0; i < (int)p_method_info->argument_count; i++) {
1630+
PropertyInfo arg(p_method_info->arguments[i]);
1631+
mi.arguments.push_back(arg);
1632+
mi.arguments_metadata.push_back(p_method_info->arguments_metadata[i]);
1633+
arg_names.push_back(arg.name);
1634+
}
1635+
1636+
add_virtual_method(p_class, mi, true, arg_names);
1637+
#endif
1638+
}
1639+
16181640
void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) {
16191641
OBJTYPE_WLOCK;
16201642

core/object/class_db.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ class ClassDB {
410410

411411
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
412412
static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
413+
static void add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info);
413414

414415
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);
415416
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);

0 commit comments

Comments
 (0)