Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow CallableCustom objects to be created from GDExtensions #78813

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions core/extension/gdextension_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,93 @@
#include "core/variant/variant.h"
#include "core/version.h"

class CallableCustomExtension : public CallableCustom {
ObjectID object;
GDExtensionCallableCustomCall call_func;
GDExtensionCallableCustomFree free_func;
void *userdata;

uint32_t h;

static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);

if (a->call_func != b->call_func || a->userdata != b->userdata) {
return false;
}
return true;
}

static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
const CallableCustomExtension *a = static_cast<const CallableCustomExtension *>(p_a);
const CallableCustomExtension *b = static_cast<const CallableCustomExtension *>(p_b);

if (a->call_func != b->call_func) {
return a->call_func < b->call_func;
}
return a->userdata < b->userdata;
}

public:
uint32_t hash() const {
return h;
}

String get_as_text() const {
return String();
}

CompareEqualFunc get_compare_equal_func() const {
return compare_equal;
}

CompareLessFunc get_compare_less_func() const {
return compare_less;
}

bool is_valid() const {
return call_func != nullptr;
}

StringName get_method() const {
return StringName();
}

ObjectID get_object() const {
return object;
}

void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
GDExtensionCallError error;

call_func(userdata, (GDExtensionConstVariantPtr *)p_arguments, p_argcount, &r_return_value, &error);

r_call_error.error = (Callable::CallError::Error)error.error;
r_call_error.argument = error.argument;
r_call_error.expected = error.expected;
}

CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
if (p_info->object != nullptr) {
object = ((Object *)p_info->object)->get_instance_id();
}
call_func = p_info->call_func;
free_func = p_info->free_func;
userdata = p_info->callable_userdata;

// Pre-calculate the hash.
h = hash_murmur3_one_64((uint64_t)call_func);
h = hash_murmur3_one_64((uint64_t)userdata, h);
}

~CallableCustomExtension() {
if (free_func != nullptr) {
free_func(userdata);
}
}
};

// Core interface functions.
GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name) {
return GDExtension::get_interface_function(p_name);
Expand Down Expand Up @@ -1036,6 +1123,10 @@ static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjec
ref->reference_ptr(o);
}

static void gdextension_callable_custom_create(GDExtensionUninitializedVariantPtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
memnew_placement(r_callable, Variant(Callable(memnew(CallableCustomExtension(p_custom_callable_info)))));
}

static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) {
ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension);
script_instance_extension->instance = p_instance_data;
Expand Down Expand Up @@ -1209,6 +1300,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(object_get_instance_id);
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
REGISTER_INTERFACE_FUNC(callable_custom_create);
REGISTER_INTERFACE_FUNC(script_instance_create);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
Expand Down
27 changes: 27 additions & 0 deletions core/extension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,20 @@ typedef struct {
GDExtensionVariantPtr *default_arguments;
} GDExtensionClassMethodInfo;

typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata);

typedef struct {
/* Only `call_func` is strictly required, however, `object` should be passed if its not a static method.
* Both `call_func` and `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes.
* `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed.
*/
GDExtensionObjectPtr object;
GDExtensionCallableCustomCall call_func;
GDExtensionCallableCustomFree free_func;
void *callable_userdata;
} GDExtensionCallableCustomInfo;

/* SCRIPT INSTANCE EXTENSION */

typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInstance native implementation.
Expand Down Expand Up @@ -2067,6 +2081,19 @@ typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDOb
*/
typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object);

/**
* @name callable_custom_create
* @since 4.2
*
* Creates a custom Callable object from a function pointer.
*
* Provided struct can be safely freed once the function returns.
*
* @param r_variant The variant to fill with the new Callable.
* @param p_callable_custom_info The info required to construct a Callable.
*/
typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedVariantPtr r_variant, GDExtensionCallableCustomInfo *p_callable_custom_info);

/* INTERFACE: Reference */

/**
Expand Down