diff options
-rw-r--r-- | binding_generator.py | 50 | ||||
-rw-r--r-- | gdextension/gdextension_interface.h | 51 | ||||
-rw-r--r-- | include/godot_cpp/classes/wrapped.hpp | 9 | ||||
-rw-r--r-- | include/godot_cpp/core/class_db.hpp | 8 | ||||
-rw-r--r-- | include/godot_cpp/core/object.hpp | 10 | ||||
-rw-r--r-- | include/godot_cpp/core/type_info.hpp | 2 | ||||
-rw-r--r-- | include/godot_cpp/godot.hpp | 2 | ||||
-rw-r--r-- | include/godot_cpp/variant/callable_custom.hpp | 1 | ||||
-rw-r--r-- | include/godot_cpp/variant/callable_method_pointer.hpp | 25 | ||||
-rw-r--r-- | src/godot.cpp | 4 | ||||
-rw-r--r-- | src/variant/callable_custom.cpp | 18 | ||||
-rw-r--r-- | src/variant/callable_method_pointer.cpp | 13 | ||||
-rw-r--r-- | test/project/main.gd | 15 | ||||
-rw-r--r-- | test/project/main.tscn | 2 | ||||
-rw-r--r-- | test/src/example.cpp | 33 | ||||
-rw-r--r-- | test/src/example.h | 26 | ||||
-rw-r--r-- | test/src/register_types.cpp | 2 |
17 files changed, 232 insertions, 39 deletions
diff --git a/binding_generator.py b/binding_generator.py index cd2a86e..ed91fa3 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -72,13 +72,14 @@ def generate_wrappers(target): def generate_virtual_version(argcount, const=False, returns=False): s = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ - StringName _gdvirtual_##m_name##_sn = #m_name;\\ + ::godot::StringName _gdvirtual_##m_name##_sn = #m_name;\\ template <bool required>\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\ if (::godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn)) { \\ GDExtensionCallError ce;\\ $CALLSIARGS\\ - $CALLSIBEGIN::godot::internal::gdextension_interface_object_call_script_method(_owner, &_gdvirtual_##m_name##_sn, $CALLSIARGPASS, $CALLSIRETPASS, &ce);\\ + ::godot::Variant ret;\\ + ::godot::internal::gdextension_interface_object_call_script_method(_owner, &_gdvirtual_##m_name##_sn, $CALLSIARGPASS, &ret, &ce);\\ if (ce.error == GDEXTENSION_CALL_OK) {\\ $CALLSIRET\\ return true;\\ @@ -91,10 +92,10 @@ def generate_virtual_version(argcount, const=False, returns=False): return false;\\ }\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\ - return godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn); \\ + return ::godot::internal::gdextension_interface_object_has_script_method(_owner, &_gdvirtual_##m_name##_sn); \\ }\\ - _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\ - MethodInfo method_info;\\ + _FORCE_INLINE_ static ::godot::MethodInfo _gdvirtual_##m_name##_get_method_info() {\\ + ::godot::MethodInfo method_info;\\ method_info.name = #m_name;\\ method_info.flags = $METHOD_FLAGS;\\ $FILL_METHOD_INFO\\ @@ -109,8 +110,8 @@ def generate_virtual_version(argcount, const=False, returns=False): sproto += "R" s = s.replace("$RET", "m_ret,") s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors - method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" - method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;" + method_info += "method_info.return_val = ::godot::GetTypeInfo<m_ret>::get_class_info();\\\n" + method_info += "\t\tmethod_info.return_val_metadata = ::godot::GetTypeInfo<m_ret>::METADATA;" else: s = s.replace("$RET ", "") s = s.replace("\t\t\t$RVOID\\\n", "") @@ -118,10 +119,10 @@ def generate_virtual_version(argcount, const=False, returns=False): if const: sproto += "C" s = s.replace("$CONST", "const") - s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST") + s = s.replace("$METHOD_FLAGS", "::godot::METHOD_FLAG_VIRTUAL | ::godot::METHOD_FLAG_CONST") else: s = s.replace("$CONST ", "") - s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL") + s = s.replace("$METHOD_FLAGS", "::godot::METHOD_FLAG_VIRTUAL") s = s.replace("$VER", sproto) argtext = "" @@ -130,8 +131,8 @@ def generate_virtual_version(argcount, const=False, returns=False): callsiargptrs = "" if argcount > 0: argtext += ", " - callsiargs = f"Variant vargs[{argcount}] = {{ " - callsiargptrs = f"\t\t\tconst Variant *vargptrs[{argcount}] = {{ " + callsiargs = f"::godot::Variant vargs[{argcount}] = {{ " + callsiargptrs = f"\t\t\tconst ::godot::Variant *vargptrs[{argcount}] = {{ " for i in range(argcount): if i > 0: argtext += ", " @@ -140,12 +141,12 @@ def generate_virtual_version(argcount, const=False, returns=False): callsiargptrs += ", " argtext += f"m_type{i + 1}" callargtext += f"m_type{i + 1} arg{i + 1}" - callsiargs += f"Variant(arg{i + 1})" + callsiargs += f"::godot::Variant(arg{i + 1})" callsiargptrs += f"&vargs[{i}]" if method_info: method_info += "\\\n\t\t" - method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n" - method_info += f"\t\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type{i + 1}>::METADATA);" + method_info += f"method_info.arguments.push_back(::godot::GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n" + method_info += f"\t\tmethod_info.arguments_metadata.push_back(::godot::GetTypeInfo<m_type{i + 1}>::METADATA);" if argcount: callsiargs += " };\\\n" @@ -160,12 +161,8 @@ def generate_virtual_version(argcount, const=False, returns=False): if argcount > 0: callargtext += ", " callargtext += "m_ret &r_ret" - s = s.replace("$CALLSIBEGIN", "Variant ret;\\\n\t\t\t") - s = s.replace("$CALLSIRETPASS", "&ret") - s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);") + s = s.replace("$CALLSIRET", "r_ret = ::godot::VariantCaster<m_ret>::cast(ret);") else: - s = s.replace("$CALLSIBEGIN", "") - s = s.replace("$CALLSIRETPASS", "nullptr") s = s.replace("\t\t\t\t$CALLSIRET\\\n", "") s = s.replace(" $ARG", argtext) @@ -545,6 +542,10 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl if class_name == "PackedVector3Array": result.append("#include <godot_cpp/variant/vector3.hpp>") + if is_packed_array(class_name): + result.append("#include <godot_cpp/core/error_macros.hpp>") + result.append("#include <initializer_list>") + if class_name == "Array": result.append("#include <godot_cpp/variant/array_helpers.hpp>") @@ -872,6 +873,17 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl } """ result.append(iterators.replace("$TYPE", return_type)) + init_list = """ + _FORCE_INLINE_ $CLASS(std::initializer_list<$TYPE> p_init) { + ERR_FAIL_COND(resize(p_init.size()) != 0); + + size_t i = 0; + for (const $TYPE &element : p_init) { + set(i++, element); + } + } +""" + result.append(init_list.replace("$TYPE", return_type).replace("$CLASS", class_name)) if class_name == "Array": result.append("\tconst Variant &operator[](int64_t p_index) const;") diff --git a/gdextension/gdextension_interface.h b/gdextension/gdextension_interface.h index 78c6860..c3e64ef 100644 --- a/gdextension/gdextension_interface.h +++ b/gdextension/gdextension_interface.h @@ -442,6 +442,8 @@ typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_user typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); +typedef GDExtensionInt (*GDExtensionCallableCustomGetArgumentCount)(void *callable_userdata, GDExtensionBool *r_is_valid); + typedef struct { /* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method. * @@ -471,7 +473,40 @@ typedef struct { GDExtensionCallableCustomLessThan less_than_func; GDExtensionCallableCustomToString to_string_func; -} GDExtensionCallableCustomInfo; +} GDExtensionCallableCustomInfo; // Deprecated. Use GDExtensionCallableCustomInfo2 instead. + +typedef struct { + /* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method. + * + * `token` should point to an address that uniquely identifies the GDExtension (for example, the + * `GDExtensionClassLibraryPtr` passed to the entry symbol function. + * + * `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and + * `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes. + * + * The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable. + * + * `is_valid_func` is necessary if the validity of the callable can change before destruction. + * + * `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed. + */ + void *callable_userdata; + void *token; + + GDObjectInstanceID object_id; + + GDExtensionCallableCustomCall call_func; + GDExtensionCallableCustomIsValid is_valid_func; + GDExtensionCallableCustomFree free_func; + + GDExtensionCallableCustomHash hash_func; + GDExtensionCallableCustomEqual equal_func; + GDExtensionCallableCustomLessThan less_than_func; + + GDExtensionCallableCustomToString to_string_func; + + GDExtensionCallableCustomGetArgumentCount get_argument_count_func; +} GDExtensionCallableCustomInfo2; /* SCRIPT INSTANCE EXTENSION */ @@ -2507,6 +2542,7 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn /** * @name callable_custom_create * @since 4.2 + * @deprecated in Godot 4.3. Use `callable_custom_create2` instead. * * Creates a custom Callable object from a function pointer. * @@ -2518,6 +2554,19 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info); /** + * @name callable_custom_create2 + * @since 4.3 + * + * Creates a custom Callable object from a function pointer. + * + * Provided struct can be safely freed once the function returns. + * + * @param r_callable A pointer that will receive the new Callable. + * @param p_callable_custom_info The info required to construct a Callable. + */ +typedef void (*GDExtensionInterfaceCallableCustomCreate2)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo2 *p_callable_custom_info); + +/** * @name callable_custom_get_userdata * @since 4.2 * diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index 25a93df..fba855d 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -239,11 +239,16 @@ public: \ static void notification_bind(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) { \ if (p_instance && m_class::_get_notification()) { \ + if (!p_reversed) { \ + m_inherits::notification_bind(p_instance, p_what, p_reversed); \ + } \ if (m_class::_get_notification() != m_inherits::_get_notification()) { \ m_class *cls = reinterpret_cast<m_class *>(p_instance); \ - return cls->_notification(p_what); \ + cls->_notification(p_what); \ + } \ + if (p_reversed) { \ + m_inherits::notification_bind(p_instance, p_what, p_reversed); \ } \ - m_inherits::notification_bind(p_instance, p_what, p_reversed); \ } \ } \ \ diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 91748e6..65960a8 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -184,20 +184,20 @@ public: }; #define BIND_CONSTANT(m_constant) \ - godot::ClassDB::bind_integer_constant(get_class_static(), "", #m_constant, m_constant); + ::godot::ClassDB::bind_integer_constant(get_class_static(), "", #m_constant, m_constant); #define BIND_ENUM_CONSTANT(m_constant) \ - godot::ClassDB::bind_integer_constant(get_class_static(), godot::_gde_constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); + ::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_enum_name(m_constant, #m_constant), #m_constant, m_constant); #define BIND_BITFIELD_FLAG(m_constant) \ - godot::ClassDB::bind_integer_constant(get_class_static(), godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true); + ::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true); #define BIND_VIRTUAL_METHOD(m_class, m_method) \ { \ auto _call##m_method = [](GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) -> void { \ call_with_ptr_args(reinterpret_cast<m_class *>(p_instance), &m_class::m_method, p_args, p_ret); \ }; \ - godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method); \ + ::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method); \ } template <class T, bool is_abstract> diff --git a/include/godot_cpp/core/object.hpp b/include/godot_cpp/core/object.hpp index c1f1069..12de658 100644 --- a/include/godot_cpp/core/object.hpp +++ b/include/godot_cpp/core/object.hpp @@ -47,11 +47,11 @@ #include <vector> -#define ADD_SIGNAL(m_signal) godot::ClassDB::add_signal(get_class_static(), m_signal) -#define ADD_GROUP(m_name, m_prefix) godot::ClassDB::add_property_group(get_class_static(), m_name, m_prefix) -#define ADD_SUBGROUP(m_name, m_prefix) godot::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) -#define ADD_PROPERTY(m_property, m_setter, m_getter) godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter) -#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter, m_index) +#define ADD_SIGNAL(m_signal) ::godot::ClassDB::add_signal(get_class_static(), m_signal) +#define ADD_GROUP(m_name, m_prefix) ::godot::ClassDB::add_property_group(get_class_static(), m_name, m_prefix) +#define ADD_SUBGROUP(m_name, m_prefix) ::godot::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix) +#define ADD_PROPERTY(m_property, m_setter, m_getter) ::godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter) +#define ADD_PROPERTYI(m_property, m_setter, m_getter, m_index) ::godot::ClassDB::add_property(get_class_static(), m_property, m_setter, m_getter, m_index) namespace godot { diff --git a/include/godot_cpp/core/type_info.hpp b/include/godot_cpp/core/type_info.hpp index a5cb174..9a8b840 100644 --- a/include/godot_cpp/core/type_info.hpp +++ b/include/godot_cpp/core/type_info.hpp @@ -255,7 +255,7 @@ class BitField { public: _FORCE_INLINE_ void set_flag(T p_flag) { value |= p_flag; } _FORCE_INLINE_ bool has_flag(T p_flag) const { return value & p_flag; } - _FORCE_INLINE_ void clear_flag(T p_flag) { return value &= ~p_flag; } + _FORCE_INLINE_ void clear_flag(T p_flag) { value &= ~p_flag; } _FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; } _FORCE_INLINE_ operator int64_t() const { return value; } _FORCE_INLINE_ operator Variant() const { return value; } diff --git a/include/godot_cpp/godot.hpp b/include/godot_cpp/godot.hpp index 7c18d25..5a62930 100644 --- a/include/godot_cpp/godot.hpp +++ b/include/godot_cpp/godot.hpp @@ -167,7 +167,7 @@ extern "C" GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_obj extern "C" GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id; extern "C" GDExtensionInterfaceObjectHasScriptMethod gdextension_interface_object_has_script_method; extern "C" GDExtensionInterfaceObjectCallScriptMethod gdextension_interface_object_call_script_method; -extern "C" GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create; +extern "C" GDExtensionInterfaceCallableCustomCreate2 gdextension_interface_callable_custom_create2; extern "C" GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata; extern "C" GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object; extern "C" GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object; diff --git a/include/godot_cpp/variant/callable_custom.hpp b/include/godot_cpp/variant/callable_custom.hpp index 34328f9..48a8142 100644 --- a/include/godot_cpp/variant/callable_custom.hpp +++ b/include/godot_cpp/variant/callable_custom.hpp @@ -41,6 +41,7 @@ class Object; class CallableCustomBase { public: virtual ObjectID get_object() const = 0; + virtual int get_argument_count(bool &r_is_valid) const; virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0; virtual ~CallableCustomBase() {} }; diff --git a/include/godot_cpp/variant/callable_method_pointer.hpp b/include/godot_cpp/variant/callable_method_pointer.hpp index 159f976..0c0ff34 100644 --- a/include/godot_cpp/variant/callable_method_pointer.hpp +++ b/include/godot_cpp/variant/callable_method_pointer.hpp @@ -73,6 +73,11 @@ public: return ObjectID(data.instance->get_instance_id()); } + virtual int get_argument_count(bool &r_is_valid) const override { + r_is_valid = true; + return sizeof...(P); + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); } @@ -110,6 +115,11 @@ public: return ObjectID(data.instance->get_instance_id()); } + virtual int get_argument_count(bool &r_is_valid) const override { + r_is_valid = true; + return sizeof...(P); + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } @@ -147,6 +157,11 @@ public: return ObjectID(data.instance->get_instance_id()); } + virtual int get_argument_count(bool &r_is_valid) const override { + r_is_valid = true; + return sizeof...(P); + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } @@ -182,6 +197,11 @@ public: return ObjectID(); } + virtual int get_argument_count(bool &r_is_valid) const override { + r_is_valid = true; + return sizeof...(P); + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); r_return_value = Variant(); @@ -218,6 +238,11 @@ public: return ObjectID(); } + virtual int get_argument_count(bool &r_is_valid) const override { + r_is_valid = true; + return sizeof...(P); + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override { call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); } diff --git a/src/godot.cpp b/src/godot.cpp index 42fc75f..8a031be 100644 --- a/src/godot.cpp +++ b/src/godot.cpp @@ -173,7 +173,7 @@ GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_ins GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id = nullptr; GDExtensionInterfaceObjectHasScriptMethod gdextension_interface_object_has_script_method = nullptr; GDExtensionInterfaceObjectCallScriptMethod gdextension_interface_object_call_script_method = nullptr; -GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create = nullptr; +GDExtensionInterfaceCallableCustomCreate2 gdextension_interface_callable_custom_create2 = nullptr; GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata = nullptr; GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object = nullptr; GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object = nullptr; @@ -413,7 +413,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge LOAD_PROC_ADDRESS(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId); LOAD_PROC_ADDRESS(object_has_script_method, GDExtensionInterfaceObjectHasScriptMethod); LOAD_PROC_ADDRESS(object_call_script_method, GDExtensionInterfaceObjectCallScriptMethod); - LOAD_PROC_ADDRESS(callable_custom_create, GDExtensionInterfaceCallableCustomCreate); + LOAD_PROC_ADDRESS(callable_custom_create2, GDExtensionInterfaceCallableCustomCreate2); LOAD_PROC_ADDRESS(callable_custom_get_userdata, GDExtensionInterfaceCallableCustomGetUserData); LOAD_PROC_ADDRESS(ref_get_object, GDExtensionInterfaceRefGetObject); LOAD_PROC_ADDRESS(ref_set_object, GDExtensionInterfaceRefSetObject); diff --git a/src/variant/callable_custom.cpp b/src/variant/callable_custom.cpp index e0540fa..ae8cc48 100644 --- a/src/variant/callable_custom.cpp +++ b/src/variant/callable_custom.cpp @@ -35,6 +35,11 @@ namespace godot { +int CallableCustomBase::get_argument_count(bool &r_is_valid) const { + r_is_valid = false; + return 0; +} + static void callable_custom_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) { CallableCustom *callable_custom = (CallableCustom *)p_userdata; callable_custom->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error); @@ -84,13 +89,21 @@ static GDExtensionBool callable_custom_less_than_func(void *p_a, void *p_b) { return func_a(a, b); } +static GDExtensionInt custom_callable_get_argument_count_func(void *p_userdata, GDExtensionBool *r_is_valid) { + CallableCustom *callable_custom = (CallableCustom *)p_userdata; + bool valid = false; + int ret = callable_custom->get_argument_count(valid); + *r_is_valid = valid; + return ret; +} + bool CallableCustom::is_valid() const { // The same default implementation as in Godot. return ObjectDB::get_instance(get_object()); } Callable::Callable(CallableCustom *p_callable_custom) { - GDExtensionCallableCustomInfo info = {}; + GDExtensionCallableCustomInfo2 info = {}; info.callable_userdata = p_callable_custom; info.token = internal::token; info.object_id = p_callable_custom->get_object(); @@ -101,8 +114,9 @@ Callable::Callable(CallableCustom *p_callable_custom) { info.equal_func = &callable_custom_equal_func; info.less_than_func = &callable_custom_less_than_func; info.to_string_func = &callable_custom_to_string; + info.get_argument_count_func = &custom_callable_get_argument_count_func; - ::godot::internal::gdextension_interface_callable_custom_create(_native_ptr(), &info); + ::godot::internal::gdextension_interface_callable_custom_create2(_native_ptr(), &info); } CallableCustom *Callable::get_custom() const { diff --git a/src/variant/callable_method_pointer.cpp b/src/variant/callable_method_pointer.cpp index 520ed05..23b7ef5 100644 --- a/src/variant/callable_method_pointer.cpp +++ b/src/variant/callable_method_pointer.cpp @@ -77,6 +77,14 @@ static GDExtensionBool custom_callable_mp_less_than_func(void *p_a, void *p_b) { return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) < 0; } +static GDExtensionInt custom_callable_mp_get_argument_count_func(void *p_userdata, GDExtensionBool *r_is_valid) { + CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata; + bool valid = false; + int ret = callable_method_pointer->get_argument_count(valid); + *r_is_valid = valid; + return ret; +} + void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) { comp_ptr = p_base_ptr; comp_size = p_ptr_size / 4; @@ -93,7 +101,7 @@ void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_pt namespace internal { Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer) { - GDExtensionCallableCustomInfo info = {}; + GDExtensionCallableCustomInfo2 info = {}; info.callable_userdata = p_callable_method_pointer; info.token = internal::token; info.object_id = p_callable_method_pointer->get_object(); @@ -103,9 +111,10 @@ Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_m info.hash_func = &custom_callable_mp_hash; info.equal_func = &custom_callable_mp_equal_func; info.less_than_func = &custom_callable_mp_less_than_func; + info.get_argument_count_func = &custom_callable_mp_get_argument_count_func; Callable callable; - ::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info); + ::godot::internal::gdextension_interface_callable_custom_create2(callable._native_ptr(), &info); return callable; } diff --git a/test/project/main.gd b/test/project/main.gd index d2cbd26..5cf483e 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -102,6 +102,7 @@ func _ready(): # mp_callable() with void method. var mp_callable: Callable = example.test_callable_mp() assert_equal(mp_callable.is_valid(), true) + assert_equal(mp_callable.get_argument_count(), 3) mp_callable.call(example, "void", 36) assert_equal(custom_signal_emitted, ["unbound_method1: Example - void", 36]) @@ -117,14 +118,17 @@ func _ready(): # mp_callable() with return value. var mp_callable_ret: Callable = example.test_callable_mp_ret() + assert_equal(mp_callable_ret.get_argument_count(), 3) assert_equal(mp_callable_ret.call(example, "test", 77), "unbound_method2: Example - test - 77") # mp_callable() with const method and return value. var mp_callable_retc: Callable = example.test_callable_mp_retc() + assert_equal(mp_callable_retc.get_argument_count(), 3) assert_equal(mp_callable_retc.call(example, "const", 101), "unbound_method3: Example - const - 101") # mp_callable_static() with void method. var mp_callable_static: Callable = example.test_callable_mp_static() + assert_equal(mp_callable_static.get_argument_count(), 3) mp_callable_static.call(example, "static", 83) assert_equal(custom_signal_emitted, ["unbound_static_method1: Example - static", 83]) @@ -140,6 +144,7 @@ func _ready(): # mp_callable_static() with return value. var mp_callable_static_ret: Callable = example.test_callable_mp_static_ret() + assert_equal(mp_callable_static_ret.get_argument_count(), 3) assert_equal(mp_callable_static_ret.call(example, "static-ret", 84), "unbound_static_method2: Example - static-ret - 84") # CallableCustom. @@ -150,10 +155,12 @@ func _ready(): assert_equal(custom_callable.hash(), 27); assert_equal(custom_callable.get_object(), null); assert_equal(custom_callable.get_method(), ""); + assert_equal(custom_callable.get_argument_count(), 2) assert_equal(str(custom_callable), "<MyCallableCustom>"); # PackedArray iterators assert_equal(example.test_vector_ops(), 105) + assert_equal(example.test_vector_init_list(), 105) # Properties. assert_equal(example.group_subgroup_custom_position, Vector2(0, 0)) @@ -245,6 +252,14 @@ func _ready(): assert_equal(example.test_virtual_implemented_in_script("Virtual", 939), "Implemented") assert_equal(custom_signal_emitted, ["Virtual", 939]) + # Test that notifications happen on both parent and child classes. + var example_child = $ExampleChild + assert_equal(example_child.get_value1(), 11) + assert_equal(example_child.get_value2(), 33) + example_child.notification(NOTIFICATION_ENTER_TREE, true) + assert_equal(example_child.get_value1(), 11) + assert_equal(example_child.get_value2(), 22) + exit_with_status() func _on_Example_custom_signal(signal_name, value): diff --git a/test/project/main.tscn b/test/project/main.tscn index 1f17597..e786025 100644 --- a/test/project/main.tscn +++ b/test/project/main.tscn @@ -24,4 +24,6 @@ offset_right = 79.0 offset_bottom = 29.0 text = "Click me!" +[node name="ExampleChild" type="ExampleChild" parent="."] + [connection signal="custom_signal" from="Example" to="." method="_on_Example_custom_signal"] diff --git a/test/src/example.cpp b/test/src/example.cpp index 136ac94..3ec8bca 100644 --- a/test/src/example.cpp +++ b/test/src/example.cpp @@ -49,6 +49,11 @@ public: return ObjectID(); } + virtual int get_argument_count(bool &r_is_valid) const { + r_is_valid = true; + return 2; + } + virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const { r_return_value = "Hi"; r_call_error.error = GDEXTENSION_CALL_OK; @@ -197,6 +202,7 @@ void Example::_bind_methods() { ClassDB::bind_method(D_METHOD("test_string_is_fourty_two"), &Example::test_string_is_fourty_two); ClassDB::bind_method(D_METHOD("test_string_resize"), &Example::test_string_resize); ClassDB::bind_method(D_METHOD("test_vector_ops"), &Example::test_vector_ops); + ClassDB::bind_method(D_METHOD("test_vector_init_list"), &Example::test_vector_init_list); ClassDB::bind_method(D_METHOD("test_object_cast_to_node", "object"), &Example::test_object_cast_to_node); ClassDB::bind_method(D_METHOD("test_object_cast_to_control", "object"), &Example::test_object_cast_to_control); @@ -411,6 +417,15 @@ int Example::test_vector_ops() const { return ret; } +int Example::test_vector_init_list() const { + PackedInt32Array arr = { 10, 20, 30, 45 }; + int ret = 0; + for (const int32_t &E : arr) { + ret += E; + } + return ret; +} + Callable Example::test_callable_mp() { return callable_mp(this, &Example::unbound_method1); } @@ -630,6 +645,24 @@ void Example::_input(const Ref<InputEvent> &event) { } } +void ExampleBase::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_value1"), &ExampleBase::get_value1); + ClassDB::bind_method(D_METHOD("get_value2"), &ExampleBase::get_value2); +} + +void ExampleBase::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + value1 = 11; + value2 = 22; + } +} + +void ExampleChild::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + value2 = 33; + } +} + String Example::test_virtual_implemented_in_script(const String &p_name, int p_value) { String ret; if (GDVIRTUAL_CALL(_do_something_virtual, p_name, p_value, ret)) { diff --git a/test/src/example.h b/test/src/example.h index 7da7b08..0ad9493 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -130,6 +130,7 @@ public: bool test_string_is_fourty_two(const String &p_str) const; String test_string_resize(String p_original) const; int test_vector_ops() const; + int test_vector_init_list() const; bool test_object_cast_to_node(Object *p_object) const; bool test_object_cast_to_control(Object *p_object) const; @@ -220,6 +221,31 @@ protected: virtual int test_function() override { return 25; } }; +class ExampleBase : public Node { + GDCLASS(ExampleBase, Node); + +protected: + int value1 = 0; + int value2 = 0; + + static void _bind_methods(); + + void _notification(int p_what); + +public: + int get_value1() { return value1; } + int get_value2() { return value2; } +}; + +class ExampleChild : public ExampleBase { + GDCLASS(ExampleChild, ExampleBase); + +protected: + static void _bind_methods() {} + + void _notification(int p_what); +}; + class ExampleRuntime : public Node { GDCLASS(ExampleRuntime, Node); diff --git a/test/src/register_types.cpp b/test/src/register_types.cpp index 5c38241..cbede66 100644 --- a/test/src/register_types.cpp +++ b/test/src/register_types.cpp @@ -27,6 +27,8 @@ void initialize_example_module(ModuleInitializationLevel p_level) { ClassDB::register_class<ExampleVirtual>(true); ClassDB::register_abstract_class<ExampleAbstractBase>(); ClassDB::register_class<ExampleConcrete>(); + ClassDB::register_class<ExampleBase>(); + ClassDB::register_class<ExampleChild>(); ClassDB::register_runtime_class<ExampleRuntime>(); } |