diff options
Diffstat (limited to 'core')
45 files changed, 384 insertions, 163 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 3049994240..cbbfe3de75 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1343,6 +1343,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f); GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f); + GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0); + GLOBAL_DEF("audio/general/ios/mix_with_others", false); + PackedStringArray extensions; extensions.push_back("gd"); if (Engine::get_singleton()->has_singleton("GodotSharp")) { diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 05fe393a2f..981d9b0025 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1211,8 +1211,7 @@ void Thread::_start_func(void *ud) { Ref<Thread> t = *tud; memdelete(tud); - Object *target_instance = t->target_callable.get_object(); - if (!target_instance) { + if (!t->target_callable.is_valid()) { t->running.clear(); ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id())); } diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 33b3271495..2f70fdf219 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -794,6 +794,8 @@ void register_global_constants() { void unregister_global_constants() { _global_constants.clear(); + _global_constants_map.clear(); + _global_enums.clear(); } int CoreConstants::get_global_constant_count() { diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 58cb51245a..f3e988633c 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -502,7 +502,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) { if (constant_doc.name == name) { - d["documentation"] = fix_doc_description(constant_doc.description); + d["description"] = fix_doc_description(constant_doc.description); break; } } @@ -521,7 +521,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { const DocData::EnumDoc *enum_doc = global_scope_doc->enums.getptr(E.key); if (enum_doc) { - d1["documentation"] = fix_doc_description(enum_doc->description); + d1["description"] = fix_doc_description(enum_doc->description); } } Array values; @@ -532,7 +532,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) { if (constant_doc.name == F.first) { - d2["documentation"] = fix_doc_description(constant_doc.description); + d2["description"] = fix_doc_description(constant_doc.description); break; } } @@ -596,7 +596,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::MethodDoc &method_doc : global_scope_doc->methods) { if (method_doc.name == name) { - func["documentation"] = fix_doc_description(method_doc.description); + func["description"] = fix_doc_description(method_doc.description); break; } } @@ -647,7 +647,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::PropertyDoc &property_doc : builtin_doc->properties) { if (property_doc.name == member_name) { - d2["documentation"] = fix_doc_description(property_doc.description); + d2["description"] = fix_doc_description(property_doc.description); break; } } @@ -673,7 +673,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) { if (constant_doc.name == constant_name) { - d2["documentation"] = fix_doc_description(constant_doc.description); + d2["description"] = fix_doc_description(constant_doc.description); break; } } @@ -706,7 +706,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) { if (constant_doc.name == enumeration) { - values_dict["documentation"] = fix_doc_description(constant_doc.description); + values_dict["description"] = fix_doc_description(constant_doc.description); break; } } @@ -717,7 +717,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { const DocData::EnumDoc *enum_doc = builtin_doc->enums.getptr(enum_name); if (enum_doc) { - enum_dict["documentation"] = fix_doc_description(enum_doc->description); + enum_dict["description"] = fix_doc_description(enum_doc->description); } } @@ -750,7 +750,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs && builtin_doc != nullptr) { for (const DocData::MethodDoc &operator_doc : builtin_doc->operators) { if (operator_doc.name == "operator " + operator_name) { - d2["documentation"] = fix_doc_description(operator_doc.description); + d2["description"] = fix_doc_description(operator_doc.description); break; } } @@ -805,7 +805,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::MethodDoc &method_doc : builtin_doc->methods) { if (method_doc.name == method_name) { - d2["documentation"] = fix_doc_description(method_doc.description); + d2["description"] = fix_doc_description(method_doc.description); break; } } @@ -853,7 +853,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { } } if (constructor_found) { - d2["documentation"] = fix_doc_description(constructor_doc.description); + d2["description"] = fix_doc_description(constructor_doc.description); } } } @@ -871,7 +871,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { } if (p_include_docs && builtin_doc != nullptr) { - d["documentation"] = fix_doc_description(builtin_doc->description); + d["brief_description"] = fix_doc_description(builtin_doc->brief_description); + d["description"] = fix_doc_description(builtin_doc->description); } builtins.push_back(d); @@ -933,7 +934,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : class_doc->constants) { if (constant_doc.name == F) { - d2["documentation"] = fix_doc_description(constant_doc.description); + d2["description"] = fix_doc_description(constant_doc.description); break; } } @@ -967,7 +968,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::ConstantDoc &constant_doc : class_doc->constants) { if (constant_doc.name == G->get()) { - d3["documentation"] = fix_doc_description(constant_doc.description); + d3["description"] = fix_doc_description(constant_doc.description); break; } } @@ -981,7 +982,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { const DocData::EnumDoc *enum_doc = class_doc->enums.getptr(F); if (enum_doc) { - d2["documentation"] = fix_doc_description(enum_doc->description); + d2["description"] = fix_doc_description(enum_doc->description); } } @@ -1039,7 +1040,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::MethodDoc &method_doc : class_doc->methods) { if (method_doc.name == method_name) { - d2["documentation"] = fix_doc_description(method_doc.description); + d2["description"] = fix_doc_description(method_doc.description); break; } } @@ -1116,7 +1117,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::MethodDoc &method_doc : class_doc->methods) { if (method_doc.name == method_name) { - d2["documentation"] = fix_doc_description(method_doc.description); + d2["description"] = fix_doc_description(method_doc.description); break; } } @@ -1159,7 +1160,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::MethodDoc &signal_doc : class_doc->signals) { if (signal_doc.name == signal_name) { - d2["documentation"] = fix_doc_description(signal_doc.description); + d2["description"] = fix_doc_description(signal_doc.description); break; } } @@ -1208,7 +1209,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { if (p_include_docs) { for (const DocData::PropertyDoc &property_doc : class_doc->properties) { if (property_doc.name == property_name) { - d2["documentation"] = fix_doc_description(property_doc.description); + d2["description"] = fix_doc_description(property_doc.description); break; } } @@ -1223,7 +1224,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { } if (p_include_docs && class_doc != nullptr) { - d["documentation"] = fix_doc_description(class_doc->description); + d["brief_description"] = fix_doc_description(class_doc->brief_description); + d["description"] = fix_doc_description(class_doc->description); } classes.push_back(d); diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index a4d4b9161e..6c3d0a6148 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -35,6 +35,7 @@ #include "core/object/method_bind.h" #include "core/os/os.h" #include "core/version.h" +#include "gdextension_manager.h" extern void gdextension_setup_interface(); extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name); @@ -218,7 +219,7 @@ public: #ifdef TOOLS_ENABLED ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name)); #endif - ERR_FAIL_COND_MSG(vararg, "Validated methods don't have ptrcall support. This is most likely an engine bug."); + ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have validated call support. This is most likely an engine bug."); GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); if (validated_call_func) { @@ -234,7 +235,7 @@ public: void *ret_opaque = nullptr; if (r_ret) { VariantInternal::initialize(r_ret, return_value_info.type); - ret_opaque = VariantInternal::get_opaque_pointer(r_ret); + ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret); } ptrcall(p_object, argptrs, ret_opaque); @@ -663,7 +664,7 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte memnew_placement(r_path, String(self->library_path)); } -HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions; +HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions; void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) { ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered."); @@ -776,7 +777,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) { level_initialized = int32_t(p_level); - ERR_FAIL_COND(initialization.initialize == nullptr); + ERR_FAIL_NULL(initialization.initialize); initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level)); } @@ -835,6 +836,10 @@ void GDExtension::initialize_gdextensions() { register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path); } +void GDExtension::finalize_gdextensions() { + gdextension_interface_functions.clear(); +} + Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) { ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library."); @@ -910,9 +915,9 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, #ifdef TOOLS_ENABLED p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled()); - p_extension->update_last_modified_time(MAX( - FileAccess::get_modified_time(library_path), - FileAccess::get_modified_time(p_path))); + p_extension->update_last_modified_time( + FileAccess::get_modified_time(p_path), + FileAccess::get_modified_time(library_path)); #endif err = p_extension->open_library(library_path, entry_symbol); @@ -949,6 +954,15 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, } Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) { + // We can't have two GDExtension resource object representing the same library, because + // loading (or unloading) a GDExtension affects global data. So, we need reuse the same + // object if one has already been loaded (even if caching is disabled at the resource + // loader level). + GDExtensionManager *manager = GDExtensionManager::get_singleton(); + if (manager->is_extension_loaded(p_path)) { + return manager->get_extension(p_path); + } + Ref<GDExtension> lib; Error err = load_gdextension_resource(p_path, lib); if (err != OK && r_error) { @@ -976,10 +990,13 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const #ifdef TOOLS_ENABLED bool GDExtension::has_library_changed() const { - if (FileAccess::get_modified_time(get_path()) > last_modified_time) { + // Check only that the last modified time is different (rather than checking + // that it's newer) since some OS's (namely Windows) will preserve the modified + // time by default when copying files. + if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) { return true; } - if (FileAccess::get_modified_time(library_path) > last_modified_time) { + if (FileAccess::get_modified_time(library_path) != library_last_modified_time) { return true; } return false; diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 2996100c9a..0d20b8e50c 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -90,7 +90,8 @@ class GDExtension : public Resource { int32_t level_initialized = -1; #ifdef TOOLS_ENABLED - uint64_t last_modified_time = 0; + uint64_t resource_last_modified_time = 0; + uint64_t library_last_modified_time = 0; bool is_reloading = false; Vector<GDExtensionMethodBind *> invalid_methods; Vector<ObjectID> instance_bindings; @@ -106,12 +107,16 @@ class GDExtension : public Resource { void clear_instance_bindings(); #endif + static HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions; + protected: static void _bind_methods(); public: HashMap<String, String> class_icon_paths; + virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way. + static String get_extension_list_config_file(); static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr); @@ -136,8 +141,9 @@ public: void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; } bool has_library_changed() const; - void update_last_modified_time(uint64_t p_last_modified_time) { - last_modified_time = MAX(last_modified_time, p_last_modified_time); + void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) { + resource_last_modified_time = p_resource_last_modified_time; + library_last_modified_time = p_library_last_modified_time; } void track_instance_binding(Object *p_object); @@ -151,6 +157,7 @@ public: static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name); static void initialize_gdextensions(); + static void finalize_gdextensions(); GDExtension(); ~GDExtension(); diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp index 9c8d6b3e7f..2dac4a3a5d 100644 --- a/core/extension/gdextension_compat_hashes.cpp +++ b/core/extension/gdextension_compat_hashes.cpp @@ -840,4 +840,8 @@ void GDExtensionCompatHashes::initialize() { // clang-format on } +void GDExtensionCompatHashes::finalize() { + mappings.clear(); +} + #endif // DISABLE_DEPRECATED diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h index 3a66ef0b97..29393dcb2d 100644 --- a/core/extension/gdextension_compat_hashes.h +++ b/core/extension/gdextension_compat_hashes.h @@ -48,6 +48,7 @@ class GDExtensionCompatHashes { public: static void initialize(); + static void finalize(); static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash); static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes); }; diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index 843f9ceecf..e02e7aa701 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -159,9 +159,7 @@ public: userdata = p_info->callable_userdata; token = p_info->token; - if (p_info->object != nullptr) { - object = ((Object *)p_info->object)->get_instance_id(); - } + object = p_info->object_id; call_func = p_info->call_func; is_valid_func = p_info->is_valid_func; @@ -400,7 +398,7 @@ static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDEx Variant *iter = (Variant *)r_iter; bool valid; - memnew_placement(r_ret, Variant(self->iter_next(*iter, valid))); + memnew_placement(r_ret, Variant(self->iter_get(*iter, valid))); *r_valid = valid; } diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index ae7c5e0d2a..d58f0226d8 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -392,7 +392,7 @@ typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_user typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out); typedef struct { - /* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method. + /* 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. @@ -409,7 +409,7 @@ typedef struct { void *callable_userdata; void *token; - GDExtensionObjectPtr object; + GDObjectInstanceID object_id; GDExtensionCallableCustomCall call_func; GDExtensionCallableCustomIsValid is_valid_func; @@ -590,7 +590,10 @@ typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(co * * For example: * - * GDExtensionInterfaceGetGodotVersion *get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version"); + * GDExtensionInterfaceGetGodotVersion get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version"); + * + * (Note that snippet may cause "cast between incompatible function types" on some compilers, you can + * silence this by adding an intermediary `void*` cast.) * * You can then call it like a normal function: * diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index 0dc84f685f..a4d032f22f 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -293,3 +293,9 @@ GDExtensionManager::GDExtensionManager() { GDExtensionCompatHashes::initialize(); #endif } + +GDExtensionManager::~GDExtensionManager() { +#ifndef DISABLE_DEPRECATED + GDExtensionCompatHashes::finalize(); +#endif +} diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h index 8cd6d5a3e2..9386e356bb 100644 --- a/core/extension/gdextension_manager.h +++ b/core/extension/gdextension_manager.h @@ -86,6 +86,7 @@ public: void reload_extensions(); GDExtensionManager(); + ~GDExtensionManager(); }; VARIANT_ENUM_CAST(GDExtensionManager::LoadStatus) diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 9a43c4e35d..77655e9b6a 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -414,6 +414,8 @@ 03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, 03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, 03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, +030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, +03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,platform:Windows, 03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, @@ -582,6 +584,7 @@ 03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows, 03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, 03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows, @@ -643,7 +646,6 @@ 030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows, 03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, -0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows, 03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, 030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows, @@ -772,6 +774,7 @@ 03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, +03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows, 03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows, @@ -928,6 +931,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X, 030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X, 030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, +030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, 03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -979,6 +983,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X, +0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Mac OS X, 03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X, 030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, @@ -1335,8 +1340,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, @@ -1490,6 +1497,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux, +0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, 190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux, 0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux, @@ -1639,6 +1647,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, +060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux, 050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux, 05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux, @@ -1792,6 +1801,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android, 30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android, 32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, diff --git a/core/input/input.cpp b/core/input/input.cpp index 19ea8c7317..2d48bdd4cf 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -697,29 +697,28 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) { if (InputMap::get_singleton()->event_is_action(p_event, E.key)) { Action &action = action_state[E.key]; + bool is_joypad_axis = jm.is_valid(); bool is_pressed = false; - if (!p_event->is_echo()) { if (p_event->is_action_pressed(E.key)) { - if (jm.is_valid()) { - // If axis is already pressed, don't increase the pressed counter. + bool is_joypad_axis_valid_zone_enter = false; + if (is_joypad_axis) { if (!action.axis_pressed) { + is_joypad_axis_valid_zone_enter = true; action.pressed++; action.axis_pressed = true; } } else { action.pressed++; } - - is_pressed = true; - if (action.pressed == 1) { + if (action.pressed == 1 && (is_joypad_axis_valid_zone_enter || !is_joypad_axis)) { action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } + is_pressed = true; } else { bool is_released = true; - if (jm.is_valid()) { - // Same as above. Don't release axis when not pressed. + if (is_joypad_axis) { if (action.axis_pressed) { action.axis_pressed = false; } else { @@ -1096,7 +1095,8 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { return; } - JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value); + JoyAxisRange range; + JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value, range); if (map.type == TYPE_BUTTON) { bool pressed = map.value > 0.5; @@ -1136,7 +1136,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) { if (map.type == TYPE_AXIS) { JoyAxis axis = JoyAxis(map.index); float value = map.value; - if (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT) { + if (range == FULL_AXIS && (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT)) { // Convert to a value between 0.0f and 1.0f. value = 0.5f + value / 2.0f; } @@ -1242,7 +1242,7 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, return event; } -Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value) { +Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range) { JoyEvent event; for (int i = 0; i < mapping.bindings.size(); i++) { @@ -1288,6 +1288,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J case TYPE_AXIS: event.index = (int)binding.output.axis.axis; event.value = value; + r_range = binding.output.axis.range; if (binding.output.axis.range != binding.input.axis.range) { switch (binding.output.axis.range) { case POSITIVE_HALF_AXIS: diff --git a/core/input/input.h b/core/input/input.h index 8ce5f64a6a..bedc3fa0e3 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -221,7 +221,7 @@ private: Vector<JoyDeviceMapping> map_db; JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button); - JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value); + JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range); void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]); JoyButton _get_output_button(String output); JoyAxis _get_output_axis(String output); diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 22b63a0929..40c1a53958 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -546,6 +546,10 @@ bool DirAccess::get_include_hidden() const { return include_hidden; } +bool DirAccess::is_case_sensitive(const String &p_path) const { + return true; +} + void DirAccess::_bind_methods() { ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open); ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error); @@ -583,6 +587,8 @@ void DirAccess::_bind_methods() { ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden); ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden); + ClassDB::bind_method(D_METHOD("is_case_sensitive", "path"), &DirAccess::is_case_sensitive); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden"); } diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 52ed688deb..4ee69571f2 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -159,6 +159,8 @@ public: void set_include_hidden(bool p_enable); bool get_include_hidden() const; + virtual bool is_case_sensitive(const String &p_path) const; + DirAccess() {} virtual ~DirAccess() {} }; diff --git a/core/io/file_access.h b/core/io/file_access.h index 7b9e66bb83..7d346ca2f4 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -223,8 +223,8 @@ public: static Vector<uint8_t> get_file_as_bytes(const String &p_path, Error *r_error = nullptr); static String get_file_as_string(const String &p_path, Error *r_error = nullptr); - static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path); } - static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path); }; + static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path, &last_file_open_error); } + static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path, &last_file_open_error); } template <class T> static void make_default(AccessType p_access) { diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index d085c29728..dd45332412 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -110,7 +110,7 @@ static void godot_free(voidpf opaque, voidpf address) { } // extern "C" void ZipArchive::close_handle(unzFile p_file) const { - ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open."); + ERR_FAIL_NULL_MSG(p_file, "Cannot close a file if none is open."); unzCloseCurrentFile(p_file); unzClose(p_file); } @@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const { io.free_mem = godot_free; unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io); - ERR_FAIL_COND_V_MSG(!pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'."); + ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'."); int unz_err = unzGoToFilePos(pkg, &file.file_pos); if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) { unzClose(pkg); @@ -168,7 +168,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6 io.zerror_file = godot_testerror; unzFile zfile = unzOpen2(p_path.utf8().get_data(), &io); - ERR_FAIL_COND_V(!zfile, false); + ERR_FAIL_NULL_V(zfile, false); unz_global_info64 gi; int err = unzGetGlobalInfo64(zfile, &gi); @@ -241,7 +241,7 @@ Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) { ZipArchive *arch = ZipArchive::get_singleton(); ERR_FAIL_NULL_V(arch, FAILED); zfile = arch->get_file_handle(p_path); - ERR_FAIL_COND_V(!zfile, FAILED); + ERR_FAIL_NULL_V(zfile, FAILED); int err = unzGetCurrentFileInfo64(zfile, &file_info, nullptr, 0, nullptr, 0, nullptr, 0); ERR_FAIL_COND_V(err != UNZ_OK, FAILED); @@ -265,28 +265,28 @@ bool FileAccessZip::is_open() const { } void FileAccessZip::seek(uint64_t p_position) { - ERR_FAIL_COND(!zfile); + ERR_FAIL_NULL(zfile); unzSeekCurrentFile(zfile, p_position); } void FileAccessZip::seek_end(int64_t p_position) { - ERR_FAIL_COND(!zfile); + ERR_FAIL_NULL(zfile); unzSeekCurrentFile(zfile, get_length() + p_position); } uint64_t FileAccessZip::get_position() const { - ERR_FAIL_COND_V(!zfile, 0); + ERR_FAIL_NULL_V(zfile, 0); return unztell(zfile); } uint64_t FileAccessZip::get_length() const { - ERR_FAIL_COND_V(!zfile, 0); + ERR_FAIL_NULL_V(zfile, 0); return file_info.uncompressed_size; } bool FileAccessZip::eof_reached() const { - ERR_FAIL_COND_V(!zfile, true); + ERR_FAIL_NULL_V(zfile, true); return at_eof; } @@ -299,7 +299,7 @@ uint8_t FileAccessZip::get_8() const { uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const { ERR_FAIL_COND_V(!p_dst && p_length > 0, -1); - ERR_FAIL_COND_V(!zfile, -1); + ERR_FAIL_NULL_V(zfile, -1); at_eof = unzeof(zfile); if (at_eof) { diff --git a/core/io/image.cpp b/core/io/image.cpp index 15d0182dfc..ce08b417a8 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3773,7 +3773,7 @@ void Image::fix_alpha_edges() { } int closest_dist = max_dist; - uint8_t closest_color[3]; + uint8_t closest_color[3] = { 0 }; int from_x = MAX(0, j - max_radius); int to_x = MIN(width - 1, j + max_radius); diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index bfef9ebeaf..3d384d9345 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -1155,10 +1155,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo #ifdef REAL_T_IS_DOUBLE case Variant::VECTOR2: case Variant::VECTOR3: + case Variant::VECTOR4: case Variant::PACKED_VECTOR2_ARRAY: case Variant::PACKED_VECTOR3_ARRAY: case Variant::TRANSFORM2D: case Variant::TRANSFORM3D: + case Variant::PROJECTION: case Variant::QUATERNION: case Variant::PLANE: case Variant::BASIS: @@ -1619,8 +1621,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo encode_uint32(datalen, buf); buf += 4; const uint8_t *r = data.ptr(); - memcpy(buf, &r[0], datalen * datasize); - buf += datalen * datasize; + if (r) { + memcpy(buf, &r[0], datalen * datasize); + buf += datalen * datasize; + } } r_len += 4 + datalen * datasize; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index f7915261af..6721ec0953 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -1053,8 +1053,9 @@ void ResourceLoader::clear_thread_load_tasks() { thread_load_mutex.lock(); } - for (KeyValue<String, LoadToken *> &E : user_load_tokens) { - memdelete(E.value); + while (user_load_tokens.begin()) { + // User load tokens remove themselves from the map on destruction. + memdelete(user_load_tokens.begin()->value); } user_load_tokens.clear(); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index 216fc24fff..edff3e1f14 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -35,6 +35,8 @@ #include "core/io/dir_access.h" #include "core/io/file_access.h" +// These constants are off by 1, causing the 'z' and '9' characters never to be used. +// This cannot be fixed without breaking compatibility; see GH-83843. static constexpr uint32_t char_count = ('z' - 'a'); static constexpr uint32_t base = char_count + ('9' - '0'); diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index f8456ec998..68d995fe67 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -2278,7 +2278,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3 uint32_t edges_copied = 0; for (uint32_t i = 0; i < ch.edges.size(); i++) { - ERR_CONTINUE(edge_faces[i] == -1); // Sanity check + ERR_CONTINUE(edge_faces[i] == -1); // Safety check. uint32_t a = (&ch.edges[i])->get_source_vertex(); uint32_t b = (&ch.edges[i])->get_target_vertex(); diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index 0e5702e0af..b37fce9e9c 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -306,10 +306,12 @@ public: Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points); Vector<int> triangles; + triangles.resize(3 * tr.size()); + int *ptr = triangles.ptrw(); for (int i = 0; i < tr.size(); i++) { - triangles.push_back(tr[i].points[0]); - triangles.push_back(tr[i].points[1]); - triangles.push_back(tr[i].points[2]); + *ptr++ = tr[i].points[0]; + *ptr++ = tr[i].points[1]; + *ptr++ = tr[i].points[2]; } return triangles; } diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 2dbb7e468e..db78b982e4 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -99,7 +99,7 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { #ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); #endif call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); } @@ -154,7 +154,7 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { #ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); #endif call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); } @@ -209,7 +209,7 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { #ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); + ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); #endif call_with_variant_args_retc(data.instance, 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 c594f4a9b4..bf1bd0de93 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -98,9 +98,24 @@ void ClassDB::get_class_list(List<StringName> *p_classes) { p_classes->push_back(E.key); } - p_classes->sort(); + p_classes->sort_custom<StringName::AlphCompare>(); } +#ifdef TOOLS_ENABLED +void ClassDB::get_extensions_class_list(List<StringName> *p_classes) { + OBJTYPE_RLOCK; + + for (const KeyValue<StringName, ClassInfo> &E : classes) { + if (E.value.api != API_EXTENSION && E.value.api != API_EDITOR_EXTENSION) { + continue; + } + p_classes->push_back(E.key); + } + + p_classes->sort_custom<StringName::AlphCompare>(); +} +#endif + void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) { OBJTYPE_RLOCK; @@ -165,8 +180,8 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) { } uint32_t ClassDB::get_api_hash(APIType p_api) { - OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED + OBJTYPE_WLOCK; if (api_hashes_cache.has(p_api)) { return api_hashes_cache[p_api]; @@ -175,7 +190,9 @@ uint32_t ClassDB::get_api_hash(APIType p_api) { uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG)); List<StringName> class_list; - ClassDB::get_class_list(&class_list); + for (const KeyValue<StringName, ClassInfo> &E : classes) { + class_list.push_back(E.key); + } // Must be alphabetically sorted for hash to compute. class_list.sort_custom<StringName::AlphCompare>(); @@ -859,8 +876,8 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_ } void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) { - OBJTYPE_RLOCK; #ifdef DEBUG_METHODS_ENABLED + OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_NULL(type); @@ -871,6 +888,7 @@ void ClassDB::set_method_error_return_values(const StringName &p_class, const St Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) { #ifdef DEBUG_METHODS_ENABLED + OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_NULL_V(type, Vector<Error>()); @@ -1415,6 +1433,8 @@ void ClassDB::_bind_compatibility(ClassInfo *type, MethodBind *p_method) { } void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility) { + OBJTYPE_WLOCK; + ClassInfo *type = classes.getptr(p_class); if (!type) { ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'."); diff --git a/core/object/class_db.h b/core/object/class_db.h index 5c2c59d508..7a4ee1afa4 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -251,6 +251,9 @@ public: } static void get_class_list(List<StringName> *p_classes); +#ifdef TOOLS_ENABLED + static void get_extensions_class_list(List<StringName> *p_classes); +#endif static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes); static StringName get_parent_class_nocheck(const StringName &p_class); diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 0f3cf3916a..79a8df6c8a 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -47,8 +47,8 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ }\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - if (_script_instance) {\\ - return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\ + if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\ + return true;\\ }\\ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ _gdvirtual_##m_name = nullptr;\\ diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index 506f8291eb..de71295ee5 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -36,7 +36,7 @@ #include "core/object/script_language.h" #ifdef DEV_ENABLED -// Includes sanity checks to ensure that a queue set as a thread singleton override +// Includes safety checks to ensure that a queue set as a thread singleton override // is only ever called from the thread it was set for. #define LOCK_MUTEX \ if (this != MessageQueue::thread_singleton) { \ @@ -537,7 +537,7 @@ CallQueue::~CallQueue() { if (!allocator_is_custom) { memdelete(allocator); } - // This is done here to avoid a circular dependency between the sanity checks and the thread singleton pointer. + // This is done here to avoid a circular dependency between the safety checks and the thread singleton pointer. if (this == MessageQueue::thread_singleton) { MessageQueue::thread_singleton = nullptr; } diff --git a/core/object/object.cpp b/core/object/object.cpp index 056334c8e1..2e5b897bce 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -1109,8 +1109,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int Error err = OK; for (const Connection &c : slot_conns) { - Object *target = c.callable.get_object(); - if (!target) { + if (!c.callable.is_valid()) { // Target might have been deleted during signal callback, this is expected and OK. continue; } @@ -1133,7 +1132,8 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int continue; } #endif - if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) { + Object *target = c.callable.get_object(); + if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && target && !ClassDB::class_exists(target->get_class_name())) { //most likely object is not initialized yet, do not throw error. } else { ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + "."); @@ -1313,8 +1313,14 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) { ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null."); - Object *target_object = p_callable.get_object(); - ERR_FAIL_NULL_V_MSG(target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); + if (p_callable.is_standard()) { + // FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes + // that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found + // and registered soon enough this branch is needed to allow `connect()` to succeed. + ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null."); + } else { + ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is not valid: " + p_callable); + } SignalData *s = signal_map.getptr(p_signal); if (!s) { @@ -1352,6 +1358,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui } } + Object *target_object = p_callable.get_object(); + SignalData::Slot slot; Connection conn; @@ -1359,7 +1367,9 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui conn.signal = ::Signal(this, p_signal); conn.flags = p_flags; slot.conn = conn; - slot.cE = target_object->connections.push_back(conn); + if (target_object) { + slot.cE = target_object->connections.push_back(conn); + } if (p_flags & CONNECT_REFERENCE_COUNTED) { slot.reference_count = 1; } @@ -1398,9 +1408,6 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable) bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) { ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null."); - Object *target_object = p_callable.get_object(); - ERR_FAIL_NULL_V_MSG(target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null."); - SignalData *s = signal_map.getptr(p_signal); if (!s) { bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) || @@ -1420,7 +1427,13 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, } } - target_object->connections.erase(slot->cE); + if (slot->cE) { + Object *target_object = p_callable.get_object(); + if (target_object) { + target_object->connections.erase(slot->cE); + } + } + s->slot_map.erase(*p_callable.get_base_comparator()); if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) { diff --git a/core/object/script_language.h b/core/object/script_language.h index 5ff7cd8582..3e4041d173 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -239,6 +239,7 @@ public: virtual void get_reserved_words(List<String> *p_words) const = 0; virtual bool is_control_flow_keyword(String p_string) const = 0; virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0; + virtual void get_doc_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp index a07bf63a02..e326baf7eb 100644 --- a/core/object/script_language_extension.cpp +++ b/core/object/script_language_extension.cpp @@ -92,6 +92,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_get_reserved_words); GDVIRTUAL_BIND(_is_control_flow_keyword, "keyword"); GDVIRTUAL_BIND(_get_comment_delimiters); + GDVIRTUAL_BIND(_get_doc_comment_delimiters); GDVIRTUAL_BIND(_get_string_delimiters); GDVIRTUAL_BIND(_make_template, "template", "class_name", "base_class_name"); GDVIRTUAL_BIND(_get_built_in_templates, "object"); diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 89bba80b90..00ab1cd6c0 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -241,6 +241,16 @@ public: } } + GDVIRTUAL0RC(Vector<String>, _get_doc_comment_delimiters) + + virtual void get_doc_comment_delimiters(List<String> *p_words) const override { + Vector<String> ret; + GDVIRTUAL_CALL(_get_doc_comment_delimiters, ret); + for (int i = 0; i < ret.size(); i++) { + p_words->push_back(ret[i]); + } + } + GDVIRTUAL0RC(Vector<String>, _get_string_delimiters) virtual void get_string_delimiters(List<String> *p_words) const override { @@ -303,6 +313,9 @@ public: ERR_CONTINUE(!err.has("message")); ScriptError serr; + if (err.has("path")) { + serr.path = err["path"]; + } serr.line = err["line"]; serr.column = err["column"]; serr.message = err["message"]; diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index b0f9501985..a8f2ac5bfe 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -71,9 +71,7 @@ bool UndoRedo::_redo(bool p_execute) { } current_action++; - if (p_execute) { - _process_operation_list(actions.write[current_action].do_ops.front()); - } + _process_operation_list(actions.write[current_action].do_ops.front(), p_execute); version++; emit_signal(SNAME("version_changed")); @@ -136,17 +134,22 @@ void UndoRedo::add_do_method(const Callable &p_callable) { ERR_FAIL_COND(action_level <= 0); ERR_FAIL_COND((current_action + 1) >= actions.size()); - Object *object = p_callable.get_object(); - ERR_FAIL_NULL(object); + ObjectID object_id = p_callable.get_object_id(); + Object *object = ObjectDB::get_instance(object_id); + ERR_FAIL_COND(object_id.is_valid() && object == nullptr); Operation do_op; do_op.callable = p_callable; - do_op.object = p_callable.get_object_id(); + do_op.object = object_id; if (Object::cast_to<RefCounted>(object)) { do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } do_op.type = Operation::TYPE_METHOD; do_op.name = p_callable.get_method(); + if (do_op.name == StringName()) { + // There's no `get_method()` for custom callables, so use `operator String()` instead. + do_op.name = static_cast<String>(p_callable); + } actions.write[current_action + 1].do_ops.push_back(do_op); } @@ -161,18 +164,23 @@ void UndoRedo::add_undo_method(const Callable &p_callable) { return; } - Object *object = p_callable.get_object(); - ERR_FAIL_NULL(object); + ObjectID object_id = p_callable.get_object_id(); + Object *object = ObjectDB::get_instance(object_id); + ERR_FAIL_COND(object_id.is_valid() && object == nullptr); Operation undo_op; undo_op.callable = p_callable; - undo_op.object = p_callable.get_object_id(); + undo_op.object = object_id; if (Object::cast_to<RefCounted>(object)) { undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object)); } undo_op.type = Operation::TYPE_METHOD; undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends; undo_op.name = p_callable.get_method(); + if (undo_op.name == StringName()) { + // There's no `get_method()` for custom callables, so use `operator String()` instead. + undo_op.name = static_cast<String>(p_callable); + } actions.write[current_action + 1].undo_ops.push_back(undo_op); } @@ -311,7 +319,7 @@ void UndoRedo::commit_action(bool p_execute) { } } -void UndoRedo::_process_operation_list(List<Operation>::Element *E) { +void UndoRedo::_process_operation_list(List<Operation>::Element *E, bool p_execute) { const int PREALLOCATE_ARGS_COUNT = 16; LocalVector<const Variant *> args; @@ -327,18 +335,20 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { switch (op.type) { case Operation::TYPE_METHOD: { - Callable::CallError ce; - Variant ret; - op.callable.callp(nullptr, 0, ret, ce); - if (ce.error != Callable::CallError::CALL_OK) { - ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce)); - } + if (p_execute) { + Callable::CallError ce; + Variant ret; + op.callable.callp(nullptr, 0, ret, ce); + if (ce.error != Callable::CallError::CALL_OK) { + ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce)); + } #ifdef TOOLS_ENABLED - Resource *res = Object::cast_to<Resource>(obj); - if (res) { - res->set_edited(true); - } + Resource *res = Object::cast_to<Resource>(obj); + if (res) { + res->set_edited(true); + } #endif + } if (method_callback) { Vector<Variant> binds; @@ -363,13 +373,16 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) { } } break; case Operation::TYPE_PROPERTY: { - obj->set(op.name, op.value); + if (p_execute) { + obj->set(op.name, op.value); #ifdef TOOLS_ENABLED - Resource *res = Object::cast_to<Resource>(obj); - if (res) { - res->set_edited(true); - } + Resource *res = Object::cast_to<Resource>(obj); + if (res) { + res->set_edited(true); + } #endif + } + if (property_callback) { property_callback(prop_callback_ud, obj, op.name, op.value); } @@ -390,7 +403,7 @@ bool UndoRedo::undo() { if (current_action < 0) { return false; //nothing to redo } - _process_operation_list(actions.write[current_action].undo_ops.front()); + _process_operation_list(actions.write[current_action].undo_ops.front(), true); current_action--; version--; emit_signal(SNAME("version_changed")); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 389d8714f7..74a6bea732 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -85,7 +85,7 @@ private: uint64_t version = 1; void _pop_history_tail(); - void _process_operation_list(List<Operation>::Element *E); + void _process_operation_list(List<Operation>::Element *E, bool p_execute); void _discard_redo(); bool _redo(bool p_execute); diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index b4ac533779..4ad9dd43c4 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -360,6 +360,7 @@ void unregister_core_extensions() { if (_is_core_extensions_registered) { gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); } + GDExtension::finalize_gdextensions(); } void unregister_core_types() { diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 4402e44ad4..5a8df07410 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -100,11 +100,9 @@ void StringName::cleanup() { lost_strings++; if (OS::get_singleton()->is_stdout_verbose()) { - if (d->cname) { - print_line("Orphan StringName: " + String(d->cname)); - } else { - print_line("Orphan StringName: " + String(d->name)); - } + String dname = String(d->cname ? d->cname : d->name); + + print_line(vformat("Orphan StringName: %s (static: %d, total: %d)", dname, d->static_count.get(), d->refcount.get())); } } @@ -113,7 +111,7 @@ void StringName::cleanup() { } } if (lost_strings) { - print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit."); + print_verbose(vformat("StringName: %d unclaimed string names at exit.", lost_strings)); } configured = false; } diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h index deb2937771..72425a8c3d 100644 --- a/core/templates/paged_allocator.h +++ b/core/templates/paged_allocator.h @@ -144,7 +144,7 @@ public: if (thread_safe) { spin_lock.lock(); } - ERR_FAIL_COND(page_pool != nullptr); //sanity check + ERR_FAIL_COND(page_pool != nullptr); // Safety check. ERR_FAIL_COND(p_page_size == 0); page_size = nearest_power_of_2_templated(p_page_size); page_mask = page_size - 1; diff --git a/core/templates/rb_map.h b/core/templates/rb_map.h index 0a33809bb2..d373713669 100644 --- a/core/templates/rb_map.h +++ b/core/templates/rb_map.h @@ -96,6 +96,8 @@ public: typedef KeyValue<K, V> ValueType; struct Iterator { + friend class RBMap<K, V, C, A>; + _FORCE_INLINE_ KeyValue<K, V> &operator*() const { return E->key_value(); } diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index bfc9f6fc9a..20fb0c6501 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -182,7 +182,7 @@ class SafeRefCount { SafeNumeric<uint32_t> count; #ifdef DEV_ENABLED - _ALWAYS_INLINE_ void _check_unref_sanity() { + _ALWAYS_INLINE_ void _check_unref_safety() { // This won't catch every misuse, but it's better than nothing. CRASH_COND_MSG(count.get() == 0, "Trying to unreference a SafeRefCount which is already zero is wrong and a symptom of it being misused.\n" @@ -202,14 +202,14 @@ public: _ALWAYS_INLINE_ bool unref() { // true if must be disposed of #ifdef DEV_ENABLED - _check_unref_sanity(); + _check_unref_safety(); #endif return count.decrement() == 0; } _ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of #ifdef DEV_ENABLED - _check_unref_sanity(); + _check_unref_safety(); #endif return count.decrement(); } diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index d7034f1c00..0b1174c873 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -47,6 +47,13 @@ void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_ret r_call_error.expected = 0; r_return_value = Variant(); } else if (is_custom()) { + if (!is_valid()) { + r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; + r_call_error.argument = 0; + r_call_error.expected = 0; + r_return_value = Variant(); + return; + } custom->call(p_arguments, p_argcount, r_return_value, r_call_error); } else { Object *obj = ObjectDB::get_instance(ObjectID(object)); diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 63ea3274ce..09fb34e7c1 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -2117,7 +2117,7 @@ Variant::operator ::RID() const { } else if (type == OBJECT && _get_obj().obj) { #ifdef DEBUG_ENABLED if (EngineDebugger::is_active()) { - ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, ::RID(), "Invalid pointer (object was freed)."); + ERR_FAIL_NULL_V_MSG(ObjectDB::get_instance(_get_obj().id), ::RID(), "Invalid pointer (object was freed)."); } #endif Callable::CallError ce; diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index fea1622222..86e7654090 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -1075,7 +1075,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, return ERR_PARSE_ERROR; } - static HashMap<StringName, Variant::Type> builtin_types; + static HashMap<String, Variant::Type> builtin_types; if (builtin_types.is_empty()) { for (int i = 1; i < Variant::VARIANT_MAX; i++) { builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 6f334d1859..cc48394b64 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -121,16 +121,27 @@ Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) case Variant::VECTOR2: { return VariantInternalAccessor<Vector2>::get(&x).floor(); } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x); + } break; case Variant::VECTOR3: { return VariantInternalAccessor<Vector3>::get(&x).floor(); } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x); + } break; case Variant::VECTOR4: { return VariantInternalAccessor<Vector4>::get(&x).floor(); } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x); + } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } break; } } @@ -154,16 +165,27 @@ Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { case Variant::VECTOR2: { return VariantInternalAccessor<Vector2>::get(&x).ceil(); } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x); + } break; case Variant::VECTOR3: { return VariantInternalAccessor<Vector3>::get(&x).ceil(); } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x); + } break; case Variant::VECTOR4: { return VariantInternalAccessor<Vector4>::get(&x).ceil(); } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x); + } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } break; } } @@ -187,16 +209,27 @@ Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) case Variant::VECTOR2: { return VariantInternalAccessor<Vector2>::get(&x).round(); } break; + case Variant::VECTOR2I: { + return VariantInternalAccessor<Vector2i>::get(&x); + } break; case Variant::VECTOR3: { return VariantInternalAccessor<Vector3>::get(&x).round(); } break; + case Variant::VECTOR3I: { + return VariantInternalAccessor<Vector3i>::get(&x); + } break; case Variant::VECTOR4: { return VariantInternalAccessor<Vector4>::get(&x).round(); } break; + case Variant::VECTOR4I: { + return VariantInternalAccessor<Vector4i>::get(&x); + } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } break; } } @@ -236,9 +269,11 @@ Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_er return VariantInternalAccessor<Vector4i>::get(&x).abs(); } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } break; } } @@ -278,9 +313,11 @@ Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_e return VariantInternalAccessor<Vector4i>::get(&x).sign(); } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } break; } } @@ -333,14 +370,40 @@ int VariantUtilityFunctions::step_decimals(float step) { } Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; - if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; - r_error.argument = 1; - r_error.expected = x.get_type(); - return Variant(); + switch (x.get_type()) { + case Variant::INT: + case Variant::FLOAT: + case Variant::VECTOR2: + case Variant::VECTOR2I: + case Variant::VECTOR3: + case Variant::VECTOR3I: + case Variant::VECTOR4: + case Variant::VECTOR4I: + break; + default: + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)"; + } + + if (x.get_type() != step.get_type()) { + if (x.get_type() == Variant::INT || x.get_type() == Variant::FLOAT) { + if (step.get_type() != Variant::INT && step.get_type() != Variant::FLOAT) { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = Variant::NIL; + return R"(Argument "step" must be "int" or "float".)"; + } + } else { + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 1; + r_error.expected = x.get_type(); + return Variant(); + } } + r_error.error = Callable::CallError::CALL_OK; switch (step.get_type()) { case Variant::INT: { return snappedi(x, VariantInternalAccessor<int64_t>::get(&step)); @@ -367,9 +430,8 @@ Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step)); } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + return Variant(); // Already handled. + } break; } } @@ -382,7 +444,23 @@ int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) { } Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) { - r_error.error = Callable::CallError::CALL_OK; + switch (from.get_type()) { + case Variant::INT: + case Variant::FLOAT: + case Variant::VECTOR2: + case Variant::VECTOR3: + case Variant::VECTOR4: + case Variant::QUATERNION: + case Variant::BASIS: + case Variant::COLOR: + break; + default: + r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument = 0; + r_error.expected = Variant::NIL; + return R"(Argument "from" must be "int", "float", "Vector2", "Vector3", "Vector4", "Quaternion", "Basis, or "Color".)"; + } + if (from.get_type() != to.get_type()) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT; r_error.argument = 1; @@ -390,6 +468,7 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do return Variant(); } + r_error.error = Callable::CallError::CALL_OK; switch (from.get_type()) { case Variant::INT: { return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight); @@ -416,9 +495,8 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight); } break; default: { - r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; - return Variant(); - } + return Variant(); // Already handled. + } break; } } |