summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/core_bind.cpp6
-rw-r--r--core/core_bind.h2
-rw-r--r--core/extension/gdextension_interface.cpp56
-rw-r--r--core/extension/gdextension_interface.h51
-rw-r--r--core/object/callable_method_pointer.h25
-rw-r--r--core/object/class_db.cpp25
-rw-r--r--core/object/class_db.h1
-rw-r--r--core/object/object.cpp55
-rw-r--r--core/object/object.h2
-rw-r--r--core/object/script_instance.cpp22
-rw-r--r--core/object/script_instance.h2
-rw-r--r--core/object/script_language.cpp16
-rw-r--r--core/object/script_language.h9
-rw-r--r--core/object/script_language_extension.cpp3
-rw-r--r--core/object/script_language_extension.h18
-rw-r--r--core/variant/callable.cpp19
-rw-r--r--core/variant/callable.h2
-rw-r--r--core/variant/callable_bind.cpp16
-rw-r--r--core/variant/callable_bind.h2
-rw-r--r--core/variant/variant_call.cpp5
-rw-r--r--core/variant/variant_callable.cpp9
-rw-r--r--core/variant/variant_callable.h1
-rw-r--r--doc/classes/@GlobalScope.xml4
-rw-r--r--doc/classes/ArrayMesh.xml2
-rw-r--r--doc/classes/Callable.xml6
-rw-r--r--doc/classes/ClassDB.xml9
-rw-r--r--doc/classes/Color.xml2
-rw-r--r--doc/classes/Crypto.xml2
-rw-r--r--doc/classes/Engine.xml4
-rw-r--r--doc/classes/Environment.xml2
-rw-r--r--doc/classes/FileAccess.xml2
-rw-r--r--doc/classes/HashingContext.xml6
-rw-r--r--doc/classes/Image.xml2
-rw-r--r--doc/classes/ImporterMesh.xml2
-rw-r--r--doc/classes/Input.xml2
-rw-r--r--doc/classes/Object.xml8
-rw-r--r--doc/classes/ProjectSettings.xml6
-rw-r--r--doc/classes/RenderingServer.xml2
-rw-r--r--doc/classes/ScriptExtension.xml6
-rw-r--r--doc/classes/SurfaceTool.xml2
-rw-r--r--doc/classes/Viewport.xml2
-rw-r--r--doc/classes/XMLParser.xml2
-rw-r--r--editor/filesystem_dock.cpp56
-rw-r--r--editor/filesystem_dock.h5
-rw-r--r--modules/gdscript/gdscript.cpp34
-rw-r--r--modules/gdscript/gdscript.h6
-rw-r--r--modules/gdscript/gdscript_function.h1
-rw-r--r--modules/gdscript/gdscript_lambda_callable.cpp18
-rw-r--r--modules/gdscript/gdscript_lambda_callable.h2
-rw-r--r--modules/gdscript/gdscript_rpc_callable.cpp4
-rw-r--r--modules/gdscript/gdscript_rpc_callable.h1
-rw-r--r--modules/gdscript/gdscript_utility_callable.cpp15
-rw-r--r--modules/gdscript/gdscript_utility_callable.h1
-rw-r--r--modules/gdscript/gdscript_utility_functions.cpp2
-rw-r--r--modules/gdscript/gdscript_utility_functions.h2
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/argument_count.gd102
-rw-r--r--modules/gdscript/tests/scripts/runtime/features/argument_count.out27
-rw-r--r--modules/gltf/doc_classes/GLTFMesh.xml2
-rw-r--r--modules/gltf/doc_classes/GLTFTexture.xml2
-rw-r--r--modules/interactive_music/audio_stream_interactive.cpp2
-rw-r--r--modules/minimp3/doc_classes/ResourceImporterMP3.xml4
-rw-r--r--modules/mono/csharp_script.cpp51
-rw-r--r--modules/mono/csharp_script.h2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs2
-rw-r--r--modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs23
-rw-r--r--modules/mono/managed_callable.cpp4
-rw-r--r--modules/mono/managed_callable.h1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.cpp1
-rw-r--r--modules/mono/mono_gd/gd_mono_cache.h2
-rw-r--r--modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml2
-rw-r--r--modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml2
-rw-r--r--platform/web/doc_classes/EditorExportPlatformWeb.xml2
-rw-r--r--tests/core/object/test_object.h6
-rw-r--r--tests/core/variant/test_callable.h140
-rw-r--r--tests/test_main.cpp1
75 files changed, 903 insertions, 42 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a0df1b6240..6927db002b 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1434,6 +1434,10 @@ bool ClassDB::class_has_method(const StringName &p_class, const StringName &p_me
return ::ClassDB::has_method(p_class, p_method, p_no_inheritance);
}
+int ClassDB::class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) const {
+ return ::ClassDB::get_method_argument_count(p_class, p_method, nullptr, p_no_inheritance);
+}
+
TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class, bool p_no_inheritance) const {
List<MethodInfo> methods;
::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
@@ -1562,6 +1566,8 @@ void ClassDB::_bind_methods() {
::ClassDB::bind_method(D_METHOD("class_has_method", "class", "method", "no_inheritance"), &ClassDB::class_has_method, DEFVAL(false));
+ ::ClassDB::bind_method(D_METHOD("class_get_method_argument_count", "class", "method", "no_inheritance"), &ClassDB::class_get_method_argument_count, DEFVAL(false));
+
::ClassDB::bind_method(D_METHOD("class_get_method_list", "class", "no_inheritance"), &ClassDB::class_get_method_list, DEFVAL(false));
::ClassDB::bind_method(D_METHOD("class_get_integer_constant_list", "class", "no_inheritance"), &ClassDB::class_get_integer_constant_list, DEFVAL(false));
diff --git a/core/core_bind.h b/core/core_bind.h
index 64ab4dd7e2..305f616cef 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -447,6 +447,8 @@ public:
bool class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
+ int class_get_method_argument_count(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
+
TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 67ec09d764..ca58d589bd 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -59,6 +59,8 @@ class CallableCustomExtension : public CallableCustom {
GDExtensionCallableCustomToString to_string_func;
+ GDExtensionCallableCustomGetArgumentCount get_argument_count_func;
+
uint32_t _hash;
static bool default_compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
@@ -143,6 +145,21 @@ public:
return object;
}
+ int get_argument_count(bool &r_is_valid) const override {
+ if (get_argument_count_func != nullptr) {
+ GDExtensionBool is_valid = false;
+
+ GDExtensionInt ret = get_argument_count_func(userdata, &is_valid);
+
+ if (is_valid) {
+ r_is_valid = true;
+ return ret;
+ }
+ }
+ r_is_valid = false;
+ return 0;
+ }
+
void *get_userdata(void *p_token) const {
return (p_token == token) ? userdata : nullptr;
}
@@ -157,6 +174,7 @@ public:
r_call_error.expected = error.expected;
}
+#ifndef DISABLE_DEPRECATED
CallableCustomExtension(GDExtensionCallableCustomInfo *p_info) {
userdata = p_info->callable_userdata;
token = p_info->token;
@@ -172,6 +190,35 @@ public:
to_string_func = p_info->to_string_func;
+ get_argument_count_func = nullptr;
+
+ // Pre-calculate the hash.
+ if (p_info->hash_func != nullptr) {
+ _hash = p_info->hash_func(userdata);
+ } else {
+ _hash = hash_murmur3_one_64((uint64_t)call_func);
+ _hash = hash_murmur3_one_64((uint64_t)userdata, _hash);
+ }
+ }
+#endif
+
+ CallableCustomExtension(GDExtensionCallableCustomInfo2 *p_info) {
+ userdata = p_info->callable_userdata;
+ token = p_info->token;
+
+ object = p_info->object_id;
+
+ call_func = p_info->call_func;
+ is_valid_func = p_info->is_valid_func;
+ free_func = p_info->free_func;
+
+ equal_func = p_info->equal_func;
+ less_than_func = p_info->less_than_func;
+
+ to_string_func = p_info->to_string_func;
+
+ get_argument_count_func = p_info->get_argument_count_func;
+
// Pre-calculate the hash.
if (p_info->hash_func != nullptr) {
_hash = p_info->hash_func(userdata);
@@ -1378,9 +1425,15 @@ static GDExtensionScriptInstancePtr gdextension_object_get_script_instance(GDExt
return script_instance_extension->instance;
}
+#ifndef DISABLE_DEPRECATED
static void gdextension_callable_custom_create(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_custom_callable_info) {
memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
}
+#endif
+
+static void gdextension_callable_custom_create2(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo2 *p_custom_callable_info) {
+ memnew_placement(r_callable, Callable(memnew(CallableCustomExtension(p_custom_callable_info))));
+}
static void *gdextension_callable_custom_get_userdata(GDExtensionTypePtr p_callable, void *p_token) {
const Callable &callable = *reinterpret_cast<const Callable *>(p_callable);
@@ -1595,7 +1648,10 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(placeholder_script_instance_create);
REGISTER_INTERFACE_FUNC(placeholder_script_instance_update);
REGISTER_INTERFACE_FUNC(object_get_script_instance);
+#ifndef DISABLE_DEPRECATED
REGISTER_INTERFACE_FUNC(callable_custom_create);
+#endif // DISABLE_DEPRECATED
+ REGISTER_INTERFACE_FUNC(callable_custom_create2);
REGISTER_INTERFACE_FUNC(callable_custom_get_userdata);
REGISTER_INTERFACE_FUNC(classdb_construct_object);
REGISTER_INTERFACE_FUNC(classdb_get_method_bind);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index e7497a9d4c..c863507019 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/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 */
@@ -2510,6 +2545,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.
*
@@ -2521,6 +2557,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/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index f8e8c4d7e9..09fe9679f7 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -93,6 +93,11 @@ public:
return data.instance->get_instance_id();
}
+ virtual int get_argument_count(bool &r_is_valid) const {
+ r_is_valid = true;
+ return sizeof...(P);
+ }
+
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
@@ -140,6 +145,11 @@ public:
return data.instance->get_instance_id();
}
+ virtual int get_argument_count(bool &r_is_valid) const {
+ r_is_valid = true;
+ return sizeof...(P);
+ }
+
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
@@ -187,6 +197,11 @@ public:
return 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, Callable::CallError &r_call_error) const override {
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
@@ -238,6 +253,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, Callable::CallError &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();
@@ -280,6 +300,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, Callable::CallError &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/core/object/class_db.cpp b/core/object/class_db.cpp
index 231a8e4d68..80a2703c2f 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -1666,6 +1666,31 @@ bool ClassDB::has_method(const StringName &p_class, const StringName &p_method,
return false;
}
+int ClassDB::get_method_argument_count(const StringName &p_class, const StringName &p_method, bool *r_is_valid, bool p_no_inheritance) {
+ OBJTYPE_RLOCK;
+
+ ClassInfo *type = classes.getptr(p_class);
+
+ while (type) {
+ MethodBind **method = type->method_map.getptr(p_method);
+ if (method && *method) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return (*method)->get_argument_count();
+ }
+ if (p_no_inheritance) {
+ break;
+ }
+ type = type->inherits_ptr;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
void ClassDB::bind_method_custom(const StringName &p_class, MethodBind *p_method) {
_bind_method_custom(p_class, p_method, false);
}
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 7f117b4a9b..3b146dd06e 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -430,6 +430,7 @@ public:
static void get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static void get_method_list_with_compatibility(const StringName &p_class, List<Pair<MethodInfo, uint32_t>> *p_methods_with_hash, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
static bool get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false);
+ static int get_method_argument_count(const StringName &p_class, const StringName &p_method, bool *r_is_valid = nullptr, bool p_no_inheritance = false);
static MethodBind *get_method(const StringName &p_class, const StringName &p_name);
static MethodBind *get_method_with_compatibility(const StringName &p_class, const StringName &p_name, uint64_t p_hash, bool *r_method_exists = nullptr, bool *r_is_deprecated = nullptr);
static Vector<uint32_t> get_method_compatibility_hashes(const StringName &p_class, const StringName &p_name);
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 6a5a9efefa..e5d771844b 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -688,6 +688,59 @@ bool Object::has_method(const StringName &p_method) const {
return false;
}
+int Object::_get_method_argument_count_bind(const StringName &p_method) const {
+ return get_method_argument_count(p_method);
+}
+
+int Object::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ if (p_method == CoreStringNames::get_singleton()->_free) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return 0;
+ }
+
+ if (script_instance) {
+ bool valid = false;
+ int ret = script_instance->get_method_argument_count(p_method, &valid);
+ if (valid) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ret;
+ }
+ }
+
+ {
+ bool valid = false;
+ int ret = ClassDB::get_method_argument_count(get_class_name(), p_method, &valid);
+ if (valid) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ret;
+ }
+ }
+
+ const Script *scr = Object::cast_to<Script>(this);
+ while (scr != nullptr) {
+ bool valid = false;
+ int ret = scr->get_script_method_argument_count(p_method, &valid);
+ if (valid) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ret;
+ }
+ scr = scr->get_base_script().ptr();
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
if (r_valid) {
*r_valid = false;
@@ -1644,6 +1697,8 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_method", "method"), &Object::has_method);
+ ClassDB::bind_method(D_METHOD("get_method_argument_count", "method"), &Object::_get_method_argument_count_bind);
+
ClassDB::bind_method(D_METHOD("has_signal", "signal"), &Object::has_signal);
ClassDB::bind_method(D_METHOD("get_signal_list"), &Object::_get_signal_list);
ClassDB::bind_method(D_METHOD("get_signal_connection_list", "signal"), &Object::_get_signal_connection_list);
diff --git a/core/object/object.h b/core/object/object.h
index cb1495296d..2efcf70670 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -654,6 +654,7 @@ private:
Variant _get_bind(const StringName &p_name) const;
void _set_indexed_bind(const NodePath &p_name, const Variant &p_value);
Variant _get_indexed_bind(const NodePath &p_name) const;
+ int _get_method_argument_count_bind(const StringName &p_name) const;
_FORCE_INLINE_ void _construct_object(bool p_reference);
@@ -865,6 +866,7 @@ public:
Variant property_get_revert(const StringName &p_name) const;
bool has_method(const StringName &p_method) const;
+ int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
void get_method_list(List<MethodInfo> *p_list) const;
Variant callv(const StringName &p_method, const Array &p_args);
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
diff --git a/core/object/script_instance.cpp b/core/object/script_instance.cpp
index 303b127db1..65f44e8a6b 100644
--- a/core/object/script_instance.cpp
+++ b/core/object/script_instance.cpp
@@ -32,6 +32,28 @@
#include "core/object/script_language.h"
+int ScriptInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ // Default implementation simply traverses hierarchy.
+ Ref<Script> script = get_script();
+ while (script.is_valid()) {
+ bool valid = false;
+ int ret = script->get_script_method_argument_count(p_method, &valid);
+ if (valid) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ret;
+ }
+
+ script = script->get_base_script();
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant ScriptInstance::call_const(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
return callp(p_method, p_args, p_argcount, r_error);
}
diff --git a/core/object/script_instance.h b/core/object/script_instance.h
index 45d51534fc..2c8132ec1f 100644
--- a/core/object/script_instance.h
+++ b/core/object/script_instance.h
@@ -53,6 +53,8 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const = 0;
virtual bool has_method(const StringName &p_method) const = 0;
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
+
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = 0;
template <typename... VarArgs>
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 14894e1710..1196c2f787 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -102,6 +102,22 @@ Dictionary Script::_get_script_constant_map() {
return ret;
}
+int Script::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ MethodInfo mi = get_method_info(p_method);
+
+ if (mi == MethodInfo()) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return mi.arguments.size();
+}
+
#ifdef TOOLS_ENABLED
PropertyInfo Script::get_class_category() const {
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 95e9d2b4af..be50e58d79 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -151,6 +151,8 @@ public:
virtual bool has_method(const StringName &p_method) const = 0;
virtual bool has_static_method(const StringName &p_method) const { return false; }
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
+
virtual MethodInfo get_method_info(const StringName &p_method) const = 0;
virtual bool is_tool() const = 0;
@@ -442,6 +444,13 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const override;
virtual bool has_method(const StringName &p_method) const override;
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
return Variant();
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index ec99c7cf4e..a18ef8d4d7 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -56,6 +56,9 @@ void ScriptExtension::_bind_methods() {
GDVIRTUAL_BIND(_has_method, "method");
GDVIRTUAL_BIND(_has_static_method, "method");
+
+ GDVIRTUAL_BIND(_get_script_method_argument_count, "method");
+
GDVIRTUAL_BIND(_get_method_info, "method");
GDVIRTUAL_BIND(_is_tool);
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 18105ec8cd..efb317b839 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -101,6 +101,19 @@ public:
EXBIND1RC(bool, has_method, const StringName &)
EXBIND1RC(bool, has_static_method, const StringName &)
+ GDVIRTUAL1RC(Variant, _get_script_method_argument_count, const StringName &)
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override {
+ Variant ret;
+ if (GDVIRTUAL_CALL(_get_script_method_argument_count, p_method, ret) && ret.get_type() == Variant::INT) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return ret.operator int();
+ }
+ // Fallback to default.
+ return Script::get_script_method_argument_count(p_method, r_is_valid);
+ }
+
GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
Dictionary mi;
@@ -807,6 +820,11 @@ public:
return false;
}
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override {
+ // Fallback to default.
+ return ScriptInstance::get_method_argument_count(p_method, r_is_valid);
+ }
+
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
Variant ret;
if (native_info->call_func) {
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 47271118a0..c6fbfd93a1 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -184,6 +184,20 @@ StringName Callable::get_method() const {
return method;
}
+int Callable::get_argument_count(bool *r_is_valid) const {
+ if (is_custom()) {
+ bool valid = false;
+ return custom->get_argument_count(r_is_valid ? *r_is_valid : valid);
+ } else if (!is_null()) {
+ return get_object()->get_method_argument_count(method, r_is_valid);
+ } else {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+}
+
int Callable::get_bound_arguments_count() const {
if (!is_null() && is_custom()) {
return custom->get_bound_arguments_count();
@@ -438,6 +452,11 @@ const Callable *CallableCustom::get_base_comparator() const {
return nullptr;
}
+int CallableCustom::get_argument_count(bool &r_is_valid) const {
+ r_is_valid = false;
+ return 0;
+}
+
int CallableCustom::get_bound_arguments_count() const {
return 0;
}
diff --git a/core/variant/callable.h b/core/variant/callable.h
index bba69d453e..63757d9d6e 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -109,6 +109,7 @@ public:
ObjectID get_object_id() const;
StringName get_method() const;
CallableCustom *get_custom() const;
+ int get_argument_count(bool *r_is_valid = nullptr) const;
int get_bound_arguments_count() const;
void get_bound_arguments_ref(Vector<Variant> &r_arguments, int &r_argcount) const; // Internal engine use, the exposed one is below.
Array get_bound_arguments() const;
@@ -155,6 +156,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const = 0;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const;
virtual const Callable *get_base_comparator() const;
+ virtual int get_argument_count(bool &r_is_valid) const;
virtual int get_bound_arguments_count() const;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const;
diff --git a/core/variant/callable_bind.cpp b/core/variant/callable_bind.cpp
index 6d6c60cbd4..d82aa3583d 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -91,6 +91,14 @@ const Callable *CallableCustomBind::get_base_comparator() const {
return callable.get_base_comparator();
}
+int CallableCustomBind::get_argument_count(bool &r_is_valid) const {
+ int ret = callable.get_argument_count(&r_is_valid);
+ if (r_is_valid) {
+ return ret - binds.size();
+ }
+ return 0;
+}
+
int CallableCustomBind::get_bound_arguments_count() const {
return callable.get_bound_arguments_count() + binds.size();
}
@@ -225,6 +233,14 @@ const Callable *CallableCustomUnbind::get_base_comparator() const {
return callable.get_base_comparator();
}
+int CallableCustomUnbind::get_argument_count(bool &r_is_valid) const {
+ int ret = callable.get_argument_count(&r_is_valid);
+ if (r_is_valid) {
+ return ret + argcount;
+ }
+ return 0;
+}
+
int CallableCustomUnbind::get_bound_arguments_count() const {
return callable.get_bound_arguments_count() - argcount;
}
diff --git a/core/variant/callable_bind.h b/core/variant/callable_bind.h
index 5798797a3d..43cebb45f0 100644
--- a/core/variant/callable_bind.h
+++ b/core/variant/callable_bind.h
@@ -53,6 +53,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
+ virtual int get_argument_count(bool &r_is_valid) const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
Callable get_callable() { return callable; }
@@ -81,6 +82,7 @@ public:
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
virtual Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
virtual const Callable *get_base_comparator() const override;
+ virtual int get_argument_count(bool &r_is_valid) const override;
virtual int get_bound_arguments_count() const override;
virtual void get_bound_arguments(Vector<Variant> &r_arguments, int &r_argcount) const override;
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 060ea007ff..40c9a588d8 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1053,6 +1053,10 @@ struct _VariantCall {
r_ret = callable->bindp(p_args, p_argcount);
}
+ static int func_Callable_get_argument_count(Callable *p_callable) {
+ return p_callable->get_argument_count();
+ }
+
static void func_Signal_emit(Variant *v, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
Signal *signal = VariantGetInternalPtr<Signal>::get_ptr(v);
signal->emit(p_args, p_argcount);
@@ -2048,6 +2052,7 @@ static void _register_variant_builtin_methods() {
bind_method(Callable, get_object, sarray(), varray());
bind_method(Callable, get_object_id, sarray(), varray());
bind_method(Callable, get_method, sarray(), varray());
+ bind_function(Callable, get_argument_count, _VariantCall::func_Callable_get_argument_count, sarray(), varray());
bind_method(Callable, get_bound_arguments_count, sarray(), varray());
bind_method(Callable, get_bound_arguments, sarray(), varray());
bind_method(Callable, hash, sarray(), varray());
diff --git a/core/variant/variant_callable.cpp b/core/variant/variant_callable.cpp
index dc31b6d1ac..21f9a4f52b 100644
--- a/core/variant/variant_callable.cpp
+++ b/core/variant/variant_callable.cpp
@@ -68,6 +68,15 @@ ObjectID VariantCallable::get_object() const {
return ObjectID();
}
+int VariantCallable::get_argument_count(bool &r_is_valid) const {
+ if (!Variant::has_builtin_method(variant.get_type(), method)) {
+ r_is_valid = false;
+ return 0;
+ }
+ r_is_valid = true;
+ return Variant::get_builtin_method_argument_count(variant.get_type(), method);
+}
+
void VariantCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
Variant v = variant;
v.callp(method, p_arguments, p_argcount, r_return_value, r_call_error);
diff --git a/core/variant/variant_callable.h b/core/variant/variant_callable.h
index 3f2b058aaf..1811f3bb9a 100644
--- a/core/variant/variant_callable.h
+++ b/core/variant/variant_callable.h
@@ -50,6 +50,7 @@ public:
bool is_valid() const override;
StringName get_method() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
VariantCallable(const Variant &p_variant, const StringName &p_method);
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 095756c1b1..3a1bd83c18 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1099,14 +1099,14 @@
<method name="rid_allocate_id">
<return type="int" />
<description>
- Allocates a unique ID which can be used by the implementation to construct a RID. This is used mainly from native extensions to implement servers.
+ Allocates a unique ID which can be used by the implementation to construct an RID. This is used mainly from native extensions to implement servers.
</description>
</method>
<method name="rid_from_int64">
<return type="RID" />
<param index="0" name="base" type="int" />
<description>
- Creates a RID from a [param base]. This is used mainly from native extensions to build servers.
+ Creates an RID from a [param base]. This is used mainly from native extensions to build servers.
</description>
</method>
<method name="rotate_toward">
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index dde7153d5f..0f6bf89cd7 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -71,7 +71,7 @@
Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
- The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents an LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of an LOD also increases the distance that the objects has to be from the camera before the LOD is used.
The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
[b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
diff --git a/doc/classes/Callable.xml b/doc/classes/Callable.xml
index 3965fd4f8f..05174abb07 100644
--- a/doc/classes/Callable.xml
+++ b/doc/classes/Callable.xml
@@ -147,6 +147,12 @@
[b]Note:[/b] This method is always necessary for the [Dictionary] type, as property syntax is used to access its entries. You may also use this method when [param variant]'s type is not known in advance (for polymorphism).
</description>
</method>
+ <method name="get_argument_count" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the total number of arguments this [Callable] should take, including optional arguments. This means that any arguments bound with [method bind] are [i]subtracted[/i] from the result, and any arguments unbound with [method unbind] are [i]added[/i] to the result.
+ </description>
+ </method>
<method name="get_bound_arguments" qualifiers="const">
<return type="Array" />
<description>
diff --git a/doc/classes/ClassDB.xml b/doc/classes/ClassDB.xml
index d24181c3d3..3f71406091 100644
--- a/doc/classes/ClassDB.xml
+++ b/doc/classes/ClassDB.xml
@@ -65,6 +65,15 @@
Returns an array with the names all the integer constants of [param class] or its ancestry.
</description>
</method>
+ <method name="class_get_method_argument_count" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="class" type="StringName" />
+ <param index="1" name="method" type="StringName" />
+ <param index="2" name="no_inheritance" type="bool" default="false" />
+ <description>
+ Returns the number of arguments of the method [param method] of [param class] or its ancestry if [param no_inheritance] is [code]false[/code].
+ </description>
+ </method>
<method name="class_get_method_list" qualifiers="const">
<return type="Dictionary[]" />
<param index="0" name="class" type="StringName" />
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index 517fb6bef7..942d3bc80d 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -181,7 +181,7 @@
<return type="Color" />
<param index="0" name="rgbe" type="int" />
<description>
- Decodes a [Color] from a RGBE9995 format integer. See [constant Image.FORMAT_RGBE9995].
+ Decodes a [Color] from an RGBE9995 format integer. See [constant Image.FORMAT_RGBE9995].
</description>
</method>
<method name="from_string" qualifiers="static">
diff --git a/doc/classes/Crypto.xml b/doc/classes/Crypto.xml
index 59d19b03f4..65abc4c641 100644
--- a/doc/classes/Crypto.xml
+++ b/doc/classes/Crypto.xml
@@ -123,7 +123,7 @@
<param index="3" name="not_after" type="String" default="&quot;20340101000000&quot;" />
<description>
Generates a self-signed [X509Certificate] from the given [CryptoKey] and [param issuer_name]. The certificate validity will be defined by [param not_before] and [param not_after] (first valid date and last valid date). The [param issuer_name] must contain at least "CN=" (common name, i.e. the domain name), "O=" (organization, i.e. your company name), "C=" (country, i.e. 2 lettered ISO-3166 code of the country the organization is based in).
- A small example to generate an RSA key and a X509 self-signed certificate.
+ A small example to generate an RSA key and an X509 self-signed certificate.
[codeblocks]
[gdscript]
var crypto = Crypto.new()
diff --git a/doc/classes/Engine.xml b/doc/classes/Engine.xml
index d923d229e0..ca78054875 100644
--- a/doc/classes/Engine.xml
+++ b/doc/classes/Engine.xml
@@ -28,7 +28,7 @@
GD.Print("Running a 32-bit build of Godot.");
[/csharp]
[/codeblocks]
- [b]Note:[/b] This method does [i]not[/i] return the name of the system's CPU architecture (like [method OS.get_processor_name]). For example, when running a [code]x86_32[/code] Godot binary on a [code]x86_64[/code] system, the returned value will still be [code]"x86_32"[/code].
+ [b]Note:[/b] This method does [i]not[/i] return the name of the system's CPU architecture (like [method OS.get_processor_name]). For example, when running an [code]x86_32[/code] Godot binary on an [code]x86_64[/code] system, the returned value will still be [code]"x86_32"[/code].
</description>
</method>
<method name="get_author_info" qualifiers="const">
@@ -314,7 +314,7 @@
The maximum number of frames that can be rendered every second (FPS). A value of [code]0[/code] means the framerate is uncapped.
Limiting the FPS can be useful to reduce the host machine's power consumption, which reduces heat, noise emissions, and improves battery life.
If [member ProjectSettings.display/window/vsync/vsync_mode] is [b]Enabled[/b] or [b]Adaptive[/b], the setting takes precedence and the max FPS number cannot exceed the monitor's refresh rate.
- If [member ProjectSettings.display/window/vsync/vsync_mode] is [b]Enabled[/b], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using a FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
+ If [member ProjectSettings.display/window/vsync/vsync_mode] is [b]Enabled[/b], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using an FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
See also [member physics_ticks_per_second] and [member ProjectSettings.application/run/max_fps].
[b]Note:[/b] The actual number of frames per second may still be below this value if the CPU or GPU cannot keep up with the project's logic and rendering.
[b]Note:[/b] If [member ProjectSettings.display/window/vsync/vsync_mode] is [b]Disabled[/b], limiting the FPS to a high value that can be consistently reached on the system can reduce input lag compared to an uncapped framerate. Since this works by ensuring the GPU load is lower than 100%, this latency reduction is only effective in GPU-bottlenecked scenarios, not CPU-bottlenecked scenarios.
diff --git a/doc/classes/Environment.xml b/doc/classes/Environment.xml
index bf91d02b3a..189517c392 100644
--- a/doc/classes/Environment.xml
+++ b/doc/classes/Environment.xml
@@ -317,7 +317,7 @@
The default exposure used for tonemapping. Higher values result in a brighter image. See also [member tonemap_white].
</member>
<member name="tonemap_mode" type="int" setter="set_tonemapper" getter="get_tonemapper" enum="Environment.ToneMapper" default="0">
- The tonemapping mode to use. Tonemapping is the process that "converts" HDR values to be suitable for rendering on a LDR display. (Godot doesn't support rendering on HDR displays yet.)
+ The tonemapping mode to use. Tonemapping is the process that "converts" HDR values to be suitable for rendering on an LDR display. (Godot doesn't support rendering on HDR displays yet.)
</member>
<member name="tonemap_white" type="float" setter="set_tonemap_white" getter="get_tonemap_white" default="1.0">
The white reference value for tonemapping (also called "whitepoint"). Higher values can make highlights look less blown out, and will also slightly darken the whole scene as a result. Only effective if the [member tonemap_mode] isn't set to [constant TONE_MAPPER_LINEAR]. See also [member tonemap_exposure].
diff --git a/doc/classes/FileAccess.xml b/doc/classes/FileAccess.xml
index 2abd9974ca..fad6cbcc93 100644
--- a/doc/classes/FileAccess.xml
+++ b/doc/classes/FileAccess.xml
@@ -257,7 +257,7 @@
<return type="String" />
<param index="0" name="path" type="String" />
<description>
- Returns a SHA-256 [String] representing the file at the given path or an empty [String] on failure.
+ Returns an SHA-256 [String] representing the file at the given path or an empty [String] on failure.
</description>
</method>
<method name="get_unix_permissions" qualifiers="static">
diff --git a/doc/classes/HashingContext.xml b/doc/classes/HashingContext.xml
index 86d69bc2f7..f2681ae7b3 100644
--- a/doc/classes/HashingContext.xml
+++ b/doc/classes/HashingContext.xml
@@ -14,7 +14,7 @@
# Check that file exists.
if not FileAccess.file_exists(path):
return
- # Start a SHA-256 context.
+ # Start an SHA-256 context.
var ctx = HashingContext.new()
ctx.start(HashingContext.HASH_SHA256)
# Open the file to hash.
@@ -37,7 +37,7 @@
{
return;
}
- // Start a SHA-256 context.
+ // Start an SHA-256 context.
var ctx = new HashingContext();
ctx.Start(HashingContext.HashType.Sha256);
// Open the file to hash.
@@ -68,7 +68,7 @@
<return type="int" enum="Error" />
<param index="0" name="type" type="int" enum="HashingContext.HashType" />
<description>
- Starts a new hash computation of the given [param type] (e.g. [constant HASH_SHA256] to start computation of a SHA-256).
+ Starts a new hash computation of the given [param type] (e.g. [constant HASH_SHA256] to start computation of an SHA-256).
</description>
</method>
<method name="update">
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 67b373ddfe..68d4deaffd 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -368,7 +368,7 @@
<param index="0" name="svg_str" type="String" />
<param index="1" name="scale" type="float" default="1.0" />
<description>
- Loads an image from the string contents of a SVG file ([b].svg[/b]).
+ Loads an image from the string contents of an SVG file ([b].svg[/b]).
[b]Note:[/b] This method is only available in engine builds with the SVG module enabled. By default, the SVG module is enabled, but it can be disabled at build-time using the [code]module_svg_enabled=no[/code] SCons option.
</description>
</method>
diff --git a/doc/classes/ImporterMesh.xml b/doc/classes/ImporterMesh.xml
index 9bc1bf035e..eeabcd6e12 100644
--- a/doc/classes/ImporterMesh.xml
+++ b/doc/classes/ImporterMesh.xml
@@ -31,7 +31,7 @@
Surfaces are created to be rendered using a [param primitive], which may be any of the values defined in [enum Mesh.PrimitiveType].
The [param arrays] argument is an array of arrays. Each of the [constant Mesh.ARRAY_MAX] elements contains an array with some of the mesh data for this surface as described by the corresponding member of [enum Mesh.ArrayType] or [code]null[/code] if it is not used by the surface. For example, [code]arrays[0][/code] is the array of vertices. That first vertex sub-array is always required; the others are optional. Adding an index array puts this surface into "index mode" where the vertex and other arrays become the sources of data and the index array defines the vertex order. All sub-arrays must have the same length as the vertex array (or be an exact multiple of the vertex array's length, when multiple elements of a sub-array correspond to a single vertex) or be empty, except for [constant Mesh.ARRAY_INDEX] if it is used.
The [param blend_shapes] argument is an array of vertex data for each blend shape. Each element is an array of the same structure as [param arrays], but [constant Mesh.ARRAY_VERTEX], [constant Mesh.ARRAY_NORMAL], and [constant Mesh.ARRAY_TANGENT] are set if and only if they are set in [param arrays] and all other entries are [code]null[/code].
- The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents a LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of a LOD also increases the distance that the objects has to be from the camera before the LOD is used.
+ The [param lods] argument is a dictionary with [float] keys and [PackedInt32Array] values. Each entry in the dictionary represents an LOD level of the surface, where the value is the [constant Mesh.ARRAY_INDEX] array to use for the LOD level and the key is roughly proportional to the distance at which the LOD stats being used. I.e., increasing the key of an LOD also increases the distance that the objects has to be from the camera before the LOD is used.
The [param flags] argument is the bitwise or of, as required: One value of [enum Mesh.ArrayCustomFormat] left shifted by [code]ARRAY_FORMAT_CUSTOMn_SHIFT[/code] for each custom channel in use, [constant Mesh.ARRAY_FLAG_USE_DYNAMIC_UPDATE], [constant Mesh.ARRAY_FLAG_USE_8_BONE_WEIGHTS], or [constant Mesh.ARRAY_FLAG_USES_EMPTY_VERTEX_ARRAY].
[b]Note:[/b] When using indices, it is recommended to only use points, lines, or triangles.
</description>
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index abb04fd462..e622a6bce3 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -118,7 +118,7 @@
<return type="String" />
<param index="0" name="device" type="int" />
<description>
- Returns a SDL2-compatible device GUID on platforms that use gamepad remapping, e.g. [code]030000004c050000c405000000010000[/code]. Returns [code]"Default Gamepad"[/code] otherwise. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names and mappings based on this GUID.
+ Returns an SDL2-compatible device GUID on platforms that use gamepad remapping, e.g. [code]030000004c050000c405000000010000[/code]. Returns [code]"Default Gamepad"[/code] otherwise. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names and mappings based on this GUID.
</description>
</method>
<method name="get_joy_info" qualifiers="const">
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index 065b75382d..85b9cf16f2 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -711,6 +711,14 @@
Returns the object's metadata entry names as a [PackedStringArray].
</description>
</method>
+ <method name="get_method_argument_count" qualifiers="const">
+ <return type="int" />
+ <param index="0" name="method" type="StringName" />
+ <description>
+ Returns the number of arguments of the given [param method] by name.
+ [b]Note:[/b] In C#, [param method] must be in snake_case when referring to built-in Godot methods. Prefer using the names exposed in the [code]MethodName[/code] class to avoid allocating a new [StringName] on each call.
+ </description>
+ </method>
<method name="get_method_list" qualifiers="const">
<return type="Dictionary[]" />
<description>
diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index 9433abf0d4..407041289c 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -353,7 +353,7 @@
Maximum number of frames per second allowed. A value of [code]0[/code] means "no limit". The actual number of frames per second may still be below this value if the CPU or GPU cannot keep up with the project logic and rendering.
Limiting the FPS can be useful to reduce system power consumption, which reduces heat and noise emissions (and improves battery life on mobile devices).
If [member display/window/vsync/vsync_mode] is set to [code]Enabled[/code] or [code]Adaptive[/code], it takes precedence and the forced FPS number cannot exceed the monitor's refresh rate.
- If [member display/window/vsync/vsync_mode] is [code]Enabled[/code], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using a FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
+ If [member display/window/vsync/vsync_mode] is [code]Enabled[/code], on monitors with variable refresh rate enabled (G-Sync/FreeSync), using an FPS limit a few frames lower than the monitor's refresh rate will [url=https://blurbusters.com/howto-low-lag-vsync-on/]reduce input lag while avoiding tearing[/url].
If [member display/window/vsync/vsync_mode] is [code]Disabled[/code], limiting the FPS to a high value that can be consistently reached on the system can reduce input lag compared to an uncapped framerate. Since this works by ensuring the GPU load is lower than 100%, this latency reduction is only effective in GPU-bottlenecked scenarios, not CPU-bottlenecked scenarios.
See also [member physics/common/physics_ticks_per_second].
This setting can be overridden using the [code]--max-fps &lt;fps&gt;[/code] command line argument (including with a value of [code]0[/code] for unlimited framerate).
@@ -963,7 +963,7 @@
<member name="editor/run/main_run_args" type="String" setter="" getter="" default="&quot;&quot;">
The command-line arguments to append to Godot's own command line when running the project. This doesn't affect the editor itself.
It is possible to make another executable run Godot by using the [code]%command%[/code] placeholder. The placeholder will be replaced with Godot's own command line. Program-specific arguments should be placed [i]before[/i] the placeholder, whereas Godot-specific arguments should be placed [i]after[/i] the placeholder.
- For example, this can be used to force the project to run on the dedicated GPU in a NVIDIA Optimus system on Linux:
+ For example, this can be used to force the project to run on the dedicated GPU in an NVIDIA Optimus system on Linux:
[codeblock]
prime-run %command%
[/codeblock]
@@ -990,7 +990,7 @@
</member>
<member name="filesystem/import/fbx2gltf/enabled" type="bool" setter="" getter="" default="true">
If [code]true[/code], Autodesk FBX 3D scene files with the [code].fbx[/code] extension will be imported by converting them to glTF 2.0.
- This requires configuring a path to a FBX2glTF executable in the editor settings at [member EditorSettings.filesystem/import/fbx2gltf/fbx2gltf_path].
+ This requires configuring a path to an FBX2glTF executable in the editor settings at [member EditorSettings.filesystem/import/fbx2gltf/fbx2gltf_path].
</member>
<member name="filesystem/import/fbx2gltf/enabled.android" type="bool" setter="" getter="" default="false">
Override for [member filesystem/import/fbx2gltf/enabled] on Android where FBX2glTF can't easily be accessed from Godot.
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml
index 152518e7da..324e6d50b6 100644
--- a/doc/classes/RenderingServer.xml
+++ b/doc/classes/RenderingServer.xml
@@ -3903,7 +3903,7 @@
<param index="0" name="viewport" type="RID" />
<param index="1" name="enabled" type="bool" />
<description>
- If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d].
+ If [code]true[/code], 2D rendering will use a high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be an [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be an [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients. This setting has the same effect as [member Viewport.use_hdr_2d].
[b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
</description>
</method>
diff --git a/doc/classes/ScriptExtension.xml b/doc/classes/ScriptExtension.xml
index 51958a2a2a..6c7888510e 100644
--- a/doc/classes/ScriptExtension.xml
+++ b/doc/classes/ScriptExtension.xml
@@ -80,6 +80,12 @@
<description>
</description>
</method>
+ <method name="_get_script_method_argument_count" qualifiers="virtual const">
+ <return type="Variant" />
+ <param index="0" name="method" type="StringName" />
+ <description>
+ </description>
+ </method>
<method name="_get_script_method_list" qualifiers="virtual const">
<return type="Dictionary[]" />
<description>
diff --git a/doc/classes/SurfaceTool.xml b/doc/classes/SurfaceTool.xml
index b4a88513ec..094275c349 100644
--- a/doc/classes/SurfaceTool.xml
+++ b/doc/classes/SurfaceTool.xml
@@ -124,7 +124,7 @@
<param index="0" name="nd_threshold" type="float" />
<param index="1" name="target_index_count" type="int" default="3" />
<description>
- Generates a LOD for a given [param nd_threshold] in linear units (square root of quadric error metric), using at most [param target_index_count] indices.
+ Generates an LOD for a given [param nd_threshold] in linear units (square root of quadric error metric), using at most [param target_index_count] indices.
</description>
</method>
<method name="generate_normals">
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index 82bc8d82cb..13d84d96d6 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -368,7 +368,7 @@
In some cases, debanding may introduce a slightly noticeable dithering pattern. It's recommended to enable debanding only when actually needed since the dithering pattern will make lossless-compressed screenshots larger.
</member>
<member name="use_hdr_2d" type="bool" setter="set_use_hdr_2d" getter="is_using_hdr_2d" default="false">
- If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be a [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be a [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients.
+ If [code]true[/code], 2D rendering will use an high dynamic range (HDR) format framebuffer matching the bit depth of the 3D framebuffer. When using the Forward+ renderer this will be an [code]RGBA16[/code] framebuffer, while when using the Mobile renderer it will be an [code]RGB10_A2[/code] framebuffer. Additionally, 2D rendering will take place in linear color space and will be converted to sRGB space immediately before blitting to the screen (if the Viewport is attached to the screen). Practically speaking, this means that the end result of the Viewport will not be clamped into the [code]0-1[/code] range and can be used in 3D rendering without color space adjustments. This allows 2D rendering to take advantage of effects requiring high dynamic range (e.g. 2D glow) as well as substantially improves the appearance of effects requiring highly detailed gradients.
[b]Note:[/b] This setting will have no effect when using the GL Compatibility renderer as the GL Compatibility renderer always renders in low dynamic range for performance reasons.
</member>
<member name="use_occlusion_culling" type="bool" setter="set_use_occlusion_culling" getter="is_using_occlusion_culling" default="false">
diff --git a/doc/classes/XMLParser.xml b/doc/classes/XMLParser.xml
index b6dd7a74f8..2b67ff6fbb 100644
--- a/doc/classes/XMLParser.xml
+++ b/doc/classes/XMLParser.xml
@@ -6,7 +6,7 @@
<description>
Provides a low-level interface for creating parsers for [url=https://en.wikipedia.org/wiki/XML]XML[/url] files. This class can serve as base to make custom XML parsers.
To parse XML, you must open a file with the [method open] method or a buffer with the [method open_buffer] method. Then, the [method read] method must be called to parse the next nodes. Most of the methods take into consideration the currently parsed node.
- Here is an example of using [XMLParser] to parse a SVG file (which is based on XML), printing each element and its attributes as a dictionary:
+ Here is an example of using [XMLParser] to parse an SVG file (which is based on XML), printing each element and its attributes as a dictionary:
[codeblocks]
[gdscript]
var parser = XMLParser.new()
diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp
index fc170d606a..0f83e109fa 100644
--- a/editor/filesystem_dock.cpp
+++ b/editor/filesystem_dock.cpp
@@ -173,14 +173,48 @@ FileSystemList::FileSystemList() {
FileSystemDock *FileSystemDock::singleton = nullptr;
-Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, const String &p_file_type) {
- Ref<Texture2D> file_icon;
+Ref<Texture2D> FileSystemDock::_get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path) {
+ if (!p_icon_path.is_empty()) {
+ Ref<Texture2D> icon = ResourceLoader::load(p_icon_path);
+ if (icon.is_valid()) {
+ return icon;
+ }
+ }
+
if (!p_is_valid) {
- file_icon = get_editor_theme_icon(SNAME("ImportFail"));
+ return get_editor_theme_icon(SNAME("ImportFail"));
+ } else if (has_theme_icon(p_file_type, EditorStringName(EditorIcons))) {
+ return get_editor_theme_icon(p_file_type);
} else {
- file_icon = (has_theme_icon(p_file_type, EditorStringName(EditorIcons))) ? get_editor_theme_icon(p_file_type) : get_editor_theme_icon(SNAME("File"));
+ return get_editor_theme_icon(SNAME("File"));
}
- return file_icon;
+}
+
+String FileSystemDock::_get_entry_script_icon(const EditorFileSystemDirectory *p_dir, int p_file) {
+ const PackedStringArray &deps = p_dir->get_file_deps(p_file);
+ if (deps.is_empty()) {
+ return String();
+ }
+
+ const String &script_path = deps[0]; // Assuming the first dependency is a script.
+ if (script_path.is_empty() || !ClassDB::is_parent_class(ResourceLoader::get_resource_type(script_path), SNAME("Script"))) {
+ return String();
+ }
+
+ String *cached = icon_cache.getptr(script_path);
+ if (cached) {
+ return *cached;
+ }
+
+ HashMap<String, String>::Iterator I;
+ int script_file;
+ EditorFileSystemDirectory *efsd = EditorFileSystem::get_singleton()->find_file(script_path, &script_file);
+ if (efsd) {
+ I = icon_cache.insert(script_path, efsd->get_file_script_class_icon_path(script_file));
+ } else {
+ I = icon_cache.insert(script_path, String());
+ }
+ return I->value;
}
bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path) {
@@ -272,6 +306,7 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
FileInfo fi;
fi.name = p_dir->get_file(i);
fi.type = p_dir->get_file_type(i);
+ fi.icon_path = _get_entry_script_icon(p_dir, i);
fi.import_broken = !p_dir->get_file_import_is_valid(i);
fi.modified_time = p_dir->get_file_modified_time(i);
@@ -282,18 +317,21 @@ bool FileSystemDock::_create_tree(TreeItem *p_parent, EditorFileSystemDirectory
_sort_file_info_list(file_list);
// Build the tree.
+ const int icon_size = get_theme_constant(SNAME("class_icon_size"), SNAME("Editor"));
+
for (const FileInfo &fi : file_list) {
TreeItem *file_item = tree->create_item(subdirectory_item);
+ const String file_metadata = lpath.path_join(fi.name);
file_item->set_text(0, fi.name);
file_item->set_structured_text_bidi_override(0, TextServer::STRUCTURED_TEXT_FILE);
- file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type));
+ file_item->set_icon(0, _get_tree_item_icon(!fi.import_broken, fi.type, fi.icon_path));
+ file_item->set_icon_max_width(0, icon_size);
Color parent_bg_color = subdirectory_item->get_custom_bg_color(0);
if (has_custom_color) {
file_item->set_custom_bg_color(0, parent_bg_color.darkened(0.3));
} else if (parent_bg_color != Color()) {
file_item->set_custom_bg_color(0, parent_bg_color);
}
- String file_metadata = lpath.path_join(fi.name);
file_item->set_metadata(0, file_metadata);
if (!p_select_in_favorites && current_path == file_metadata) {
file_item->select(0);
@@ -366,6 +404,8 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
updating_tree = true;
TreeItem *root = tree->create_item();
+ icon_cache.clear();
+
// Handles the favorites.
TreeItem *favorites_item = tree->create_item(root);
favorites_item->set_icon(0, get_editor_theme_icon(SNAME("Favorites")));
@@ -413,7 +453,7 @@ void FileSystemDock::_update_tree(const Vector<String> &p_uncollapsed_paths, boo
int index;
EditorFileSystemDirectory *dir = EditorFileSystem::get_singleton()->find_file(favorite, &index);
if (dir) {
- icon = _get_tree_item_icon(dir->get_file_import_is_valid(index), dir->get_file_type(index));
+ icon = _get_tree_item_icon(dir->get_file_import_is_valid(index), dir->get_file_path(index), dir->get_file_type(index));
} else {
icon = get_editor_theme_icon(SNAME("File"));
}
diff --git a/editor/filesystem_dock.h b/editor/filesystem_dock.h
index acb7ca017b..15a43dc6f2 100644
--- a/editor/filesystem_dock.h
+++ b/editor/filesystem_dock.h
@@ -139,6 +139,7 @@ private:
FILE_NEW_SCENE,
};
+ HashMap<String, String> icon_cache;
HashMap<String, Color> folder_colors;
Dictionary assigned_folder_colors;
@@ -245,7 +246,8 @@ private:
void _tree_mouse_exited();
void _reselect_items_selected_on_drag_begin(bool reset = false);
- Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type);
+ Ref<Texture2D> _get_tree_item_icon(bool p_is_valid, const String &p_file_type, const String &p_icon_path);
+ String _get_entry_script_icon(const EditorFileSystemDirectory *p_dir, int p_file);
bool _create_tree(TreeItem *p_parent, EditorFileSystemDirectory *p_dir, Vector<String> &uncollapsed_paths, bool p_select_in_favorites, bool p_unfold_path = false);
void _update_tree(const Vector<String> &p_uncollapsed_paths = Vector<String>(), bool p_uncollapse_root = false, bool p_select_in_favorites = false, bool p_unfold_path = false);
void _navigate_to_path(const String &p_path, bool p_select_in_favorites = false);
@@ -323,6 +325,7 @@ private:
struct FileInfo {
String name;
String path;
+ String icon_path;
StringName type;
Vector<String> sources;
bool import_broken = false;
diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp
index 94aa077014..8e74de4242 100644
--- a/modules/gdscript/gdscript.cpp
+++ b/modules/gdscript/gdscript.cpp
@@ -354,6 +354,21 @@ bool GDScript::has_static_method(const StringName &p_method) const {
return member_functions.has(p_method) && member_functions[p_method]->is_static();
}
+int GDScript::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
+ if (!E) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+}
+
MethodInfo GDScript::get_method_info(const StringName &p_method) const {
HashMap<StringName, GDScriptFunction *>::ConstIterator E = member_functions.find(p_method);
if (!E) {
@@ -1916,6 +1931,25 @@ bool GDScriptInstance::has_method(const StringName &p_method) const {
return false;
}
+int GDScriptInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ const GDScript *sptr = script.ptr();
+ while (sptr) {
+ HashMap<StringName, GDScriptFunction *>::ConstIterator E = sptr->member_functions.find(p_method);
+ if (E) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E->value->get_argument_count();
+ }
+ sptr = sptr->_base;
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant GDScriptInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
GDScript *sptr = script.ptr();
if (unlikely(p_method == SNAME("_ready"))) {
diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h
index 7c471c285b..fd5ad837f9 100644
--- a/modules/gdscript/gdscript.h
+++ b/modules/gdscript/gdscript.h
@@ -312,6 +312,9 @@ public:
virtual void get_script_method_list(List<MethodInfo> *p_list) const override;
virtual bool has_method(const StringName &p_method) const override;
virtual bool has_static_method(const StringName &p_method) const override;
+
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
+
virtual MethodInfo get_method_info(const StringName &p_method) const override;
virtual void get_script_property_list(List<PropertyInfo> *p_list) const override;
@@ -376,6 +379,9 @@ public:
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName &p_method) const;
+
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const;
+
virtual Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error);
Variant debug_get_member_by_index(int p_idx) const { return members[p_idx]; }
diff --git a/modules/gdscript/gdscript_function.h b/modules/gdscript/gdscript_function.h
index 177c68533e..002fc159fa 100644
--- a/modules/gdscript/gdscript_function.h
+++ b/modules/gdscript/gdscript_function.h
@@ -511,6 +511,7 @@ public:
_FORCE_INLINE_ GDScript *get_script() const { return _script; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ MethodInfo get_method_info() const { return method_info; }
+ _FORCE_INLINE_ int get_argument_count() const { return _argument_count; }
_FORCE_INLINE_ Variant get_rpc_config() const { return rpc_config; }
_FORCE_INLINE_ int get_max_stack_size() const { return _stack_size; }
diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp
index f6fa17c84f..626ef6ccb0 100644
--- a/modules/gdscript/gdscript_lambda_callable.cpp
+++ b/modules/gdscript/gdscript_lambda_callable.cpp
@@ -78,6 +78,15 @@ StringName GDScriptLambdaCallable::get_method() const {
return function->get_name();
}
+int GDScriptLambdaCallable::get_argument_count(bool &r_is_valid) const {
+ if (function == nullptr) {
+ r_is_valid = false;
+ return 0;
+ }
+ r_is_valid = true;
+ return function->get_argument_count();
+}
+
void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
int captures_amount = captures.size();
@@ -189,6 +198,15 @@ ObjectID GDScriptLambdaSelfCallable::get_object() const {
return object->get_instance_id();
}
+int GDScriptLambdaSelfCallable::get_argument_count(bool &r_is_valid) const {
+ if (function == nullptr) {
+ r_is_valid = false;
+ return 0;
+ }
+ r_is_valid = true;
+ return function->get_argument_count();
+}
+
void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
#ifdef DEBUG_ENABLED
if (object->get_script_instance() == nullptr || object->get_script_instance()->get_language() != GDScriptLanguage::get_singleton()) {
diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h
index 2c5d01aa16..45c0235913 100644
--- a/modules/gdscript/gdscript_lambda_callable.h
+++ b/modules/gdscript/gdscript_lambda_callable.h
@@ -59,6 +59,7 @@ public:
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
StringName get_method() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaCallable(GDScriptLambdaCallable &) = delete;
@@ -86,6 +87,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptLambdaSelfCallable(GDScriptLambdaSelfCallable &) = delete;
diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp
index df014d3cfe..3139371eb5 100644
--- a/modules/gdscript/gdscript_rpc_callable.cpp
+++ b/modules/gdscript/gdscript_rpc_callable.cpp
@@ -68,6 +68,10 @@ StringName GDScriptRPCCallable::get_method() const {
return method;
}
+int GDScriptRPCCallable::get_argument_count(bool &r_is_valid) const {
+ return object->get_method_argument_count(method, &r_is_valid);
+}
+
void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_return_value = object->callp(method, p_arguments, p_argcount, r_call_error);
}
diff --git a/modules/gdscript/gdscript_rpc_callable.h b/modules/gdscript/gdscript_rpc_callable.h
index 66052157be..2ca6290951 100644
--- a/modules/gdscript/gdscript_rpc_callable.h
+++ b/modules/gdscript/gdscript_rpc_callable.h
@@ -52,6 +52,7 @@ public:
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
StringName get_method() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
Error rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const override;
diff --git a/modules/gdscript/gdscript_utility_callable.cpp b/modules/gdscript/gdscript_utility_callable.cpp
index 7708a18044..edd7e05b22 100644
--- a/modules/gdscript/gdscript_utility_callable.cpp
+++ b/modules/gdscript/gdscript_utility_callable.cpp
@@ -80,6 +80,21 @@ ObjectID GDScriptUtilityCallable::get_object() const {
return ObjectID();
}
+int GDScriptUtilityCallable::get_argument_count(bool &r_is_valid) const {
+ switch (type) {
+ case TYPE_INVALID:
+ r_is_valid = false;
+ return 0;
+ case TYPE_GLOBAL:
+ r_is_valid = true;
+ return Variant::get_utility_function_argument_count(function_name);
+ case TYPE_GDSCRIPT:
+ r_is_valid = true;
+ return GDScriptUtilityFunctions::get_function_argument_count(function_name);
+ }
+ ERR_FAIL_V_MSG(0, "Invalid type.");
+}
+
void GDScriptUtilityCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
switch (type) {
case TYPE_INVALID:
diff --git a/modules/gdscript/gdscript_utility_callable.h b/modules/gdscript/gdscript_utility_callable.h
index 675bc4ddd9..c5736e815f 100644
--- a/modules/gdscript/gdscript_utility_callable.h
+++ b/modules/gdscript/gdscript_utility_callable.h
@@ -57,6 +57,7 @@ public:
bool is_valid() const override;
StringName get_method() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
GDScriptUtilityCallable(const StringName &p_function_name);
diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp
index f8cb460e40..e5b0f55df8 100644
--- a/modules/gdscript/gdscript_utility_functions.cpp
+++ b/modules/gdscript/gdscript_utility_functions.cpp
@@ -759,7 +759,7 @@ Variant::Type GDScriptUtilityFunctions::get_function_argument_type(const StringN
return info->info.arguments[p_arg].type;
}
-int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function, int p_arg) {
+int GDScriptUtilityFunctions::get_function_argument_count(const StringName &p_function) {
GDScriptUtilityFunctionInfo *info = utility_function_table.lookup_ptr(p_function);
ERR_FAIL_NULL_V(info, 0);
return info->info.arguments.size();
diff --git a/modules/gdscript/gdscript_utility_functions.h b/modules/gdscript/gdscript_utility_functions.h
index 40e9379a3a..1c4e4452c8 100644
--- a/modules/gdscript/gdscript_utility_functions.h
+++ b/modules/gdscript/gdscript_utility_functions.h
@@ -46,7 +46,7 @@ public:
static Variant::Type get_function_return_type(const StringName &p_function);
static StringName get_function_return_class(const StringName &p_function);
static Variant::Type get_function_argument_type(const StringName &p_function, int p_arg);
- static int get_function_argument_count(const StringName &p_function, int p_arg);
+ static int get_function_argument_count(const StringName &p_function);
static bool is_function_vararg(const StringName &p_function);
static bool is_function_constant(const StringName &p_function);
diff --git a/modules/gdscript/tests/scripts/runtime/features/argument_count.gd b/modules/gdscript/tests/scripts/runtime/features/argument_count.gd
new file mode 100644
index 0000000000..c67ce25cbe
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/argument_count.gd
@@ -0,0 +1,102 @@
+extends Node
+
+func my_func_1(_foo, _bar):
+ pass
+
+func my_func_2(_foo, _bar, _baz):
+ pass
+
+static func my_static_func_1(_foo, _bar):
+ pass
+
+static func my_static_func_2(_foo, _bar, _baz):
+ pass
+
+@rpc
+func my_rpc_func_1(_foo, _bar):
+ pass
+
+@rpc
+func my_rpc_func_2(_foo, _bar, _baz):
+ pass
+
+func test():
+ # Test built-in methods.
+ var builtin_callable_1 : Callable = add_to_group
+ print(builtin_callable_1.get_argument_count()) # Should print 2.
+ var builtin_callable_2 : Callable = find_child
+ print(builtin_callable_2.get_argument_count()) # Should print 3.
+
+ # Test built-in vararg methods.
+ var builtin_vararg_callable_1 : Callable = call_thread_safe
+ print(builtin_vararg_callable_1.get_argument_count()) # Should print 1.
+ var builtin_vararg_callable_2 : Callable = rpc_id
+ print(builtin_vararg_callable_2.get_argument_count()) # Should print 2.
+
+ # Test plain methods.
+ var callable_1 : Callable = my_func_1
+ print(callable_1.get_argument_count()) # Should print 2.
+ var callable_2 : Callable = my_func_2
+ print(callable_2.get_argument_count()) # Should print 3.
+
+ # Test static methods.
+ var static_callable_1 : Callable = my_static_func_1
+ print(static_callable_1.get_argument_count()) # Should print 2.
+ var static_callable_2 : Callable = my_static_func_2
+ print(static_callable_2.get_argument_count()) # Should print 3.
+
+ # Test rpc methods.
+ var rpc_callable_1 : Callable = my_rpc_func_1
+ print(rpc_callable_1.get_argument_count()) # Should print 2.
+ var rpc_callable_2 : Callable = my_rpc_func_2
+ print(rpc_callable_2.get_argument_count()) # Should print 3.
+
+ # Test lambdas.
+ var lambda_callable_1 : Callable = func(_foo, _bar): pass
+ print(lambda_callable_1.get_argument_count()) # Should print 2.
+ var lambda_callable_2 : Callable = func(_foo, _bar, _baz): pass
+ print(lambda_callable_2.get_argument_count()) # Should print 3.
+
+ # Test lambas with self.
+ var lambda_self_callable_1 : Callable = func(_foo, _bar): return self
+ print(lambda_self_callable_1.get_argument_count()) # Should print 2.
+ var lambda_self_callable_2 : Callable = func(_foo, _bar, _baz): return self
+ print(lambda_self_callable_2.get_argument_count()) # Should print 3.
+
+ # Test bind.
+ var bind_callable_1 : Callable = my_func_2.bind(1)
+ print(bind_callable_1.get_argument_count()) # Should print 2.
+ var bind_callable_2 : Callable = my_func_2.bind(1, 2)
+ print(bind_callable_2.get_argument_count()) # Should print 1.
+
+ # Test unbind.
+ var unbind_callable_1 : Callable = my_func_2.unbind(1)
+ print(unbind_callable_1.get_argument_count()) # Should print 4.
+ var unbind_callable_2 : Callable = my_func_2.unbind(2)
+ print(unbind_callable_2.get_argument_count()) # Should print 5.
+
+ # Test variant callables.
+ var string_tmp := String()
+ var variant_callable_1 : Callable = string_tmp.replace
+ print(variant_callable_1.get_argument_count()) # Should print 2.
+ var variant_callable_2 : Callable = string_tmp.rsplit
+ print(variant_callable_2.get_argument_count()) # Should print 3.
+
+ # Test variant vararg callables.
+ var callable_tmp := Callable()
+ var variant_vararg_callable_1 : Callable = callable_tmp.call
+ print(variant_vararg_callable_1.get_argument_count()) # Should print 0.
+ var variant_vararg_callable_2 : Callable = callable_tmp.rpc_id
+ print(variant_vararg_callable_2.get_argument_count()) # Should print 1.
+
+ # Test global methods.
+ var global_callable_1 = is_equal_approx
+ print(global_callable_1.get_argument_count()) # Should print 2.
+ var global_callable_2 = inverse_lerp
+ print(global_callable_2.get_argument_count()) # Should print 3.
+
+ # Test GDScript methods.
+ var gdscript_callable_1 = char
+ print(gdscript_callable_1.get_argument_count()) # Should print 1.
+ var gdscript_callable_2 = is_instance_of
+ print(gdscript_callable_2.get_argument_count()) # Should print 2.
diff --git a/modules/gdscript/tests/scripts/runtime/features/argument_count.out b/modules/gdscript/tests/scripts/runtime/features/argument_count.out
new file mode 100644
index 0000000000..42c4ece37d
--- /dev/null
+++ b/modules/gdscript/tests/scripts/runtime/features/argument_count.out
@@ -0,0 +1,27 @@
+GDTEST_OK
+2
+3
+1
+2
+2
+3
+2
+3
+2
+3
+2
+3
+2
+3
+2
+1
+4
+5
+2
+3
+0
+1
+2
+3
+1
+2
diff --git a/modules/gltf/doc_classes/GLTFMesh.xml b/modules/gltf/doc_classes/GLTFMesh.xml
index 8234ed9eac..b4c3db7618 100644
--- a/modules/gltf/doc_classes/GLTFMesh.xml
+++ b/modules/gltf/doc_classes/GLTFMesh.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFMesh" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFMesh represents an GLTF mesh.
+ GLTFMesh represents a GLTF mesh.
</brief_description>
<description>
GLTFMesh handles 3D mesh data imported from GLTF files. It includes properties for blend channels, blend weights, instance materials, and the mesh itself.
diff --git a/modules/gltf/doc_classes/GLTFTexture.xml b/modules/gltf/doc_classes/GLTFTexture.xml
index 50789a0ebf..9ad7c0f4c6 100644
--- a/modules/gltf/doc_classes/GLTFTexture.xml
+++ b/modules/gltf/doc_classes/GLTFTexture.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="GLTFTexture" inherits="Resource" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- GLTFTexture represents a texture in an GLTF file.
+ GLTFTexture represents a texture in a GLTF file.
</brief_description>
<description>
</description>
diff --git a/modules/interactive_music/audio_stream_interactive.cpp b/modules/interactive_music/audio_stream_interactive.cpp
index ddac458463..01764d66ed 100644
--- a/modules/interactive_music/audio_stream_interactive.cpp
+++ b/modules/interactive_music/audio_stream_interactive.cpp
@@ -353,7 +353,7 @@ static void _test_and_swap(T &p_elem, uint32_t p_a, uint32_t p_b) {
}
void AudioStreamInteractive::_inspector_array_swap_clip(uint32_t p_item_a, uint32_t p_item_b) {
- ERR_FAIL_INDEX(p_item_a, (uint32_t)clip_count);
+ ERR_FAIL_UNSIGNED_INDEX(p_item_a, (uint32_t)clip_count);
ERR_FAIL_UNSIGNED_INDEX(p_item_b, (uint32_t)clip_count);
for (int i = 0; i < clip_count; i++) {
diff --git a/modules/minimp3/doc_classes/ResourceImporterMP3.xml b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
index a84c51cf68..72868623c7 100644
--- a/modules/minimp3/doc_classes/ResourceImporterMP3.xml
+++ b/modules/minimp3/doc_classes/ResourceImporterMP3.xml
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ResourceImporterMP3" inherits="ResourceImporter" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd">
<brief_description>
- Imports a MP3 audio file for playback.
+ Imports an MP3 audio file for playback.
</brief_description>
<description>
MP3 is a lossy audio format, with worse audio quality compared to [ResourceImporterOggVorbis] at a given bitrate.
- In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using an MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
MP3 requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
</description>
<tutorials>
diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp
index 93fb5f1dc6..858d1d3e4e 100644
--- a/modules/mono/csharp_script.cpp
+++ b/modules/mono/csharp_script.cpp
@@ -1735,6 +1735,34 @@ bool CSharpInstance::has_method(const StringName &p_method) const {
gchandle.get_intptr(), &p_method);
}
+int CSharpInstance::get_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ if (!script->is_valid() || !script->valid) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ const CSharpScript *top = script.ptr();
+ while (top != nullptr) {
+ for (const CSharpScript::CSharpMethodInfo &E : top->methods) {
+ if (E.name == p_method) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E.method_info.arguments.size();
+ }
+ }
+
+ top = top->base_script.ptr();
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
Variant CSharpInstance::callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
ERR_FAIL_COND_V(!script.is_valid(), Variant());
@@ -2579,6 +2607,29 @@ bool CSharpScript::has_method(const StringName &p_method) const {
return false;
}
+int CSharpScript::get_script_method_argument_count(const StringName &p_method, bool *r_is_valid) const {
+ if (!valid) {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
+
+ for (const CSharpMethodInfo &E : methods) {
+ if (E.name == p_method) {
+ if (r_is_valid) {
+ *r_is_valid = true;
+ }
+ return E.method_info.arguments.size();
+ }
+ }
+
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+}
+
MethodInfo CSharpScript::get_method_info(const StringName &p_method) const {
if (!valid) {
return MethodInfo();
diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h
index 7821420620..06d526f494 100644
--- a/modules/mono/csharp_script.h
+++ b/modules/mono/csharp_script.h
@@ -278,6 +278,7 @@ public:
void get_script_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
+ virtual int get_script_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
MethodInfo get_method_info(const StringName &p_method) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
@@ -346,6 +347,7 @@ public:
void get_method_list(List<MethodInfo> *p_list) const override;
bool has_method(const StringName &p_method) const override;
+ virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override;
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override;
void mono_object_disposed(GCHandleIntPtr p_gchandle_to_free);
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
index 6c34d7c29d..c7be0464b6 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Bridge/ManagedCallbacks.cs
@@ -12,6 +12,7 @@ namespace Godot.Bridge
public delegate* unmanaged<IntPtr, void*, godot_variant**, int, godot_variant*, void> DelegateUtils_InvokeWithVariantArgs;
public delegate* unmanaged<IntPtr, IntPtr, godot_bool> DelegateUtils_DelegateEquals;
public delegate* unmanaged<IntPtr, int> DelegateUtils_DelegateHash;
+ public delegate* unmanaged<IntPtr, godot_bool*, int> DelegateUtils_GetArgumentCount;
public delegate* unmanaged<IntPtr, godot_array*, godot_bool> DelegateUtils_TrySerializeDelegateWithGCHandle;
public delegate* unmanaged<godot_array*, IntPtr*, godot_bool> DelegateUtils_TryDeserializeDelegateWithGCHandle;
public delegate* unmanaged<void> ScriptManagerBridge_FrameCallback;
@@ -55,6 +56,7 @@ namespace Godot.Bridge
DelegateUtils_InvokeWithVariantArgs = &DelegateUtils.InvokeWithVariantArgs,
DelegateUtils_DelegateEquals = &DelegateUtils.DelegateEquals,
DelegateUtils_DelegateHash = &DelegateUtils.DelegateHash,
+ DelegateUtils_GetArgumentCount = &DelegateUtils.GetArgumentCount,
DelegateUtils_TrySerializeDelegateWithGCHandle = &DelegateUtils.TrySerializeDelegateWithGCHandle,
DelegateUtils_TryDeserializeDelegateWithGCHandle = &DelegateUtils.TryDeserializeDelegateWithGCHandle,
ScriptManagerBridge_FrameCallback = &ScriptManagerBridge.FrameCallback,
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
index c680142638..ef3c9c79d4 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/DelegateUtils.cs
@@ -46,6 +46,29 @@ namespace Godot
}
[UnmanagedCallersOnly]
+ internal static unsafe int GetArgumentCount(IntPtr delegateGCHandle, godot_bool* outIsValid)
+ {
+ try
+ {
+ var @delegate = (Delegate?)GCHandle.FromIntPtr(delegateGCHandle).Target;
+ int? argCount = @delegate?.Method?.GetParameters().Length;
+ if (argCount is null)
+ {
+ *outIsValid = godot_bool.False;
+ return 0;
+ }
+ *outIsValid = godot_bool.True;
+ return argCount.Value;
+ }
+ catch (Exception e)
+ {
+ ExceptionUtils.LogException(e);
+ *outIsValid = godot_bool.False;
+ return 0;
+ }
+ }
+
+ [UnmanagedCallersOnly]
internal static unsafe void InvokeWithVariantArgs(IntPtr delegateGCHandle, void* trampoline,
godot_variant** args, int argc, godot_variant* outRet)
{
diff --git a/modules/mono/managed_callable.cpp b/modules/mono/managed_callable.cpp
index c55c5d8111..7c48110199 100644
--- a/modules/mono/managed_callable.cpp
+++ b/modules/mono/managed_callable.cpp
@@ -85,6 +85,10 @@ ObjectID ManagedCallable::get_object() const {
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
}
+int ManagedCallable::get_argument_count(bool &r_is_valid) const {
+ return GDMonoCache::managed_callbacks.DelegateUtils_GetArgumentCount(delegate_handle, &r_is_valid);
+}
+
void ManagedCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
r_call_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; // Can't find anything better
r_return_value = Variant();
diff --git a/modules/mono/managed_callable.h b/modules/mono/managed_callable.h
index 290d49be14..388c321d5d 100644
--- a/modules/mono/managed_callable.h
+++ b/modules/mono/managed_callable.h
@@ -56,6 +56,7 @@ public:
CompareEqualFunc get_compare_equal_func() const override;
CompareLessFunc get_compare_less_func() const override;
ObjectID get_object() const override;
+ int get_argument_count(bool &r_is_valid) const override;
void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
_FORCE_INLINE_ GCHandleIntPtr get_delegate() const { return delegate_handle; }
diff --git a/modules/mono/mono_gd/gd_mono_cache.cpp b/modules/mono/mono_gd/gd_mono_cache.cpp
index 145f4cee90..5292bcd1ea 100644
--- a/modules/mono/mono_gd/gd_mono_cache.cpp
+++ b/modules/mono/mono_gd/gd_mono_cache.cpp
@@ -53,6 +53,7 @@ void update_godot_api_cache(const ManagedCallbacks &p_managed_callbacks) {
CHECK_CALLBACK_NOT_NULL(DelegateUtils, InvokeWithVariantArgs);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateEquals);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, DelegateHash);
+ CHECK_CALLBACK_NOT_NULL(DelegateUtils, GetArgumentCount);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TrySerializeDelegateWithGCHandle);
CHECK_CALLBACK_NOT_NULL(DelegateUtils, TryDeserializeDelegateWithGCHandle);
CHECK_CALLBACK_NOT_NULL(ScriptManagerBridge, FrameCallback);
diff --git a/modules/mono/mono_gd/gd_mono_cache.h b/modules/mono/mono_gd/gd_mono_cache.h
index 46e9ab10cb..7c24f28b3a 100644
--- a/modules/mono/mono_gd/gd_mono_cache.h
+++ b/modules/mono/mono_gd/gd_mono_cache.h
@@ -78,6 +78,7 @@ struct ManagedCallbacks {
using FuncDelegateUtils_InvokeWithVariantArgs = void(GD_CLR_STDCALL *)(GCHandleIntPtr, void *, const Variant **, int32_t, const Variant *);
using FuncDelegateUtils_DelegateEquals = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, GCHandleIntPtr);
using FuncDelegateUtils_DelegateHash = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr);
+ using FuncDelegateUtils_GetArgumentCount = int32_t(GD_CLR_STDCALL *)(GCHandleIntPtr, bool *);
using FuncDelegateUtils_TrySerializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(GCHandleIntPtr, const Array *);
using FuncDelegateUtils_TryDeserializeDelegateWithGCHandle = bool(GD_CLR_STDCALL *)(const Array *, GCHandleIntPtr *);
using FuncScriptManagerBridge_FrameCallback = void(GD_CLR_STDCALL *)();
@@ -115,6 +116,7 @@ struct ManagedCallbacks {
FuncDelegateUtils_InvokeWithVariantArgs DelegateUtils_InvokeWithVariantArgs;
FuncDelegateUtils_DelegateEquals DelegateUtils_DelegateEquals;
FuncDelegateUtils_DelegateHash DelegateUtils_DelegateHash;
+ FuncDelegateUtils_GetArgumentCount DelegateUtils_GetArgumentCount;
FuncDelegateUtils_TrySerializeDelegateWithGCHandle DelegateUtils_TrySerializeDelegateWithGCHandle;
FuncDelegateUtils_TryDeserializeDelegateWithGCHandle DelegateUtils_TryDeserializeDelegateWithGCHandle;
FuncScriptManagerBridge_FrameCallback ScriptManagerBridge_FrameCallback;
diff --git a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
index ee2aa33108..20ce1c42bf 100644
--- a/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
+++ b/modules/openxr/doc_classes/OpenXRExtensionWrapperExtension.xml
@@ -12,7 +12,7 @@
<method name="_get_composition_layer" qualifiers="virtual">
<return type="int" />
<description>
- Returns a pointer to a [code]XrCompositionLayerBaseHeader[/code] struct to provide a composition layer. This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
+ Returns a pointer to an [code]XrCompositionLayerBaseHeader[/code] struct to provide a composition layer. This will only be called if the extension previously registered itself with [method OpenXRAPIExtension.register_composition_layer_provider].
</description>
</method>
<method name="_get_requested_extensions" qualifiers="virtual">
diff --git a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
index dd6c181eae..8ae63140f5 100644
--- a/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
+++ b/modules/vorbis/doc_classes/ResourceImporterOggVorbis.xml
@@ -5,7 +5,7 @@
</brief_description>
<description>
Ogg Vorbis is a lossy audio format, with better audio quality compared to [ResourceImporterMP3] at a given bitrate.
- In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using a MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
+ In most cases, it's recommended to use Ogg Vorbis over MP3. However, if you're using an MP3 sound source with no higher quality source available, then it's recommended to use the MP3 file directly to avoid double lossy compression.
Ogg Vorbis requires more CPU to decode than [ResourceImporterWAV]. If you need to play a lot of simultaneous sounds, it's recommended to use WAV for those sounds instead, especially if targeting low-end devices.
</description>
<tutorials>
diff --git a/platform/web/doc_classes/EditorExportPlatformWeb.xml b/platform/web/doc_classes/EditorExportPlatformWeb.xml
index c759701d2f..755308de9a 100644
--- a/platform/web/doc_classes/EditorExportPlatformWeb.xml
+++ b/platform/web/doc_classes/EditorExportPlatformWeb.xml
@@ -84,7 +84,7 @@
</member>
<member name="variant/thread_support" type="bool" setter="" getter="">
If [code]true[/code], the exported game will support threads. It requires [url=https://web.dev/articles/coop-coep]a "cross-origin isolated" website[/url], which may be difficult to set up and is limited for security reasons (such as not being able to communicate with third-party websites).
- If [code]false[/code], the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on a HTTPS website.
+ If [code]false[/code], the exported game will not support threads. As a result, it is more prone to performance and audio issues, but will only require to be run on an HTTPS website.
</member>
<member name="vram_texture_compression/for_desktop" type="bool" setter="" getter="">
If [code]true[/code], allows textures to be optimized for desktop through the S3TC algorithm.
diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h
index e5d91db650..3a3013a102 100644
--- a/tests/core/object/test_object.h
+++ b/tests/core/object/test_object.h
@@ -95,6 +95,12 @@ public:
bool has_method(const StringName &p_method) const override {
return false;
}
+ int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override {
+ if (r_is_valid) {
+ *r_is_valid = false;
+ }
+ return 0;
+ }
Variant callp(const StringName &p_method, const Variant **p_args, int p_argcount, Callable::CallError &r_error) override {
return Variant();
}
diff --git a/tests/core/variant/test_callable.h b/tests/core/variant/test_callable.h
new file mode 100644
index 0000000000..3228e0a583
--- /dev/null
+++ b/tests/core/variant/test_callable.h
@@ -0,0 +1,140 @@
+/**************************************************************************/
+/* test_callable.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_CALLABLE_H
+#define TEST_CALLABLE_H
+
+#include "core/object/class_db.h"
+#include "core/object/object.h"
+
+#include "tests/test_macros.h"
+
+namespace TestCallable {
+
+class TestClass : public Object {
+ GDCLASS(TestClass, Object);
+
+protected:
+ static void _bind_methods() {
+ ClassDB::bind_method(D_METHOD("test_func_1", "foo", "bar"), &TestClass::test_func_1);
+ ClassDB::bind_method(D_METHOD("test_func_2", "foo", "bar", "baz"), &TestClass::test_func_2);
+ ClassDB::bind_static_method("TestClass", D_METHOD("test_func_5", "foo", "bar"), &TestClass::test_func_5);
+ ClassDB::bind_static_method("TestClass", D_METHOD("test_func_6", "foo", "bar", "baz"), &TestClass::test_func_6);
+
+ {
+ MethodInfo mi;
+ mi.name = "test_func_7";
+ mi.arguments.push_back(PropertyInfo(Variant::INT, "foo"));
+ mi.arguments.push_back(PropertyInfo(Variant::INT, "bar"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "test_func_7", &TestClass::test_func_7, mi, varray(), false);
+ }
+
+ {
+ MethodInfo mi;
+ mi.name = "test_func_8";
+ mi.arguments.push_back(PropertyInfo(Variant::INT, "foo"));
+ mi.arguments.push_back(PropertyInfo(Variant::INT, "bar"));
+ mi.arguments.push_back(PropertyInfo(Variant::INT, "baz"));
+
+ ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "test_func_8", &TestClass::test_func_8, mi, varray(), false);
+ }
+ }
+
+public:
+ void test_func_1(int p_foo, int p_bar) {}
+ void test_func_2(int p_foo, int p_bar, int p_baz) {}
+
+ int test_func_3(int p_foo, int p_bar) const { return 0; }
+ int test_func_4(int p_foo, int p_bar, int p_baz) const { return 0; }
+
+ static void test_func_5(int p_foo, int p_bar) {}
+ static void test_func_6(int p_foo, int p_bar, int p_baz) {}
+
+ void test_func_7(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {}
+ void test_func_8(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {}
+};
+
+TEST_CASE("[Callable] Argument count") {
+ TestClass *my_test = memnew(TestClass);
+
+ // Bound methods tests.
+
+ // Test simple methods.
+ Callable callable_1 = Callable(my_test, "test_func_1");
+ CHECK_EQ(callable_1.get_argument_count(), 2);
+ Callable callable_2 = Callable(my_test, "test_func_2");
+ CHECK_EQ(callable_2.get_argument_count(), 3);
+ Callable callable_3 = Callable(my_test, "test_func_5");
+ CHECK_EQ(callable_3.get_argument_count(), 2);
+ Callable callable_4 = Callable(my_test, "test_func_6");
+ CHECK_EQ(callable_4.get_argument_count(), 3);
+
+ // Test vararg methods.
+ Callable callable_vararg_1 = Callable(my_test, "test_func_7");
+ CHECK_MESSAGE(callable_vararg_1.get_argument_count() == 2, "vararg Callable should return the number of declared arguments");
+ Callable callable_vararg_2 = Callable(my_test, "test_func_8");
+ CHECK_MESSAGE(callable_vararg_2.get_argument_count() == 3, "vararg Callable should return the number of declared arguments");
+
+ // Callable MP tests.
+
+ // Test simple methods.
+ Callable callable_mp_1 = callable_mp(my_test, &TestClass::test_func_1);
+ CHECK_EQ(callable_mp_1.get_argument_count(), 2);
+ Callable callable_mp_2 = callable_mp(my_test, &TestClass::test_func_2);
+ CHECK_EQ(callable_mp_2.get_argument_count(), 3);
+ Callable callable_mp_3 = callable_mp(my_test, &TestClass::test_func_3);
+ CHECK_EQ(callable_mp_3.get_argument_count(), 2);
+ Callable callable_mp_4 = callable_mp(my_test, &TestClass::test_func_4);
+ CHECK_EQ(callable_mp_4.get_argument_count(), 3);
+
+ // Test static methods.
+ Callable callable_mp_static_1 = callable_mp_static(&TestClass::test_func_5);
+ CHECK_EQ(callable_mp_static_1.get_argument_count(), 2);
+ Callable callable_mp_static_2 = callable_mp_static(&TestClass::test_func_6);
+ CHECK_EQ(callable_mp_static_2.get_argument_count(), 3);
+
+ // Test bind.
+ Callable callable_mp_bind_1 = callable_mp_2.bind(1);
+ CHECK_MESSAGE(callable_mp_bind_1.get_argument_count() == 2, "bind should subtract from the argument count");
+ Callable callable_mp_bind_2 = callable_mp_2.bind(1, 2);
+ CHECK_MESSAGE(callable_mp_bind_2.get_argument_count() == 1, "bind should subtract from the argument count");
+
+ // Test unbind.
+ Callable callable_mp_unbind_1 = callable_mp_2.unbind(1);
+ CHECK_MESSAGE(callable_mp_unbind_1.get_argument_count() == 4, "unbind should add to the argument count");
+ Callable callable_mp_unbind_2 = callable_mp_2.unbind(2);
+ CHECK_MESSAGE(callable_mp_unbind_2.get_argument_count() == 5, "unbind should add to the argument count");
+
+ memdelete(my_test);
+}
+} // namespace TestCallable
+
+#endif // TEST_CALLABLE_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 423932e2cd..24eb84127b 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -93,6 +93,7 @@
#include "tests/core/test_time.h"
#include "tests/core/threads/test_worker_thread_pool.h"
#include "tests/core/variant/test_array.h"
+#include "tests/core/variant/test_callable.h"
#include "tests/core/variant/test_dictionary.h"
#include "tests/core/variant/test_variant.h"
#include "tests/core/variant/test_variant_utility.h"