diff options
Diffstat (limited to 'core')
263 files changed, 10993 insertions, 4052 deletions
diff --git a/core/SCsub b/core/SCsub index 3b1a7ca79a..ec4658e8ca 100644 --- a/core/SCsub +++ b/core/SCsub @@ -3,6 +3,7 @@ Import("env") import core_builders +import methods env.core_sources = [] @@ -35,10 +36,12 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ: ) Exit(255) -# NOTE: It is safe to generate this file here, since this is still executed serially -with open("script_encryption_key.gen.cpp", "w") as f: - f.write('#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n") +script_encryption_key_contents = ( + '#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n" +) + +methods.write_file_if_needed("script_encryption_key.gen.cpp", script_encryption_key_contents) # Add required thirdparty code. @@ -58,7 +61,6 @@ thirdparty_misc_sources = [ # C++ sources "pcg.cpp", "polypartition.cpp", - "clipper.cpp", "smolv.cpp", ] thirdparty_misc_sources = [thirdparty_misc_dir + file for file in thirdparty_misc_sources] @@ -202,27 +204,23 @@ env.Depends( env.CommandNoCache( "#core/io/certs_compressed.gen.h", "#thirdparty/certs/ca-certificates.crt", - env.Run(core_builders.make_certs_header, "Building ca-certificates header."), + env.Run(core_builders.make_certs_header), ) # Authors env.Depends("#core/authors.gen.h", "../AUTHORS.md") -env.CommandNoCache( - "#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header, "Generating authors header.") -) +env.CommandNoCache("#core/authors.gen.h", "../AUTHORS.md", env.Run(core_builders.make_authors_header)) # Donors env.Depends("#core/donors.gen.h", "../DONORS.md") -env.CommandNoCache( - "#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header, "Generating donors header.") -) +env.CommandNoCache("#core/donors.gen.h", "../DONORS.md", env.Run(core_builders.make_donors_header)) # License env.Depends("#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"]) env.CommandNoCache( "#core/license.gen.h", ["../COPYRIGHT.txt", "../LICENSE.txt"], - env.Run(core_builders.make_license_header, "Generating license header."), + env.Run(core_builders.make_license_header), ) # Chain load SCsubs diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 0e27d556ec..f2f8aebe8b 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const { return _audio_output_latency; } +void Engine::increment_frames_drawn() { + if (frame_server_synced) { + server_syncs++; + } else { + server_syncs = 0; + } + frame_server_synced = false; + + frames_drawn++; +} + uint64_t Engine::get_frames_drawn() { return frames_drawn; } @@ -110,11 +121,12 @@ Dictionary Engine::get_version_info() const { dict["hex"] = VERSION_HEX; dict["status"] = VERSION_STATUS; dict["build"] = VERSION_BUILD; - dict["year"] = VERSION_YEAR; String hash = String(VERSION_HASH); dict["hash"] = hash.is_empty() ? String("unknown") : hash; + dict["timestamp"] = VERSION_TIMESTAMP; + String stringver = String(dict["major"]) + "." + String(dict["minor"]); if ((int)dict["patch"] != 0) { stringver += "." + String(dict["patch"]); @@ -176,14 +188,14 @@ TypedArray<Dictionary> Engine::get_copyright_info() const { Dictionary Engine::get_donor_info() const { Dictionary donors; - donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLATINUM); - donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD); - donors["silver_sponsors"] = array_from_info(DONORS_SPONSOR_SILVER); - donors["bronze_sponsors"] = array_from_info(DONORS_SPONSOR_BRONZE); - donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI); - donors["gold_donors"] = array_from_info(DONORS_GOLD); - donors["silver_donors"] = array_from_info(DONORS_SILVER); - donors["bronze_donors"] = array_from_info(DONORS_BRONZE); + donors["patrons"] = array_from_info(DONORS_PATRONS); + donors["platinum_sponsors"] = array_from_info(DONORS_SPONSORS_PLATINUM); + donors["gold_sponsors"] = array_from_info(DONORS_SPONSORS_GOLD); + donors["silver_sponsors"] = array_from_info(DONORS_SPONSORS_SILVER); + donors["diamond_members"] = array_from_info(DONORS_MEMBERS_DIAMOND); + donors["titanium_members"] = array_from_info(DONORS_MEMBERS_TITANIUM); + donors["platinum_members"] = array_from_info(DONORS_MEMBERS_PLATINUM); + donors["gold_members"] = array_from_info(DONORS_MEMBERS_GOLD); return donors; } @@ -259,6 +271,18 @@ bool Engine::is_printing_error_messages() const { return CoreGlobals::print_error_enabled; } +void Engine::print_header(const String &p_string) const { + if (_print_header) { + print_line(p_string); + } +} + +void Engine::print_header_rich(const String &p_string) const { + if (_print_header) { + print_line_rich(p_string); + } +} + void Engine::add_singleton(const Singleton &p_singleton) { ERR_FAIL_COND_MSG(singleton_ptrs.has(p_singleton.name), vformat("Can't register singleton '%s' because it already exists.", p_singleton.name)); singletons.push_back(p_singleton); @@ -351,6 +375,11 @@ Engine *Engine::get_singleton() { return singleton; } +bool Engine::notify_frame_server_synced() { + frame_server_synced = true; + return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING; +} + Engine::Engine() { singleton = this; } diff --git a/core/config/engine.h b/core/config/engine.h index b64309a9e8..8dece803e3 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -84,11 +84,17 @@ private: bool project_manager_hint = false; bool extension_reloading = false; + bool _print_header = true; + static Engine *singleton; String write_movie_path; String shader_cache_path; + static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5; + int server_syncs = 0; + bool frame_server_synced = false; + public: static Engine *get_singleton(); @@ -123,6 +129,8 @@ public: void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; + void print_header(const String &p_string) const; + void print_header_rich(const String &p_string) const; void set_frame_delay(uint32_t p_msec); uint32_t get_frame_delay() const; @@ -175,6 +183,9 @@ public: bool is_generate_spirv_debug_info_enabled() const; int32_t get_gpu_index() const; + void increment_frames_drawn(); + bool notify_frame_server_synced(); + Engine(); virtual ~Engine() {} }; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index cbbfe3de75..869fef23a5 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -38,7 +38,10 @@ #include "core/io/file_access.h" #include "core/io/file_access_pack.h" #include "core/io/marshalls.h" +#include "core/io/resource_uid.h" +#include "core/object/script_language.h" #include "core/os/keyboard.h" +#include "core/templates/rb_set.h" #include "core/variant/typed_array.h" #include "core/variant/variant_parser.h" #include "core/version.h" @@ -95,7 +98,7 @@ const PackedStringArray ProjectSettings::_get_supported_features() { features.append(VERSION_FULL_CONFIG); features.append(VERSION_FULL_BUILD); -#ifdef VULKAN_ENABLED +#ifdef RD_ENABLED features.append("Forward Plus"); features.append("Mobile"); #endif @@ -281,6 +284,11 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { if (autoloads.has(node_name)) { remove_autoload(node_name); } + } else if (p_name.operator String().begins_with("global_group/")) { + String group_name = p_name.operator String().get_slice("/", 1); + if (global_groups.has(group_name)) { + remove_global_group(group_name); + } } } else { if (p_name == CoreStringNames::get_singleton()->_custom_features) { @@ -327,6 +335,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) { autoload.path = path; } add_autoload(autoload); + } else if (p_name.operator String().begins_with("global_group/")) { + String group_name = p_name.operator String().get_slice("/", 1); + add_global_group(group_name, p_value); } } @@ -469,6 +480,14 @@ bool ProjectSettings::_load_resource_pack(const String &p_pack, bool p_replace_f return false; } + if (project_loaded) { + // This pack may have declared new global classes (make sure they are picked up). + refresh_global_class_list(); + + // This pack may have defined new UIDs, make sure they are cached. + ResourceUID::get_singleton()->load_from_cache(false); + } + //if data.pck is found, all directory access will be from here DirAccess::make_default<DirAccessPack>(DirAccess::ACCESS_RESOURCES); using_datapack = true; @@ -674,11 +693,13 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level"); + load_scene_groups_cache(); + project_loaded = err == OK; return err; } -bool ProjectSettings::has_setting(String p_var) const { +bool ProjectSettings::has_setting(const String &p_var) const { _THREAD_SAFE_METHOD_ return props.has(p_var); @@ -852,8 +873,8 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S } if (!p_custom_features.is_empty()) { + // Store how many properties are saved, add one for custom features, which must always go first. file->store_32(count + 1); - //store how many properties are saved, add one for custom featuers, which must always go first String key = CoreStringNames::get_singleton()->_custom_features; file->store_pascal_string(key); @@ -870,7 +891,8 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S file->store_buffer(buff.ptr(), buff.size()); } else { - file->store_32(count); //store how many properties are saved + // Store how many properties are saved. + file->store_32(count); } for (const KeyValue<String, List<String>> &E : p_props) { @@ -960,7 +982,7 @@ Error ProjectSettings::_save_custom_bnd(const String &p_file) { // add other par } #ifdef TOOLS_ENABLED -bool _csproj_exists(String p_root_dir) { +bool _csproj_exists(const String &p_root_dir) { Ref<DirAccess> dir = DirAccess::open(p_root_dir); ERR_FAIL_COND_V(dir.is_null(), false); @@ -995,7 +1017,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } } // Check for the existence of a csproj file. - if (_csproj_exists(get_resource_path())) { + if (_csproj_exists(p_path.get_base_dir())) { // If there is a csproj file, add the C# feature if it doesn't already exist. if (!project_features.has("C#")) { project_features.append("C#"); @@ -1082,7 +1104,7 @@ Error ProjectSettings::save_custom(const String &p_path, const CustomMap &p_cust } else if (p_path.ends_with(".binary")) { return _save_settings_binary(p_path, save_props, p_custom, save_features); } else { - ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path + "."); + ERR_FAIL_V_MSG(ERR_FILE_UNRECOGNIZED, "Unknown config file format: " + p_path); } } @@ -1177,6 +1199,19 @@ Variant ProjectSettings::get_setting(const String &p_setting, const Variant &p_d } } +void ProjectSettings::refresh_global_class_list() { + // This is called after mounting a new PCK file to pick up class changes. + is_global_class_list_loaded = false; // Make sure we read from the freshly mounted PCK. + Array script_classes = get_global_class_list(); + for (int i = 0; i < script_classes.size(); i++) { + Dictionary c = script_classes[i]; + if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { + continue; + } + ScriptServer::add_global_class(c["class"], c["base"], c["language"], c["path"]); + } +} + TypedArray<Dictionary> ProjectSettings::get_global_class_list() { if (is_global_class_list_loaded) { return global_class_list; @@ -1240,6 +1275,93 @@ ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_ return autoloads[p_name]; } +const HashMap<StringName, String> &ProjectSettings::get_global_groups_list() const { + return global_groups; +} + +void ProjectSettings::add_global_group(const StringName &p_name, const String &p_description) { + ERR_FAIL_COND_MSG(p_name == StringName(), "Trying to add global group with no name."); + global_groups[p_name] = p_description; +} + +void ProjectSettings::remove_global_group(const StringName &p_name) { + ERR_FAIL_COND_MSG(!global_groups.has(p_name), "Trying to remove non-existent global group."); + global_groups.erase(p_name); +} + +bool ProjectSettings::has_global_group(const StringName &p_name) const { + return global_groups.has(p_name); +} + +void ProjectSettings::remove_scene_groups_cache(const StringName &p_path) { + scene_groups_cache.erase(p_path); +} + +void ProjectSettings::add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache) { + scene_groups_cache[p_path] = p_cache; +} + +void ProjectSettings::save_scene_groups_cache() { + Ref<ConfigFile> cf; + cf.instantiate(); + for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) { + if (E.value.is_empty()) { + continue; + } + Array list; + for (const StringName &group : E.value) { + list.push_back(group); + } + cf->set_value(E.key, "groups", list); + } + cf->save(get_scene_groups_cache_path()); +} + +String ProjectSettings::get_scene_groups_cache_path() const { + return get_project_data_path().path_join("scene_groups_cache.cfg"); +} + +void ProjectSettings::load_scene_groups_cache() { + Ref<ConfigFile> cf; + cf.instantiate(); + if (cf->load(get_scene_groups_cache_path()) == OK) { + List<String> scene_paths; + cf->get_sections(&scene_paths); + for (const String &E : scene_paths) { + Array scene_groups = cf->get_value(E, "groups", Array()); + HashSet<StringName> cache; + for (const Variant &scene_group : scene_groups) { + cache.insert(scene_group); + } + add_scene_groups_cache(E, cache); + } + } +} + +const HashMap<StringName, HashSet<StringName>> &ProjectSettings::get_scene_groups_cache() const { + return scene_groups_cache; +} + +#ifdef TOOLS_ENABLED +void ProjectSettings::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + if (p_idx == 0) { + if (pf == "has_setting" || pf == "set_setting" || pf == "get_setting" || pf == "get_setting_with_override" || + pf == "set_order" || pf == "get_order" || pf == "set_initial_value" || pf == "set_as_basic" || + pf == "set_as_internal" || pf == "set_restart_if_changed" || pf == "clear") { + for (const KeyValue<StringName, VariantContainer> &E : props) { + if (E.value.hide_from_editor) { + continue; + } + + r_options->push_back(String(E.key).quote()); + } + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif + void ProjectSettings::_bind_methods() { ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting); ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting); @@ -1294,6 +1416,19 @@ ProjectSettings::ProjectSettings() { CRASH_COND_MSG(singleton != nullptr, "Instantiating a new ProjectSettings singleton is not supported."); singleton = this; +#ifdef TOOLS_ENABLED + // Available only at runtime in editor builds. Needs to be processed before anything else to work properly. + if (!Engine::get_singleton()->is_editor_hint()) { + String editor_features = OS::get_singleton()->get_environment("GODOT_EDITOR_CUSTOM_FEATURES"); + if (!editor_features.is_empty()) { + PackedStringArray feature_list = editor_features.split(","); + for (const String &s : feature_list) { + custom_features.insert(s); + } + } + } +#endif + GLOBAL_DEF_BASIC("application/config/name", ""); GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()); GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), ""); @@ -1302,6 +1437,8 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/run/main_scene", PROPERTY_HINT_FILE, "*.tscn,*.scn,*.res"), ""); GLOBAL_DEF("application/run/disable_stdout", false); GLOBAL_DEF("application/run/disable_stderr", false); + GLOBAL_DEF("application/run/print_header", true); + GLOBAL_DEF("application/run/enable_alt_space_menu", false); GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true); GLOBAL_DEF("application/config/use_custom_user_dir", false); GLOBAL_DEF("application/config/custom_user_dir_name", ""); @@ -1315,8 +1452,8 @@ ProjectSettings::ProjectSettings() { // - Have a 16:9 aspect ratio, // - Have both dimensions divisible by 8 to better play along with video recording, // - Be displayable correctly in windowed mode on a 1366×768 display (tested on Windows 10 with default settings). - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_width", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 1152); // 8K resolution - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_height", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 648); // 8K resolution + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_width", PROPERTY_HINT_RANGE, "1,7680,1,or_greater"), 1152); // 8K resolution + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_height", PROPERTY_HINT_RANGE, "1,4320,1,or_greater"), 648); // 8K resolution GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen,Exclusive Fullscreen"), 0); @@ -1338,6 +1475,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true); GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false); + GLOBAL_DEF("animation/warnings/check_invalid_track_paths", true); + GLOBAL_DEF("animation/warnings/check_angle_interpolation_type_conflicting", true); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "audio/buses/default_bus_layout", PROPERTY_HINT_FILE, "*.tres"), "res://default_bus_layout.tres"); GLOBAL_DEF_RST("audio/general/text_to_speech", false); GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f); @@ -1384,20 +1524,34 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF("debug/settings/crash_handler/message.editor", String("Please include this when reporting the bug on: https://github.com/godotengine/godot/issues")); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2); - GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes. + GLOBAL_DEF_RST("rendering/occlusion_culling/jitter_projection", true); + GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false); - GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0); + GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0); + GLOBAL_DEF_BASIC("internationalization/rendering/root_node_auto_translate", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000); GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true); GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true); - GLOBAL_DEF("rendering/rendering_device/staging_buffer/block_size_kb", 256); - GLOBAL_DEF("rendering/rendering_device/staging_buffer/max_size_mb", 128); - GLOBAL_DEF("rendering/rendering_device/staging_buffer/texture_upload_region_size_px", 64); - GLOBAL_DEF("rendering/rendering_device/pipeline_cache/save_chunk_size_mb", 3.0); - GLOBAL_DEF("rendering/rendering_device/vulkan/max_descriptors_per_pool", 64); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/frame_queue_size", PROPERTY_HINT_RANGE, "2,3,1"), 2); + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/swapchain_image_count", PROPERTY_HINT_RANGE, "2,4,1"), 3); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); + GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true); + GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0); + GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64); + + GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame", 16384); + custom_prop_info["rendering/rendering_device/d3d12/max_resource_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_resource_descriptors_per_frame", PROPERTY_HINT_RANGE, "512,262144"); + GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", 1024); + custom_prop_info["rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", PROPERTY_HINT_RANGE, "256,2048"); + GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", 512); + custom_prop_info["rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", PROPERTY_HINT_RANGE, "32,4096"); + + GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/agility_sdk_version"), 610); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1); GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0); @@ -1409,10 +1563,19 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_INTERNAL("internationalization/locale/translation_remaps", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translations", PackedStringArray()); GLOBAL_DEF_INTERNAL("internationalization/locale/translations_pot_files", PackedStringArray()); + GLOBAL_DEF_INTERNAL("internationalization/locale/translation_add_builtin_strings_to_pot", false); ProjectSettings::get_singleton()->add_hidden_prefix("input/"); } +ProjectSettings::ProjectSettings(const String &p_path) { + if (load_custom(p_path) == OK) { + project_loaded = true; + } +} + ProjectSettings::~ProjectSettings() { - singleton = nullptr; + if (singleton == this) { + singleton = nullptr; + } } diff --git a/core/config/project_settings.h b/core/config/project_settings.h index 302df7e8d0..922c88c151 100644 --- a/core/config/project_settings.h +++ b/core/config/project_settings.h @@ -32,10 +32,6 @@ #define PROJECT_SETTINGS_H #include "core/object/class_db.h" -#include "core/os/thread_safe.h" -#include "core/templates/hash_map.h" -#include "core/templates/local_vector.h" -#include "core/templates/rb_set.h" template <typename T> class TypedArray; @@ -106,6 +102,8 @@ protected: LocalVector<String> hidden_prefixes; HashMap<StringName, AutoloadInfo> autoloads; + HashMap<StringName, String> global_groups; + HashMap<StringName, HashSet<StringName>> scene_groups_cache; Array global_class_list; bool is_global_class_list_loaded = false; @@ -156,10 +154,11 @@ public: void set_setting(const String &p_setting, const Variant &p_value); Variant get_setting(const String &p_setting, const Variant &p_default_value = Variant()) const; TypedArray<Dictionary> get_global_class_list(); + void refresh_global_class_list(); void store_global_class_list(const Array &p_classes); String get_global_class_list_path() const; - bool has_setting(String p_var) const; + bool has_setting(const String &p_var) const; String localize_path(const String &p_path) const; String globalize_path(const String &p_path) const; @@ -208,7 +207,24 @@ public: bool has_autoload(const StringName &p_autoload) const; AutoloadInfo get_autoload(const StringName &p_name) const; + const HashMap<StringName, String> &get_global_groups_list() const; + void add_global_group(const StringName &p_name, const String &p_description); + void remove_global_group(const StringName &p_name); + bool has_global_group(const StringName &p_name) const; + + const HashMap<StringName, HashSet<StringName>> &get_scene_groups_cache() const; + void add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache); + void remove_scene_groups_cache(const StringName &p_path); + void save_scene_groups_cache(); + String get_scene_groups_cache_path() const; + void load_scene_groups_cache(); + +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif + ProjectSettings(); + ProjectSettings(const String &p_path); ~ProjectSettings(); }; diff --git a/core/core_bind.cpp b/core/core_bind.cpp index 981d9b0025..467b696eae 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -145,17 +145,17 @@ void ResourceLoader::_bind_methods() { BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE); BIND_ENUM_CONSTANT(CACHE_MODE_REUSE); BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE); + BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE_DEEP); + BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE_DEEP); } ////// ResourceSaver ////// Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, BitField<SaverFlags> p_flags) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + p_path + "'."); return ::ResourceSaver::save(p_resource, p_path, p_flags); } Vector<String> ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_resource) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), Vector<String>(), "It's not a reference to a valid Resource object."); List<String> exts; ::ResourceSaver::get_recognized_extensions(p_resource, &exts); Vector<String> ret; @@ -257,7 +257,7 @@ String OS::get_executable_path() const { return ::OS::get_singleton()->get_executable_path(); } -Error OS::shell_open(String p_uri) { +Error OS::shell_open(const String &p_uri) { if (p_uri.begins_with("res://")) { WARN_PRINT("Attempting to open an URL with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_open()`."); } else if (p_uri.begins_with("user://")) { @@ -266,7 +266,7 @@ Error OS::shell_open(String p_uri) { return ::OS::get_singleton()->shell_open(p_uri); } -Error OS::shell_show_in_file_manager(String p_path, bool p_open_folder) { +Error OS::shell_show_in_file_manager(const String &p_path, bool p_open_folder) { if (p_path.begins_with("res://")) { WARN_PRINT("Attempting to explore file path with the \"res://\" protocol. Use `ProjectSettings.globalize_path()` to convert a Godot-specific path to a system path before opening it with `OS.shell_show_in_file_manager()`."); } else if (p_path.begins_with("user://")) { @@ -281,8 +281,8 @@ String OS::read_string_from_stdin() { int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) { List<String> args; - for (int i = 0; i < p_arguments.size(); i++) { - args.push_back(p_arguments[i]); + for (const String &arg : p_arguments) { + args.push_back(arg); } String pipe; int exitcode = 0; @@ -294,10 +294,18 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r return exitcode; } +Dictionary OS::execute_with_pipe(const String &p_path, const Vector<String> &p_arguments) { + List<String> args; + for (const String &arg : p_arguments) { + args.push_back(arg); + } + return ::OS::get_singleton()->execute_with_pipe(p_path, args); +} + int OS::create_instance(const Vector<String> &p_arguments) { List<String> args; - for (int i = 0; i < p_arguments.size(); i++) { - args.push_back(p_arguments[i]); + for (const String &arg : p_arguments) { + args.push_back(arg); } ::OS::ProcessID pid = 0; Error err = ::OS::get_singleton()->create_instance(args, &pid); @@ -309,8 +317,8 @@ int OS::create_instance(const Vector<String> &p_arguments) { int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) { List<String> args; - for (int i = 0; i < p_arguments.size(); i++) { - args.push_back(p_arguments[i]); + for (const String &arg : p_arguments) { + args.push_back(arg); } ::OS::ProcessID pid = 0; Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console); @@ -328,6 +336,10 @@ bool OS::is_process_running(int p_pid) const { return ::OS::get_singleton()->is_process_running(p_pid); } +int OS::get_process_exit_code(int p_pid) const { + return ::OS::get_singleton()->get_process_exit_code(p_pid); +} + int OS::get_process_id() const { return ::OS::get_singleton()->get_process_id(); } @@ -585,12 +597,14 @@ void OS::_bind_methods() { ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path); ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin); ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::execute_with_pipe); ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false)); ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance); ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open); ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true)); ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running); + ClassDB::bind_method(D_METHOD("get_process_exit_code", "pid"), &OS::get_process_exit_code); ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); @@ -662,6 +676,7 @@ void OS::_bind_methods() { BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN); BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3); + BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12); BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP); BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM); @@ -1039,6 +1054,10 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const return ::Geometry3D::clip_polygon(p_points, p_plane); } +Vector<int32_t> Geometry3D::tetrahedralize_delaunay(const Vector<Vector3> &p_points) { + return ::Geometry3D::tetrahedralize_delaunay(p_points); +} + void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points); ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes); @@ -1060,6 +1079,7 @@ void Geometry3D::_bind_methods() { ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &Geometry3D::segment_intersects_convex); ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &Geometry3D::clip_polygon); + ClassDB::bind_method(D_METHOD("tetrahedralize_delaunay", "points"), &Geometry3D::tetrahedralize_delaunay); } ////// Marshalls ////// @@ -1369,11 +1389,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const { } } -bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const { +bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const { return ::ClassDB::has_signal(p_class, p_signal); } -Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const { +Dictionary ClassDB::class_get_signal(const StringName &p_class, const StringName &p_signal) const { MethodInfo signal; if (::ClassDB::get_signal(p_class, p_signal, &signal)) { return signal.operator Dictionary(); @@ -1382,7 +1402,7 @@ Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) co } } -TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_signal_list(const StringName &p_class, bool p_no_inheritance) const { List<MethodInfo> signals; ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1394,7 +1414,7 @@ TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p return ret; } -TypedArray<Dictionary> ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_class, bool p_no_inheritance) const { List<PropertyInfo> plist; ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1422,11 +1442,24 @@ Error ClassDB::class_set_property(Object *p_object, const StringName &p_property return OK; } -bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { +Variant ClassDB::class_get_property_default_value(const StringName &p_class, const StringName &p_property) const { + bool valid; + Variant ret = ::ClassDB::class_get_default_property_value(p_class, p_property, &valid); + if (valid) { + return ret; + } + return Variant(); +} + +bool ClassDB::class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) const { return ::ClassDB::has_method(p_class, p_method, p_no_inheritance); } -TypedArray<Dictionary> ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const { +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); TypedArray<Dictionary> ret; @@ -1507,10 +1540,38 @@ StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, c return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); } -bool ClassDB::is_class_enabled(StringName p_class) const { +bool ClassDB::is_class_enum_bitfield(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance) const { + return ::ClassDB::is_enum_bitfield(p_class, p_enum, p_no_inheritance); +} + +bool ClassDB::is_class_enabled(const StringName &p_class) const { return ::ClassDB::is_class_enabled(p_class); } +#ifdef TOOLS_ENABLED +void ClassDB::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + bool first_argument_is_class = false; + if (p_idx == 0) { + first_argument_is_class = (pf == "get_inheriters_from_class" || pf == "get_parent_class" || + pf == "class_exists" || pf == "can_instantiate" || pf == "instantiate" || + pf == "class_has_signal" || pf == "class_get_signal" || pf == "class_get_signal_list" || + pf == "class_get_property_list" || pf == "class_get_property" || pf == "class_set_property" || + pf == "class_has_method" || pf == "class_get_method_list" || + pf == "class_get_integer_constant_list" || pf == "class_has_integer_constant" || pf == "class_get_integer_constant" || + pf == "class_has_enum" || pf == "class_get_enum_list" || pf == "class_get_enum_constants" || pf == "class_get_integer_constant_enum" || + pf == "is_class_enabled" || pf == "is_class_enum_bitfield"); + } + if (first_argument_is_class || pf == "is_parent_class") { + for (const String &E : get_class_list()) { + r_options->push_back(E.quote()); + } + } + + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif + void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("get_class_list"), &ClassDB::get_class_list); ::ClassDB::bind_method(D_METHOD("get_inheriters_from_class", "class"), &ClassDB::get_inheriters_from_class); @@ -1528,8 +1589,12 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("class_get_property", "object", "property"), &ClassDB::class_get_property); ::ClassDB::bind_method(D_METHOD("class_set_property", "object", "property", "value"), &ClassDB::class_set_property); + ::ClassDB::bind_method(D_METHOD("class_get_property_default_value", "class", "property"), &ClassDB::class_get_property_default_value); + ::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)); @@ -1542,6 +1607,8 @@ void ClassDB::_bind_methods() { ::ClassDB::bind_method(D_METHOD("class_get_enum_constants", "class", "enum", "no_inheritance"), &ClassDB::class_get_enum_constants, DEFVAL(false)); ::ClassDB::bind_method(D_METHOD("class_get_integer_constant_enum", "class", "name", "no_inheritance"), &ClassDB::class_get_integer_constant_enum, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("is_class_enum_bitfield", "class", "enum", "no_inheritance"), &ClassDB::is_class_enum_bitfield, DEFVAL(false)); + ::ClassDB::bind_method(D_METHOD("is_class_enabled", "class"), &ClassDB::is_class_enabled); } @@ -1717,6 +1784,18 @@ bool Engine::is_printing_error_messages() const { return ::Engine::get_singleton()->is_printing_error_messages(); } +#ifdef TOOLS_ENABLED +void Engine::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + if (p_idx == 0 && (pf == "has_singleton" || pf == "get_singleton" || pf == "unregister_singleton")) { + for (const String &E : get_singleton_list()) { + r_options->push_back(E.quote()); + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif + void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_physics_ticks_per_second", "physics_ticks_per_second"), &Engine::set_physics_ticks_per_second); ClassDB::bind_method(D_METHOD("get_physics_ticks_per_second"), &Engine::get_physics_ticks_per_second); diff --git a/core/core_bind.h b/core/core_bind.h index 5f51b64eb7..148e0ad83e 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -63,9 +63,11 @@ public: }; enum CacheMode { - CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource. - CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available. - CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk. + CACHE_MODE_IGNORE, + CACHE_MODE_REUSE, + CACHE_MODE_REPLACE, + CACHE_MODE_IGNORE_DEEP, + CACHE_MODE_REPLACE_DEEP, }; static ResourceLoader *get_singleton() { return singleton; } @@ -129,6 +131,7 @@ public: enum RenderingDriver { RENDERING_DRIVER_VULKAN, RENDERING_DRIVER_OPENGL3, + RENDERING_DRIVER_D3D12, }; virtual PackedStringArray get_connected_midi_inputs(); @@ -153,13 +156,15 @@ public: String get_executable_path() const; String read_string_from_stdin(); int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false); + Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments); int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false); int create_instance(const Vector<String> &p_arguments); Error kill(int p_pid); - Error shell_open(String p_uri); - Error shell_show_in_file_manager(String p_path, bool p_open_folder = true); + Error shell_open(const String &p_uri); + Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true); bool is_process_running(int p_pid) const; + int get_process_exit_code(int p_pid) const; int get_process_id() const; void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>()); @@ -336,6 +341,7 @@ public: Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const TypedArray<Plane> &p_planes); Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane); + Vector<int32_t> tetrahedralize_delaunay(const Vector<Vector3> &p_points); Geometry3D() { singleton = this; } }; @@ -433,17 +439,21 @@ public: bool can_instantiate(const StringName &p_class) const; Variant instantiate(const StringName &p_class) const; - bool class_has_signal(StringName p_class, StringName p_signal) const; - Dictionary class_get_signal(StringName p_class, StringName p_signal) const; - TypedArray<Dictionary> class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const; + bool class_has_signal(const StringName &p_class, const StringName &p_signal) const; + Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const; + TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const; - TypedArray<Dictionary> class_get_property_list(StringName p_class, bool p_no_inheritance = false) const; + TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const; Variant class_get_property(Object *p_object, const StringName &p_property) const; Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; - bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; + Variant class_get_property_default_value(const StringName &p_class, const StringName &p_property) const; - TypedArray<Dictionary> class_get_method_list(StringName p_class, bool p_no_inheritance = false) const; + 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; bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; @@ -454,7 +464,13 @@ public: PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; - bool is_class_enabled(StringName p_class) const; + bool is_class_enum_bitfield(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; + + bool is_class_enabled(const StringName &p_class) const; + +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif ClassDB() {} ~ClassDB() {} @@ -526,6 +542,10 @@ public: void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif + Engine() { singleton = this; } }; diff --git a/core/core_builders.py b/core/core_builders.py index e40ebbb14d..a401f03693 100644 --- a/core/core_builders.py +++ b/core/core_builders.py @@ -1,11 +1,7 @@ -"""Functions used to generate source files during build time +"""Functions used to generate source files during build time""" -All such functions are invoked in a subprocess on Windows to prevent build flakiness. -""" import zlib -from platform_methods import subprocess_main - def escape_string(s): def charcode_to_c_escapes(c): @@ -28,37 +24,33 @@ def escape_string(s): def make_certs_header(target, source, env): - src = source[0] - dst = target[0] - f = open(src, "rb") - g = open(dst, "w", encoding="utf-8") - buf = f.read() - decomp_size = len(buf) - - # Use maximum zlib compression level to further reduce file size - # (at the cost of initial build times). - buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef CERTS_COMPRESSED_GEN_H\n") - g.write("#define CERTS_COMPRESSED_GEN_H\n") - - # System certs path. Editor will use them if defined. (for package maintainers) - path = env["system_certs_path"] - g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path)) - if env["builtin_certs"]: - # Defined here and not in env so changing it does not trigger a full rebuild. - g.write("#define BUILTIN_CERTS_ENABLED\n") - g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") - g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") - g.write("static const unsigned char _certs_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + str(buf[i]) + ",\n") - g.write("};\n") - g.write("#endif // CERTS_COMPRESSED_GEN_H") - - g.close() - f.close() + src = str(source[0]) + dst = str(target[0]) + with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: + buf = f.read() + decomp_size = len(buf) + + # Use maximum zlib compression level to further reduce file size + # (at the cost of initial build times). + buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef CERTS_COMPRESSED_GEN_H\n") + g.write("#define CERTS_COMPRESSED_GEN_H\n") + + # System certs path. Editor will use them if defined. (for package maintainers) + path = env["system_certs_path"] + g.write('#define _SYSTEM_CERTS_PATH "%s"\n' % str(path)) + if env["builtin_certs"]: + # Defined here and not in env so changing it does not trigger a full rebuild. + g.write("#define BUILTIN_CERTS_ENABLED\n") + g.write("static const int _certs_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _certs_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _certs_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + str(buf[i]) + ",\n") + g.write("};\n") + g.write("#endif // CERTS_COMPRESSED_GEN_H") def make_authors_header(target, source, env): @@ -75,112 +67,102 @@ def make_authors_header(target, source, env): "AUTHORS_DEVELOPERS", ] - src = source[0] - dst = target[0] - f = open(src, "r", encoding="utf-8") - g = open(dst, "w", encoding="utf-8") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef AUTHORS_GEN_H\n") - g.write("#define AUTHORS_GEN_H\n") + src = str(source[0]) + dst = str(target[0]) + with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef AUTHORS_GEN_H\n") + g.write("#define AUTHORS_GEN_H\n") - reading = False + reading = False - def close_section(): - g.write("\t0\n") - g.write("};\n") + def close_section(): + g.write("\t0\n") + g.write("};\n") - for line in f: - if reading: - if line.startswith(" "): - g.write('\t"' + escape_string(line.strip()) + '",\n') - continue - if line.startswith("## "): + for line in f: if reading: - close_section() - reading = False - for section, section_id in zip(sections, sections_id): - if line.strip().endswith(section): - current_section = escape_string(section_id) - reading = True - g.write("const char *const " + current_section + "[] = {\n") - break - - if reading: - close_section() + if line.startswith(" "): + g.write('\t"' + escape_string(line.strip()) + '",\n') + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break - g.write("#endif // AUTHORS_GEN_H\n") + if reading: + close_section() - g.close() - f.close() + g.write("#endif // AUTHORS_GEN_H\n") def make_donors_header(target, source, env): sections = [ + "Patrons", "Platinum sponsors", "Gold sponsors", "Silver sponsors", - "Bronze sponsors", - "Mini sponsors", - "Gold donors", - "Silver donors", - "Bronze donors", + "Diamond members", + "Titanium members", + "Platinum members", + "Gold members", ] sections_id = [ - "DONORS_SPONSOR_PLATINUM", - "DONORS_SPONSOR_GOLD", - "DONORS_SPONSOR_SILVER", - "DONORS_SPONSOR_BRONZE", - "DONORS_SPONSOR_MINI", - "DONORS_GOLD", - "DONORS_SILVER", - "DONORS_BRONZE", + "DONORS_PATRONS", + "DONORS_SPONSORS_PLATINUM", + "DONORS_SPONSORS_GOLD", + "DONORS_SPONSORS_SILVER", + "DONORS_MEMBERS_DIAMOND", + "DONORS_MEMBERS_TITANIUM", + "DONORS_MEMBERS_PLATINUM", + "DONORS_MEMBERS_GOLD", ] - src = source[0] - dst = target[0] - f = open(src, "r", encoding="utf-8") - g = open(dst, "w", encoding="utf-8") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#ifndef DONORS_GEN_H\n") - g.write("#define DONORS_GEN_H\n") - - reading = False + src = str(source[0]) + dst = str(target[0]) + with open(src, "r", encoding="utf-8") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#ifndef DONORS_GEN_H\n") + g.write("#define DONORS_GEN_H\n") + + reading = False + + def close_section(): + g.write("\t0\n") + g.write("};\n") + + for line in f: + if reading >= 0: + if line.startswith(" "): + g.write('\t"' + escape_string(line.strip()) + '",\n') + continue + if line.startswith("## "): + if reading: + close_section() + reading = False + for section, section_id in zip(sections, sections_id): + if line.strip().endswith(section): + current_section = escape_string(section_id) + reading = True + g.write("const char *const " + current_section + "[] = {\n") + break - def close_section(): - g.write("\t0\n") - g.write("};\n") - - for line in f: - if reading >= 0: - if line.startswith(" "): - g.write('\t"' + escape_string(line.strip()) + '",\n') - continue - if line.startswith("## "): - if reading: - close_section() - reading = False - for section, section_id in zip(sections, sections_id): - if line.strip().endswith(section): - current_section = escape_string(section_id) - reading = True - g.write("const char *const " + current_section + "[] = {\n") - break - - if reading: - close_section() - - g.write("#endif // DONORS_GEN_H\n") + if reading: + close_section() - g.close() - f.close() + g.write("#endif // DONORS_GEN_H\n") def make_license_header(target, source, env): - src_copyright = source[0] - src_license = source[1] - dst = target[0] + src_copyright = str(source[0]) + src_license = str(source[1]) + dst = str(target[0]) class LicenseReader: def __init__(self, license_file): @@ -238,7 +220,7 @@ def make_license_header(target, source, env): part["copyright_index"] = len(data_list) data_list += part["Copyright"] - with open(dst, "w", encoding="utf-8") as f: + with open(dst, "w", encoding="utf-8", newline="\n") as f: f.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") f.write("#ifndef LICENSE_GEN_H\n") f.write("#define LICENSE_GEN_H\n") @@ -331,7 +313,3 @@ def make_license_header(target, source, env): f.write("};\n\n") f.write("#endif // LICENSE_GEN_H\n") - - -if __name__ == "__main__": - subprocess_main(globals()) diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 2f70fdf219..aaabbabfd9 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -507,6 +507,10 @@ void register_global_constants() { BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, KPAD); BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, GROUP_SWITCH); + BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, UNSPECIFIED); + BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, LEFT); + BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, RIGHT); + BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE); BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT); BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT); @@ -845,7 +849,7 @@ bool CoreConstants::is_global_enum(const StringName &p_enum) { return _global_enums.has(p_enum); } -void CoreConstants::get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values) { +void CoreConstants::get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values) { ERR_FAIL_NULL_MSG(p_values, "Trying to get enum values with null map."); ERR_FAIL_COND_MSG(!_global_enums.has(p_enum), "Trying to get values of non-existing enum."); for (const _CoreConstant &constant : _global_enums[p_enum]) { diff --git a/core/core_constants.h b/core/core_constants.h index 51842490c8..82d626c749 100644 --- a/core/core_constants.h +++ b/core/core_constants.h @@ -45,7 +45,7 @@ public: static bool is_global_constant(const StringName &p_name); static int get_global_constant_index(const StringName &p_name); static bool is_global_enum(const StringName &p_enum); - static void get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values); + static void get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values); }; #endif // CORE_CONSTANTS_H diff --git a/core/crypto/SCsub b/core/crypto/SCsub index ac79e10d19..8cff3cf679 100644 --- a/core/crypto/SCsub +++ b/core/crypto/SCsub @@ -21,9 +21,9 @@ if is_builtin or not has_module: # to make a "light" build with only the necessary mbedtls files. if not has_module: # Minimal mbedTLS config file - env_crypto.Append( - CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')] - ) + config_path = "thirdparty/mbedtls/include/godot_core_mbedtls_config.h" + config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' + env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) # Build minimal mbedTLS library (MD5/SHA/Base64/AES). env_thirdparty = env_crypto.Clone() env_thirdparty.disable_warnings() @@ -34,6 +34,7 @@ if not has_module: "constant_time.c", "ctr_drbg.c", "entropy.c", + "md.c", "md5.c", "sha1.c", "sha256.c", @@ -46,9 +47,9 @@ if not has_module: env.core_sources += thirdparty_obj elif is_builtin: # Module mbedTLS config file - env_crypto.Append( - CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')] - ) + config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h" + config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"' + env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)]) # Needed to force rebuilding the core files when the configuration file is updated. thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"] diff --git a/core/crypto/aes_context.cpp b/core/crypto/aes_context.cpp index 8a8d3f875e..7596f4e0e2 100644 --- a/core/crypto/aes_context.cpp +++ b/core/crypto/aes_context.cpp @@ -30,7 +30,7 @@ #include "core/crypto/aes_context.h" -Error AESContext::start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv) { +Error AESContext::start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv) { ERR_FAIL_COND_V_MSG(mode != MODE_MAX, ERR_ALREADY_IN_USE, "AESContext already started. Call 'finish' before starting a new one."); ERR_FAIL_COND_V_MSG(p_mode < 0 || p_mode >= MODE_MAX, ERR_INVALID_PARAMETER, "Invalid mode requested."); // Key check. @@ -52,7 +52,7 @@ Error AESContext::start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv return OK; } -PackedByteArray AESContext::update(PackedByteArray p_src) { +PackedByteArray AESContext::update(const PackedByteArray &p_src) { ERR_FAIL_COND_V_MSG(mode < 0 || mode >= MODE_MAX, PackedByteArray(), "AESContext not started. Call 'start' before calling 'update'."); int len = p_src.size(); ERR_FAIL_COND_V_MSG(len % 16, PackedByteArray(), "The number of bytes to be encrypted must be multiple of 16. Add padding if needed"); diff --git a/core/crypto/aes_context.h b/core/crypto/aes_context.h index c4d26d815a..f6aeab221f 100644 --- a/core/crypto/aes_context.h +++ b/core/crypto/aes_context.h @@ -55,8 +55,8 @@ protected: static void _bind_methods(); public: - Error start(Mode p_mode, PackedByteArray p_key, PackedByteArray p_iv = PackedByteArray()); - PackedByteArray update(PackedByteArray p_src); + Error start(Mode p_mode, const PackedByteArray &p_key, const PackedByteArray &p_iv = PackedByteArray()); + PackedByteArray update(const PackedByteArray &p_src); PackedByteArray get_iv_state(); void finish(); diff --git a/core/crypto/crypto.cpp b/core/crypto/crypto.cpp index 6b1c2a9cb2..7fef819159 100644 --- a/core/crypto/crypto.cpp +++ b/core/crypto/crypto.cpp @@ -124,7 +124,7 @@ HMACContext *HMACContext::create() { /// Crypto -void (*Crypto::_load_default_certificates)(String p_path) = nullptr; +void (*Crypto::_load_default_certificates)(const String &p_path) = nullptr; Crypto *(*Crypto::_create)() = nullptr; Crypto *Crypto::create() { if (_create) { @@ -133,13 +133,13 @@ Crypto *Crypto::create() { ERR_FAIL_V_MSG(nullptr, "Crypto is not available when the mbedtls module is disabled."); } -void Crypto::load_default_certificates(String p_path) { +void Crypto::load_default_certificates(const String &p_path) { if (_load_default_certificates) { _load_default_certificates(p_path); } } -PackedByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, PackedByteArray p_key, PackedByteArray p_msg) { +PackedByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, const PackedByteArray &p_key, const PackedByteArray &p_msg) { Ref<HMACContext> ctx = Ref<HMACContext>(HMACContext::create()); ERR_FAIL_COND_V_MSG(ctx.is_null(), PackedByteArray(), "HMAC is not available without mbedtls module."); Error err = ctx->start(p_hash_type, p_key); @@ -151,7 +151,7 @@ PackedByteArray Crypto::hmac_digest(HashingContext::HashType p_hash_type, Packed // Compares two HMACS for equality without leaking timing information in order to prevent timing attacks. // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy -bool Crypto::constant_time_compare(PackedByteArray p_trusted, PackedByteArray p_received) { +bool Crypto::constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received) { const uint8_t *t = p_trusted.ptr(); const uint8_t *r = p_received.ptr(); int tlen = p_trusted.size(); diff --git a/core/crypto/crypto.h b/core/crypto/crypto.h index 4b5bf8305f..fbd01be86d 100644 --- a/core/crypto/crypto.h +++ b/core/crypto/crypto.h @@ -46,10 +46,10 @@ protected: public: static CryptoKey *create(); - virtual Error load(String p_path, bool p_public_only = false) = 0; - virtual Error save(String p_path, bool p_public_only = false) = 0; + virtual Error load(const String &p_path, bool p_public_only = false) = 0; + virtual Error save(const String &p_path, bool p_public_only = false) = 0; virtual String save_to_string(bool p_public_only = false) = 0; - virtual Error load_from_string(String p_string_key, bool p_public_only = false) = 0; + virtual Error load_from_string(const String &p_string_key, bool p_public_only = false) = 0; virtual bool is_public_only() const = 0; }; @@ -62,9 +62,9 @@ protected: public: static X509Certificate *create(); - virtual Error load(String p_path) = 0; + virtual Error load(const String &p_path) = 0; virtual Error load_from_memory(const uint8_t *p_buffer, int p_len) = 0; - virtual Error save(String p_path) = 0; + virtual Error save(const String &p_path) = 0; virtual String save_to_string() = 0; virtual Error load_from_string(const String &string) = 0; }; @@ -113,8 +113,8 @@ protected: public: static HMACContext *create(); - virtual Error start(HashingContext::HashType p_hash_type, PackedByteArray p_key) = 0; - virtual Error update(PackedByteArray p_data) = 0; + virtual Error start(HashingContext::HashType p_hash_type, const PackedByteArray &p_key) = 0; + virtual Error update(const PackedByteArray &p_data) = 0; virtual PackedByteArray finish() = 0; HMACContext() {} @@ -127,43 +127,43 @@ class Crypto : public RefCounted { protected: static void _bind_methods(); static Crypto *(*_create)(); - static void (*_load_default_certificates)(String p_path); + static void (*_load_default_certificates)(const String &p_path); public: static Crypto *create(); - static void load_default_certificates(String p_path); + static void load_default_certificates(const String &p_path); virtual PackedByteArray generate_random_bytes(int p_bytes) = 0; virtual Ref<CryptoKey> generate_rsa(int p_bytes) = 0; - virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, String p_issuer_name, String p_not_before, String p_not_after) = 0; + virtual Ref<X509Certificate> generate_self_signed_certificate(Ref<CryptoKey> p_key, const String &p_issuer_name, const String &p_not_before, const String &p_not_after) = 0; - virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Ref<CryptoKey> p_key) = 0; - virtual bool verify(HashingContext::HashType p_hash_type, Vector<uint8_t> p_hash, Vector<uint8_t> p_signature, Ref<CryptoKey> p_key) = 0; - virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_plaintext) = 0; - virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, Vector<uint8_t> p_ciphertext) = 0; + virtual Vector<uint8_t> sign(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, Ref<CryptoKey> p_key) = 0; + virtual bool verify(HashingContext::HashType p_hash_type, const Vector<uint8_t> &p_hash, const Vector<uint8_t> &p_signature, Ref<CryptoKey> p_key) = 0; + virtual Vector<uint8_t> encrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_plaintext) = 0; + virtual Vector<uint8_t> decrypt(Ref<CryptoKey> p_key, const Vector<uint8_t> &p_ciphertext) = 0; - PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, PackedByteArray p_key, PackedByteArray p_msg); + PackedByteArray hmac_digest(HashingContext::HashType p_hash_type, const PackedByteArray &p_key, const PackedByteArray &p_msg); // Compares two PackedByteArrays for equality without leaking timing information in order to prevent timing attacks. // @see: https://paragonie.com/blog/2015/11/preventing-timing-attacks-on-string-comparison-with-double-hmac-strategy - bool constant_time_compare(PackedByteArray p_trusted, PackedByteArray p_received); + bool constant_time_compare(const PackedByteArray &p_trusted, const PackedByteArray &p_received); Crypto() {} }; class ResourceFormatLoaderCrypto : public ResourceFormatLoader { public: - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; }; class ResourceFormatSaverCrypto : public ResourceFormatSaver { public: - virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const Ref<Resource> &p_resource) const; + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override; + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override; + virtual bool recognize(const Ref<Resource> &p_resource) const override; }; #endif // CRYPTO_H diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp index 17b34c08e2..69a83284cc 100644 --- a/core/crypto/crypto_core.cpp +++ b/core/crypto/crypto_core.cpp @@ -39,6 +39,9 @@ #include <mbedtls/md5.h> #include <mbedtls/sha1.h> #include <mbedtls/sha256.h> +#if MBEDTLS_VERSION_MAJOR >= 3 +#include <mbedtls/compat-2.x.h> +#endif // RandomGenerator CryptoCore::RandomGenerator::RandomGenerator() { diff --git a/core/crypto/hashing_context.cpp b/core/crypto/hashing_context.cpp index 157a0c091b..01acbdbc17 100644 --- a/core/crypto/hashing_context.cpp +++ b/core/crypto/hashing_context.cpp @@ -47,7 +47,7 @@ Error HashingContext::start(HashType p_type) { return ERR_UNAVAILABLE; } -Error HashingContext::update(PackedByteArray p_chunk) { +Error HashingContext::update(const PackedByteArray &p_chunk) { ERR_FAIL_NULL_V(ctx, ERR_UNCONFIGURED); size_t len = p_chunk.size(); ERR_FAIL_COND_V(len == 0, FAILED); diff --git a/core/crypto/hashing_context.h b/core/crypto/hashing_context.h index 464261935a..ab7affabaa 100644 --- a/core/crypto/hashing_context.h +++ b/core/crypto/hashing_context.h @@ -54,7 +54,7 @@ protected: public: Error start(HashType p_type); - Error update(PackedByteArray p_chunk); + Error update(const PackedByteArray &p_chunk); PackedByteArray finish(); HashingContext() {} diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index 32dc060aa2..a7655c874a 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -127,7 +127,7 @@ void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, singleton->poll_events(true); } -void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints, void (*p_allow_focus_steal_fn)()) { +void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()) { register_uri_handler("tcp://", RemoteDebuggerPeerTCP::create); // TCP is the default protocol. Platforms/modules can add more. if (p_uri.is_empty()) { return; @@ -162,7 +162,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve singleton_script_debugger->set_skip_breakpoints(p_skip_breakpoints); for (int i = 0; i < p_breakpoints.size(); i++) { - String bp = p_breakpoints[i]; + const String &bp = p_breakpoints[i]; int sp = bp.rfind(":"); ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format."); diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index 88d5490794..16050778aa 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -108,7 +108,7 @@ public: _FORCE_INLINE_ static ScriptDebugger *get_script_debugger() { return script_debugger; }; - static void initialize(const String &p_uri, bool p_skip_breakpoints, Vector<String> p_breakpoints, void (*p_allow_focus_steal_fn)()); + static void initialize(const String &p_uri, bool p_skip_breakpoints, const Vector<String> &p_breakpoints, void (*p_allow_focus_steal_fn)()); static void deinitialize(); static void register_profiler(const StringName &p_name, const Profiler &p_profiler); static void unregister_profiler(const StringName &p_name); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index a817ea871d..1973663c72 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -36,6 +36,7 @@ #include "core/debugger/engine_profiler.h" #include "core/debugger/script_debugger.h" #include "core/input/input.h" +#include "core/io/resource_loader.h" #include "core/object/script_language.h" #include "core/os/os.h" @@ -91,7 +92,7 @@ public: } }; -Error RemoteDebugger::_put_msg(String p_message, Array p_data) { +Error RemoteDebugger::_put_msg(const String &p_message, const Array &p_data) { Array msg; msg.push_back(p_message); msg.push_back(Thread::get_caller_id()); @@ -435,9 +436,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { messages.insert(Thread::get_caller_id(), List<Message>()); } - mutex.lock(); while (is_peer_connected()) { - mutex.unlock(); flush_output(); _poll_messages(); @@ -515,8 +514,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { _send_stack_vars(globals, globals_vals, 2); } else if (command == "reload_scripts") { + script_paths_to_reload = data; + } else if (command == "reload_all_scripts") { reload_all_scripts = true; - } else if (command == "breakpoint") { ERR_FAIL_COND(data.size() < 3); bool set = data[2]; @@ -527,7 +527,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { } } else if (command == "set_skip_breakpoints") { - ERR_FAIL_COND(data.size() < 1); + ERR_FAIL_COND(data.is_empty()); script_debugger->set_skip_breakpoints(data[0]); } else { bool captured = false; @@ -591,19 +591,36 @@ void RemoteDebugger::poll_events(bool p_is_idle) { } // Reload scripts during idle poll only. - if (p_is_idle && reload_all_scripts) { - for (int i = 0; i < ScriptServer::get_language_count(); i++) { - ScriptServer::get_language(i)->reload_all_scripts(); + if (p_is_idle) { + if (reload_all_scripts) { + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->reload_all_scripts(); + } + reload_all_scripts = false; + } else if (!script_paths_to_reload.is_empty()) { + Array scripts_to_reload; + for (int i = 0; i < script_paths_to_reload.size(); ++i) { + String path = script_paths_to_reload[i]; + Error err = OK; + Ref<Script> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err); + ERR_CONTINUE_MSG(err != OK, vformat("Could not reload script '%s': %s", path, error_names[err])); + ERR_CONTINUE_MSG(script.is_null(), vformat("Could not reload script '%s': Not a script!", path, error_names[err])); + scripts_to_reload.push_back(script); + } + for (int i = 0; i < ScriptServer::get_language_count(); i++) { + ScriptServer::get_language(i)->reload_scripts(scripts_to_reload, true); + } } - reload_all_scripts = false; + script_paths_to_reload.clear(); } } Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) { r_captured = true; if (p_cmd == "reload_scripts") { + script_paths_to_reload = p_data; + } else if (p_cmd == "reload_all_scripts") { reload_all_scripts = true; - } else if (p_cmd == "breakpoint") { ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA); bool set = p_data[2]; @@ -614,7 +631,7 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo } } else if (p_cmd == "set_skip_breakpoints") { - ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA); script_debugger->set_skip_breakpoints(p_data[0]); } else if (p_cmd == "break") { script_debugger->debug(script_debugger->get_break_language()); @@ -626,7 +643,7 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) { r_captured = false; - ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA); ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA); ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE); Array opts; @@ -648,7 +665,7 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) { // Performance Profiler Object *perf = Engine::get_singleton()->get_singleton_object("Performance"); if (perf) { - performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf))); + performance_profiler.instantiate(perf); performance_profiler->bind("performance"); profiler_enable("performance", true); } diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h index 7c399178c6..e91d09be17 100644 --- a/core/debugger/remote_debugger.h +++ b/core/debugger/remote_debugger.h @@ -74,6 +74,7 @@ private: int warn_count = 0; int last_reset = 0; bool reload_all_scripts = false; + Array script_paths_to_reload; // Make handlers and send_message thread safe. Mutex mutex; @@ -97,7 +98,7 @@ private: static void _err_handler(void *p_this, const char *p_func, const char *p_file, int p_line, const char *p_err, const char *p_descr, bool p_editor_notify, ErrorHandlerType p_type); ErrorMessage _create_overflow_error(const String &p_what, const String &p_descr); - Error _put_msg(String p_message, Array p_data); + Error _put_msg(const String &p_message, const Array &p_data); bool is_peer_connected() { return peer->is_peer_connected(); } void flush_output(); diff --git a/core/doc_data.h b/core/doc_data.h index b8c92a4b67..04bd55eaba 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -114,9 +114,12 @@ public: String qualifiers; String description; bool is_deprecated = false; + String deprecated_message; bool is_experimental = false; + String experimental_message; Vector<ArgumentDoc> arguments; Vector<int> errors_returned; + String keywords; bool operator<(const MethodDoc &p_method) const { if (name == p_method.name) { // Must be an operator or a constructor since there is no other overloading @@ -171,6 +174,7 @@ public: doc.description = p_dict["description"]; } +#ifndef DISABLE_DEPRECATED if (p_dict.has("is_deprecated")) { doc.is_deprecated = p_dict["is_deprecated"]; } @@ -178,6 +182,17 @@ public: if (p_dict.has("is_experimental")) { doc.is_experimental = p_dict["is_experimental"]; } +#endif + + if (p_dict.has("deprecated")) { + doc.is_deprecated = true; + doc.deprecated_message = p_dict["deprecated"]; + } + + if (p_dict.has("experimental")) { + doc.is_experimental = true; + doc.experimental_message = p_dict["experimental"]; + } Array arguments; if (p_dict.has("arguments")) { @@ -195,6 +210,10 @@ public: doc.errors_returned.push_back(errors_returned[i]); } + if (p_dict.has("keywords")) { + doc.keywords = p_dict["keywords"]; + } + return doc; } static Dictionary to_dict(const MethodDoc &p_doc) { @@ -221,9 +240,17 @@ public: dict["description"] = p_doc.description; } - dict["is_deprecated"] = p_doc.is_deprecated; + if (p_doc.is_deprecated) { + dict["deprecated"] = p_doc.deprecated_message; + } - dict["is_experimental"] = p_doc.is_experimental; + if (p_doc.is_experimental) { + dict["experimental"] = p_doc.experimental_message; + } + + if (!p_doc.keywords.is_empty()) { + dict["keywords"] = p_doc.keywords; + } if (!p_doc.arguments.is_empty()) { Array arguments; @@ -253,7 +280,10 @@ public: bool is_bitfield = false; String description; bool is_deprecated = false; + String deprecated_message; bool is_experimental = false; + String experimental_message; + String keywords; bool operator<(const ConstantDoc &p_const) const { return name < p_const.name; } @@ -283,6 +313,7 @@ public: doc.description = p_dict["description"]; } +#ifndef DISABLE_DEPRECATED if (p_dict.has("is_deprecated")) { doc.is_deprecated = p_dict["is_deprecated"]; } @@ -290,6 +321,21 @@ public: if (p_dict.has("is_experimental")) { doc.is_experimental = p_dict["is_experimental"]; } +#endif + + if (p_dict.has("deprecated")) { + doc.is_deprecated = true; + doc.deprecated_message = p_dict["deprecated"]; + } + + if (p_dict.has("experimental")) { + doc.is_experimental = true; + doc.experimental_message = p_dict["experimental"]; + } + + if (p_dict.has("keywords")) { + doc.keywords = p_dict["keywords"]; + } return doc; } @@ -315,9 +361,17 @@ public: dict["description"] = p_doc.description; } - dict["is_deprecated"] = p_doc.is_deprecated; + if (p_doc.is_deprecated) { + dict["deprecated"] = p_doc.deprecated_message; + } + + if (p_doc.is_experimental) { + dict["experimental"] = p_doc.experimental_message; + } - dict["is_experimental"] = p_doc.is_experimental; + if (!p_doc.keywords.is_empty()) { + dict["keywords"] = p_doc.keywords; + } return dict; } @@ -334,7 +388,10 @@ public: bool overridden = false; String overrides; bool is_deprecated = false; + String deprecated_message; bool is_experimental = false; + String experimental_message; + String keywords; bool operator<(const PropertyDoc &p_prop) const { return name.naturalcasecmp_to(p_prop.name) < 0; } @@ -380,6 +437,7 @@ public: doc.overrides = p_dict["overrides"]; } +#ifndef DISABLE_DEPRECATED if (p_dict.has("is_deprecated")) { doc.is_deprecated = p_dict["is_deprecated"]; } @@ -387,6 +445,21 @@ public: if (p_dict.has("is_experimental")) { doc.is_experimental = p_dict["is_experimental"]; } +#endif + + if (p_dict.has("deprecated")) { + doc.is_deprecated = true; + doc.deprecated_message = p_dict["deprecated"]; + } + + if (p_dict.has("experimental")) { + doc.is_experimental = true; + doc.experimental_message = p_dict["experimental"]; + } + + if (p_dict.has("keywords")) { + doc.keywords = p_dict["keywords"]; + } return doc; } @@ -428,9 +501,17 @@ public: dict["overrides"] = p_doc.overrides; } - dict["is_deprecated"] = p_doc.is_deprecated; + if (p_doc.is_deprecated) { + dict["deprecated"] = p_doc.deprecated_message; + } - dict["is_experimental"] = p_doc.is_experimental; + if (p_doc.is_experimental) { + dict["experimental"] = p_doc.experimental_message; + } + + if (!p_doc.keywords.is_empty()) { + dict["keywords"] = p_doc.keywords; + } return dict; } @@ -442,6 +523,7 @@ public: String data_type; String description; String default_value; + String keywords; bool operator<(const ThemeItemDoc &p_theme_item) const { // First sort by the data type, then by name. if (data_type == p_theme_item.data_type) { @@ -472,6 +554,10 @@ public: doc.default_value = p_dict["default_value"]; } + if (p_dict.has("keywords")) { + doc.keywords = p_dict["keywords"]; + } + return doc; } static Dictionary to_dict(const ThemeItemDoc &p_doc) { @@ -497,6 +583,10 @@ public: dict["default_value"] = p_doc.default_value; } + if (!p_doc.keywords.is_empty()) { + dict["keywords"] = p_doc.keywords; + } + return dict; } }; @@ -535,7 +625,9 @@ public: struct EnumDoc { String description; bool is_deprecated = false; + String deprecated_message; bool is_experimental = false; + String experimental_message; static EnumDoc from_dict(const Dictionary &p_dict) { EnumDoc doc; @@ -543,6 +635,7 @@ public: doc.description = p_dict["description"]; } +#ifndef DISABLE_DEPRECATED if (p_dict.has("is_deprecated")) { doc.is_deprecated = p_dict["is_deprecated"]; } @@ -550,6 +643,17 @@ public: if (p_dict.has("is_experimental")) { doc.is_experimental = p_dict["is_experimental"]; } +#endif + + if (p_dict.has("deprecated")) { + doc.is_deprecated = true; + doc.deprecated_message = p_dict["deprecated"]; + } + + if (p_dict.has("experimental")) { + doc.is_experimental = true; + doc.experimental_message = p_dict["experimental"]; + } return doc; } @@ -560,9 +664,13 @@ public: dict["description"] = p_doc.description; } - dict["is_deprecated"] = p_doc.is_deprecated; + if (p_doc.is_deprecated) { + dict["deprecated"] = p_doc.deprecated_message; + } - dict["is_experimental"] = p_doc.is_experimental; + if (p_doc.is_experimental) { + dict["experimental"] = p_doc.experimental_message; + } return dict; } @@ -573,6 +681,7 @@ public: String inherits; String brief_description; String description; + String keywords; Vector<TutorialDoc> tutorials; Vector<MethodDoc> constructors; Vector<MethodDoc> methods; @@ -584,7 +693,9 @@ public: Vector<MethodDoc> annotations; Vector<ThemeItemDoc> theme_properties; bool is_deprecated = false; + String deprecated_message; bool is_experimental = false; + String experimental_message; bool is_script_doc = false; String script_path; bool operator<(const ClassDoc &p_class) const { @@ -609,6 +720,10 @@ public: doc.description = p_dict["description"]; } + if (p_dict.has("keywords")) { + doc.keywords = p_dict["keywords"]; + } + Array tutorials; if (p_dict.has("tutorials")) { tutorials = p_dict["tutorials"]; @@ -689,6 +804,7 @@ public: doc.theme_properties.push_back(ThemeItemDoc::from_dict(theme_properties[i])); } +#ifndef DISABLE_DEPRECATED if (p_dict.has("is_deprecated")) { doc.is_deprecated = p_dict["is_deprecated"]; } @@ -696,6 +812,17 @@ public: if (p_dict.has("is_experimental")) { doc.is_experimental = p_dict["is_experimental"]; } +#endif + + if (p_dict.has("deprecated")) { + doc.is_deprecated = true; + doc.deprecated_message = p_dict["deprecated"]; + } + + if (p_dict.has("experimental")) { + doc.is_experimental = true; + doc.experimental_message = p_dict["experimental"]; + } if (p_dict.has("is_script_doc")) { doc.is_script_doc = p_dict["is_script_doc"]; @@ -806,9 +933,13 @@ public: dict["theme_properties"] = theme_properties; } - dict["is_deprecated"] = p_doc.is_deprecated; + if (p_doc.is_deprecated) { + dict["deprecated"] = p_doc.deprecated_message; + } - dict["is_experimental"] = p_doc.is_experimental; + if (p_doc.is_experimental) { + dict["experimental"] = p_doc.experimental_message; + } dict["is_script_doc"] = p_doc.is_script_doc; @@ -816,6 +947,10 @@ public: dict["script_path"] = p_doc.script_path; } + if (!p_doc.keywords.is_empty()) { + dict["keywords"] = p_doc.keywords; + } + return dict; } }; diff --git a/core/error/error_macros.h b/core/error/error_macros.h index c8182975d5..ab7dbcbd44 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -730,6 +730,16 @@ void _err_flush_stdout(); } else \ ((void)0) +/** + * Warns about `m_msg` only when verbose mode is enabled. + */ +#define WARN_VERBOSE(m_msg) \ + { \ + if (is_print_verbose_enabled()) { \ + WARN_PRINT(m_msg); \ + } \ + } + // Print deprecated warning message macros. /** @@ -812,4 +822,14 @@ void _err_flush_stdout(); #define DEV_ASSERT(m_cond) #endif +#ifdef DEV_ENABLED +#define DEV_CHECK_ONCE(m_cond) \ + if (unlikely(!(m_cond))) { \ + ERR_PRINT_ONCE("DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \ + } else \ + ((void)0) +#else +#define DEV_CHECK_ONCE(m_cond) +#endif + #endif // ERROR_MACROS_H diff --git a/core/extension/SCsub b/core/extension/SCsub index 361ce43a2c..901ceec1e8 100644 --- a/core/extension/SCsub +++ b/core/extension/SCsub @@ -4,13 +4,12 @@ Import("env") import make_wrappers import make_interface_dumper -from platform_methods import run_in_subprocess -env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", run_in_subprocess(make_wrappers.run)) +env.CommandNoCache(["ext_wrappers.gen.inc"], "make_wrappers.py", env.Run(make_wrappers.run)) env.CommandNoCache( "gdextension_interface_dump.gen.h", ["gdextension_interface.h", "make_interface_dumper.py"], - run_in_subprocess(make_interface_dumper.run), + env.Run(make_interface_dumper.run), ) env_extension = env.Clone() diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index f3e988633c..69be2d2a8f 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -742,14 +742,19 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) { Dictionary d2; String operator_name = Variant::get_operator_name(Variant::Operator(k)); d2["name"] = operator_name; - if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) { - d2["right_type"] = get_builtin_or_variant_type_name(Variant::Type(j)); + + String right_type_name = get_builtin_or_variant_type_name(Variant::Type(j)); + bool is_unary = k == Variant::OP_NEGATE || k == Variant::OP_POSITIVE || k == Variant::OP_NOT || k == Variant::OP_BIT_NEGATE; + if (!is_unary) { + d2["right_type"] = right_type_name; } + d2["return_type"] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j))); if (p_include_docs && builtin_doc != nullptr) { for (const DocData::MethodDoc &operator_doc : builtin_doc->operators) { - if (operator_doc.name == "operator " + operator_name) { + if (operator_doc.name == "operator " + operator_name && + (is_unary || operator_doc.arguments[0].type == right_type_name)) { d2["description"] = fix_doc_description(operator_doc.description); break; } @@ -1308,9 +1313,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var } else if (p_old_value.get_type() == Variant::DICTIONARY && p_new_value.get_type() == Variant::DICTIONARY) { Dictionary old_dict = p_old_value; Dictionary new_dict = p_new_value; - Array old_keys = old_dict.keys(); - for (int i = 0; i < old_keys.size(); i++) { - Variant key = old_keys[i]; + for (const Variant &key : old_dict.keys()) { if (!new_dict.has(key)) { failed = true; print_error(vformat("Validate extension JSON: Error: Field '%s': %s was removed.", p_path, key)); @@ -1323,9 +1326,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var failed = true; } } - Array new_keys = old_dict.keys(); - for (int i = 0; i < new_keys.size(); i++) { - Variant key = new_keys[i]; + for (const Variant &key : old_dict.keys()) { if (!old_dict.has(key)) { failed = true; print_error(vformat("Validate extension JSON: Error: Field '%s': %s was added with value %s.", p_path, key, new_dict[key])); @@ -1351,8 +1352,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ Array new_api = p_new_api[p_base_array]; HashMap<String, Dictionary> new_api_assoc; - for (int i = 0; i < new_api.size(); i++) { - Dictionary elem = new_api[i]; + for (const Variant &var : new_api) { + Dictionary elem = var; ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field)); String name = elem[p_name_field]; if (p_compare_operators && elem.has("right_type")) { @@ -1362,8 +1363,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_ } Array old_api = p_old_api[p_base_array]; - for (int i = 0; i < old_api.size(); i++) { - Dictionary old_elem = old_api[i]; + for (const Variant &var : old_api) { + Dictionary old_elem = var; if (!old_elem.has(p_name_field)) { failed = true; print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", base_array, p_name_field)); @@ -1503,16 +1504,16 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered Array new_api = p_new_api[p_outer]; HashMap<String, Dictionary> new_api_assoc; - for (int i = 0; i < new_api.size(); i++) { - Dictionary elem = new_api[i]; + for (const Variant &var : new_api) { + Dictionary elem = var; ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", p_outer, p_outer_name)); new_api_assoc.insert(elem[p_outer_name], elem); } Array old_api = p_old_api[p_outer]; - for (int i = 0; i < old_api.size(); i++) { - Dictionary old_elem = old_api[i]; + for (const Variant &var : old_api) { + Dictionary old_elem = var; if (!old_elem.has(p_outer_name)) { failed = true; print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", p_outer, p_outer_name)); diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 6c3d0a6148..22a5df9935 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -46,6 +46,41 @@ String GDExtension::get_extension_list_config_file() { return ProjectSettings::get_singleton()->get_project_data_path().path_join("extension_list.cfg"); } +Vector<SharedObject> GDExtension::find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature) { + Vector<SharedObject> dependencies_shared_objects; + if (p_config->has_section("dependencies")) { + List<String> config_dependencies; + p_config->get_section_keys("dependencies", &config_dependencies); + + for (const String &dependency : config_dependencies) { + Vector<String> dependency_tags = dependency.split("."); + bool all_tags_met = true; + for (int i = 0; i < dependency_tags.size(); i++) { + String tag = dependency_tags[i].strip_edges(); + if (!p_has_feature(tag)) { + all_tags_met = false; + break; + } + } + + if (all_tags_met) { + Dictionary dependency_value = p_config->get_value("dependencies", dependency); + for (const Variant *key = dependency_value.next(nullptr); key; key = dependency_value.next(key)) { + String dependency_path = *key; + String target_path = dependency_value[*key]; + if (dependency_path.is_relative_path()) { + dependency_path = p_path.get_base_dir().path_join(dependency_path); + } + dependencies_shared_objects.push_back(SharedObject(dependency_path, dependency_tags, target_path)); + } + break; + } + } + } + + return dependencies_shared_objects; +} + String GDExtension::find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags) { // First, check the explicit libraries. if (p_config->has_section("libraries")) { @@ -205,6 +240,7 @@ public: virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { #ifdef TOOLS_ENABLED ERR_FAIL_COND_V_MSG(!valid, Variant(), vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name)); + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder(), Variant(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif Variant ret; GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); @@ -218,6 +254,7 @@ public: virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { #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)); + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif 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(); @@ -238,7 +275,7 @@ public: ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret); } - ptrcall(p_object, argptrs, ret_opaque); + ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(argptrs), (GDExtensionTypePtr)ret_opaque); if (r_ret && r_ret->get_type() == Variant::OBJECT) { VariantInternal::update_object_id(r_ret); @@ -249,9 +286,10 @@ public: virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { #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)); + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder(), vformat("Cannot call GDExtension method bind '%s' on placeholder instance.", name)); #endif ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have ptrcall support. This is most likely an engine bug."); - GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); + GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance(); ptrcall_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstTypePtr *>(p_args), (GDExtensionTypePtr)r_ret); } @@ -341,10 +379,11 @@ public: #ifndef DISABLE_DEPRECATED void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs) { - const GDExtensionClassCreationInfo2 class_info2 = { + const GDExtensionClassCreationInfo3 class_info3 = { p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; true, // GDExtensionBool is_exposed; + false, // GDExtensionBool is_runtime; p_extension_funcs->set_func, // GDExtensionClassSet set_func; p_extension_funcs->get_func, // GDExtensionClassGet get_func; p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; @@ -369,15 +408,45 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library const ClassCreationDeprecatedInfo legacy = { p_extension_funcs->notification_func, }; - _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info2, &legacy); + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3, &legacy); } -#endif // DISABLE_DEPRECATED void GDExtension::_register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs) { + const GDExtensionClassCreationInfo3 class_info3 = { + p_extension_funcs->is_virtual, // GDExtensionBool is_virtual; + p_extension_funcs->is_abstract, // GDExtensionBool is_abstract; + p_extension_funcs->is_exposed, // GDExtensionBool is_exposed; + false, // GDExtensionBool is_runtime; + p_extension_funcs->set_func, // GDExtensionClassSet set_func; + p_extension_funcs->get_func, // GDExtensionClassGet get_func; + p_extension_funcs->get_property_list_func, // GDExtensionClassGetPropertyList get_property_list_func; + p_extension_funcs->free_property_list_func, // GDExtensionClassFreePropertyList free_property_list_func; + p_extension_funcs->property_can_revert_func, // GDExtensionClassPropertyCanRevert property_can_revert_func; + p_extension_funcs->property_get_revert_func, // GDExtensionClassPropertyGetRevert property_get_revert_func; + p_extension_funcs->validate_property_func, // GDExtensionClassValidateProperty validate_property_func; + p_extension_funcs->notification_func, // GDExtensionClassNotification2 notification_func; + p_extension_funcs->to_string_func, // GDExtensionClassToString to_string_func; + p_extension_funcs->reference_func, // GDExtensionClassReference reference_func; + p_extension_funcs->unreference_func, // GDExtensionClassUnreference unreference_func; + p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ + p_extension_funcs->recreate_instance_func, // GDExtensionClassRecreateInstance recreate_instance_func; + p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; + p_extension_funcs->get_virtual_call_data_func, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + p_extension_funcs->call_virtual_with_data_func, // GDExtensionClassCallVirtualWithData call_virtual_func; + p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid; + p_extension_funcs->class_userdata, // void *class_userdata; + }; + + _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, &class_info3); +} +#endif // DISABLE_DEPRECATED + +void GDExtension::_register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs) { _register_extension_class_internal(p_library, p_class_name, p_parent_class_name, p_extension_funcs); } -void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { +void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); @@ -397,15 +466,20 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr //inheriting from engine class } } else { - ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'"); + ERR_FAIL_MSG("Attempt to register an extension class '" + String(class_name) + "' using non-existing parent class '" + String(parent_class_name) + "'."); } #ifdef TOOLS_ENABLED Extension *extension = nullptr; + bool is_runtime = (bool)p_extension_funcs->is_runtime; if (self->is_reloading && self->extension_classes.has(class_name)) { extension = &self->extension_classes[class_name]; if (!parent_extension && parent_class_name != extension->gdextension.parent_class_name) { - ERR_FAIL_MSG(vformat("GDExtension class '%s' attempt to change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name)); + ERR_FAIL_MSG(vformat("GDExtension class '%s' cannot change parent type from '%s' to '%s' on hot reload. Restart Godot for this change to take effect.", class_name, extension->gdextension.parent_class_name, parent_class_name)); + } + if (extension->gdextension.is_runtime != is_runtime) { + ERR_PRINT(vformat("GDExtension class '%s' cannot change to/from runtime class on hot reload. Restart Godot for this change to take effect.", class_name)); + is_runtime = extension->gdextension.is_runtime; } extension->is_reloading = false; } else { @@ -434,6 +508,9 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr extension->gdextension.is_virtual = p_extension_funcs->is_virtual; extension->gdextension.is_abstract = p_extension_funcs->is_abstract; extension->gdextension.is_exposed = p_extension_funcs->is_exposed; +#ifdef TOOLS_ENABLED + extension->gdextension.is_runtime = is_runtime; +#endif extension->gdextension.set = p_extension_funcs->set_func; extension->gdextension.get = p_extension_funcs->get_func; extension->gdextension.get_property_list = p_extension_funcs->get_property_list_func; @@ -518,6 +595,12 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_ ClassDB::bind_method_custom(class_name, method); } + +void GDExtension::_register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info) { + StringName class_name = *reinterpret_cast<const StringName *>(p_class_name); + ClassDB::add_extension_class_virtual_method(class_name, p_method_info); +} + void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield) { GDExtension *self = reinterpret_cast<GDExtension *>(p_library); @@ -653,6 +736,8 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra if (!ext->is_reloading) { self->extension_classes.erase(class_name); } + + GDExtensionEditorHelp::remove_class(class_name); #else self->extension_classes.erase(class_name); #endif @@ -666,59 +751,46 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions; -void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) { +void GDExtension::register_interface_function(const 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."); gdextension_interface_functions.insert(p_function_name, p_function_pointer); } -GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) { +GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) { GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name); ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + "."); return *function; } -Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) { - library_path = p_path; - +Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies) { String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path); -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - // If running on the editor on Windows, we copy the library and open the copy. - // This is so the original file isn't locked and can be updated by a compiler. - if (Engine::get_singleton()->is_editor_hint()) { - if (!FileAccess::exists(abs_path)) { - ERR_PRINT("GDExtension library not found: " + library_path); - return ERR_FILE_NOT_FOUND; - } - - // Copy the file to the same directory as the original with a prefix in the name. - // This is so relative path to dependencies are satisfied. - String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file()); - // If there's a left-over copy (possibly from a crash) then delete it first. - if (FileAccess::exists(copy_path)) { - DirAccess::remove_absolute(copy_path); + Vector<String> abs_dependencies_paths; + if (p_dependencies != nullptr && !p_dependencies->is_empty()) { + for (const SharedObject &dependency : *p_dependencies) { + abs_dependencies_paths.push_back(ProjectSettings::get_singleton()->globalize_path(dependency.path)); } + } - Error copy_err = DirAccess::copy_absolute(abs_path, copy_path); - if (copy_err) { - ERR_PRINT("Error copying GDExtension library: " + library_path); - return ERR_CANT_CREATE; - } - FileAccess::set_hidden_attribute(copy_path, true); - - // Save the copied path so it can be deleted later. - temp_lib_path = copy_path; + String actual_lib_path; + OS::GDExtensionData data = { + true, // also_set_library_path + &actual_lib_path, // r_resolved_path + Engine::get_singleton()->is_editor_hint(), // generate_temp_files + &abs_dependencies_paths, // library_dependencies + }; + Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, &data); - // Use the copy to open the library. - abs_path = copy_path; + if (actual_lib_path.get_file() != abs_path.get_file()) { + // If temporary files are generated, let's change the library path to point at the original, + // because that's what we want to check to see if it's changed. + library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file()); + } else { + library_path = p_path; } -#endif - Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true); - if (err != OK) { - ERR_PRINT("GDExtension dynamic library not found: " + abs_path); - return err; - } + ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path); + ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path); void *entry_funcptr = nullptr; @@ -747,13 +819,6 @@ void GDExtension::close_library() { ERR_FAIL_NULL(library); OS::get_singleton()->close_dynamic_library(library); -#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED) - // Delete temporary copy of library if it exists. - if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) { - DirAccess::remove_absolute(temp_lib_path); - } -#endif - library = nullptr; class_icon_paths.clear(); @@ -786,16 +851,15 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) { ERR_FAIL_COND(p_level > int32_t(level_initialized)); level_initialized = int32_t(p_level) - 1; + + ERR_FAIL_NULL(initialization.deinitialize); + initialization.deinitialize(initialization.userdata, GDExtensionInitializationLevel(p_level)); } void GDExtension::_bind_methods() { - ClassDB::bind_method(D_METHOD("open_library", "path", "entry_symbol"), &GDExtension::open_library); - ClassDB::bind_method(D_METHOD("close_library"), &GDExtension::close_library); ClassDB::bind_method(D_METHOD("is_library_open"), &GDExtension::is_library_open); - ClassDB::bind_method(D_METHOD("get_minimum_library_initialization_level"), &GDExtension::get_minimum_library_initialization_level); - ClassDB::bind_method(D_METHOD("initialize_library", "level"), &GDExtension::initialize_library); BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_CORE); BIND_ENUM_CONSTANT(INITIALIZATION_LEVEL_SERVERS); @@ -823,9 +887,11 @@ void GDExtension::initialize_gdextensions() { #ifndef DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class); -#endif // DISABLE_DEPRECATED register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2); +#endif // DISABLE_DEPRECATED + register_interface_function("classdb_register_extension_class3", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class3); register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method); + register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method); register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant); register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property); register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed); @@ -896,7 +962,40 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, return ERR_INVALID_DATA; } - String library_path = GDExtension::find_extension_library(p_path, config, [](String p_feature) { return OS::get_singleton()->has_feature(p_feature); }); + // Optionally check maximum compatibility. + if (config->has_section_key("configuration", "compatibility_maximum")) { + uint32_t compatibility_maximum[3] = { 0, 0, 0 }; + String compat_string = config->get_value("configuration", "compatibility_maximum"); + Vector<int> parts = compat_string.split_ints("."); + for (int i = 0; i < 3; i++) { + if (i < parts.size() && parts[i] >= 0) { + compatibility_maximum[i] = parts[i]; + } else { + // If a version part is missing, set the maximum to an arbitrary high value. + compatibility_maximum[i] = 9999; + } + } + + compatible = true; + if (VERSION_MAJOR != compatibility_maximum[0]) { + compatible = VERSION_MAJOR < compatibility_maximum[0]; + } else if (VERSION_MINOR != compatibility_maximum[1]) { + compatible = VERSION_MINOR < compatibility_maximum[1]; + } +#if VERSION_PATCH + // #if check to avoid -Wtype-limits warning when 0. + else { + compatible = VERSION_PATCH <= compatibility_maximum[2]; + } +#endif + + if (!compatible) { + ERR_PRINT(vformat("GDExtension only compatible with Godot version %s or earlier: %s", compat_string, p_path)); + return ERR_INVALID_DATA; + } + } + + String library_path = GDExtension::find_extension_library(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); if (library_path.is_empty()) { const String os_arch = OS::get_singleton()->get_name().to_lower() + "." + Engine::get_singleton()->get_architecture_name(); @@ -904,6 +1003,8 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, return ERR_FILE_NOT_FOUND; } + bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework"); + if (!library_path.is_resource_file() && !library_path.is_absolute_path()) { library_path = p_path.get_base_dir().path_join(library_path); } @@ -920,15 +1021,9 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, FileAccess::get_modified_time(library_path)); #endif - err = p_extension->open_library(library_path, entry_symbol); + Vector<SharedObject> library_dependencies = GDExtension::find_extension_dependencies(p_path, config, [](const String &p_feature) { return OS::get_singleton()->has_feature(p_feature); }); + err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol, &library_dependencies); if (err != OK) { -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - // If the DLL fails to load, make sure that temporary DLL copies are cleaned up. - if (Engine::get_singleton()->is_editor_hint()) { - DirAccess::remove_absolute(p_extension->get_temp_library_path()); - } -#endif - // Unreference the extension so that this loading can be considered a failure. p_extension.unref(); @@ -1040,7 +1135,10 @@ void GDExtension::prepare_reload() { state.push_back(Pair<String, Variant>(P.name, value)); } - E.value.instance_state[obj_id] = state; + E.value.instance_state[obj_id] = { + state, // List<Pair<String, Variant>> properties; + obj->is_extension_placeholder(), // bool is_placeholder; + }; } } } @@ -1115,25 +1213,29 @@ void GDExtension::finish_reload() { for (KeyValue<StringName, Extension> &E : extension_classes) { // Loop over 'instance_state' rather than 'instance' because new instances // may have been created when re-initializing the extension. - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; } - obj->reset_internal_extension(&E.value.gdextension); + if (S.value.is_placeholder) { + obj->reset_internal_extension(ClassDB::get_placeholder_extension(E.value.gdextension.class_name)); + } else { + obj->reset_internal_extension(&E.value.gdextension); + } } } // Now that all the classes are back, restore the state. for (KeyValue<StringName, Extension> &E : extension_classes) { - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; } - for (const Pair<String, Variant> &state : S.value) { + for (const Pair<String, Variant> &state : S.value.properties) { obj->set(state.first, state.second); } } @@ -1141,7 +1243,7 @@ void GDExtension::finish_reload() { // Finally, let the objects know that we are done reloading them. for (KeyValue<StringName, Extension> &E : extension_classes) { - for (const KeyValue<ObjectID, List<Pair<String, Variant>>> &S : E.value.instance_state) { + for (const KeyValue<ObjectID, Extension::InstanceState> &S : E.value.instance_state) { Object *obj = ObjectDB::get_instance(S.key); if (!obj) { continue; @@ -1188,4 +1290,17 @@ void GDExtensionEditorPlugins::remove_extension_class(const StringName &p_class_ extension_classes.erase(p_class_name); } } + +GDExtensionEditorHelp::EditorHelpLoadXmlBufferFunc GDExtensionEditorHelp::editor_help_load_xml_buffer = nullptr; +GDExtensionEditorHelp::EditorHelpRemoveClassFunc GDExtensionEditorHelp::editor_help_remove_class = nullptr; + +void GDExtensionEditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) { + ERR_FAIL_NULL(editor_help_load_xml_buffer); + editor_help_load_xml_buffer(p_buffer, p_size); +} + +void GDExtensionEditorHelp::remove_class(const String &p_class) { + ERR_FAIL_NULL(editor_help_remove_class); + editor_help_remove_class(p_class); +} #endif // TOOLS_ENABLED diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 0d20b8e50c..23b1f51208 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -37,6 +37,7 @@ #include "core/io/config_file.h" #include "core/io/resource_loader.h" #include "core/object/ref_counted.h" +#include "core/os/shared_object.h" class GDExtensionMethodBind; @@ -47,9 +48,6 @@ class GDExtension : public Resource { void *library = nullptr; // pointer if valid, String library_path; -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - String temp_lib_path; -#endif bool reloadable = false; struct Extension { @@ -59,7 +57,12 @@ class GDExtension : public Resource { bool is_reloading = false; HashMap<StringName, GDExtensionMethodBind *> methods; HashSet<ObjectID> instances; - HashMap<ObjectID, List<Pair<String, Variant>>> instance_state; + + struct InstanceState { + List<Pair<String, Variant>> properties; + bool is_placeholder = false; + }; + HashMap<ObjectID, InstanceState> instance_state; #endif }; @@ -73,10 +76,12 @@ class GDExtension : public Resource { #ifndef DISABLE_DEPRECATED static void _register_extension_class(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo *p_extension_funcs); -#endif // DISABLE_DEPRECATED static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); - static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); +#endif // DISABLE_DEPRECATED + static void _register_extension_class3(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); + static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr); static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); + static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield); static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter); static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index); @@ -119,14 +124,11 @@ public: 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); + static Vector<SharedObject> find_extension_dependencies(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature); - Error open_library(const String &p_path, const String &p_entry_symbol); + Error open_library(const String &p_path, const String &p_entry_symbol, Vector<SharedObject> *p_dependencies = nullptr); void close_library(); -#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED) - String get_temp_library_path() const { return temp_lib_path; } -#endif - enum InitializationLevel { INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE, INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS, @@ -154,8 +156,8 @@ public: void initialize_library(InitializationLevel p_level); void deinitialize_library(InitializationLevel p_level); - static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); - static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name); + static void register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); + static GDExtensionInterfaceFunctionPtr get_interface_function(const StringName &p_function_name); static void initialize_gdextensions(); static void finalize_gdextensions(); @@ -169,10 +171,10 @@ class GDExtensionResourceLoader : public ResourceFormatLoader { public: static Error load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension); - virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; }; #ifdef TOOLS_ENABLED @@ -197,6 +199,26 @@ public: return extension_classes; } }; + +class GDExtensionEditorHelp { +protected: + friend class EditorHelp; + + // Similarly to EditorNode above, we need to be able to ask EditorHelp to parse + // new documentation data. Note though that, differently from EditorHelp, this + // is initialized even _before_ it gets instantiated, as we need to rely on + // this method while initializing the engine. + typedef void (*EditorHelpLoadXmlBufferFunc)(const uint8_t *p_buffer, int p_size); + static EditorHelpLoadXmlBufferFunc editor_help_load_xml_buffer; + + typedef void (*EditorHelpRemoveClassFunc)(const String &p_class); + static EditorHelpRemoveClassFunc editor_help_remove_class; + +public: + static void load_xml_buffer(const uint8_t *p_buffer, int p_size); + static void remove_class(const String &p_class); +}; + #endif // TOOLS_ENABLED #endif // GDEXTENSION_H diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp index 2dac4a3a5d..ebbf795070 100644 --- a/core/extension/gdextension_compat_hashes.cpp +++ b/core/extension/gdextension_compat_hashes.cpp @@ -32,6 +32,7 @@ #ifndef DISABLE_DEPRECATED +#include "core/object/class_db.h" #include "core/variant/variant.h" HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings; @@ -52,7 +53,7 @@ bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, con return false; } -bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes) { +bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) { LocalVector<Mapping> *methods = mappings.getptr(p_class); if (!methods) { return false; @@ -61,6 +62,13 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const bool found = false; for (const Mapping &mapping : *methods) { if (mapping.method == p_method) { + if (p_check_valid) { + MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash); + if (!mb) { + WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash)); + continue; + } + } r_hashes.push_back(mapping.legacy_hash); found = true; } @@ -99,7 +107,11 @@ void GDExtensionCompatHashes::initialize() { { "add_track", 2393815928, 3843682357 }, { "track_insert_key", 1985425300, 808952278 }, { "track_find_key", 3898229885, 3245197284 }, +#ifdef REAL_T_IS_DOUBLE + { "bezier_track_insert_key", 1057544502, 3767441357 }, +#else { "bezier_track_insert_key", 1057544502, 3656773645 }, +#endif { "bezier_track_set_key_in_handle", 1028302688, 1719223284 }, { "bezier_track_set_key_out_handle", 1028302688, 1719223284 }, { "audio_track_insert_key", 3489962123, 4021027286 }, @@ -117,10 +129,18 @@ void GDExtensionCompatHashes::initialize() { { "add_triangle", 642454959, 753017335 }, }); mappings.insert("AnimationNodeBlendTree", { +#ifdef REAL_T_IS_DOUBLE + { "add_node", 2055804584, 1407702499 }, +#else { "add_node", 2055804584, 1980270704 }, +#endif }); mappings.insert("AnimationNodeStateMachine", { +#ifdef REAL_T_IS_DOUBLE + { "add_node", 2055804584, 1407702499 }, +#else { "add_node", 2055804584, 1980270704 }, +#endif }); mappings.insert("AnimationNodeStateMachinePlayback", { { "travel", 3683006648, 3823612587 }, @@ -169,8 +189,13 @@ void GDExtensionCompatHashes::initialize() { { "draw_multiline_string_outline", 3717870722, 1912318525 }, { "draw_char", 2329089032, 3339793283 }, { "draw_char_outline", 419453826, 3302344391 }, +#ifdef REAL_T_IS_DOUBLE + { "draw_mesh", 1634855856, 4036154158 }, + { "draw_set_transform", 3283884939, 156553079 }, +#else { "draw_mesh", 1634855856, 153818295 }, { "draw_set_transform", 3283884939, 288975085 }, +#endif { "draw_animation_slice", 2295343543, 3112831842 }, }); mappings.insert("CodeEdit", { @@ -202,10 +227,18 @@ void GDExtensionCompatHashes::initialize() { { "add_point", 2766148617, 434072736 }, }); mappings.insert("Curve2D", { +#ifdef REAL_T_IS_DOUBLE + { "add_point", 529706502, 3343370600 }, +#else { "add_point", 2437345566, 4175465202 }, +#endif }); mappings.insert("Curve3D", { +#ifdef REAL_T_IS_DOUBLE + { "add_point", 3544159631, 917388502 }, +#else { "add_point", 3836314258, 2931053748 }, +#endif }); mappings.insert("DirAccess", { { "list_dir_begin", 2018049411, 2610976713 }, @@ -245,8 +278,13 @@ void GDExtensionCompatHashes::initialize() { { "window_set_ime_active", 450484987, 1661950165 }, { "window_set_ime_position", 3614040015, 2019273902 }, { "window_set_vsync_mode", 1708924624, 2179333492 }, - { "virtual_keyboard_show", 860410478, 3042891259 }, +#ifdef REAL_T_IS_DOUBLE + { "cursor_set_custom_image", 1358907026, 4163678968 }, + { "virtual_keyboard_show", 384539973, 1323934605 }, +#else { "cursor_set_custom_image", 1358907026, 1816663697 }, + { "virtual_keyboard_show", 860410478, 3042891259 }, +#endif }); mappings.insert("ENetConnection", { { "create_host_bound", 866250949, 1515002313 }, @@ -281,7 +319,11 @@ void GDExtensionCompatHashes::initialize() { }); mappings.insert("EditorNode3DGizmo", { { "add_lines", 302451090, 2910971437 }, + #ifdef REAL_T_IS_DOUBLE + { "add_mesh", 3332776472, 2161761131 }, + #else { "add_mesh", 1868867708, 1579955111 }, + #endif { "add_unscaled_billboard", 3719733075, 520007164 }, }); mappings.insert("EditorNode3DGizmoPlugin", { @@ -308,7 +350,6 @@ void GDExtensionCompatHashes::initialize() { { "add_filter", 233059325, 3388804757 }, }); mappings.insert("Font", { - { "find_variation", 1222433716, 3344325384 }, { "get_string_size", 3678918099, 1868866121 }, { "get_multiline_string_size", 2427690650, 519636710 }, { "draw_string", 2565402639, 1983721962 }, @@ -317,8 +358,13 @@ void GDExtensionCompatHashes::initialize() { { "draw_multiline_string_outline", 1649790182, 3206388178 }, { "draw_char", 1462476057, 3815617597 }, { "draw_char_outline", 4161008124, 209525354 }, + #ifdef REAL_T_IS_DOUBLE + { "find_variation", 625117670, 2196349508 }, + #else + { "find_variation", 1222433716, 3344325384 }, // Pre-existing compatibility hash. { "find_variation", 1149405976, 1851767612 }, + #endif }); mappings.insert("GLTFDocument", { { "append_from_file", 1862991421, 866380864 }, @@ -372,11 +418,19 @@ void GDExtensionCompatHashes::initialize() { { "get_vector", 1517139831, 2479607902 }, { "start_joy_vibration", 1890603622, 2576575033 }, { "action_press", 573731101, 1713091165 }, +#ifdef REAL_T_IS_DOUBLE + { "set_custom_mouse_cursor", 3489634142, 1277868338 }, +#else { "set_custom_mouse_cursor", 3489634142, 703945977 }, +#endif }); mappings.insert("InputEvent", { { "is_match", 3392494811, 1754951977 }, +#ifdef REAL_T_IS_DOUBLE + { "xformed_by", 2747409789, 3242949850 }, +#else { "xformed_by", 2747409789, 1282766827 }, +#endif }); mappings.insert("InputMap", { { "add_action", 573731101, 4100757082 }, @@ -421,8 +475,13 @@ void GDExtensionCompatHashes::initialize() { { "set_multiplayer_authority", 4023243586, 972357352 }, }); mappings.insert("Node3D", { +#ifdef REAL_T_IS_DOUBLE + { "look_at", 136915519, 819337406 }, + { "look_at_from_position", 4067663783, 1809580162 }, +#else { "look_at", 3123400617, 2882425029 }, { "look_at_from_position", 4067663783, 2086826090 }, +#endif }); mappings.insert("Noise", { { "get_image", 2569233413, 3180683109 }, @@ -462,7 +521,11 @@ void GDExtensionCompatHashes::initialize() { { "add_custom_monitor", 2865980031, 4099036814 }, }); mappings.insert("PhysicalBone3D", { +#ifdef REAL_T_IS_DOUBLE + { "apply_impulse", 1002852006, 2485728502 }, +#else { "apply_impulse", 1002852006, 2754756483 }, +#endif }); mappings.insert("PhysicsBody2D", { { "move_and_collide", 1529961754, 3681923724 }, @@ -473,14 +536,26 @@ void GDExtensionCompatHashes::initialize() { { "test_move", 680299713, 2481691619 }, }); mappings.insert("PhysicsDirectBodyState2D", { +#ifdef REAL_T_IS_DOUBLE + { "apply_impulse", 496058220, 1271588277 }, + { "apply_force", 496058220, 1271588277 }, + { "add_constant_force", 496058220, 1271588277 }, +#else { "apply_impulse", 496058220, 4288681949 }, { "apply_force", 496058220, 4288681949 }, { "add_constant_force", 496058220, 4288681949 }, +#endif }); mappings.insert("PhysicsDirectBodyState3D", { +#ifdef REAL_T_IS_DOUBLE + { "apply_impulse", 1002852006, 2485728502 }, + { "apply_force", 1002852006, 2485728502 }, + { "add_constant_force", 1002852006, 2485728502 }, +#else { "apply_impulse", 1002852006, 2754756483 }, { "apply_force", 1002852006, 2754756483 }, { "add_constant_force", 1002852006, 2754756483 }, +#endif }); mappings.insert("PhysicsDirectSpaceState2D", { { "intersect_point", 3278207904, 2118456068 }, @@ -499,21 +574,37 @@ void GDExtensionCompatHashes::initialize() { { "create", 680321959, 3110599579 }, }); mappings.insert("PhysicsServer2D", { +#ifdef REAL_T_IS_DOUBLE + { "area_add_shape", 754862190, 3597527023 }, + { "body_add_shape", 754862190, 3597527023 }, + { "body_apply_impulse", 34330743, 1124035137 }, + { "body_apply_force", 34330743, 1124035137 }, + { "body_add_constant_force", 34330743, 1124035137 }, +#else { "area_add_shape", 754862190, 339056240 }, { "body_add_shape", 754862190, 339056240 }, { "body_apply_impulse", 34330743, 205485391 }, { "body_apply_force", 34330743, 205485391 }, { "body_add_constant_force", 34330743, 205485391 }, +#endif { "joint_make_pin", 2288600450, 1612646186 }, { "joint_make_groove", 3573265764, 481430435 }, { "joint_make_damped_spring", 206603952, 1994657646 }, }); mappings.insert("PhysicsServer3D", { +#ifdef REAL_T_IS_DOUBLE + { "area_add_shape", 4040559639, 183938777 }, + { "body_add_shape", 4040559639, 183938777 }, + { "body_apply_impulse", 110375048, 2238283471 }, + { "body_apply_force", 110375048, 2238283471 }, + { "body_add_constant_force", 110375048, 2238283471 }, +#else { "area_add_shape", 4040559639, 3711419014 }, { "body_add_shape", 4040559639, 3711419014 }, { "body_apply_impulse", 110375048, 390416203 }, { "body_apply_force", 110375048, 390416203 }, { "body_add_constant_force", 110375048, 390416203 }, +#endif }); mappings.insert("PopupMenu", { { "add_item", 3224536192, 3674230041 }, @@ -573,10 +664,16 @@ void GDExtensionCompatHashes::initialize() { { "buffer_get_data", 125363422, 3101830688 }, { "render_pipeline_create", 2911419500, 2385451958 }, { "compute_pipeline_create", 403593840, 1448838280 }, + { "draw_list_draw", 3710874499, 4230067973 }, +#ifdef REAL_T_IS_DOUBLE + { "draw_list_begin", 4252992020, 848735039 }, + { "draw_list_begin_split", 832527510, 2228306807 }, + { "draw_list_enable_scissor", 338791288, 730833978 }, +#else { "draw_list_begin", 4252992020, 2468082605 }, { "draw_list_begin_split", 832527510, 2406300660 }, - { "draw_list_draw", 3710874499, 4230067973 }, { "draw_list_enable_scissor", 338791288, 244650101 }, +#endif }); mappings.insert("RenderingServer", { { "texture_rd_create", 3291180269, 1434128712 }, @@ -584,12 +681,10 @@ void GDExtensionCompatHashes::initialize() { { "shader_get_default_texture_parameter", 2523186822, 1464608890 }, { "mesh_create_from_surfaces", 4007581507, 4291747531 }, { "mesh_add_surface_from_arrays", 1247008646, 2342446560 }, - { "viewport_attach_to_screen", 1278520651, 1062245816 }, { "environment_set_ambient_light", 491659071, 1214961493 }, { "instances_cull_aabb", 2031554939, 2570105777 }, { "instances_cull_ray", 3388524336, 2208759584 }, { "instances_cull_convex", 3690700105, 2488539944 }, - { "canvas_item_set_custom_rect", 2180266943, 1333997032 }, { "canvas_item_add_line", 2843922985, 1819681853 }, { "canvas_item_add_polyline", 3438017257, 3098767073 }, { "canvas_item_add_multiline", 3176074788, 2088642721 }, @@ -599,11 +694,19 @@ void GDExtensionCompatHashes::initialize() { { "canvas_item_add_nine_patch", 904428547, 389957886 }, { "canvas_item_add_polygon", 2907936855, 3580000528 }, { "canvas_item_add_triangle_array", 749685193, 660261329 }, - { "canvas_item_add_mesh", 3548053052, 316450961 }, { "canvas_item_add_multimesh", 1541595251, 2131855138 }, { "canvas_item_add_animation_slice", 4107531031, 2646834499 }, { "canvas_item_set_canvas_group_mode", 41973386, 3973586316 }, { "set_boot_image", 2244367877, 3759744527 }, +#ifdef REAL_T_IS_DOUBLE + { "viewport_attach_to_screen", 1410474027, 2248302004 }, + { "canvas_item_set_custom_rect", 2180266943, 1134449082 }, + { "canvas_item_add_mesh", 3877492181, 3024949314 }, +#else + { "viewport_attach_to_screen", 1278520651, 1062245816 }, + { "canvas_item_set_custom_rect", 2180266943, 1333997032 }, + { "canvas_item_add_mesh", 3548053052, 316450961 }, +#endif }); mappings.insert("ResourceLoader", { { "load_threaded_request", 1939848623, 3614384323 }, @@ -615,23 +718,40 @@ void GDExtensionCompatHashes::initialize() { { "save", 2303056517, 2983274697 }, }); mappings.insert("RichTextLabel", { - { "add_image", 3346058748, 3580801207 }, { "push_font", 814287596, 2347424842 }, { "push_paragraph", 3218895358, 3089306873 }, { "push_list", 4036303897, 3017143144 }, { "push_table", 1125058220, 2623499273 }, - { "push_dropcap", 311501835, 4061635501 }, { "set_table_column_expand", 4132157579, 2185176273 }, +#ifdef REAL_T_IS_DOUBLE + { "add_image", 3346058748, 1507062345 }, + { "push_dropcap", 981432822, 763534173 }, +#else + { "add_image", 3346058748, 3580801207 }, + { "push_dropcap", 311501835, 4061635501 }, +#endif }); mappings.insert("RigidBody2D", { +#ifdef REAL_T_IS_DOUBLE + { "apply_impulse", 496058220, 1271588277 }, + { "apply_force", 496058220, 1271588277 }, + { "add_constant_force", 496058220, 1271588277 }, +#else { "apply_impulse", 496058220, 4288681949 }, { "apply_force", 496058220, 4288681949 }, { "add_constant_force", 496058220, 4288681949 }, +#endif }); mappings.insert("RigidBody3D", { +#ifdef REAL_T_IS_DOUBLE + { "apply_impulse", 1002852006, 2485728502 }, + { "apply_force", 1002852006, 2485728502 }, + { "add_constant_force", 1002852006, 2485728502 }, +#else { "apply_impulse", 1002852006, 2754756483 }, { "apply_force", 1002852006, 2754756483 }, { "add_constant_force", 1002852006, 2754756483 }, +#endif }); mappings.insert("SceneMultiplayer", { { "send_bytes", 2742700601, 1307428718 }, @@ -691,7 +811,11 @@ void GDExtensionCompatHashes::initialize() { { "draw_outline", 1364491366, 1343401456 }, }); mappings.insert("TextParagraph", { +#ifdef REAL_T_IS_DOUBLE + { "set_dropcap", 2613124475, 2897844600 }, +#else { "set_dropcap", 2613124475, 2498990330 }, +#endif { "add_string", 867188035, 621426851 }, { "add_object", 735420116, 1316529304 }, { "resize_object", 960819067, 2095776372 }, diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h index 29393dcb2d..813859d9e6 100644 --- a/core/extension/gdextension_compat_hashes.h +++ b/core/extension/gdextension_compat_hashes.h @@ -50,7 +50,7 @@ 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); + static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid = true); }; #endif // DISABLE_DEPRECATED diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp index e02e7aa701..9b4aa98357 100644 --- a/core/extension/gdextension_interface.cpp +++ b/core/extension/gdextension_interface.cpp @@ -42,6 +42,8 @@ #include "core/variant/variant.h" #include "core/version.h" +#include <string.h> + class CallableCustomExtension : public CallableCustom { void *userdata; void *token; @@ -57,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) { @@ -141,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; } @@ -155,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; @@ -170,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); @@ -1192,6 +1241,33 @@ static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObj return (GDObjectInstanceID)o->get_instance_id(); } +static GDExtensionBool gdextension_object_has_script_method(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method) { + Object *o = (Object *)p_object; + const StringName method = *reinterpret_cast<const StringName *>(p_method); + + ScriptInstance *script_instance = o->get_script_instance(); + if (script_instance) { + return script_instance->has_method(method); + } + return false; +} + +static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) { + Object *o = (Object *)p_object; + const StringName method = *reinterpret_cast<const StringName *>(p_method); + const Variant **args = (const Variant **)p_args; + + Callable::CallError error; + memnew_placement(r_return, Variant); + *(Variant *)r_return = o->callp(method, args, p_argument_count, error); + + if (r_error) { + r_error->error = (GDExtensionCallErrorType)(error.error); + r_error->argument = error.argument; + r_error->expected = error.expected; + } +} + static GDExtensionObjectPtr gdextension_ref_get_object(GDExtensionConstRefPtr p_ref) { const Ref<RefCounted> *ref = (const Ref<RefCounted> *)p_ref; if (ref == nullptr || ref->is_null()) { @@ -1211,43 +1287,86 @@ static void gdextension_ref_set_object(GDExtensionRefPtr p_ref, GDExtensionObjec #ifndef DISABLE_DEPRECATED static GDExtensionScriptInstancePtr gdextension_script_instance_create(const GDExtensionScriptInstanceInfo *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { - GDExtensionScriptInstanceInfo2 *info_2 = memnew(GDExtensionScriptInstanceInfo2); - info_2->set_func = p_info->set_func; - info_2->get_func = p_info->get_func; - info_2->get_property_list_func = p_info->get_property_list_func; - info_2->free_property_list_func = p_info->free_property_list_func; - info_2->get_class_category_func = nullptr; - info_2->property_can_revert_func = p_info->property_can_revert_func; - info_2->property_get_revert_func = p_info->property_get_revert_func; - info_2->get_owner_func = p_info->get_owner_func; - info_2->get_property_state_func = p_info->get_property_state_func; - info_2->get_method_list_func = p_info->get_method_list_func; - info_2->free_method_list_func = p_info->free_method_list_func; - info_2->get_property_type_func = p_info->get_property_type_func; - info_2->validate_property_func = nullptr; - info_2->has_method_func = p_info->has_method_func; - info_2->call_func = p_info->call_func; - info_2->notification_func = nullptr; - info_2->to_string_func = p_info->to_string_func; - info_2->refcount_incremented_func = p_info->refcount_incremented_func; - info_2->refcount_decremented_func = p_info->refcount_decremented_func; - info_2->get_script_func = p_info->get_script_func; - info_2->is_placeholder_func = p_info->is_placeholder_func; - info_2->set_fallback_func = p_info->set_fallback_func; - info_2->get_fallback_func = p_info->get_fallback_func; - info_2->get_language_func = p_info->get_language_func; - info_2->free_func = p_info->free_func; + GDExtensionScriptInstanceInfo3 *info_3 = memnew(GDExtensionScriptInstanceInfo3); + info_3->set_func = p_info->set_func; + info_3->get_func = p_info->get_func; + info_3->get_property_list_func = p_info->get_property_list_func; + info_3->free_property_list_func = nullptr; + info_3->get_class_category_func = nullptr; + info_3->property_can_revert_func = p_info->property_can_revert_func; + info_3->property_get_revert_func = p_info->property_get_revert_func; + info_3->get_owner_func = p_info->get_owner_func; + info_3->get_property_state_func = p_info->get_property_state_func; + info_3->get_method_list_func = p_info->get_method_list_func; + info_3->free_method_list_func = nullptr; + info_3->get_property_type_func = p_info->get_property_type_func; + info_3->validate_property_func = nullptr; + info_3->has_method_func = p_info->has_method_func; + info_3->get_method_argument_count_func = nullptr; + info_3->call_func = p_info->call_func; + info_3->notification_func = nullptr; + info_3->to_string_func = p_info->to_string_func; + info_3->refcount_incremented_func = p_info->refcount_incremented_func; + info_3->refcount_decremented_func = p_info->refcount_decremented_func; + info_3->get_script_func = p_info->get_script_func; + info_3->is_placeholder_func = p_info->is_placeholder_func; + info_3->set_fallback_func = p_info->set_fallback_func; + info_3->get_fallback_func = p_info->get_fallback_func; + info_3->get_language_func = p_info->get_language_func; + info_3->free_func = p_info->free_func; ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); script_instance_extension->instance = p_instance_data; - script_instance_extension->native_info = info_2; + script_instance_extension->native_info = info_3; script_instance_extension->free_native_info = true; - script_instance_extension->deprecated_native_info.notification_func = p_info->notification_func; + script_instance_extension->deprecated_native_info = memnew(ScriptInstanceExtension::DeprecatedNativeInfo); + script_instance_extension->deprecated_native_info->notification_func = p_info->notification_func; + script_instance_extension->deprecated_native_info->free_property_list_func = p_info->free_property_list_func; + script_instance_extension->deprecated_native_info->free_method_list_func = p_info->free_method_list_func; return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); } -#endif // DISABLE_DEPRECATED static GDExtensionScriptInstancePtr gdextension_script_instance_create2(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { + GDExtensionScriptInstanceInfo3 *info_3 = memnew(GDExtensionScriptInstanceInfo3); + info_3->set_func = p_info->set_func; + info_3->get_func = p_info->get_func; + info_3->get_property_list_func = p_info->get_property_list_func; + info_3->free_property_list_func = nullptr; + info_3->get_class_category_func = nullptr; + info_3->property_can_revert_func = p_info->property_can_revert_func; + info_3->property_get_revert_func = p_info->property_get_revert_func; + info_3->get_owner_func = p_info->get_owner_func; + info_3->get_property_state_func = p_info->get_property_state_func; + info_3->get_method_list_func = p_info->get_method_list_func; + info_3->free_method_list_func = nullptr; + info_3->get_property_type_func = p_info->get_property_type_func; + info_3->validate_property_func = nullptr; + info_3->has_method_func = p_info->has_method_func; + info_3->get_method_argument_count_func = nullptr; + info_3->call_func = p_info->call_func; + info_3->notification_func = p_info->notification_func; + info_3->to_string_func = p_info->to_string_func; + info_3->refcount_incremented_func = p_info->refcount_incremented_func; + info_3->refcount_decremented_func = p_info->refcount_decremented_func; + info_3->get_script_func = p_info->get_script_func; + info_3->is_placeholder_func = p_info->is_placeholder_func; + info_3->set_fallback_func = p_info->set_fallback_func; + info_3->get_fallback_func = p_info->get_fallback_func; + info_3->get_language_func = p_info->get_language_func; + info_3->free_func = p_info->free_func; + + ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); + script_instance_extension->instance = p_instance_data; + script_instance_extension->native_info = info_3; + script_instance_extension->free_native_info = true; + script_instance_extension->deprecated_native_info = memnew(ScriptInstanceExtension::DeprecatedNativeInfo); + script_instance_extension->deprecated_native_info->free_property_list_func = p_info->free_property_list_func; + script_instance_extension->deprecated_native_info->free_method_list_func = p_info->free_method_list_func; + return reinterpret_cast<GDExtensionScriptInstancePtr>(script_instance_extension); +} +#endif // DISABLE_DEPRECATED + +static GDExtensionScriptInstancePtr gdextension_script_instance_create3(const GDExtensionScriptInstanceInfo3 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data) { ScriptInstanceExtension *script_instance_extension = memnew(ScriptInstanceExtension); script_instance_extension->instance = p_instance_data; script_instance_extension->native_info = p_info; @@ -1308,9 +1427,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); @@ -1350,7 +1475,7 @@ static GDExtensionMethodBindPtr gdextension_classdb_get_method_bind(GDExtensionC static GDExtensionObjectPtr gdextension_classdb_construct_object(GDExtensionConstStringNamePtr p_classname) { const StringName classname = *reinterpret_cast<const StringName *>(p_classname); - return (GDExtensionObjectPtr)ClassDB::instantiate(classname); + return (GDExtensionObjectPtr)ClassDB::instantiate_no_placeholders(classname); } static void *gdextension_classdb_get_class_tag(GDExtensionConstStringNamePtr p_classname) { @@ -1373,7 +1498,20 @@ static void gdextension_editor_remove_plugin(GDExtensionConstStringNamePtr p_cla #endif } -#define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr)&gdextension_##m_name) +static void gdextension_editor_help_load_xml_from_utf8_chars_and_len(const char *p_data, GDExtensionInt p_size) { +#ifdef TOOLS_ENABLED + GDExtensionEditorHelp::load_xml_buffer((const uint8_t *)p_data, p_size); +#endif +} + +static void gdextension_editor_help_load_xml_from_utf8_chars(const char *p_data) { +#ifdef TOOLS_ENABLED + size_t len = strlen(p_data); + gdextension_editor_help_load_xml_from_utf8_chars_and_len(p_data, len); +#endif +} + +#define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr) & gdextension_##m_name) void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(get_godot_version); @@ -1500,22 +1638,30 @@ void gdextension_setup_interface() { REGISTER_INTERFACE_FUNC(object_cast_to); REGISTER_INTERFACE_FUNC(object_get_instance_from_id); REGISTER_INTERFACE_FUNC(object_get_instance_id); + REGISTER_INTERFACE_FUNC(object_has_script_method); + REGISTER_INTERFACE_FUNC(object_call_script_method); REGISTER_INTERFACE_FUNC(ref_get_object); REGISTER_INTERFACE_FUNC(ref_set_object); #ifndef DISABLE_DEPRECATED REGISTER_INTERFACE_FUNC(script_instance_create); -#endif // DISABLE_DEPRECATED REGISTER_INTERFACE_FUNC(script_instance_create2); +#endif // DISABLE_DEPRECATED + REGISTER_INTERFACE_FUNC(script_instance_create3); 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); REGISTER_INTERFACE_FUNC(classdb_get_class_tag); REGISTER_INTERFACE_FUNC(editor_add_plugin); REGISTER_INTERFACE_FUNC(editor_remove_plugin); + REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars); + REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars_and_len); } #undef REGISTER_INTERFACE_FUNCTION diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index d58f0226d8..e9c570e994 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -290,7 +290,7 @@ typedef struct { GDExtensionClassGetVirtual get_virtual_func; // Queries a virtual function by name and returns a callback to invoke the requested virtual function. GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo2 instead. +} GDExtensionClassCreationInfo; // Deprecated. Use GDExtensionClassCreationInfo3 instead. typedef struct { GDExtensionBool is_virtual; @@ -323,7 +323,41 @@ typedef struct { GDExtensionClassCallVirtualWithData call_virtual_with_data_func; GDExtensionClassGetRID get_rid_func; void *class_userdata; // Per-class user data, later accessible in instance bindings. -} GDExtensionClassCreationInfo2; +} GDExtensionClassCreationInfo2; // Deprecated. Use GDExtensionClassCreationInfo3 instead. + +typedef struct { + GDExtensionBool is_virtual; + GDExtensionBool is_abstract; + GDExtensionBool is_exposed; + GDExtensionBool is_runtime; + GDExtensionClassSet set_func; + GDExtensionClassGet get_func; + GDExtensionClassGetPropertyList get_property_list_func; + GDExtensionClassFreePropertyList free_property_list_func; + GDExtensionClassPropertyCanRevert property_can_revert_func; + GDExtensionClassPropertyGetRevert property_get_revert_func; + GDExtensionClassValidateProperty validate_property_func; + GDExtensionClassNotification2 notification_func; + GDExtensionClassToString to_string_func; + GDExtensionClassReference reference_func; + GDExtensionClassUnreference unreference_func; + GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract. + GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory. + GDExtensionClassRecreateInstance recreate_instance_func; + // Queries a virtual function by name and returns a callback to invoke the requested virtual function. + GDExtensionClassGetVirtual get_virtual_func; + // Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that + // need or benefit from extra data when calling virtual functions. + // Returns user data that will be passed to `call_virtual_with_data_func`. + // Returning `NULL` from this function signals to Godot that the virtual function is not overridden. + // Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized. + // You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`. + GDExtensionClassGetVirtualCallData get_virtual_call_data_func; + // Used to call virtual functions when `get_virtual_call_data_func` is not null. + GDExtensionClassCallVirtualWithData call_virtual_with_data_func; + GDExtensionClassGetRID get_rid_func; + void *class_userdata; // Per-class user data, later accessible in instance bindings. +} GDExtensionClassCreationInfo3; typedef void *GDExtensionClassLibraryPtr; @@ -364,13 +398,18 @@ typedef struct { GDExtensionClassMethodPtrCall ptrcall_func; uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. - /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */ + /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. + * + * @todo Consider dropping `has_return_value` and making the other two properties match `GDExtensionMethodInfo` and `GDExtensionClassVirtualMethod` for consistency in future version of this struct. + */ GDExtensionBool has_return_value; GDExtensionPropertyInfo *return_value_info; GDExtensionClassMethodArgumentMetadata return_value_metadata; /* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`. * Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies. + * + * @todo Consider renaming `arguments_info` to `arguments` for consistency in future version of this struct. */ uint32_t argument_count; GDExtensionPropertyInfo *arguments_info; @@ -381,6 +420,18 @@ typedef struct { GDExtensionVariantPtr *default_arguments; } GDExtensionClassMethodInfo; +typedef struct { + GDExtensionStringNamePtr name; + uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`. + + GDExtensionPropertyInfo return_value; + GDExtensionClassMethodArgumentMetadata return_value_metadata; + + uint32_t argument_count; + GDExtensionPropertyInfo *arguments; + GDExtensionClassMethodArgumentMetadata *arguments_metadata; +} GDExtensionClassVirtualMethodInfo; + typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata); typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata); @@ -391,6 +442,39 @@ 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. + * + * `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; +} 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. * @@ -420,7 +504,9 @@ typedef struct { GDExtensionCallableCustomLessThan less_than_func; GDExtensionCallableCustomToString to_string_func; -} GDExtensionCallableCustomInfo; + + GDExtensionCallableCustomGetArgumentCount get_argument_count_func; +} GDExtensionCallableCustomInfo2; /* SCRIPT INSTANCE EXTENSION */ @@ -429,7 +515,8 @@ typedef void *GDExtensionScriptInstanceDataPtr; // Pointer to custom ScriptInsta typedef GDExtensionBool (*GDExtensionScriptInstanceSet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value); typedef GDExtensionBool (*GDExtensionScriptInstanceGet)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret); typedef const GDExtensionPropertyInfo *(*GDExtensionScriptInstanceGetPropertyList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); -typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); +typedef void (*GDExtensionScriptInstanceFreePropertyList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list); // Deprecated. Use GDExtensionScriptInstanceFreePropertyList2 instead. +typedef void (*GDExtensionScriptInstanceFreePropertyList2)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionPropertyInfo *p_list, uint32_t p_count); typedef GDExtensionBool (*GDExtensionScriptInstanceGetClassCategory)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionPropertyInfo *p_class_category); typedef GDExtensionVariantType (*GDExtensionScriptInstanceGetPropertyType)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); @@ -443,10 +530,13 @@ typedef void (*GDExtensionScriptInstancePropertyStateAdd)(GDExtensionConstString typedef void (*GDExtensionScriptInstanceGetPropertyState)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionScriptInstancePropertyStateAdd p_add_func, void *p_userdata); typedef const GDExtensionMethodInfo *(*GDExtensionScriptInstanceGetMethodList)(GDExtensionScriptInstanceDataPtr p_instance, uint32_t *r_count); -typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list); +typedef void (*GDExtensionScriptInstanceFreeMethodList)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list); // Deprecated. Use GDExtensionScriptInstanceFreeMethodList2 instead. +typedef void (*GDExtensionScriptInstanceFreeMethodList2)(GDExtensionScriptInstanceDataPtr p_instance, const GDExtensionMethodInfo *p_list, uint32_t p_count); typedef GDExtensionBool (*GDExtensionScriptInstanceHasMethod)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name); +typedef GDExtensionInt (*GDExtensionScriptInstanceGetMethodArgumentCount)(GDExtensionScriptInstanceDataPtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionBool *r_is_valid); + typedef void (*GDExtensionScriptInstanceCall)(GDExtensionScriptInstanceDataPtr p_self, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); typedef void (*GDExtensionScriptInstanceNotification)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what); // Deprecated. Use GDExtensionScriptInstanceNotification2 instead. typedef void (*GDExtensionScriptInstanceNotification2)(GDExtensionScriptInstanceDataPtr p_instance, int32_t p_what, GDExtensionBool p_reversed); @@ -503,7 +593,7 @@ typedef struct { GDExtensionScriptInstanceFree free_func; -} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo2 instead. +} GDExtensionScriptInstanceInfo; // Deprecated. Use GDExtensionScriptInstanceInfo3 instead. typedef struct { GDExtensionScriptInstanceSet set_func; @@ -544,7 +634,50 @@ typedef struct { GDExtensionScriptInstanceFree free_func; -} GDExtensionScriptInstanceInfo2; +} GDExtensionScriptInstanceInfo2; // Deprecated. Use GDExtensionScriptInstanceInfo3 instead. + +typedef struct { + GDExtensionScriptInstanceSet set_func; + GDExtensionScriptInstanceGet get_func; + GDExtensionScriptInstanceGetPropertyList get_property_list_func; + GDExtensionScriptInstanceFreePropertyList2 free_property_list_func; + GDExtensionScriptInstanceGetClassCategory get_class_category_func; // Optional. Set to NULL for the default behavior. + + GDExtensionScriptInstancePropertyCanRevert property_can_revert_func; + GDExtensionScriptInstancePropertyGetRevert property_get_revert_func; + + GDExtensionScriptInstanceGetOwner get_owner_func; + GDExtensionScriptInstanceGetPropertyState get_property_state_func; + + GDExtensionScriptInstanceGetMethodList get_method_list_func; + GDExtensionScriptInstanceFreeMethodList2 free_method_list_func; + GDExtensionScriptInstanceGetPropertyType get_property_type_func; + GDExtensionScriptInstanceValidateProperty validate_property_func; + + GDExtensionScriptInstanceHasMethod has_method_func; + + GDExtensionScriptInstanceGetMethodArgumentCount get_method_argument_count_func; + + GDExtensionScriptInstanceCall call_func; + GDExtensionScriptInstanceNotification2 notification_func; + + GDExtensionScriptInstanceToString to_string_func; + + GDExtensionScriptInstanceRefCountIncremented refcount_incremented_func; + GDExtensionScriptInstanceRefCountDecremented refcount_decremented_func; + + GDExtensionScriptInstanceGetScript get_script_func; + + GDExtensionScriptInstanceIsPlaceholder is_placeholder_func; + + GDExtensionScriptInstanceSet set_fallback_func; + GDExtensionScriptInstanceGet get_fallback_func; + + GDExtensionScriptInstanceGetLanguage get_language_func; + + GDExtensionScriptInstanceFree free_func; + +} GDExtensionScriptInstanceInfo3; /* INITIALIZATION */ @@ -2223,6 +2356,9 @@ typedef void (*GDExtensionInterfaceObjectSetInstance)(GDExtensionObjectPtr p_o, * * Gets the class name of an Object. * + * If the GDExtension wraps the Godot object in an abstraction specific to its class, this is the + * function that should be used to determine which wrapper to use. + * * @param p_object A pointer to the Object. * @param p_library A pointer the library received by the GDExtension's entry point function. * @param r_class_name A pointer to a String to receive the class name. @@ -2268,6 +2404,34 @@ typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDOb */ typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object); +/** + * @name object_has_script_method + * @since 4.3 + * + * Checks if this object has a script with the given method. + * + * @param p_object A pointer to the Object. + * @param p_method A pointer to a StringName identifying the method. + * + * @returns true if the object has a script and that script has a method with the given name. Returns false if the object has no script. + */ +typedef GDExtensionBool (*GDExtensionInterfaceObjectHasScriptMethod)(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method); + +/** + * @name object_call_script_method + * @since 4.3 + * + * Call the given script method on this object. + * + * @param p_object A pointer to the Object. + * @param p_method A pointer to a StringName identifying the method. + * @param p_args A pointer to a C array of Variant. + * @param p_argument_count The number of arguments. + * @param r_return A pointer a Variant which will be assigned the return value. + * @param r_error A pointer the structure which will hold error information. + */ +typedef void (*GDExtensionInterfaceObjectCallScriptMethod)(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error); + /* INTERFACE: Reference */ /** @@ -2298,7 +2462,7 @@ typedef void (*GDExtensionInterfaceRefSetObject)(GDExtensionRefPtr p_ref, GDExte /** * @name script_instance_create * @since 4.1 - * @deprecated in Godot 4.2. Use `script_instance_create2` instead. + * @deprecated in Godot 4.2. Use `script_instance_create3` instead. * * Creates a script instance that contains the given info and instance data. * @@ -2312,6 +2476,7 @@ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate) /** * @name script_instance_create2 * @since 4.2 + * @deprecated in Godot 4.3. Use `script_instance_create3` instead. * * Creates a script instance that contains the given info and instance data. * @@ -2323,6 +2488,19 @@ typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate) typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate2)(const GDExtensionScriptInstanceInfo2 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); /** + * @name script_instance_create3 + * @since 4.3 + * + * Creates a script instance that contains the given info and instance data. + * + * @param p_info A pointer to a GDExtensionScriptInstanceInfo3 struct. + * @param p_instance_data A pointer to a data representing the script instance in the GDExtension. This will be passed to all the function pointers on p_info. + * + * @return A pointer to a ScriptInstanceExtension object. + */ +typedef GDExtensionScriptInstancePtr (*GDExtensionInterfaceScriptInstanceCreate3)(const GDExtensionScriptInstanceInfo3 *p_info, GDExtensionScriptInstanceDataPtr p_instance_data); + +/** * @name placeholder_script_instance_create * @since 4.2 * @@ -2371,6 +2549,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. * @@ -2382,6 +2561,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 * @@ -2441,7 +2633,7 @@ typedef void *(*GDExtensionInterfaceClassdbGetClassTag)(GDExtensionConstStringNa /** * @name classdb_register_extension_class * @since 4.1 - * @deprecated in Godot 4.2. Use `classdb_register_extension_class2` instead. + * @deprecated in Godot 4.2. Use `classdb_register_extension_class3` instead. * * Registers an extension class in the ClassDB. * @@ -2457,6 +2649,7 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla /** * @name classdb_register_extension_class2 * @since 4.2 + * @deprecated in Godot 4.3. Use `classdb_register_extension_class3` instead. * * Registers an extension class in the ClassDB. * @@ -2470,6 +2663,21 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass)(GDExtensionCla typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs); /** + * @name classdb_register_extension_class3 + * @since 4.3 + * + * Registers an extension class in the ClassDB. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_parent_class_name A pointer to a StringName with the parent class name. + * @param p_extension_funcs A pointer to a GDExtensionClassCreationInfo2 struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass3)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo3 *p_extension_funcs); + +/** * @name classdb_register_extension_class_method * @since 4.1 * @@ -2484,6 +2692,20 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info); /** + * @name classdb_register_extension_class_virtual_method + * @since 4.3 + * + * Registers a virtual method on an extension class in ClassDB, that can be implemented by scripts or other extensions. + * + * Provided struct can be safely freed once the function returns. + * + * @param p_library A pointer the library received by the GDExtension's entry point function. + * @param p_class_name A pointer to a StringName with the class name. + * @param p_method_info A pointer to a GDExtensionClassMethodInfo struct. + */ +typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info); + +/** * @name classdb_register_extension_class_integer_constant * @since 4.1 * @@ -2617,6 +2839,31 @@ typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePt */ typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name); +/** + * @name editor_help_load_xml_from_utf8_chars + * @since 4.3 + * + * Loads new XML-formatted documentation data in the editor. + * + * The provided pointer can be immediately freed once the function returns. + * + * @param p_data A pointer to an UTF-8 encoded C string (null terminated). + */ +typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars)(const char *p_data); + +/** + * @name editor_help_load_xml_from_utf8_chars_and_len + * @since 4.3 + * + * Loads new XML-formatted documentation data in the editor. + * + * The provided pointer can be immediately freed once the function returns. + * + * @param p_data A pointer to an UTF-8 encoded C string. + * @param p_size The number of bytes (not code units). + */ +typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const char *p_data, GDExtensionInt p_size); + #ifdef __cplusplus } #endif diff --git a/core/extension/make_interface_dumper.py b/core/extension/make_interface_dumper.py index a85d62eff3..af35688200 100644 --- a/core/extension/make_interface_dumper.py +++ b/core/extension/make_interface_dumper.py @@ -2,20 +2,18 @@ import zlib def run(target, source, env): - src = source[0] - dst = target[0] - f = open(src, "rb") - g = open(dst, "w", encoding="utf-8") - - buf = f.read() - decomp_size = len(buf) - - # Use maximum zlib compression level to further reduce file size - # (at the cost of initial build times). - buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) - - g.write( - """/* THIS FILE IS GENERATED DO NOT EDIT */ + src = str(source[0]) + dst = str(target[0]) + with open(src, "rb") as f, open(dst, "w", encoding="utf-8", newline="\n") as g: + buf = f.read() + decomp_size = len(buf) + + # Use maximum zlib compression level to further reduce file size + # (at the cost of initial build times). + buf = zlib.compress(buf, zlib.Z_BEST_COMPRESSION) + + g.write( + """/* THIS FILE IS GENERATED DO NOT EDIT */ #ifndef GDEXTENSION_INTERFACE_DUMP_H #define GDEXTENSION_INTERFACE_DUMP_H @@ -26,17 +24,17 @@ def run(target, source, env): #include "core/string/ustring.h" """ - ) + ) - g.write("static const int _gdextension_interface_data_compressed_size = " + str(len(buf)) + ";\n") - g.write("static const int _gdextension_interface_data_uncompressed_size = " + str(decomp_size) + ";\n") - g.write("static const unsigned char _gdextension_interface_data_compressed[] = {\n") - for i in range(len(buf)): - g.write("\t" + str(buf[i]) + ",\n") - g.write("};\n") + g.write("static const int _gdextension_interface_data_compressed_size = " + str(len(buf)) + ";\n") + g.write("static const int _gdextension_interface_data_uncompressed_size = " + str(decomp_size) + ";\n") + g.write("static const unsigned char _gdextension_interface_data_compressed[] = {\n") + for i in range(len(buf)): + g.write("\t" + str(buf[i]) + ",\n") + g.write("};\n") - g.write( - """ + g.write( + """ class GDExtensionInterfaceDump { public: static void generate_gdextension_interface_file(const String &p_path) { @@ -54,12 +52,4 @@ class GDExtensionInterfaceDump { #endif // GDEXTENSION_INTERFACE_DUMP_H """ - ) - g.close() - f.close() - - -if __name__ == "__main__": - from platform_methods import subprocess_main - - subprocess_main(globals()) + ) diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py index 1e4634ad2c..655b90d2b1 100644 --- a/core/extension/make_wrappers.py +++ b/core/extension/make_wrappers.py @@ -142,11 +142,5 @@ def run(target, source, env): txt += "\n#endif\n" - with open(target[0], "w") as f: + with open(str(target[0]), "w", encoding="utf-8", newline="\n") as f: f.write(txt) - - -if __name__ == "__main__": - from platform_methods import subprocess_main - - subprocess_main(globals()) diff --git a/core/input/SCsub b/core/input/SCsub index b12bf561de..da29637135 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -14,7 +14,7 @@ controller_databases = [ gensource = env.CommandNoCache( "default_controller_mappings.gen.cpp", controller_databases, - env.Run(input_builders.make_default_controller_mappings, "Generating default controller mappings."), + env.Run(input_builders.make_default_controller_mappings), ) env.add_source_files(env.core_sources, "*.cpp") diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 77655e9b6a..7150911e75 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -23,7 +23,7 @@ 03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows, -03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, @@ -104,6 +104,7 @@ 030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,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:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows, 03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows, 03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows, +0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows, 03000000120c0000100e000000000000,Armor 3,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, 03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, @@ -114,6 +115,10 @@ 03000000050b00000679000000000000,ASUS ROG Kunai 3,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,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows, 03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, +030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows, 03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d6200000e557000000000000,Batarang PlayStation 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, 03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows, @@ -167,6 +172,7 @@ 03000000317300000100000000000000,DualShock 3,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:a5,start:b11,x:b3,y:b4,platform:Windows, 030000006f0e00003001000000000000,EA Sports 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, 03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows, +03000000bc2000000091000000000000,EasySMX Controller,a:b2,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:b3,y:b0,platform:Windows, 030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows, 03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,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:b0,y:b1,platform:Windows, 03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,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:b0,y:b1,platform:Windows, @@ -254,6 +260,8 @@ 030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008400000000000000,Hori Fighting Commander 5,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:a5,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,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,start:b9,x:b0,y:b3,platform:Windows, +030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,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:a5,start:b9,x:b0,y:b3,platform:Windows, 030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,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:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, @@ -314,6 +322,7 @@ 03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows, 03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows, 03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows, +03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows, 03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows, 03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows, 03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,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:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows, @@ -352,7 +361,7 @@ 030000006d040000d2ca000000000000,Logitech Cordless Precision,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, 030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows, 030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,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:b0,y:b3,platform:Windows, -030000006d0400001dc2000000000000,Logitech F310,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:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, +030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,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:b0,y:b3,platform:Windows, 030000006d0400001ec2000000000000,Logitech F510,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:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,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:b0,y:b3,platform:Windows, @@ -429,6 +438,8 @@ 03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows, 030000006b140000010c000000000000,Nacon GC 400ES,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, 030000006b1400001106000000000000,Nacon Revolution 3 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +0300000085320000170d000000000000,Nacon Revolution 5 Pro,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:Windows, +0300000085320000190d000000000000,Nacon Revolution 5 Pro,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:Windows, 030000006b140000100d000000000000,Nacon Revolution Infinity 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:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows, 030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,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:b0,y:b3,platform:Windows, 03000000bd12000001c0000000000000,Nebular,a:b2,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:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows, @@ -450,7 +461,7 @@ 03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows, 030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows, -030000004b120000014d000000000000,Nyko Airflo,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows, +030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows, 03000000d62000001d57000000000000,Nyko Airflo 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, 03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows, 03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows, @@ -459,7 +470,7 @@ 030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000d62000006d57000000000000,OPP 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, 030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows, -03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, +03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows, 03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows, 03000000790000002201000000000000,PC Controller,a:b2,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:b3,y:b0,platform:Windows, 030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,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, @@ -532,6 +543,7 @@ 030000004c050000a00b000000000000,PS4 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:Windows, 030000004c050000c405000000000000,PS4 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:Windows, 030000004c050000cc09000000000000,PS4 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:Windows, +030000004c0500005f0e000000000000,PS5 Access 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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000e60c000000000000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 030000004c050000f20d000000000000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows, 03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Windows, @@ -552,16 +564,21 @@ 030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, 030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows, 030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows, +030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows, 030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows, 030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows, +030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows, 030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows, 030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows, 030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows, 030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 030000009b2800000300000000000000,Raphnet SNES Adapter,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, +030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, +030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows, 030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows, @@ -608,6 +625,7 @@ 03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows, 030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows, +030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,platform:Windows, 030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,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:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, @@ -646,6 +664,7 @@ 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, +03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,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, @@ -674,6 +693,7 @@ 03000000457500002211000000000000,Szmy Power PC Gamepad,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, 030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows, 030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,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:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, 03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows, 03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,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:b0,y:b3,platform:Windows, 03000000fa1900000706000000000000,Team 5,a:b2,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:b3,y:b0,platform:Windows, @@ -780,6 +800,7 @@ 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, xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows, 030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows, +030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows, 03000000790000004f18000000000000,ZDT Android 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:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows, 03000000120c0000101e000000000000,Zeroplus P4 Gamepad,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:Windows, @@ -797,7 +818,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,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:b4,y:b3,platform:Mac OS X, 03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X, 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X, @@ -806,7 +827,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X, 03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, -03000000c82d00002690000000010000,8BitDo NEOGEOa:b0,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X, @@ -822,6 +843,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X, +03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, @@ -843,6 +865,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X, +0300000008100000e501000019040000,Anbernic Gamepad,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,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,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:b9,x:b2,y:b3,platform:Mac OS X, @@ -850,6 +873,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X, 03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X, +030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000201000000010000,Backbone One,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, +030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, +030000008a3500000402000000010000,Backbone One,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, +030000008a3500000302000000010000,Backbone One PlayStationÆ Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X, 03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X, 03000000c62400001b89000000010000,BDA MOGA XP5-X 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, 03000000d62000002a79000000010000,BDA PS4 Fightpad,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, @@ -889,11 +917,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00003801000008010000,Hori PC Engine Mini Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,platform:Mac OS X, 030000000d0f00009200000000010000,Hori Pokken Tournament DX Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000aa00000072050000,Hori Real Arcade Pro for Nintendo Switch,a:b2,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:b3,y:b0,platform:Mac OS X, +030000000d0f00000002000017010000,Hori Split Pad Fit,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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00000002000015010000,Hori Switch Split Pad Pro,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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f00006e00000000010000,Horipad 4 PS3,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:Mac OS X, 030000000d0f00006600000000010000,Horipad 4 PS4,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, 030000000d0f00006600000000000000,Horipad FPS Plus 4,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:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X, 030000000d0f0000ee00000000010000,Horipad Mini 4,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:a5,start:b9,x:b0,y:b3,platform:Mac OS X, +030000000d0f0000c100000072050000,Horipad Nintendo Switch 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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X, 03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X, 03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X, @@ -940,7 +970,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X, 030000007e0500000920000000000000,Nintendo Switch Pro Controller,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:Mac OS X, 030000007e0500000920000001000000,Nintendo Switch Pro Controller,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:Mac OS X, -050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:Mac OS X, +030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X, +050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X, 030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X, 030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X, 03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X, @@ -961,6 +992,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000004c050000c405000000000000,PS4 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, 030000004c050000c405000000010000,PS4 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, 030000004c050000cc09000000010000,PS4 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, +0300004b4c0500005f0e000000010000,PS5 Access 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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000e60c000000010000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 030000004c050000f20d000000010000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X, 050000004c050000e60c000000010000,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:Mac OS X, @@ -968,6 +1000,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000222c00000225000000010000,Qanba Dragon Arcade Joystick (PS3),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:Mac OS X, 03000000222c00000020000000010000,Qanba Drone Arcade Stick,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, +030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X, +030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X, 030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000321500000204000000010000,Razer Panthera 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:Mac OS X, 03000000321500000104000000010000,Razer Panthera PS4,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, @@ -978,7 +1012,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000003215000000090000163a0000,Razer Serval,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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X, 0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X, 03000000632500008005000000010000,Redgear,a:b2,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:b3,y:b0,platform:Mac OS X, -030000000d0f0000c100000072050000,Retro Bit Sega Genesis 6B Controller,a:b2,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b8,rightshoulder:b6,righttrigger:b7,start:b9,x:b3,y:b0,platform:Mac OS X, 03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,platform:Mac OS X, 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, @@ -1013,6 +1046,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X, 030000000d0f0000f600000000010000,Switch Hori Pad,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:Mac OS X, 03000000457500002211000000010000,SZMY Power PC Gamepad,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:Mac OS X, +03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, +03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X, 03000000790000001c18000003100000,TGZ Controller,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:Mac OS X, 03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X, 03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X, @@ -1053,12 +1088,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000130b000009050000,Xbox Series 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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 030000005e040000130b000013050000,Xbox Series 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:Mac OS X, 030000005e040000130b000015050000,Xbox Series 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:Mac OS X, +030000005e040000130b000017050000,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:Mac OS X, +030000005e040000220b000017050000,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:Mac OS X, 03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000120c0000100e000000010000,Zeroplus P4,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, 03000000120c0000101e000000010000,Zeroplus P4,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, # Linux -030000005e0400008e02000020010000,8BitDo Adapter,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, 03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,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, 03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux, 03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,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:b4,y:b3,platform:Linux, @@ -1069,7 +1105,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Linux, +03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux, 03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, 05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux, @@ -1141,6 +1177,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux, 05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux, +0300000008100000e501000001010000,Anbernic Gamepad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux, 03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, 03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, @@ -1148,30 +1185,35 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux, 03000000050b00000579000011010000,ASUS ROG Kunai 3,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,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 05000000050b00000679000000010000,ASUS ROG Kunai 3,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,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -03000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000110000011010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000044010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -05000000503200000110000046010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, -03000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, -03000000503200000210000011010000,Atari 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:b3,y:b2,platform:Linux, -05000000503200000210000000000000,Atari 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:b3,y:b2,platform:Linux, -05000000503200000210000045010000,Atari 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:b3,y:b2,platform:Linux, -05000000503200000210000046010000,Atari 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:b3,y:b2,platform:Linux, +03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux, +03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, +03000000503200000210000011010000,Atari VCS Modern 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:b3,y:b2,platform:Linux, +05000000503200000210000000000000,Atari VCS Modern 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:b3,y:b2,platform:Linux, +05000000503200000210000045010000,Atari VCS Modern 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:b3,y:b2,platform:Linux, +05000000503200000210000046010000,Atari VCS Modern 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:b3,y:b2,platform:Linux, 05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux, +030000008a3500000201000011010000,Backbone One,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, +030000008a3500000202000011010000,Backbone One,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, +030000008a3500000302000011010000,Backbone One,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, +030000008a3500000402000011010000,Backbone One,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, 03000000c62400001b89000011010000,BDA MOGA XP5X 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:Linux, 03000000d62000002a79000011010000,BDA PS4 Fightpad,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:Linux, 03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,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:b3,y:b0,platform:Linux, 03000000c31100000791000011010000,Be1 GC101 Controller 1.03,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, 030000005e0400008e02000003030000,Be1 GC101 Xbox 360,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, -03000000bc2000004d50000011010000,BEITONG A1T2 BFM,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,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, -05000000bc2000000055000001000000,BETOP AX1 BFM,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,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +03000000bc2000004d50000011010000,Beitong A1T2 BFM,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,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, +05000000bc2000000055000001000000,Betop AX1 BFM,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,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux, 030000006b1400000209000011010000,Bigben,a:b1,b:b2,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:b0,y:b3,platform:Linux, 03000000120c0000200e000011010000,Brook Mars PS4 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:Linux, 03000000120c0000210e000011010000,Brook Mars PS4 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:Linux, 03000000120c0000f70e000011010000,Brook Universal Fighting Board,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,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 03000000e82000006058000001010000,Cideko AK08b,a:b2,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:b3,y:b0,platform:Linux, +03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux, 030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux, 03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux, 03000000a306000022f6000011010000,Cyborg V3 Rumble,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:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux, @@ -1218,7 +1260,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 06000000adde0000efbe000002010000,Hidromancer 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, 03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux, 03000000c9110000f055000011010000,HJC Gamepad,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, -03000000632500002605000010010000,HJDX,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:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux, 030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux, 030000000d0f00006d00000020010000,Hori EDGE 301,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, 030000000d0f00008400000011010000,Hori Fighting Commander,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:Linux, @@ -1242,6 +1283,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000000d0f00006b00000011010000,Hori Real Arcade Pro 4,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:Linux, 030000000d0f00001600000000010000,Hori Real Arcade Pro EXSE,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b2,y:b3,platform:Linux, 030000000d0f0000aa00000011010000,Hori Real Arcade Pro for Nintendo Switch,a:b2,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:b3,y:b0,platform:Linux, +030000000d0f00008501000017010000,Hori Split Pad Fit,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, 030000000d0f00008501000015010000,Hori Switch Split Pad Pro,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, 030000000d0f00006e00000011010000,Horipad 4 PS3,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:Linux, 030000000d0f00006600000011010000,Horipad 4 PS4,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:Linux, @@ -1256,6 +1298,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000242e00008816000001010000,Hyperkin X91,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, 03000000f00300008d03000011010000,HyperX Clutch,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, 03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux, +030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux, 050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,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:b0,y:b3,platform:Linux, 03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, 03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux, @@ -1264,7 +1307,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux, 03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux, 0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,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, -03000000632500007505000011010000,Ipega PG 9099,a:b2,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:b3,y:b0,platform:Linux, +03000000632500007505000011010000,Ipega PG 9099,a:b2,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:b3,y:b0,platform:Linux, 0500000049190000030400001b010000,Ipega PG9099,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, 05000000491900000204000000000000,Ipega PG9118,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, 03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux, @@ -1364,7 +1407,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux, 060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux, -030000009b2800008000000020020000,Nintendo Classic Controller,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux, 03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux, 060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, @@ -1389,10 +1431,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, 05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux, +030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000451300000830000010010000,NYKO CORE,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:a5,start:b9,x:b0,y:b3,platform:Linux, 19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux, 19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux, -03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux, 05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux, 05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux, @@ -1407,6 +1449,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006f0e0000c802000012010000,PDP Kingdom Hearts 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, 030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,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:b0,y:b3,platform:Linux, 030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, +030000006f0e00002f01000011010000,PDP Wired 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:Linux, 03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux, 030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,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, 030000006f0e0000a702000023020000,PDP Xbox One Raven Black,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, @@ -1463,6 +1506,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004c050000cc09000000010000,PS4 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:Linux, 050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, +0300004b4c0500005f0e000011010000,PS5 Access 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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000e60c000011010000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, 030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux, 030000004c050000f20d000011010000,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,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux, @@ -1480,6 +1524,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, 030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, 030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux, +030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux, 03000000f8270000bf0b000011010000,Razer Kishi,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, 030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,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, 03000000321500000204000011010000,Razer Panthera PS3,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:Linux, @@ -1528,9 +1573,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux, 030000001f08000001e4000010010000,SFC 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:Linux, 03000000632500002305000010010000,ShanWan Gamepad,a:b2,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:b3,y:b0,platform:Linux, +03000000632500002605000010010000,Shanwan Gamepad,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, +03000000632500007505000010010000,Shanwan Gamepad,a:b2,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:b3,y:b0,platform:Linux, +03000000bc2000000055000010010000,Shanwan Gamepad,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, 03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,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:b3,y:b0,platform:Linux, -03000000632500007505000010010000,Shanwan PS3 PC,a:b2,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:b3,y:b0,platform:Linux, -03000000bc2000000055000010010000,Shanwan PS3 PC ,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, 03000000341a00000908000010010000,SL6566,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, 050000004c050000cc09000001000000,Sony DualShock 4,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:Linux, 03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, @@ -1550,6 +1596,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, +03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux, 03000000de280000ff11000001000000,Steam Virtual Gamepad,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, 050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux, 03000000381000003014000075010000,SteelSeries Stratus Duo,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, @@ -1564,6 +1611,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000457500000401000011010000,SZMY Power DS4 Wired 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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 03000000457500002211000010010000,SZMY Power Gamepad,a:b2,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:b3,y:b0,platform:Linux, 030000008f0e00001431000010010000,SZMY Power PS3,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:Linux, +03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, +03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux, 03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,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:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux, 03000000790000001c18000011010000,TGZ Controller,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, 03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux, @@ -1647,11 +1696,17 @@ 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, +050000005e040000130b000017050000,Xbox Series X 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,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,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, +030000005e040000120b000011050000,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, +030000005e040000120b000014050000,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, +050000005e040000200b000017050000,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, +050000005e040000220b000017050000,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, 03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux, +030000005e0400008e02000020010000,XInput Adapter,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, xinput,XInput 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, 03000000120c0000100e000011010000,Zeroplus P4,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:Linux, 03000000120c0000101e000011010000,Zeroplus P4,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:Linux, @@ -1738,6 +1793,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,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,x:b2,y:b3,platform:Android, +4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android, 417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android, 35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,platform:Android, 32353831643566306563643065356239,Atari VCS Modern 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, @@ -1871,9 +1927,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android, 526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android, 61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android, +526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, +582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, -526574726f696420506f636b65742043,Retroid Pocket Flip,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android, -582d426f7820436f6e74726f6c6c6572,Retroid Pocket Flip,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android, 37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, 5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android, @@ -1902,6 +1958,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android, 0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android, 35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android, +33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android, 54475a20436f6e74726f6c6c65720000,TGZ Controller,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, 62363434353532386238336663643836,TGZ Controller,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, 37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android, diff --git a/core/input/input.cpp b/core/input/input.cpp index 2d48bdd4cf..c24a59203f 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -130,6 +130,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer); ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope); ClassDB::bind_method(D_METHOD("get_last_mouse_velocity"), &Input::get_last_mouse_velocity); + ClassDB::bind_method(D_METHOD("get_last_mouse_screen_velocity"), &Input::get_last_mouse_screen_velocity); ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask); ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode); @@ -143,9 +144,15 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); ClassDB::bind_method(D_METHOD("is_using_accumulated_input"), &Input::is_using_accumulated_input); ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events); + ClassDB::bind_method(D_METHOD("set_emulate_mouse_from_touch", "enable"), &Input::set_emulate_mouse_from_touch); + ClassDB::bind_method(D_METHOD("is_emulating_mouse_from_touch"), &Input::is_emulating_mouse_from_touch); + ClassDB::bind_method(D_METHOD("set_emulate_touch_from_mouse", "enable"), &Input::set_emulate_touch_from_mouse); + ClassDB::bind_method(D_METHOD("is_emulating_touch_from_mouse"), &Input::is_emulating_touch_from_mouse); ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse"); BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); @@ -174,8 +181,9 @@ void Input::_bind_methods() { ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); } +#ifdef TOOLS_ENABLED void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { - String pf = p_function; + const String pf = p_function; if ((p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength" || pf == "get_action_raw_strength")) || (p_idx < 2 && pf == "get_axis") || @@ -192,9 +200,11 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S r_options->push_back(name.quote()); } } + Object::get_argument_options(p_function, p_idx, r_options); } +#endif -void Input::VelocityTrack::update(const Vector2 &p_delta_p) { +void Input::VelocityTrack::update(const Vector2 &p_delta_p, const Vector2 &p_screen_delta_p) { uint64_t tick = OS::get_singleton()->get_ticks_usec(); uint32_t tdiff = tick - last_tick; float delta_t = tdiff / 1000000.0; @@ -203,12 +213,15 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) { if (delta_t > max_ref_frame) { // First movement in a long time, reset and start again. velocity = Vector2(); + screen_velocity = Vector2(); accum = p_delta_p; + screen_accum = p_screen_delta_p; accum_t = 0; return; } accum += p_delta_p; + screen_accum += p_screen_delta_p; accum_t += delta_t; if (accum_t < min_ref_frame) { @@ -217,6 +230,7 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) { } velocity = accum / accum_t; + screen_velocity = screen_accum / accum_t; accum = Vector2(); accum_t = 0; } @@ -241,8 +255,8 @@ bool Input::is_anything_pressed() const { return true; } - for (const KeyValue<StringName, Input::Action> &E : action_state) { - if (E.value.pressed) { + for (const KeyValue<StringName, Input::ActionState> &E : action_states) { + if (E.value.cache.pressed) { return true; } } @@ -285,12 +299,17 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const { bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); + if (!E) { + return false; + } + + return E->value.cache.pressed && (p_exact ? E->value.exact : true); } bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; } @@ -300,7 +319,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con } // Backward compatibility for legacy behavior, only return true if currently pressed. - bool pressed_requirement = legacy_just_pressed_behavior ? E->value.pressed : true; + bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true; if (Engine::get_singleton()->is_in_physics_frame()) { return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames(); @@ -311,7 +330,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return false; } @@ -321,7 +340,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co } // Backward compatibility for legacy behavior, only return true if currently released. - bool released_requirement = legacy_just_pressed_behavior ? !E->value.pressed : true; + bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true; if (Engine::get_singleton()->is_in_physics_frame()) { return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames(); @@ -332,7 +351,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co float Input::get_action_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; } @@ -341,12 +360,12 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const return 0.0f; } - return E->value.strength; + return E->value.cache.strength; } float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const { ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action)); - HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action); + HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action); if (!E) { return 0.0f; } @@ -355,7 +374,7 @@ float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) c return 0.0f; } - return E->value.raw_strength; + return E->value.cache.raw_strength; } float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const { @@ -438,8 +457,20 @@ static String _hex_str(uint8_t p_byte) { return ret; } -void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) { +void Input::joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid, const Dictionary &p_joypad_info) { _THREAD_SAFE_METHOD_ + + // Clear the pressed status if a Joypad gets disconnected. + if (!p_connected) { + for (KeyValue<StringName, ActionState> &E : action_states) { + HashMap<int, ActionState::DeviceState>::Iterator it = E.value.device_states.find(p_idx); + if (it) { + E.value.device_states.remove(it); + _update_action_cache(E.key, E.value); + } + } + } + Joypad js; js.name = p_connected ? p_name : ""; js.uid = p_connected ? p_guid : ""; @@ -570,7 +601,8 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em set_mouse_position(position); } Vector2 relative = mm->get_relative(); - mouse_velocity_track.update(relative); + Vector2 screen_relative = mm->get_relative_screen_position(); + mouse_velocity_track.update(relative, screen_relative); if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) { Ref<InputEventScreenDrag> drag_event; @@ -578,10 +610,12 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em drag_event->set_position(position); drag_event->set_relative(relative); + drag_event->set_relative_screen_position(screen_relative); drag_event->set_tilt(mm->get_tilt()); drag_event->set_pen_inverted(mm->get_pen_inverted()); drag_event->set_pressure(mm->get_pressure()); drag_event->set_velocity(get_last_mouse_velocity()); + drag_event->set_screen_velocity(get_last_mouse_screen_velocity()); drag_event->set_device(InputEvent::DEVICE_ID_EMULATION); _THREAD_SAFE_UNLOCK_ @@ -645,8 +679,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em if (sd.is_valid()) { VelocityTrack &track = touch_velocity_track[sd->get_index()]; - track.update(sd->get_relative()); + track.update(sd->get_relative(), sd->get_relative_screen_position()); sd->set_velocity(track.velocity); + sd->set_screen_velocity(track.screen_velocity); if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) { Ref<InputEventMouseMotion> motion_event; @@ -659,7 +694,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em motion_event->set_position(sd->get_position()); motion_event->set_global_position(sd->get_position()); motion_event->set_relative(sd->get_relative()); + motion_event->set_relative_screen_position(sd->get_relative_screen_position()); motion_event->set_velocity(sd->get_velocity()); + motion_event->set_screen_velocity(sd->get_screen_velocity()); motion_event->set_button_mask(mouse_button_mask); _parse_input_event_impl(motion_event, true); @@ -695,52 +732,38 @@ 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)) { - 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++; - } - 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 (is_joypad_axis) { - if (action.axis_pressed) { - action.axis_pressed = false; - } else { - is_released = false; - } - } - - if (is_released) { - if (action.pressed == 1) { - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - } - action.pressed = MAX(action.pressed - 1, 0); - } - } - action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); - } + const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key); + if (event_index == -1) { + continue; + } + ERR_FAIL_COND_MSG(event_index >= (int)MAX_EVENT, vformat("Input singleton does not support more than %d events assigned to an action.", MAX_EVENT)); + + int device_id = p_event->get_device(); + bool is_pressed = p_event->is_action_pressed(E.key, true); + ActionState &action_state = action_states[E.key]; + + // Update the action's per-device state. + ActionState::DeviceState &device_state = action_state.device_states[device_id]; + device_state.pressed[event_index] = is_pressed; + device_state.strength[event_index] = p_event->get_action_strength(E.key); + device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key); + + // Update the action's global state and cache. + if (!is_pressed) { + action_state.api_pressed = false; // Always release the event from action_press() method. + action_state.api_strength = 0.0; + } + action_state.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true); - if (is_pressed || action.pressed == 0) { - action.strength = p_event->get_action_strength(E.key); - action.raw_strength = p_event->get_action_raw_strength(E.key); - } + bool was_pressed = action_state.cache.pressed; + _update_action_cache(E.key, action_state); + if (action_state.cache.pressed && !was_pressed) { + action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + } + if (!action_state.cache.pressed && was_pressed) { + action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); } } @@ -817,10 +840,15 @@ Point2 Input::get_mouse_position() const { } Point2 Input::get_last_mouse_velocity() { - mouse_velocity_track.update(Vector2()); + mouse_velocity_track.update(Vector2(), Vector2()); return mouse_velocity_track.velocity; } +Point2 Input::get_last_mouse_screen_velocity() { + mouse_velocity_track.update(Vector2(), Vector2()); + return mouse_velocity_track.screen_velocity; +} + BitField<MouseButtonMask> Input::get_mouse_button_mask() const { return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state(); } @@ -855,31 +883,35 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con } void Input::action_press(const StringName &p_action, float p_strength) { + ERR_FAIL_COND_MSG(!InputMap::get_singleton()->has_action(p_action), InputMap::get_singleton()->suggest_actions(p_action)); + // Create or retrieve existing action. - Action &action = action_state[p_action]; + ActionState &action_state = action_states[p_action]; - action.pressed++; - if (action.pressed == 1) { - action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.pressed_process_frame = Engine::get_singleton()->get_process_frames(); + if (!action_state.cache.pressed) { + action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames(); } - action.strength = p_strength; - action.raw_strength = p_strength; - action.exact = true; + action_state.exact = true; + action_state.api_pressed = true; + action_state.api_strength = CLAMP(p_strength, 0.0f, 1.0f); + _update_action_cache(p_action, action_state); } void Input::action_release(const StringName &p_action) { - // Create or retrieve existing action. - Action &action = action_state[p_action]; + ERR_FAIL_COND_MSG(!InputMap::get_singleton()->has_action(p_action), InputMap::get_singleton()->suggest_actions(p_action)); - action.pressed--; - if (action.pressed == 0) { - action.released_physics_frame = Engine::get_singleton()->get_physics_frames(); - action.released_process_frame = Engine::get_singleton()->get_process_frames(); - } - action.strength = 0.0f; - action.raw_strength = 0.0f; - action.exact = true; + // Create or retrieve existing action. + ActionState &action_state = action_states[p_action]; + action_state.cache.pressed = 0; + action_state.cache.strength = 0.0; + action_state.cache.raw_strength = 0.0; + action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames(); + action_state.released_process_frame = Engine::get_singleton()->get_process_frames(); + action_state.device_states.clear(); + action_state.exact = true; + action_state.api_pressed = false; + action_state.api_strength = 0.0; } void Input::set_emulate_touch_from_mouse(bool p_emulate) { @@ -1037,10 +1069,8 @@ void Input::release_pressed_events() { joy_buttons_pressed.clear(); _joy_axis.clear(); - for (KeyValue<StringName, Input::Action> &E : action_state) { - if (E.value.pressed > 0) { - // Make sure the action is really released. - E.value.pressed = 1; + for (KeyValue<StringName, Input::ActionState> &E : action_states) { + if (E.value.cache.pressed) { action_release(E.key); } } @@ -1207,6 +1237,29 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) { parse_input_event(ievent); } +void Input::_update_action_cache(const StringName &p_action_name, ActionState &r_action_state) { + // Update the action cache, computed from the per-device and per-event states. + r_action_state.cache.pressed = false; + r_action_state.cache.strength = 0.0; + r_action_state.cache.raw_strength = 0.0; + + int max_event = InputMap::get_singleton()->action_get_events(p_action_name)->size(); + for (const KeyValue<int, ActionState::DeviceState> &kv : r_action_state.device_states) { + const ActionState::DeviceState &device_state = kv.value; + for (int i = 0; i < max_event; i++) { + r_action_state.cache.pressed = r_action_state.cache.pressed || device_state.pressed[i]; + r_action_state.cache.strength = MAX(r_action_state.cache.strength, device_state.strength[i]); + r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, device_state.raw_strength[i]); + } + } + + if (r_action_state.api_pressed) { + r_action_state.cache.pressed = true; + r_action_state.cache.strength = MAX(r_action_state.cache.strength, r_action_state.api_strength); + r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, r_action_state.api_strength); // Use the strength as raw_strength for API-pressed states. + } +} + Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) { JoyEvent event; @@ -1363,7 +1416,7 @@ void Input::_get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat } } -JoyButton Input::_get_output_button(String output) { +JoyButton Input::_get_output_button(const String &output) { for (int i = 0; i < (int)JoyButton::SDL_MAX; i++) { if (output == _joy_buttons[i]) { return JoyButton(i); @@ -1372,7 +1425,7 @@ JoyButton Input::_get_output_button(String output) { return JoyButton::INVALID; } -JoyAxis Input::_get_output_axis(String output) { +JoyAxis Input::_get_output_axis(const String &output) { for (int i = 0; i < (int)JoyAxis::SDL_MAX; i++) { if (output == _joy_axes[i]) { return JoyAxis(i); @@ -1381,7 +1434,7 @@ JoyAxis Input::_get_output_axis(String output) { return JoyAxis::INVALID; } -void Input::parse_mapping(String p_mapping) { +void Input::parse_mapping(const String &p_mapping) { _THREAD_SAFE_METHOD_; JoyDeviceMapping mapping; @@ -1484,11 +1537,11 @@ void Input::parse_mapping(String p_mapping) { map_db.push_back(mapping); } -void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { +void Input::add_joy_mapping(const String &p_mapping, bool p_update_existing) { parse_mapping(p_mapping); if (p_update_existing) { Vector<String> entry = p_mapping.split(","); - String uid = entry[0]; + const String &uid = entry[0]; for (KeyValue<int, Joypad> &E : joy_names) { Joypad &joy = E.value; if (joy.uid == uid) { @@ -1498,7 +1551,7 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { } } -void Input::remove_joy_mapping(String p_guid) { +void Input::remove_joy_mapping(const String &p_guid) { for (int i = map_db.size() - 1; i >= 0; i--) { if (p_guid == map_db[i].uid) { map_db.remove_at(i); @@ -1512,7 +1565,7 @@ void Input::remove_joy_mapping(String p_guid) { } } -void Input::set_fallback_mapping(String p_guid) { +void Input::set_fallback_mapping(const String &p_guid) { for (int i = 0; i < map_db.size(); i++) { if (map_db[i].uid == p_guid) { fallback_mapping = i; diff --git a/core/input/input.h b/core/input/input.h index bedc3fa0e3..d1f284e8f7 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -44,6 +44,8 @@ class Input : public Object { static Input *singleton; + static constexpr uint64_t MAX_EVENT = 32; + public: enum MouseMode { MOUSE_MODE_VISIBLE, @@ -98,19 +100,31 @@ private: int64_t mouse_window = 0; bool legacy_just_pressed_behavior = false; - struct Action { + struct ActionState { uint64_t pressed_physics_frame = UINT64_MAX; uint64_t pressed_process_frame = UINT64_MAX; uint64_t released_physics_frame = UINT64_MAX; uint64_t released_process_frame = UINT64_MAX; - int pressed = 0; - bool axis_pressed = false; bool exact = true; - float strength = 0.0f; - float raw_strength = 0.0f; + + struct DeviceState { + bool pressed[MAX_EVENT] = { false }; + float strength[MAX_EVENT] = { 0.0 }; + float raw_strength[MAX_EVENT] = { 0.0 }; + }; + bool api_pressed = false; + float api_strength = 0.0; + HashMap<int, DeviceState> device_states; + + // Cache. + struct ActionStateCache { + bool pressed = false; + float strength = false; + float raw_strength = false; + } cache; }; - HashMap<StringName, Action> action_state; + HashMap<StringName, ActionState> action_states; bool emulate_touch_from_mouse = false; bool emulate_mouse_from_touch = false; @@ -131,12 +145,14 @@ private: struct VelocityTrack { uint64_t last_tick = 0; Vector2 velocity; + Vector2 screen_velocity; Vector2 accum; + Vector2 screen_accum; float accum_t = 0.0f; float min_ref_frame; float max_ref_frame; - void update(const Vector2 &p_delta_p); + void update(const Vector2 &p_delta_p, const Vector2 &p_screen_delta_p); void reset(); VelocityTrack(); }; @@ -223,10 +239,11 @@ private: 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, 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); + JoyButton _get_output_button(const String &output); + JoyAxis _get_output_axis(const String &output); void _button_event(int p_device, JoyButton p_index, bool p_pressed); void _axis_event(int p_device, JoyAxis p_axis, float p_value); + void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state); void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated); @@ -253,7 +270,10 @@ protected: public: void set_mouse_mode(MouseMode p_mode); MouseMode get_mouse_mode() const; + +#ifdef TOOLS_ENABLED void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif static Input *get_singleton(); @@ -278,7 +298,7 @@ public: Vector2 get_joy_vibration_strength(int p_device); float get_joy_vibration_duration(int p_device); uint64_t get_joy_vibration_timestamp(int p_device); - void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary()); + void joy_connection_changed(int p_idx, bool p_connected, const String &p_name, const String &p_guid = "", const Dictionary &p_joypad_info = Dictionary()); Vector3 get_gravity() const; Vector3 get_accelerometer() const; @@ -287,6 +307,7 @@ public: Point2 get_mouse_position() const; Vector2 get_last_mouse_velocity(); + Vector2 get_last_mouse_screen_velocity(); BitField<MouseButtonMask> get_mouse_button_mask() const; void warp_mouse(const Vector2 &p_position); @@ -321,13 +342,13 @@ public: CursorShape get_current_cursor_shape() const; void set_custom_mouse_cursor(const Ref<Resource> &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); - void parse_mapping(String p_mapping); + void parse_mapping(const String &p_mapping); void joy_button(int p_device, JoyButton p_button, bool p_pressed); void joy_axis(int p_device, JoyAxis p_axis, float p_value); void joy_hat(int p_device, BitField<HatMask> p_val); - void add_joy_mapping(String p_mapping, bool p_update_existing = false); - void remove_joy_mapping(String p_guid); + void add_joy_mapping(const String &p_mapping, bool p_update_existing = false); + void remove_joy_mapping(const String &p_guid); int get_unused_joy_id(); @@ -335,7 +356,7 @@ public: String get_joy_guid(int p_device) const; bool should_ignore_device(int p_vendor_id, int p_product_id) const; Dictionary get_joy_info(int p_device) const; - void set_fallback_mapping(String p_guid); + void set_fallback_mapping(const String &p_guid); void flush_buffered_events(); bool is_using_input_buffering(); diff --git a/core/input/input_builders.py b/core/input/input_builders.py index e98e2441e2..ae848f4e7c 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -1,69 +1,59 @@ -"""Functions used to generate source files during build time +"""Functions used to generate source files during build time""" -All such functions are invoked in a subprocess on Windows to prevent build flakiness. -""" - -from platform_methods import subprocess_main from collections import OrderedDict def make_default_controller_mappings(target, source, env): - dst = target[0] - g = open(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write('#include "core/typedefs.h"\n') - g.write('#include "core/input/default_controller_mappings.h"\n') - - # ensure mappings have a consistent order - platform_mappings: dict = OrderedDict() - for src_path in source: - with open(src_path, "r") as f: - # read mapping file and skip header - mapping_file_lines = f.readlines()[2:] - - current_platform = None - for line in mapping_file_lines: - if not line: - continue - line = line.strip() - if len(line) == 0: - continue - if line[0] == "#": - current_platform = line[1:].strip() - if current_platform not in platform_mappings: - platform_mappings[current_platform] = {} - elif current_platform: - line_parts = line.split(",") - guid = line_parts[0] - if guid in platform_mappings[current_platform]: - g.write( - "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( - src_path, current_platform, platform_mappings[current_platform][guid] + dst = str(target[0]) + with open(dst, "w", encoding="utf-8", newline="\n") as g: + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write('#include "core/typedefs.h"\n') + g.write('#include "core/input/default_controller_mappings.h"\n') + + # ensure mappings have a consistent order + platform_mappings: dict = OrderedDict() + for src_path in source: + with open(str(src_path), "r", encoding="utf-8") as f: + # read mapping file and skip header + mapping_file_lines = f.readlines()[2:] + + current_platform = None + for line in mapping_file_lines: + if not line: + continue + line = line.strip() + if len(line) == 0: + continue + if line[0] == "#": + current_platform = line[1:].strip() + if current_platform not in platform_mappings: + platform_mappings[current_platform] = {} + elif current_platform: + line_parts = line.split(",") + guid = line_parts[0] + if guid in platform_mappings[current_platform]: + g.write( + "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( + src_path, current_platform, platform_mappings[current_platform][guid] + ) ) - ) - platform_mappings[current_platform][guid] = line - - platform_variables = { - "Linux": "#if LINUXBSD_ENABLED", - "Windows": "#ifdef WINDOWS_ENABLED", - "Mac OS X": "#ifdef MACOS_ENABLED", - "Android": "#if defined(__ANDROID__)", - "iOS": "#ifdef IOS_ENABLED", - "Web": "#ifdef WEB_ENABLED", - } - - g.write("const char* DefaultControllerMappings::mappings[] = {\n") - for platform, mappings in platform_mappings.items(): - variable = platform_variables[platform] - g.write("{}\n".format(variable)) - for mapping in mappings.values(): - g.write('\t"{}",\n'.format(mapping)) - g.write("#endif\n") - - g.write("\tnullptr\n};\n") - g.close() - - -if __name__ == "__main__": - subprocess_main(globals()) + platform_mappings[current_platform][guid] = line + + platform_variables = { + "Linux": "#ifdef LINUXBSD_ENABLED", + "Windows": "#ifdef WINDOWS_ENABLED", + "Mac OS X": "#ifdef MACOS_ENABLED", + "Android": "#ifdef ANDROID_ENABLED", + "iOS": "#ifdef IOS_ENABLED", + "Web": "#ifdef WEB_ENABLED", + } + + g.write("const char* DefaultControllerMappings::mappings[] = {\n") + for platform, mappings in platform_mappings.items(): + variable = platform_variables[platform] + g.write("{}\n".format(variable)) + for mapping in mappings.values(): + g.write('\t"{}",\n'.format(mapping)) + g.write("#endif\n") + + g.write("\tnullptr\n};\n") diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index e99dd04599..bf1de8d3b2 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -132,6 +132,8 @@ void InputEvent::_bind_methods() { ClassDB::bind_method(D_METHOD("xformed_by", "xform", "local_ofs"), &InputEvent::xformed_by, DEFVAL(Vector2())); ADD_PROPERTY(PropertyInfo(Variant::INT, "device"), "set_device", "get_device"); + + BIND_CONSTANT(DEVICE_ID_EMULATION); } /////////////////////////////////// @@ -364,6 +366,15 @@ char32_t InputEventKey::get_unicode() const { return unicode; } +void InputEventKey::set_location(KeyLocation p_key_location) { + location = p_key_location; + emit_changed(); +} + +KeyLocation InputEventKey::get_location() const { + return location; +} + void InputEventKey::set_echo(bool p_enable) { echo = p_enable; emit_changed(); @@ -436,6 +447,23 @@ String InputEventKey::as_text_key_label() const { return mods_text.is_empty() ? kc : mods_text + "+" + kc; } +String InputEventKey::as_text_location() const { + String loc; + + switch (location) { + case KeyLocation::LEFT: + loc = "left"; + break; + case KeyLocation::RIGHT: + loc = "right"; + break; + default: + break; + } + + return loc; +} + String InputEventKey::as_text() const { String kc; @@ -464,6 +492,11 @@ String InputEventKey::to_string() { String kc = ""; String physical = "false"; + String loc = as_text_location(); + if (loc.is_empty()) { + loc = "unspecified"; + } + if (keycode == Key::NONE && physical_keycode == Key::NONE && unicode != 0) { kc = "U+" + String::num_uint64(unicode, 16) + " (" + String::chr(unicode) + ")"; } else if (keycode != Key::NONE) { @@ -478,7 +511,7 @@ String InputEventKey::to_string() { String mods = InputEventWithModifiers::as_text(); mods = mods.is_empty() ? "none" : mods; - return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e); + return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, location=%s, pressed=%s, echo=%s", kc, mods, physical, loc, p, e); } Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode, bool p_physical) { @@ -531,6 +564,9 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_ma match = keycode == key->keycode; } else if (physical_keycode != Key::NONE) { match = physical_keycode == key->physical_keycode; + if (location != KeyLocation::UNSPECIFIED) { + match &= location == key->location; + } } else { match = false; } @@ -572,6 +608,9 @@ bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match) return (keycode == key->keycode) && (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); } else if (physical_keycode != Key::NONE) { + if (location != KeyLocation::UNSPECIFIED && location != key->location) { + return false; + } return (physical_keycode == key->physical_keycode) && (!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask()); } else { @@ -594,6 +633,9 @@ void InputEventKey::_bind_methods() { ClassDB::bind_method(D_METHOD("set_unicode", "unicode"), &InputEventKey::set_unicode); ClassDB::bind_method(D_METHOD("get_unicode"), &InputEventKey::get_unicode); + ClassDB::bind_method(D_METHOD("set_location", "location"), &InputEventKey::set_location); + ClassDB::bind_method(D_METHOD("get_location"), &InputEventKey::get_location); + ClassDB::bind_method(D_METHOD("set_echo", "echo"), &InputEventKey::set_echo); ClassDB::bind_method(D_METHOD("get_keycode_with_modifiers"), &InputEventKey::get_keycode_with_modifiers); @@ -603,12 +645,14 @@ void InputEventKey::_bind_methods() { ClassDB::bind_method(D_METHOD("as_text_keycode"), &InputEventKey::as_text_keycode); ClassDB::bind_method(D_METHOD("as_text_physical_keycode"), &InputEventKey::as_text_physical_keycode); ClassDB::bind_method(D_METHOD("as_text_key_label"), &InputEventKey::as_text_key_label); + ClassDB::bind_method(D_METHOD("as_text_location"), &InputEventKey::as_text_location); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed"); ADD_PROPERTY(PropertyInfo(Variant::INT, "keycode"), "set_keycode", "get_keycode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_keycode"), "set_physical_keycode", "get_physical_keycode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "key_label"), "set_key_label", "get_key_label"); ADD_PROPERTY(PropertyInfo(Variant::INT, "unicode"), "set_unicode", "get_unicode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "location", PROPERTY_HINT_ENUM, "Unspecified,Left,Right"), "set_location", "get_location"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo"); } @@ -885,6 +929,14 @@ Vector2 InputEventMouseMotion::get_relative() const { return relative; } +void InputEventMouseMotion::set_relative_screen_position(const Vector2 &p_relative) { + screen_relative = p_relative; +} + +Vector2 InputEventMouseMotion::get_relative_screen_position() const { + return screen_relative; +} + void InputEventMouseMotion::set_velocity(const Vector2 &p_velocity) { velocity = p_velocity; } @@ -893,6 +945,14 @@ Vector2 InputEventMouseMotion::get_velocity() const { return velocity; } +void InputEventMouseMotion::set_screen_velocity(const Vector2 &p_velocity) { + screen_velocity = p_velocity; +} + +Vector2 InputEventMouseMotion::get_screen_velocity() const { + return screen_velocity; +} + Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { Ref<InputEventMouseMotion> mm; mm.instantiate(); @@ -910,7 +970,9 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co mm->set_button_mask(get_button_mask()); mm->set_relative(p_xform.basis_xform(get_relative())); + mm->set_relative_screen_position(get_relative_screen_position()); mm->set_velocity(p_xform.basis_xform(get_velocity())); + mm->set_screen_velocity(get_screen_velocity()); return mm; } @@ -985,7 +1047,9 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) { set_position(motion->get_position()); set_global_position(motion->get_global_position()); set_velocity(motion->get_velocity()); + set_screen_velocity(motion->get_screen_velocity()); relative += motion->get_relative(); + screen_relative += motion->get_relative_screen_position(); return true; } @@ -1003,14 +1067,22 @@ void InputEventMouseMotion::_bind_methods() { ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventMouseMotion::set_relative); ClassDB::bind_method(D_METHOD("get_relative"), &InputEventMouseMotion::get_relative); + ClassDB::bind_method(D_METHOD("set_screen_relative", "relative"), &InputEventMouseMotion::set_relative_screen_position); + ClassDB::bind_method(D_METHOD("get_screen_relative"), &InputEventMouseMotion::get_relative_screen_position); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMouseMotion::set_velocity); ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMouseMotion::get_velocity); + ClassDB::bind_method(D_METHOD("set_screen_velocity", "velocity"), &InputEventMouseMotion::set_screen_velocity); + ClassDB::bind_method(D_METHOD("get_screen_velocity"), &InputEventMouseMotion::get_screen_velocity); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pen_inverted"), "set_pen_inverted", "get_pen_inverted"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative", PROPERTY_HINT_NONE, "suffix:px"), "set_relative", "get_relative"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_relative", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_relative", "get_screen_relative"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_screen_velocity", "get_screen_velocity"); } /////////////////////////////////// @@ -1380,6 +1452,14 @@ Vector2 InputEventScreenDrag::get_relative() const { return relative; } +void InputEventScreenDrag::set_relative_screen_position(const Vector2 &p_relative) { + screen_relative = p_relative; +} + +Vector2 InputEventScreenDrag::get_relative_screen_position() const { + return screen_relative; +} + void InputEventScreenDrag::set_velocity(const Vector2 &p_velocity) { velocity = p_velocity; } @@ -1388,6 +1468,14 @@ Vector2 InputEventScreenDrag::get_velocity() const { return velocity; } +void InputEventScreenDrag::set_screen_velocity(const Vector2 &p_velocity) { + screen_velocity = p_velocity; +} + +Vector2 InputEventScreenDrag::get_screen_velocity() const { + return screen_velocity; +} + Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const { Ref<InputEventScreenDrag> sd; @@ -1402,7 +1490,9 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con sd->set_tilt(get_tilt()); sd->set_position(p_xform.xform(pos + p_local_ofs)); sd->set_relative(p_xform.basis_xform(relative)); + sd->set_relative_screen_position(get_relative_screen_position()); sd->set_velocity(p_xform.basis_xform(velocity)); + sd->set_screen_velocity(get_screen_velocity()); return sd; } @@ -1427,7 +1517,9 @@ bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) { set_position(drag->get_position()); set_velocity(drag->get_velocity()); + set_screen_velocity(drag->get_screen_velocity()); relative += drag->get_relative(); + screen_relative += drag->get_relative_screen_position(); return true; } @@ -1451,16 +1543,24 @@ void InputEventScreenDrag::_bind_methods() { ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventScreenDrag::set_relative); ClassDB::bind_method(D_METHOD("get_relative"), &InputEventScreenDrag::get_relative); + ClassDB::bind_method(D_METHOD("set_screen_relative", "relative"), &InputEventScreenDrag::set_relative_screen_position); + ClassDB::bind_method(D_METHOD("get_screen_relative"), &InputEventScreenDrag::get_relative_screen_position); + ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventScreenDrag::set_velocity); ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventScreenDrag::get_velocity); + ClassDB::bind_method(D_METHOD("set_screen_velocity", "velocity"), &InputEventScreenDrag::set_screen_velocity); + ClassDB::bind_method(D_METHOD("get_screen_velocity"), &InputEventScreenDrag::get_screen_velocity); + ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pen_inverted"), "set_pen_inverted", "get_pen_inverted"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative", PROPERTY_HINT_NONE, "suffix:px"), "set_relative", "get_relative"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_relative", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_relative", "get_screen_relative"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_velocity", "get_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_screen_velocity", "get_screen_velocity"); } /////////////////////////////////// diff --git a/core/input/input_event.h b/core/input/input_event.h index ed7ccf0a9f..21b61f3bc2 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -157,6 +157,7 @@ class InputEventKey : public InputEventWithModifiers { Key physical_keycode = Key::NONE; Key key_label = Key::NONE; uint32_t unicode = 0; ///unicode + KeyLocation location = KeyLocation::UNSPECIFIED; bool echo = false; /// true if this is an echo key @@ -178,6 +179,9 @@ public: void set_unicode(char32_t p_unicode); char32_t get_unicode() const; + void set_location(KeyLocation p_key_location); + KeyLocation get_location() const; + void set_echo(bool p_enable); virtual bool is_echo() const override; @@ -193,6 +197,7 @@ public: virtual String as_text_physical_keycode() const; virtual String as_text_keycode() const; virtual String as_text_key_label() const; + virtual String as_text_location() const; virtual String as_text() const override; virtual String to_string() override; @@ -266,7 +271,9 @@ class InputEventMouseMotion : public InputEventMouse { Vector2 tilt; float pressure = 0; Vector2 relative; + Vector2 screen_relative; Vector2 velocity; + Vector2 screen_velocity; bool pen_inverted = false; protected: @@ -285,9 +292,15 @@ public: void set_relative(const Vector2 &p_relative); Vector2 get_relative() const; + void set_relative_screen_position(const Vector2 &p_relative); + Vector2 get_relative_screen_position() const; + void set_velocity(const Vector2 &p_velocity); Vector2 get_velocity() const; + void set_screen_velocity(const Vector2 &p_velocity); + Vector2 get_screen_velocity() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; virtual String as_text() const override; virtual String to_string() override; @@ -388,7 +401,9 @@ class InputEventScreenDrag : public InputEventFromWindow { int index = 0; Vector2 pos; Vector2 relative; + Vector2 screen_relative; Vector2 velocity; + Vector2 screen_velocity; Vector2 tilt; float pressure = 0; bool pen_inverted = false; @@ -415,9 +430,15 @@ public: void set_relative(const Vector2 &p_relative); Vector2 get_relative() const; + void set_relative_screen_position(const Vector2 &p_relative); + Vector2 get_relative_screen_position() const; + void set_velocity(const Vector2 &p_velocity); Vector2 get_velocity() const; + void set_screen_velocity(const Vector2 &p_velocity); + Vector2 get_screen_velocity() const; + virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override; virtual String as_text() const override; virtual String to_string() override; diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index ddfde0e7cd..7fd1806b31 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -85,6 +85,35 @@ String InputMap::suggest_actions(const StringName &p_action) const { return error_message; } +#ifdef TOOLS_ENABLED +void InputMap::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + bool first_argument_is_action = false; + if (p_idx == 0) { + first_argument_is_action = (pf == "has_action" || pf == "erase_action" || + pf == "action_set_deadzone" || pf == "action_get_deadzone" || + pf == "action_has_event" || pf == "action_add_event" || pf == "action_get_events" || + pf == "action_erase_event" || pf == "action_erase_events"); + } + if (first_argument_is_action || (p_idx == 1 && pf == "event_is_action")) { + // Cannot rely on `get_actions()`, otherwise the actions would be in the context of the Editor (no user-defined actions). + List<PropertyInfo> pinfo; + ProjectSettings::get_singleton()->get_property_list(&pinfo); + + for (const PropertyInfo &pi : pinfo) { + if (!pi.name.begins_with("input/")) { + continue; + } + + String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); + r_options->push_back(name.quote()); + } + } + + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif + void InputMap::add_action(const StringName &p_action, float p_deadzone) { ERR_FAIL_COND_MSG(input_map.has(p_action), "InputMap already has action \"" + String(p_action) + "\"."); input_map[p_action] = Action(); @@ -127,16 +156,21 @@ List<StringName> InputMap::get_actions() const { return actions; } -List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { +List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); + int i = 0; for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { int device = E->get()->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) { + if (r_event_index) { + *r_event_index = i; + } return E; } } + i++; } return nullptr; @@ -179,6 +213,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true); if (E) { input_map[p_action].inputs.erase(E); + if (Input::get_singleton()->is_action_pressed(p_action)) { Input::get_singleton()->action_release(p_action); } @@ -216,7 +251,13 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName return event_get_action_status(p_event, p_action, p_exact_match); } -bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const { +int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const { + int index = -1; + event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index); + return index; +} + +bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const { HashMap<StringName, Action>::Iterator E = input_map.find(p_action); ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action)); @@ -236,7 +277,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str return input_event_action->get_action() == p_action; } - List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength); + List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index); return event != nullptr; } @@ -342,6 +383,7 @@ static const _BuiltinActionDisplayName _builtin_action_display_names[] = { { "ui_text_select_all", TTRC("Select All") }, { "ui_text_select_word_under_caret", TTRC("Select Word Under Caret") }, { "ui_text_add_selection_for_next_occurrence", TTRC("Add Selection for Next Occurrence") }, + { "ui_text_skip_selection_for_next_occurrence", TTRC("Skip Selection for Next Occurrence") }, { "ui_text_clear_carets_and_selection", TTRC("Clear Carets and Selection") }, { "ui_text_toggle_insert_mode", TTRC("Toggle Insert Mode") }, { "ui_text_submit", TTRC("Submit Text") }, @@ -681,6 +723,10 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins() { default_builtin_cache.insert("ui_text_add_selection_for_next_occurrence", inputs); inputs = List<Ref<InputEvent>>(); + inputs.push_back(InputEventKey::create_reference(Key::D | KeyModifierMask::CMD_OR_CTRL | KeyModifierMask::ALT)); + default_builtin_cache.insert("ui_text_skip_selection_for_next_occurrence", inputs); + + inputs = List<Ref<InputEvent>>(); inputs.push_back(InputEventKey::create_reference(Key::ESCAPE)); default_builtin_cache.insert("ui_text_clear_carets_and_selection", inputs); @@ -742,7 +788,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur String fullname = E.key; Vector<String> split = fullname.split("."); - String name = split[0]; + const String &name = split[0]; String override_for = split.size() > 1 ? split[1] : String(); if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) { @@ -754,7 +800,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur String fullname = E.key; Vector<String> split = fullname.split("."); - String name = split[0]; + const String &name = split[0]; String override_for = split.size() > 1 ? split[1] : String(); if (builtins_with_overrides.has(name) && override_for.is_empty()) { diff --git a/core/input/input_map.h b/core/input/input_map.h index b4d5beacb3..3774a131e6 100644 --- a/core/input/input_map.h +++ b/core/input/input_map.h @@ -61,7 +61,7 @@ private: HashMap<String, List<Ref<InputEvent>>> default_builtin_cache; HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache; - List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; + List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; TypedArray<InputEvent> _action_get_events(const StringName &p_action); TypedArray<StringName> _get_actions(); @@ -86,7 +86,8 @@ public: const List<Ref<InputEvent>> *action_get_events(const StringName &p_action); bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; - bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const; + int event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const; + bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const; const HashMap<StringName, Action> &get_action_map() const; void load_from_project_settings(); @@ -94,6 +95,10 @@ public: String suggest_actions(const StringName &p_action) const; +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif + String get_builtin_display_name(const String &p_name) const; // Use an Ordered Map so insertion order is preserved. We want the elements to be 'grouped' somewhat. const HashMap<String, List<Ref<InputEvent>>> &get_builtins(); diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 98f8c3de41..0a4d5a3be6 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -81,7 +81,7 @@ void ConfigFile::set_value(const String &p_section, const String &p_key, const V } } -Variant ConfigFile::get_value(const String &p_section, const String &p_key, Variant p_default) const { +Variant ConfigFile::get_value(const String &p_section, const String &p_key, const Variant &p_default) const { if (!values.has(p_section) || !values[p_section].has(p_key)) { ERR_FAIL_COND_V_MSG(p_default.get_type() == Variant::NIL, Variant(), vformat("Couldn't find the given section \"%s\" and key \"%s\", and no default was given.", p_section, p_key)); diff --git a/core/io/config_file.h b/core/io/config_file.h index 80973ab9d0..05b4ed899e 100644 --- a/core/io/config_file.h +++ b/core/io/config_file.h @@ -53,7 +53,7 @@ protected: public: void set_value(const String &p_section, const String &p_key, const Variant &p_value); - Variant get_value(const String &p_section, const String &p_key, Variant p_default = Variant()) const; + Variant get_value(const String &p_section, const String &p_key, const Variant &p_default = Variant()) const; bool has_section(const String &p_section) const; bool has_section_key(const String &p_section, const String &p_key) const; diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index 40c1a53958..e99885befa 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -84,7 +84,7 @@ static Error _erase_recursive(DirAccess *da) { String n = da->get_next(); while (!n.is_empty()) { if (n != "." && n != "..") { - if (da->current_is_dir()) { + if (da->current_is_dir() && !da->is_link(n)) { dirs.push_back(n); } else { files.push_back(n); @@ -131,7 +131,7 @@ Error DirAccess::erase_contents_recursive() { return _erase_recursive(this); } -Error DirAccess::make_dir_recursive(String p_dir) { +Error DirAccess::make_dir_recursive(const String &p_dir) { if (p_dir.length() < 1) { return OK; } @@ -188,7 +188,7 @@ DirAccess::AccessType DirAccess::get_access_type() const { return _access_type; } -String DirAccess::fix_path(String p_path) const { +String DirAccess::fix_path(const String &p_path) const { switch (_access_type) { case ACCESS_RESOURCES: { if (ProjectSettings::get_singleton()) { @@ -338,7 +338,7 @@ String DirAccess::get_full_path(const String &p_path, AccessType p_access) { return full; } -Error DirAccess::copy(String p_from, String p_to, int p_chmod_flags) { +Error DirAccess::copy(const String &p_from, const String &p_to, int p_chmod_flags) { //printf("copy %s -> %s\n",p_from.ascii().get_data(),p_to.ascii().get_data()); Error err; { @@ -396,7 +396,7 @@ class DirChanger { String original_dir; public: - DirChanger(DirAccess *p_da, String p_dir) : + DirChanger(DirAccess *p_da, const String &p_dir) : da(p_da), original_dir(p_da->get_current_dir()) { p_da->change_dir(p_dir); @@ -407,7 +407,7 @@ public: } }; -Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links) { +Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int p_chmod_flags, bool p_copy_links) { List<String> dirs; String curdir = get_current_dir(); @@ -460,7 +460,7 @@ Error DirAccess::_copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod return OK; } -Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_copy_links) { +Error DirAccess::copy_dir(const String &p_from, String p_to, int p_chmod_flags, bool p_copy_links) { ERR_FAIL_COND_V_MSG(!dir_exists(p_from), ERR_FILE_NOT_FOUND, "Source directory doesn't exist."); Ref<DirAccess> target_da = DirAccess::create_for_path(p_to); @@ -481,7 +481,7 @@ Error DirAccess::copy_dir(String p_from, String p_to, int p_chmod_flags, bool p_ return err; } -bool DirAccess::exists(String p_dir) { +bool DirAccess::exists(const String &p_dir) { Ref<DirAccess> da = DirAccess::create_for_path(p_dir); return da->change_dir(p_dir) == OK; } diff --git a/core/io/dir_access.h b/core/io/dir_access.h index 4ee69571f2..e9c864c56b 100644 --- a/core/io/dir_access.h +++ b/core/io/dir_access.h @@ -54,7 +54,7 @@ private: static CreateFunc create_func[ACCESS_MAX]; ///< set this to instance a filesystem object static Ref<DirAccess> _open(const String &p_path); - Error _copy_dir(Ref<DirAccess> &p_target_da, String p_to, int p_chmod_flags, bool p_copy_links); + Error _copy_dir(Ref<DirAccess> &p_target_da, const String &p_to, int p_chmod_flags, bool p_copy_links); PackedStringArray _get_contents(bool p_directories); thread_local static Error last_dir_open_error; @@ -68,9 +68,9 @@ protected: virtual String _get_root_string() const; AccessType get_access_type() const; - virtual String fix_path(String p_path) const; + virtual String fix_path(const String &p_path) const; - template <class T> + template <typename T> static Ref<DirAccess> _create_builtin() { return memnew(T); } @@ -91,18 +91,18 @@ public: virtual Error change_dir(String p_dir) = 0; ///< can be relative or absolute, return false on success virtual String get_current_dir(bool p_include_drive = true) const = 0; ///< return current dir location virtual Error make_dir(String p_dir) = 0; - virtual Error make_dir_recursive(String p_dir); + virtual Error make_dir_recursive(const String &p_dir); virtual Error erase_contents_recursive(); //super dangerous, use with care! virtual bool file_exists(String p_file) = 0; virtual bool dir_exists(String p_dir) = 0; virtual bool is_readable(String p_dir) { return true; }; virtual bool is_writable(String p_dir) { return true; }; - static bool exists(String p_dir); + static bool exists(const String &p_dir); virtual uint64_t get_space_left() = 0; - Error copy_dir(String p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false); - virtual Error copy(String p_from, String p_to, int p_chmod_flags = -1); + Error copy_dir(const String &p_from, String p_to, int p_chmod_flags = -1, bool p_copy_links = false); + virtual Error copy(const String &p_from, const String &p_to, int p_chmod_flags = -1); virtual Error rename(String p_from, String p_to) = 0; virtual Error remove(String p_name) = 0; @@ -112,7 +112,7 @@ public: // Meant for editor code when we want to quickly remove a file without custom // handling (e.g. removing a cache file). - static void remove_file_or_error(String p_path) { + static void remove_file_or_error(const String &p_path) { Ref<DirAccess> da = create(ACCESS_FILESYSTEM); if (da->file_exists(p_path)) { if (da->remove(p_path) != OK) { @@ -130,7 +130,7 @@ public: static Ref<DirAccess> create(AccessType p_access); static Error get_open_error(); - template <class T> + template <typename T> static void make_default(AccessType p_access) { create_func[p_access] = _create_builtin<T>; } diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index 6026dbf896..1cf388b33a 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -38,7 +38,7 @@ #include "core/io/marshalls.h" #include "core/os/os.h" -FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr }; +FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {}; FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr; @@ -47,6 +47,7 @@ thread_local Error FileAccess::last_file_open_error = OK; Ref<FileAccess> FileAccess::create(AccessType p_access) { ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr); + ERR_FAIL_NULL_V(create_func[p_access], nullptr); Ref<FileAccess> ret = create_func[p_access](); ret->_set_access_type(p_access); @@ -75,7 +76,8 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) { ret = create(ACCESS_RESOURCES); } else if (p_path.begins_with("user://")) { ret = create(ACCESS_USERDATA); - + } else if (p_path.begins_with("pipe://")) { + ret = create(ACCESS_PIPE); } else { ret = create(ACCESS_FILESYSTEM); } @@ -209,6 +211,9 @@ String FileAccess::fix_path(const String &p_path) const { } } break; + case ACCESS_PIPE: { + return r_path; + } break; case ACCESS_FILESYSTEM: { return r_path; } break; @@ -862,6 +867,7 @@ void FileAccess::_bind_methods() { ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_bytes", "path"), &FileAccess::_get_file_as_bytes); ClassDB::bind_static_method("FileAccess", D_METHOD("get_file_as_string", "path"), &FileAccess::_get_file_as_string); + ClassDB::bind_method(D_METHOD("resize", "length"), &FileAccess::resize); ClassDB::bind_method(D_METHOD("flush"), &FileAccess::flush); ClassDB::bind_method(D_METHOD("get_path"), &FileAccess::get_path); ClassDB::bind_method(D_METHOD("get_path_absolute"), &FileAccess::get_path_absolute); diff --git a/core/io/file_access.h b/core/io/file_access.h index 7d346ca2f4..2ab84db4b6 100644 --- a/core/io/file_access.h +++ b/core/io/file_access.h @@ -50,6 +50,7 @@ public: ACCESS_RESOURCES, ACCESS_USERDATA, ACCESS_FILESYSTEM, + ACCESS_PIPE, ACCESS_MAX }; @@ -114,7 +115,7 @@ private: AccessType _access_type = ACCESS_FILESYSTEM; static CreateFunc create_func[ACCESS_MAX]; /** default file access creation function for a platform */ - template <class T> + template <typename T> static Ref<FileAccess> _create_builtin() { return memnew(T); } @@ -165,6 +166,7 @@ public: virtual Error get_error() const = 0; ///< get last error + virtual Error resize(int64_t p_length) = 0; virtual void flush() = 0; virtual void store_8(uint8_t p_dest) = 0; ///< store a byte virtual void store_16(uint16_t p_dest); ///< store 16 bits uint @@ -226,7 +228,7 @@ public: 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> + template <typename T> static void make_default(AccessType p_access) { create_func[p_access] = _create_builtin<T>; } diff --git a/core/io/file_access_compressed.h b/core/io/file_access_compressed.h index bf57eaa07c..f706c82f8e 100644 --- a/core/io/file_access_compressed.h +++ b/core/io/file_access_compressed.h @@ -88,6 +88,7 @@ public: virtual Error get_error() const override; ///< get last error + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; virtual void store_8(uint8_t p_dest) override; ///< store a byte diff --git a/core/io/file_access_encrypted.h b/core/io/file_access_encrypted.h index 489d213b8f..42afe49a5e 100644 --- a/core/io/file_access_encrypted.h +++ b/core/io/file_access_encrypted.h @@ -78,6 +78,7 @@ public: virtual Error get_error() const override; ///< get last error + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; virtual void store_8(uint8_t p_dest) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp index 0983920b94..9521a4f666 100644 --- a/core/io/file_access_memory.cpp +++ b/core/io/file_access_memory.cpp @@ -36,7 +36,7 @@ static HashMap<String, Vector<uint8_t>> *files = nullptr; -void FileAccessMemory::register_file(String p_name, Vector<uint8_t> p_data) { +void FileAccessMemory::register_file(const String &p_name, const Vector<uint8_t> &p_data) { if (!files) { files = memnew((HashMap<String, Vector<uint8_t>>)); } @@ -119,7 +119,7 @@ uint64_t FileAccessMemory::get_length() const { } bool FileAccessMemory::eof_reached() const { - return pos > length; + return pos >= length; } uint8_t FileAccessMemory::get_8() const { diff --git a/core/io/file_access_memory.h b/core/io/file_access_memory.h index ac08e5406f..e9fbc26d75 100644 --- a/core/io/file_access_memory.h +++ b/core/io/file_access_memory.h @@ -41,7 +41,7 @@ class FileAccessMemory : public FileAccess { static Ref<FileAccess> create(); public: - static void register_file(String p_name, Vector<uint8_t> p_data); + static void register_file(const String &p_name, const Vector<uint8_t> &p_data); static void cleanup(); virtual Error open_custom(const uint8_t *p_data, uint64_t p_len); ///< open a file @@ -61,6 +61,7 @@ public: virtual Error get_error() const override; ///< get last error + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; virtual void store_8(uint8_t p_byte) override; ///< store a byte virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index 74c5c1c191..991b94db38 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -196,6 +196,8 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, return false; } + int64_t pck_start_pos = f->get_position() - 4; + uint32_t version = f->get_32(); uint32_t ver_major = f->get_32(); uint32_t ver_minor = f->get_32(); @@ -208,6 +210,7 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, uint64_t file_base = f->get_64(); bool enc_directory = (pack_flags & PACK_DIR_ENCRYPTED); + bool rel_filebase = (pack_flags & PACK_REL_FILEBASE); for (int i = 0; i < 16; i++) { //reserved @@ -216,6 +219,10 @@ bool PackedSourcePCK::try_open_pack(const String &p_path, bool p_replace_files, int file_count = f->get_32(); + if (rel_filebase) { + file_base += pck_start_pos; + } + if (enc_directory) { Ref<FileAccessEncrypted> fae; fae.instantiate(); @@ -327,7 +334,7 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const { to_read = (int64_t)pf.size - (int64_t)pos; } - pos += p_length; + pos += to_read; if (to_read <= 0) { return 0; @@ -455,7 +462,7 @@ String DirAccessPack::get_drive(int p_drive) { return ""; } -PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) { +PackedData::PackedDir *DirAccessPack::_find_dir(const String &p_dir) { String nd = p_dir.replace("\\", "/"); // Special handling since simplify_path() will forbid it @@ -491,7 +498,7 @@ PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) { } for (int i = 0; i < paths.size(); i++) { - String p = paths[i]; + const String &p = paths[i]; if (p == ".") { continue; } else if (p == "..") { diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 97391a5611..594ac8f089 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -44,7 +44,8 @@ #define PACK_FORMAT_VERSION 2 enum PackFlags { - PACK_DIR_ENCRYPTED = 1 << 0 + PACK_DIR_ENCRYPTED = 1 << 0, + PACK_REL_FILEBASE = 1 << 1, }; enum PackFileFlags { @@ -176,6 +177,7 @@ public: virtual Error get_error() const override; + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; virtual void store_8(uint8_t p_dest) override; @@ -222,7 +224,7 @@ class DirAccessPack : public DirAccess { List<String> list_files; bool cdir = false; - PackedData::PackedDir *_find_dir(String p_dir); + PackedData::PackedDir *_find_dir(const String &p_dir); public: virtual Error list_dir_begin() override; diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp index dd45332412..3c7f67664d 100644 --- a/core/io/file_access_zip.cpp +++ b/core/io/file_access_zip.cpp @@ -115,7 +115,7 @@ void ZipArchive::close_handle(unzFile p_file) const { unzClose(p_file); } -unzFile ZipArchive::get_file_handle(String p_file) const { +unzFile ZipArchive::get_file_handle(const String &p_file) const { ERR_FAIL_COND_V_MSG(!file_exists(p_file), nullptr, "File '" + p_file + " doesn't exist."); File file = files[p_file]; @@ -206,7 +206,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6 return true; } -bool ZipArchive::file_exists(String p_name) const { +bool ZipArchive::file_exists(const String &p_name) const { return files.has(p_name); } diff --git a/core/io/file_access_zip.h b/core/io/file_access_zip.h index 1062a06238..88b63e93e2 100644 --- a/core/io/file_access_zip.h +++ b/core/io/file_access_zip.h @@ -61,11 +61,11 @@ private: public: void close_handle(unzFile p_file) const; - unzFile get_file_handle(String p_file) const; + unzFile get_file_handle(const String &p_file) const; - Error add_package(String p_name); + Error add_package(const String &p_name); - bool file_exists(String p_name) const; + bool file_exists(const String &p_name) const; virtual bool try_open_pack(const String &p_path, bool p_replace_files, uint64_t p_offset) override; Ref<FileAccess> get_file(const String &p_path, PackedData::PackedFile *p_file) override; @@ -100,6 +100,7 @@ public: virtual Error get_error() const override; ///< get last error + virtual Error resize(int64_t p_length) override { return ERR_UNAVAILABLE; } virtual void flush() override; virtual void store_8(uint8_t p_dest) override; ///< store a byte diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 09505ea05d..833fd1adc3 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -73,7 +73,7 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) { Array keys = p_dict.keys(); for (int i = 0; i < keys.size(); ++i) { String encoded_key = String(keys[i]).uri_encode(); - Variant value = p_dict[keys[i]]; + const Variant &value = p_dict[keys[i]]; switch (value.get_type()) { case Variant::ARRAY: { // Repeat the key with every values diff --git a/core/io/image.cpp b/core/io/image.cpp index ce08b417a8..6096211cff 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -509,6 +509,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p } void Image::convert(Format p_new_format) { + ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum."); if (data.size() == 0) { return; } @@ -678,7 +679,7 @@ static double _bicubic_interp_kernel(double x) { return bc; } -template <int CC, class T> +template <int CC, typename T> static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { // get source image size int width = p_src_width; @@ -765,7 +766,7 @@ static void _scale_cubic(const uint8_t *__restrict p_src, uint8_t *__restrict p_ } } -template <int CC, class T> +template <int CC, typename T> static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { enum { FRAC_BITS = 8, @@ -855,7 +856,7 @@ static void _scale_bilinear(const uint8_t *__restrict p_src, uint8_t *__restrict } } -template <int CC, class T> +template <int CC, typename T> static void _scale_nearest(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { for (uint32_t i = 0; i < p_dst_height; i++) { uint32_t src_yofs = i * p_src_height / p_dst_height; @@ -882,7 +883,7 @@ static float _lanczos(float p_x) { return Math::abs(p_x) >= LANCZOS_TYPE ? 0 : Math::sincn(p_x) * Math::sincn(p_x / LANCZOS_TYPE); } -template <int CC, class T> +template <int CC, typename T> static void _scale_lanczos(const uint8_t *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_width, uint32_t p_src_height, uint32_t p_dst_width, uint32_t p_dst_height) { int32_t src_width = p_src_width; int32_t src_height = p_src_height; @@ -1028,7 +1029,7 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) { } void Image::resize(int p_width, int p_height, Interpolation p_interpolation) { - ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use set_data() first."); + ERR_FAIL_COND_MSG(data.is_empty(), "Cannot resize image before creating it, use set_data() first."); ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats."); bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */; @@ -1664,7 +1665,7 @@ bool Image::_can_modify(Format p_format) const { return p_format <= FORMAT_RGBE9995; } -template <class Component, int CC, bool renormalize, +template <typename Component, int CC, bool renormalize, void (*average_func)(Component &, const Component &, const Component &, const Component &, const Component &), void (*renormalize_func)(Component *)> static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint32_t p_width, uint32_t p_height) { @@ -1699,7 +1700,7 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3 } void Image::shrink_x2() { - ERR_FAIL_COND(data.size() == 0); + ERR_FAIL_COND(data.is_empty()); if (mipmaps) { //just use the lower mipmap as base and copy all @@ -1709,7 +1710,7 @@ void Image::shrink_x2() { int new_size = data.size() - ofs; new_img.resize(new_size); - ERR_FAIL_COND(new_img.size() == 0); + ERR_FAIL_COND(new_img.is_empty()); { uint8_t *w = new_img.ptrw(); @@ -1728,8 +1729,8 @@ void Image::shrink_x2() { ERR_FAIL_COND(!_can_modify(format)); int ps = get_format_pixel_size(format); new_img.resize((width / 2) * (height / 2) * ps); - ERR_FAIL_COND(new_img.size() == 0); - ERR_FAIL_COND(data.size() == 0); + ERR_FAIL_COND(new_img.is_empty()); + ERR_FAIL_COND(data.is_empty()); { uint8_t *w = new_img.ptrw(); @@ -2780,7 +2781,7 @@ void Image::_get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2 } void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blit_rect an image: invalid source Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -2822,8 +2823,8 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const P } void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); - ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blit_rect_mask an image: invalid source Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "Cannot blit_rect_mask an image: invalid mask Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); @@ -2872,7 +2873,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co } void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blend_rect an image: invalid source Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); ERR_FAIL_COND(dsize == 0); @@ -2907,8 +2908,8 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const } void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) { - ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object."); - ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blend_rect_mask an image: invalid source Image object."); + ERR_FAIL_COND_MSG(p_mask.is_null(), "Cannot blend_rect_mask an image: invalid mask Image object."); int dsize = data.size(); int srcdsize = p_src->data.size(); int maskdsize = p_mask->data.size(); @@ -3012,6 +3013,7 @@ void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) { } ImageMemLoadFunc Image::_png_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr; ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; @@ -3298,6 +3300,18 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { _set_color_at_ofs(data.ptrw(), ofs, p_color); } +const uint8_t *Image::ptr() const { + return data.ptr(); +} + +uint8_t *Image::ptrw() { + return data.ptrw(); +} + +int64_t Image::data_size() const { + return data.size(); +} + void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) { ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot adjust_bcs in compressed or custom image formats."); @@ -3321,7 +3335,7 @@ void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation) } Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const { - ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); + ERR_FAIL_COND_V(data.is_empty(), USED_CHANNELS_RGBA); ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA); bool r = false, g = false, b = false, a = false, c = false; @@ -3876,7 +3890,7 @@ Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) { void Image::convert_rg_to_ra_rgba8() { ERR_FAIL_COND(format != FORMAT_RGBA8); - ERR_FAIL_COND(!data.size()); + ERR_FAIL_COND(data.is_empty()); int s = data.size(); uint8_t *w = data.ptrw(); @@ -3889,7 +3903,7 @@ void Image::convert_rg_to_ra_rgba8() { void Image::convert_ra_rgba8_to_rg() { ERR_FAIL_COND(format != FORMAT_RGBA8); - ERR_FAIL_COND(!data.size()); + ERR_FAIL_COND(data.is_empty()); int s = data.size(); uint8_t *w = data.ptrw(); @@ -3902,7 +3916,7 @@ void Image::convert_ra_rgba8_to_rg() { void Image::convert_rgba8_to_bgra8() { ERR_FAIL_COND(format != FORMAT_RGBA8); - ERR_FAIL_COND(!data.size()); + ERR_FAIL_COND(data.is_empty()); int s = data.size(); uint8_t *w = data.ptrw(); @@ -4100,7 +4114,7 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool continue; } - image_metric_max = MAX(image_metric_max, i); + image_metric_max = i; double x = i * hist[i]; diff --git a/core/io/image.h b/core/io/image.h index a21d05187b..2cabbb767a 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -145,6 +145,7 @@ public: }; static ImageMemLoadFunc _png_mem_loader_func; + static ImageMemLoadFunc _png_mem_unpacker_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; static ImageMemLoadFunc _tga_mem_loader_func; @@ -312,8 +313,8 @@ public: Error save_jpg(const String &p_path, float p_quality = 0.75) const; Vector<uint8_t> save_png_to_buffer() const; Vector<uint8_t> save_jpg_to_buffer(float p_quality = 0.75) const; - Vector<uint8_t> save_exr_to_buffer(bool p_grayscale) const; - Error save_exr(const String &p_path, bool p_grayscale) const; + Vector<uint8_t> save_exr_to_buffer(bool p_grayscale = false) const; + Error save_exr(const String &p_path, bool p_grayscale = false) const; Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const; Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const; @@ -425,12 +426,16 @@ public: void set_pixelv(const Point2i &p_point, const Color &p_color); void set_pixel(int p_x, int p_y, const Color &p_color); + const uint8_t *ptr() const; + uint8_t *ptrw(); + int64_t data_size() const; + void adjust_bcs(float p_brightness, float p_contrast, float p_saturation); void set_as_black(); void copy_internals_from(const Ref<Image> &p_image) { - ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object."); + ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object."); format = p_image->format; width = p_image->width; height = p_image->height; diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index c6452f1033..92c690dc2a 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -80,8 +80,8 @@ void ImageFormatLoaderExtension::_bind_methods() { ClassDB::bind_method(D_METHOD("remove_format_loader"), &ImageFormatLoaderExtension::remove_format_loader); } -Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { - ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object."); +Error ImageLoader::load_image(const String &p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) { + ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "Can't load an image: invalid Image object."); Ref<FileAccess> f = p_custom; if (f.is_null()) { diff --git a/core/io/image_loader.h b/core/io/image_loader.h index ac51f13376..26af650344 100644 --- a/core/io/image_loader.h +++ b/core/io/image_loader.h @@ -91,7 +91,7 @@ class ImageLoader { protected: public: - static Error load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), BitField<ImageFormatLoader::LoaderFlags> p_flags = ImageFormatLoader::FLAG_NONE, float p_scale = 1.0); + static Error load_image(const String &p_file, Ref<Image> p_image, Ref<FileAccess> p_custom = Ref<FileAccess>(), BitField<ImageFormatLoader::LoaderFlags> p_flags = ImageFormatLoader::FLAG_NONE, float p_scale = 1.0); static void get_recognized_extensions(List<String> *p_extensions); static Ref<ImageFormatLoader> recognize(const String &p_extension); @@ -103,10 +103,10 @@ public: class ResourceFormatLoaderImage : public ResourceFormatLoader { public: - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; }; #endif // IMAGE_LOADER_H diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 254351897d..ec86104926 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -117,7 +117,7 @@ struct _IP_ResolverPrivate { HashMap<String, List<IPAddress>> cache; - static String get_cache_key(String p_hostname, IP::Type p_type) { + static String get_cache_key(const String &p_hostname, IP::Type p_type) { return itos(p_type) + p_hostname; } }; diff --git a/core/io/json.cpp b/core/io/json.cpp index 496400a5ea..61051727c1 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -86,7 +86,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_ case Variant::PACKED_STRING_ARRAY: case Variant::ARRAY: { Array a = p_var; - if (a.size() == 0) { + if (a.is_empty()) { return "[]"; } String s = "["; @@ -95,12 +95,15 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_ ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON."); p_markers.insert(a.id()); - for (int i = 0; i < a.size(); i++) { - if (i > 0) { + bool first = true; + for (const Variant &var : a) { + if (first) { + first = false; + } else { s += ","; s += end_statement; } - s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(a[i], p_indent, p_cur_indent + 1, p_sort_keys, p_markers); + s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(var, p_indent, p_cur_indent + 1, p_sort_keys, p_markers); } s += end_statement + _make_indent(p_indent, p_cur_indent) + "]"; p_markers.erase(a.id()); @@ -341,10 +344,10 @@ Error JSON::_get_token(const char32_t *p_str, int &index, int p_len, Token &r_to r_token.value = number; return OK; - } else if (is_ascii_char(p_str[index])) { + } else if (is_ascii_alphabet_char(p_str[index])) { String id; - while (is_ascii_char(p_str[index])) { + while (is_ascii_alphabet_char(p_str[index])) { id += p_str[index]; index++; } diff --git a/core/io/json.h b/core/io/json.h index a21cc542fd..801fa29b4b 100644 --- a/core/io/json.h +++ b/core/io/json.h @@ -98,17 +98,17 @@ public: class ResourceFormatLoaderJSON : public ResourceFormatLoader { public: - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; }; class ResourceFormatSaverJSON : public ResourceFormatSaver { public: - virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); - virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; - virtual bool recognize(const Ref<Resource> &p_resource) const; + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override; + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override; + virtual bool recognize(const Ref<Resource> &p_resource) const override; }; #endif // JSON_H diff --git a/core/io/logger.cpp b/core/io/logger.cpp index b262c1cf28..1476b8ccac 100644 --- a/core/io/logger.cpp +++ b/core/io/logger.cpp @@ -37,6 +37,8 @@ #include "core/os/time.h" #include "core/string/print_string.h" +#include "modules/modules_enabled.gen.h" // For regex. + #if defined(MINGW_ENABLED) || defined(_MSC_VER) #define sprintf sprintf_s #endif @@ -180,6 +182,12 @@ RotatedFileLogger::RotatedFileLogger(const String &p_base_path, int p_max_files) base_path(p_base_path.simplify_path()), max_files(p_max_files > 0 ? p_max_files : 1) { rotate_file(); + +#ifdef MODULE_REGEX_ENABLED + strip_ansi_regex.instantiate(); + strip_ansi_regex->detach_from_objectdb(); // Note: This RegEx instance will exist longer than ObjectDB, therefore can't be registered in ObjectDB. + strip_ansi_regex->compile("\u001b\\[((?:\\d|;)*)([a-zA-Z])"); +#endif // MODULE_REGEX_ENABLED } void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { @@ -199,7 +207,15 @@ void RotatedFileLogger::logv(const char *p_format, va_list p_list, bool p_err) { vsnprintf(buf, len + 1, p_format, list_copy); } va_end(list_copy); + +#ifdef MODULE_REGEX_ENABLED + // Strip ANSI escape codes (such as those inserted by `print_rich()`) + // before writing to file, as text editors cannot display those + // correctly. + file->store_string(strip_ansi_regex->sub(String(buf), "", true)); +#else file->store_buffer((uint8_t *)buf, len); +#endif // MODULE_REGEX_ENABLED if (len >= static_buf_size) { Memory::free_static(buf); @@ -230,7 +246,7 @@ void StdLogger::logv(const char *p_format, va_list p_list, bool p_err) { } } -CompositeLogger::CompositeLogger(Vector<Logger *> p_loggers) : +CompositeLogger::CompositeLogger(const Vector<Logger *> &p_loggers) : loggers(p_loggers) { } diff --git a/core/io/logger.h b/core/io/logger.h index 943d61e2e8..85ef3031ec 100644 --- a/core/io/logger.h +++ b/core/io/logger.h @@ -34,6 +34,10 @@ #include "core/io/file_access.h" #include "core/string/ustring.h" #include "core/templates/vector.h" +#include "modules/modules_enabled.gen.h" // For regex. +#ifdef MODULE_REGEX_ENABLED +#include "modules/regex/regex.h" +#endif // MODULE_REGEX_ENABLED #include <stdarg.h> @@ -86,6 +90,10 @@ class RotatedFileLogger : public Logger { void clear_old_backups(); void rotate_file(); +#ifdef MODULE_REGEX_ENABLED + Ref<RegEx> strip_ansi_regex; +#endif // MODULE_REGEX_ENABLED + public: explicit RotatedFileLogger(const String &p_base_path, int p_max_files = 10); @@ -96,7 +104,7 @@ class CompositeLogger : public Logger { Vector<Logger *> loggers; public: - explicit CompositeLogger(Vector<Logger *> p_loggers); + explicit CompositeLogger(const Vector<Logger *> &p_loggers); virtual void logv(const char *p_format, va_list p_list, bool p_err) override _PRINTF_FORMAT_ATTRIBUTE_2_0; virtual void log_error(const char *p_function, const char *p_file, int p_line, const char *p_code, const char *p_rationale, bool p_editor_notify, ErrorType p_type = ERR_ERROR) override; diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index 3d384d9345..18dbac991c 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -30,7 +30,10 @@ #include "marshalls.h" +#include "core/core_string_names.h" +#include "core/io/resource_loader.h" #include "core/object/ref_counted.h" +#include "core/object/script_language.h" #include "core/os/keyboard.h" #include "core/string/print_string.h" @@ -55,9 +58,22 @@ ObjectID EncodedObjectAsID::get_object_id() const { #define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err) #define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err) -#define ENCODE_MASK 0xFF -#define ENCODE_FLAG_64 1 << 16 -#define ENCODE_FLAG_OBJECT_AS_ID 1 << 16 +// Byte 0: `Variant::Type`, byte 1: unused, bytes 2 and 3: additional data. +#define HEADER_TYPE_MASK 0xFF + +// For `Variant::INT`, `Variant::FLOAT` and other math types. +#define HEADER_DATA_FLAG_64 (1 << 16) + +// For `Variant::OBJECT`. +#define HEADER_DATA_FLAG_OBJECT_AS_ID (1 << 16) + +// For `Variant::ARRAY`. +// Occupies bits 16 and 17. +#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16) +#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16) +#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16) +#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16) +#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16) static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); @@ -101,9 +117,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - uint32_t type = decode_uint32(buf); + uint32_t header = decode_uint32(buf); - ERR_FAIL_COND_V((type & ENCODE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA); + ERR_FAIL_COND_V((header & HEADER_TYPE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA); buf += 4; len -= 4; @@ -114,7 +130,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded. // Decoding math types always checks for the encoded size, while encoding always uses compilation setting. // This does lead to some code duplication for decoding, but compatibility is the priority. - switch (type & ENCODE_MASK) { + switch (header & HEADER_TYPE_MASK) { case Variant::NIL: { r_variant = Variant(); } break; @@ -127,7 +143,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } break; case Variant::INT: { - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); int64_t val = decode_uint64(buf); r_variant = val; @@ -146,7 +162,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::FLOAT: { - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA); double val = decode_double(buf); r_variant = val; @@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int // math types case Variant::VECTOR2: { Vector2 val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA); val.x = decode_double(&buf[0]); val.y = decode_double(&buf[sizeof(double)]); @@ -210,7 +226,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::RECT2: { Rect2 val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA); val.position.x = decode_double(&buf[0]); val.position.y = decode_double(&buf[sizeof(double)]); @@ -250,7 +266,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR3: { Vector3 val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA); val.x = decode_double(&buf[0]); val.y = decode_double(&buf[sizeof(double)]); @@ -287,7 +303,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::VECTOR4: { Vector4 val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA); val.x = decode_double(&buf[0]); val.y = decode_double(&buf[sizeof(double)]); @@ -327,7 +343,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM2D: { Transform2D val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA); for (int i = 0; i < 3; i++) { for (int j = 0; j < 2; j++) { @@ -355,7 +371,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PLANE: { Plane val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA); val.normal.x = decode_double(&buf[0]); val.normal.y = decode_double(&buf[sizeof(double)]); @@ -381,7 +397,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::QUATERNION: { Quaternion val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA); val.x = decode_double(&buf[0]); val.y = decode_double(&buf[sizeof(double)]); @@ -407,7 +423,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::AABB: { AABB val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA); val.position.x = decode_double(&buf[0]); val.position.y = decode_double(&buf[sizeof(double)]); @@ -437,7 +453,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::BASIS: { Basis val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -465,7 +481,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::TRANSFORM3D: { Transform3D val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { @@ -499,7 +515,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PROJECTION: { Projection val; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA); for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { @@ -560,12 +576,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int uint32_t namecount = strlen &= 0x7FFFFFFF; uint32_t subnamecount = decode_uint32(buf + 4); - uint32_t flags = decode_uint32(buf + 8); + uint32_t np_flags = decode_uint32(buf + 8); len -= 12; buf += 12; - if (flags & 2) { // Obsolete format with property separate from subpath + if (np_flags & 2) { // Obsolete format with property separate from subpath. subnamecount++; } @@ -589,7 +605,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } } - r_variant = NodePath(names, subnames, flags & 1); + r_variant = NodePath(names, subnames, np_flags & 1); } else { //old format, just a string @@ -608,8 +624,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = RID::from_uint64(id); } break; case Variant::OBJECT: { - if (type & ENCODE_FLAG_OBJECT_AS_ID) { - //this _is_ allowed + if (header & HEADER_DATA_FLAG_OBJECT_AS_ID) { + // This _is_ allowed. ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA); ObjectID val = ObjectID(decode_uint64(buf)); if (r_len) { @@ -625,7 +641,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int r_variant = obj_as_id; } - } else { ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED); @@ -638,11 +653,22 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int if (str.is_empty()) { r_variant = (Object *)nullptr; } else { - Object *obj = ClassDB::instantiate(str); + ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA); + Object *obj = ClassDB::instantiate(str); ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE); - ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + // Avoid premature free `RefCounted`. This must be done before properties are initialized, + // since script functions (setters, implicit initializer) may be called. See GH-68666. + Variant variant; + if (Object::cast_to<RefCounted>(obj)) { + Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj)); + variant = ref; + } else { + variant = obj; + } + + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); int32_t count = decode_uint32(buf); buf += 4; len -= 4; @@ -670,15 +696,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int (*r_len) += used; } - obj->set(str, value); + if (str == "script" && value.get_type() != Variant::NIL) { + ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String."); + String path = value; + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'."); + Ref<Script> script = ResourceLoader::load(path, "Script"); + ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'."); + obj->set_script(script); + } else { + obj->set(str, value); + } } - if (Object::cast_to<RefCounted>(obj)) { - Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj)); - r_variant = ref; - } else { - r_variant = obj; - } + r_variant = variant; } } @@ -745,7 +775,66 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::ARRAY: { + Variant::Type builtin_type = Variant::VARIANT_MAX; + StringName class_name; + Ref<Script> script; + + switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) { + case HEADER_DATA_FIELD_TYPED_ARRAY_NONE: + break; // Untyped array. + case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: { + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + + int32_t bt = decode_uint32(buf); + buf += 4; + len -= 4; + if (r_len) { + (*r_len) += 4; + } + + ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA); + builtin_type = (Variant::Type)bt; + if (!p_allow_objects && builtin_type == Variant::OBJECT) { + class_name = EncodedObjectAsID::get_class_static(); + } + } break; + case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: { + String str; + Error err = _decode_string(buf, len, r_len, str); + if (err) { + return err; + } + + builtin_type = Variant::OBJECT; + if (p_allow_objects) { + class_name = str; + } else { + class_name = EncodedObjectAsID::get_class_static(); + } + } break; + case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: { + String path; + Error err = _decode_string(buf, len, r_len, path); + if (err) { + return err; + } + + builtin_type = Variant::OBJECT; + if (p_allow_objects) { + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'."); + script = ResourceLoader::load(path, "Script"); + ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'."); + class_name = script->get_instance_base_type(); + } else { + class_name = EncodedObjectAsID::get_class_static(); + } + } break; + default: + ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing. + } + ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); + int32_t count = decode_uint32(buf); // bool shared = count&0x80000000; count &= 0x7FFFFFFF; @@ -758,6 +847,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } Array varr; + if (builtin_type != Variant::VARIANT_MAX) { + varr.set_typed(builtin_type, class_name, script); + } for (int i = 0; i < count; i++) { int used = 0; @@ -934,7 +1026,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector<Vector2> varray; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA); @@ -994,7 +1086,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector<Vector3> varray; - if (type & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA); ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA); @@ -1120,20 +1212,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len = 0; - uint32_t flags = 0; + uint32_t header = p_variant.get_type(); switch (p_variant.get_type()) { case Variant::INT: { int64_t val = p_variant; if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) { - flags |= ENCODE_FLAG_64; + header |= HEADER_DATA_FLAG_64; } } break; case Variant::FLOAT: { double d = p_variant; float f = d; if (double(f) != d) { - flags |= ENCODE_FLAG_64; + header |= HEADER_DATA_FLAG_64; } } break; case Variant::OBJECT: { @@ -1149,7 +1241,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } if (!p_full_objects) { - flags |= ENCODE_FLAG_OBJECT_AS_ID; + header |= HEADER_DATA_FLAG_OBJECT_AS_ID; + } + } break; + case Variant::ARRAY: { + Array array = p_variant; + if (array.is_typed()) { + Ref<Script> script = array.get_typed_script(); + if (script.is_valid()) { + header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT; + } else if (array.get_typed_class_name() != StringName()) { + header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME; + } else { + header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN; + } } } break; #ifdef REAL_T_IS_DOUBLE @@ -1166,7 +1271,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo case Variant::BASIS: case Variant::RECT2: case Variant::AABB: { - flags |= ENCODE_FLAG_64; + header |= HEADER_DATA_FLAG_64; } break; #endif // REAL_T_IS_DOUBLE default: { @@ -1174,7 +1279,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } if (buf) { - encode_uint32(p_variant.get_type() | flags, buf); + encode_uint32(header, buf); buf += 4; } r_len += 4; @@ -1192,7 +1297,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::INT: { - if (flags & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { //64 bits if (buf) { encode_uint64(p_variant.operator int64_t(), buf); @@ -1208,7 +1313,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } } break; case Variant::FLOAT: { - if (flags & ENCODE_FLAG_64) { + if (header & HEADER_DATA_FLAG_64) { if (buf) { encode_double(p_variant.operator double(), buf); } @@ -1492,6 +1597,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; } else { + ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), ERR_INVALID_PARAMETER); + _encode_string(obj->get_class(), buf, r_len); List<PropertyInfo> props; @@ -1519,8 +1626,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo _encode_string(E.name, buf, r_len); + Variant value; + + if (E.name == CoreStringNames::get_singleton()->_script) { + Ref<Script> script = obj->get_script(); + if (script.is_valid()) { + String path = script->get_path(); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script."); + value = path; + } + } else { + value = obj->get(E.name); + } + int len; - Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1); + Error err = encode_variant(value, buf, len, p_full_objects, p_depth + 1); ERR_FAIL_COND_V(err, err); ERR_FAIL_COND_V(len % 4, ERR_BUG); r_len += len; @@ -1590,24 +1710,41 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo } break; case Variant::ARRAY: { - Array v = p_variant; + Array array = p_variant; + + if (array.is_typed()) { + Variant variant = array.get_typed_script(); + Ref<Script> script = variant; + if (script.is_valid()) { + String path = script->get_path(); + ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type."); + _encode_string(path, buf, r_len); + } else if (array.get_typed_class_name() != StringName()) { + _encode_string(array.get_typed_class_name(), buf, r_len); + } else { + if (buf) { + encode_uint32(array.get_typed_builtin(), buf); + buf += 4; + } + r_len += 4; + } + } if (buf) { - encode_uint32(uint32_t(v.size()), buf); + encode_uint32(uint32_t(array.size()), buf); buf += 4; } - r_len += 4; - for (int i = 0; i < v.size(); i++) { + for (const Variant &var : array) { int len; - Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1); + Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1); ERR_FAIL_COND_V(err, err); ERR_FAIL_COND_V(len % 4, ERR_BUG); - r_len += len; if (buf) { buf += len; } + r_len += len; } } break; diff --git a/core/io/net_socket.h b/core/io/net_socket.h index 2c1a020a46..120ad5e85b 100644 --- a/core/io/net_socket.h +++ b/core/io/net_socket.h @@ -74,8 +74,8 @@ public: virtual void set_ipv6_only_enabled(bool p_enabled) = 0; virtual void set_tcp_no_delay_enabled(bool p_enabled) = 0; virtual void set_reuse_address_enabled(bool p_enabled) = 0; - virtual Error join_multicast_group(const IPAddress &p_multi_address, String p_if_name) = 0; - virtual Error leave_multicast_group(const IPAddress &p_multi_address, String p_if_name) = 0; + virtual Error join_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0; + virtual Error leave_multicast_group(const IPAddress &p_multi_address, const String &p_if_name) = 0; }; #endif // NET_SOCKET_H diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 48806fba73..0329ace313 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -318,9 +318,9 @@ int PacketPeerStream::get_output_buffer_max_size() const { } PacketPeerStream::PacketPeerStream() { - int rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2"); + int64_t rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2"); ring_buffer.resize(rbsize); - input_buffer.resize(1 << rbsize); - output_buffer.resize(1 << rbsize); + input_buffer.resize(int64_t(1) << rbsize); + output_buffer.resize(int64_t(1) << rbsize); } diff --git a/core/io/packet_peer_udp.cpp b/core/io/packet_peer_udp.cpp index dfd4798d2e..32030146bb 100644 --- a/core/io/packet_peer_udp.cpp +++ b/core/io/packet_peer_udp.cpp @@ -45,7 +45,7 @@ void PacketPeerUDP::set_broadcast_enabled(bool p_enabled) { } } -Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, String p_if_name) { +Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, const String &p_if_name) { ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!p_multi_address.is_valid(), ERR_INVALID_PARAMETER); @@ -60,7 +60,7 @@ Error PacketPeerUDP::join_multicast_group(IPAddress p_multi_address, String p_if return _sock->join_multicast_group(p_multi_address, p_if_name); } -Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, String p_if_name) { +Error PacketPeerUDP::leave_multicast_group(IPAddress p_multi_address, const String &p_if_name) { ERR_FAIL_COND_V(udp_server, ERR_LOCKED); ERR_FAIL_COND_V(!_sock.is_valid(), ERR_UNAVAILABLE); ERR_FAIL_COND_V(!_sock->is_open(), ERR_UNCONFIGURED); diff --git a/core/io/packet_peer_udp.h b/core/io/packet_peer_udp.h index 3e0e3b437e..c69a138c53 100644 --- a/core/io/packet_peer_udp.h +++ b/core/io/packet_peer_udp.h @@ -91,8 +91,8 @@ public: int get_available_packet_count() const override; int get_max_packet_size() const override; void set_broadcast_enabled(bool p_enabled); - Error join_multicast_group(IPAddress p_multi_address, String p_if_name); - Error leave_multicast_group(IPAddress p_multi_address, String p_if_name); + Error join_multicast_group(IPAddress p_multi_address, const String &p_if_name); + Error leave_multicast_group(IPAddress p_multi_address, const String &p_if_name); PacketPeerUDP(); ~PacketPeerUDP(); diff --git a/core/io/plist.cpp b/core/io/plist.cpp new file mode 100644 index 0000000000..86737609bf --- /dev/null +++ b/core/io/plist.cpp @@ -0,0 +1,868 @@ +/**************************************************************************/ +/* plist.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "plist.h" + +PList::PLNodeType PListNode::get_type() const { + return data_type; +} + +Variant PListNode::get_value() const { + switch (data_type) { + case PList::PL_NODE_TYPE_NIL: { + return Variant(); + } break; + case PList::PL_NODE_TYPE_STRING: { + return String::utf8(data_string.get_data()); + } break; + case PList::PL_NODE_TYPE_ARRAY: { + Array arr; + for (const Ref<PListNode> &E : data_array) { + arr.push_back(E); + } + return arr; + } break; + case PList::PL_NODE_TYPE_DICT: { + Dictionary dict; + for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { + dict[E.key] = E.value; + } + return dict; + } break; + case PList::PL_NODE_TYPE_BOOLEAN: { + return data_bool; + } break; + case PList::PL_NODE_TYPE_INTEGER: { + return data_int; + } break; + case PList::PL_NODE_TYPE_REAL: { + return data_real; + } break; + case PList::PL_NODE_TYPE_DATA: { + int strlen = data_string.length(); + + size_t arr_len = 0; + Vector<uint8_t> buf; + { + buf.resize(strlen / 4 * 3 + 1); + uint8_t *w = buf.ptrw(); + + ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &arr_len, (unsigned char *)data_string.get_data(), strlen) != OK, Vector<uint8_t>()); + } + buf.resize(arr_len); + return buf; + } break; + case PList::PL_NODE_TYPE_DATE: { + return String(data_string.get_data()); + } break; + } + return Variant(); +} + +Ref<PListNode> PListNode::new_node(const Variant &p_value) { + Ref<PListNode> node; + node.instantiate(); + + switch (p_value.get_type()) { + case Variant::NIL: { + node->data_type = PList::PL_NODE_TYPE_NIL; + } break; + case Variant::BOOL: { + node->data_type = PList::PL_NODE_TYPE_BOOLEAN; + node->data_bool = p_value; + } break; + case Variant::INT: { + node->data_type = PList::PL_NODE_TYPE_INTEGER; + node->data_int = p_value; + } break; + case Variant::FLOAT: { + node->data_type = PList::PL_NODE_TYPE_REAL; + node->data_real = p_value; + } break; + case Variant::STRING_NAME: + case Variant::STRING: { + node->data_type = PList::PL_NODE_TYPE_STRING; + node->data_string = p_value.operator String().utf8(); + } break; + case Variant::DICTIONARY: { + node->data_type = PList::PL_NODE_TYPE_DICT; + Dictionary dict = p_value; + const Variant *next = dict.next(nullptr); + while (next) { + Ref<PListNode> sub_node = dict[*next]; + ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid dictionary element, should be PListNode."); + node->data_dict[*next] = sub_node; + next = dict.next(next); + } + } break; + case Variant::ARRAY: { + node->data_type = PList::PL_NODE_TYPE_ARRAY; + Array ar = p_value; + for (int i = 0; i < ar.size(); i++) { + Ref<PListNode> sub_node = ar[i]; + ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid array element, should be PListNode."); + node->data_array.push_back(sub_node); + } + } break; + case Variant::PACKED_BYTE_ARRAY: { + node->data_type = PList::PL_NODE_TYPE_DATA; + PackedByteArray buf = p_value; + node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8(); + } break; + default: { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Unsupported data type."); + } break; + } + return node; +} + +Ref<PListNode> PListNode::new_array() { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY; + return node; +} + +Ref<PListNode> PListNode::new_dict() { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT; + return node; +} + +Ref<PListNode> PListNode::new_string(const String &p_string) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING; + node->data_string = p_string.utf8(); + return node; +} + +Ref<PListNode> PListNode::new_data(const String &p_string) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA; + node->data_string = p_string.utf8(); + return node; +} + +Ref<PListNode> PListNode::new_date(const String &p_string) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE; + node->data_string = p_string.utf8(); + node->data_real = (double)Time::get_singleton()->get_unix_time_from_datetime_string(p_string) - 978307200.0; + return node; +} + +Ref<PListNode> PListNode::new_bool(bool p_bool) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN; + node->data_bool = p_bool; + return node; +} + +Ref<PListNode> PListNode::new_int(int64_t p_int) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER; + node->data_int = p_int; + return node; +} + +Ref<PListNode> PListNode::new_real(double p_real) { + Ref<PListNode> node = memnew(PListNode()); + ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>()); + node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL; + node->data_real = p_real; + return node; +} + +bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) { + ERR_FAIL_COND_V(p_node.is_null(), false); + if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) { + ERR_FAIL_COND_V(p_key.is_empty(), false); + ERR_FAIL_COND_V(data_dict.has(p_key), false); + data_dict[p_key] = p_node; + return true; + } else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) { + data_array.push_back(p_node); + return true; + } else { + ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY."); + } +} + +size_t PListNode::get_asn1_size(uint8_t p_len_octets) const { + // Get size of all data, excluding type and size information. + switch (data_type) { + case PList::PLNodeType::PL_NODE_TYPE_NIL: { + return 0; + } break; + case PList::PLNodeType::PL_NODE_TYPE_DATA: + case PList::PLNodeType::PL_NODE_TYPE_DATE: { + ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization."); + } break; + case PList::PLNodeType::PL_NODE_TYPE_STRING: { + return data_string.length(); + } break; + case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { + return 1; + } break; + case PList::PLNodeType::PL_NODE_TYPE_INTEGER: + case PList::PLNodeType::PL_NODE_TYPE_REAL: { + return 4; + } break; + case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { + size_t size = 0; + for (int i = 0; i < data_array.size(); i++) { + size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets); + } + return size; + } break; + case PList::PLNodeType::PL_NODE_TYPE_DICT: { + size_t size = 0; + + for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { + size += 1 + _asn1_size_len(p_len_octets); // Sequence. + size += 1 + _asn1_size_len(p_len_octets) + E.key.utf8().length(); //Key. + size += 1 + _asn1_size_len(p_len_octets) + E.value->get_asn1_size(p_len_octets); // Value. + } + return size; + } break; + default: { + return 0; + } break; + } +} + +int PListNode::_asn1_size_len(uint8_t p_len_octets) { + if (p_len_octets > 1) { + return p_len_octets + 1; + } else { + return 1; + } +} + +void PListNode::store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const { + uint32_t size = get_asn1_size(p_len_octets); + if (p_len_octets > 1) { + p_stream.push_back(0x80 + p_len_octets); + } + for (int i = p_len_octets - 1; i >= 0; i--) { + uint8_t x = (size >> i * 8) & 0xFF; + p_stream.push_back(x); + } +} + +bool PListNode::store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const { + // Convert to binary ASN1 stream. + bool valid = true; + switch (data_type) { + case PList::PLNodeType::PL_NODE_TYPE_NIL: { + // Nothing to store. + } break; + case PList::PLNodeType::PL_NODE_TYPE_DATE: + case PList::PLNodeType::PL_NODE_TYPE_DATA: { + ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization."); + } break; + case PList::PLNodeType::PL_NODE_TYPE_STRING: { + p_stream.push_back(0x0C); + store_asn1_size(p_stream, p_len_octets); + for (int i = 0; i < data_string.size(); i++) { + p_stream.push_back(data_string[i]); + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { + p_stream.push_back(0x01); + store_asn1_size(p_stream, p_len_octets); + if (data_bool) { + p_stream.push_back(0x01); + } else { + p_stream.push_back(0x00); + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_INTEGER: { + p_stream.push_back(0x02); + store_asn1_size(p_stream, p_len_octets); + for (int i = 4; i >= 0; i--) { + uint8_t x = (data_int >> i * 8) & 0xFF; + p_stream.push_back(x); + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_REAL: { + p_stream.push_back(0x03); + store_asn1_size(p_stream, p_len_octets); + for (int i = 4; i >= 0; i--) { + uint8_t x = (data_int >> i * 8) & 0xFF; + p_stream.push_back(x); + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { + p_stream.push_back(0x30); // Sequence. + store_asn1_size(p_stream, p_len_octets); + for (int i = 0; i < data_array.size(); i++) { + valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets); + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_DICT: { + p_stream.push_back(0x31); // Set. + store_asn1_size(p_stream, p_len_octets); + for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { + CharString cs = E.key.utf8(); + uint32_t size = cs.length(); + + // Sequence. + p_stream.push_back(0x30); + uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + E.value->get_asn1_size(p_len_octets); + if (p_len_octets > 1) { + p_stream.push_back(0x80 + p_len_octets); + } + for (int i = p_len_octets - 1; i >= 0; i--) { + uint8_t x = (seq_size >> i * 8) & 0xFF; + p_stream.push_back(x); + } + // Key. + p_stream.push_back(0x0C); + if (p_len_octets > 1) { + p_stream.push_back(0x80 + p_len_octets); + } + for (int i = p_len_octets - 1; i >= 0; i--) { + uint8_t x = (size >> i * 8) & 0xFF; + p_stream.push_back(x); + } + for (uint32_t i = 0; i < size; i++) { + p_stream.push_back(cs[i]); + } + // Value. + valid = valid && E.value->store_asn1(p_stream, p_len_octets); + } + } break; + } + return valid; +} + +void PListNode::store_text(String &p_stream, uint8_t p_indent) const { + // Convert to text XML stream. + switch (data_type) { + case PList::PLNodeType::PL_NODE_TYPE_NIL: { + // Nothing to store. + } break; + case PList::PLNodeType::PL_NODE_TYPE_DATA: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<data>\n"; + p_stream += String("\t").repeat(p_indent); + p_stream += data_string + "\n"; + p_stream += String("\t").repeat(p_indent); + p_stream += "</data>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_DATE: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<date>"; + p_stream += data_string; + p_stream += "</date>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_STRING: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<string>"; + p_stream += String::utf8(data_string); + p_stream += "</string>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: { + p_stream += String("\t").repeat(p_indent); + if (data_bool) { + p_stream += "<true/>\n"; + } else { + p_stream += "<false/>\n"; + } + } break; + case PList::PLNodeType::PL_NODE_TYPE_INTEGER: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<integer>"; + p_stream += itos(data_int); + p_stream += "</integer>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_REAL: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<real>"; + p_stream += rtos(data_real); + p_stream += "</real>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_ARRAY: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<array>\n"; + for (int i = 0; i < data_array.size(); i++) { + data_array[i]->store_text(p_stream, p_indent + 1); + } + p_stream += String("\t").repeat(p_indent); + p_stream += "</array>\n"; + } break; + case PList::PLNodeType::PL_NODE_TYPE_DICT: { + p_stream += String("\t").repeat(p_indent); + p_stream += "<dict>\n"; + for (const KeyValue<String, Ref<PListNode>> &E : data_dict) { + p_stream += String("\t").repeat(p_indent + 1); + p_stream += "<key>"; + p_stream += E.key; + p_stream += "</key>\n"; + E.value->store_text(p_stream, p_indent + 1); + } + p_stream += String("\t").repeat(p_indent); + p_stream += "</dict>\n"; + } break; + } +} + +/*************************************************************************/ + +PList::PList() { + root = PListNode::new_dict(); +} + +PList::PList(const String &p_string) { + String err_str; + bool ok = load_string(p_string, err_str); + ERR_FAIL_COND_MSG(!ok, "PList: " + err_str); +} + +uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) { + uint64_t pos = p_file->get_position(); + uint64_t ret = 0; + switch (p_size) { + case 1: { + ret = p_file->get_8(); + } break; + case 2: { + ret = BSWAP16(p_file->get_16()); + } break; + case 3: { + ret = BSWAP32(p_file->get_32() & 0x00FFFFFF); + } break; + case 4: { + ret = BSWAP32(p_file->get_32()); + } break; + case 5: { + ret = BSWAP64(p_file->get_64() & 0x000000FFFFFFFFFF); + } break; + case 6: { + ret = BSWAP64(p_file->get_64() & 0x0000FFFFFFFFFFFF); + } break; + case 7: { + ret = BSWAP64(p_file->get_64() & 0x00FFFFFFFFFFFFFF); + } break; + case 8: { + ret = BSWAP64(p_file->get_64()); + } break; + default: { + ret = 0; + } + } + p_file->seek(pos + p_size); + + return ret; +} + +Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx) { + Ref<PListNode> node; + node.instantiate(); + + uint64_t ot_off = trailer.offset_table_start + p_offset_idx * trailer.offset_size; + p_file->seek(ot_off); + uint64_t marker_off = read_bplist_var_size_int(p_file, trailer.offset_size); + ERR_FAIL_COND_V_MSG(marker_off == 0, Ref<PListNode>(), "Invalid marker size."); + + p_file->seek(marker_off); + uint8_t marker = p_file->get_8(); + uint8_t marker_type = marker & 0xF0; + uint64_t marker_size = marker & 0x0F; + + switch (marker_type) { + case 0x00: { + if (marker_size == 0x00) { + node->data_type = PL_NODE_TYPE_NIL; + } else if (marker_size == 0x08) { + node->data_type = PL_NODE_TYPE_BOOLEAN; + node->data_bool = false; + } else if (marker_size == 0x09) { + node->data_type = PL_NODE_TYPE_BOOLEAN; + node->data_bool = true; + } else { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid nil/bool marker value."); + } + } break; + case 0x10: { + node->data_type = PL_NODE_TYPE_INTEGER; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size))); + } break; + case 0x20: { + node->data_type = PL_NODE_TYPE_REAL; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size))); + } break; + case 0x30: { + node->data_type = PL_NODE_TYPE_DATE; + node->data_int = BSWAP64(p_file->get_64()); + node->data_string = Time::get_singleton()->get_datetime_string_from_unix_time(node->data_real + 978307200.0).utf8(); + } break; + case 0x40: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + node->data_type = PL_NODE_TYPE_DATA; + PackedByteArray buf; + buf.resize(marker_size + 1); + p_file->get_buffer(reinterpret_cast<uint8_t *>(buf.ptrw()), marker_size); + node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8(); + } break; + case 0x50: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + node->data_type = PL_NODE_TYPE_STRING; + node->data_string.resize(marker_size + 1); + p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size); + } break; + case 0x60: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + Char16String cs16; + cs16.resize(marker_size + 1); + for (uint64_t i = 0; i < marker_size; i++) { + cs16[i] = BSWAP16(p_file->get_16()); + } + node->data_type = PL_NODE_TYPE_STRING; + node->data_string = String::utf16(cs16.ptr(), cs16.length()).utf8(); + } break; + case 0x80: { + node->data_type = PL_NODE_TYPE_INTEGER; + node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, marker_size + 1)); + } break; + case 0xA0: + case 0xC0: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + uint64_t pos = p_file->get_position(); + + node->data_type = PL_NODE_TYPE_ARRAY; + for (uint64_t i = 0; i < marker_size; i++) { + p_file->seek(pos + trailer.ref_size * i); + uint64_t ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + Ref<PListNode> element = read_bplist_obj(p_file, ref); + ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>()); + node->data_array.push_back(element); + } + } break; + case 0xD0: { + if (marker_size == 0x0F) { + uint8_t ext = p_file->get_8() & 0xF; + marker_size = read_bplist_var_size_int(p_file, pow(2, ext)); + } + uint64_t pos = p_file->get_position(); + + node->data_type = PL_NODE_TYPE_DICT; + for (uint64_t i = 0; i < marker_size; i++) { + p_file->seek(pos + trailer.ref_size * i); + uint64_t key_ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + p_file->seek(pos + trailer.ref_size * (i + marker_size)); + uint64_t obj_ref = read_bplist_var_size_int(p_file, trailer.ref_size); + + Ref<PListNode> element_key = read_bplist_obj(p_file, key_ref); + ERR_FAIL_COND_V(element_key.is_null() || element_key->data_type != PL_NODE_TYPE_STRING, Ref<PListNode>()); + Ref<PListNode> element = read_bplist_obj(p_file, obj_ref); + ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>()); + node->data_dict[String::utf8(element_key->data_string.ptr(), element_key->data_string.length())] = element; + } + } break; + default: { + ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid marker type."); + } + } + return node; +} + +bool PList::load_file(const String &p_filename) { + root = Ref<PListNode>(); + + Ref<FileAccess> fb = FileAccess::open(p_filename, FileAccess::READ); + if (fb.is_null()) { + return false; + } + + unsigned char magic[8]; + fb->get_buffer(magic, 8); + + if (String((const char *)magic, 8) == "bplist00") { + fb->seek_end(-26); + trailer.offset_size = fb->get_8(); + trailer.ref_size = fb->get_8(); + trailer.object_num = BSWAP64(fb->get_64()); + trailer.root_offset_idx = BSWAP64(fb->get_64()); + trailer.offset_table_start = BSWAP64(fb->get_64()); + root = read_bplist_obj(fb, trailer.root_offset_idx); + + return root.is_valid(); + } else { + // Load text plist. + Error err; + Vector<uint8_t> array = FileAccess::get_file_as_bytes(p_filename, &err); + ERR_FAIL_COND_V(err != OK, false); + + String ret; + ret.parse_utf8((const char *)array.ptr(), array.size()); + String err_str; + bool ok = load_string(ret, err_str); + ERR_FAIL_COND_V_MSG(!ok, false, "PList: " + err_str); + + return true; + } +} + +bool PList::load_string(const String &p_string, String &r_err_out) { + root = Ref<PListNode>(); + + int pos = 0; + bool in_plist = false; + bool done_plist = false; + List<Ref<PListNode>> stack; + String key; + while (pos >= 0) { + int open_token_s = p_string.find("<", pos); + if (open_token_s == -1) { + r_err_out = "Unexpected end of data. No tags found."; + return false; + } + int open_token_e = p_string.find(">", open_token_s); + pos = open_token_e; + + String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1); + if (token.is_empty()) { + r_err_out = "Invalid token name."; + return false; + } + String value; + if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... > + int end_token_e = p_string.find(">", open_token_s); + pos = end_token_e; + continue; + } + + if (token.find("plist", 0) == 0) { + in_plist = true; + continue; + } + + if (token == "/plist") { + done_plist = true; + break; + } + + if (!in_plist) { + r_err_out = "Node outside of <plist> tag."; + return false; + } + + if (token == "dict") { + if (!stack.is_empty()) { + // Add subnode end enter it. + Ref<PListNode> dict = PListNode::new_dict(); + dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT; + if (!stack.back()->get()->push_subnode(dict, key)) { + r_err_out = "Can't push subnode, invalid parent type."; + return false; + } + stack.push_back(dict); + } else { + // Add root node. + if (!root.is_null()) { + r_err_out = "Root node already set."; + return false; + } + Ref<PListNode> dict = PListNode::new_dict(); + stack.push_back(dict); + root = dict; + } + continue; + } + + if (token == "/dict") { + // Exit current dict. + if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) { + r_err_out = "Mismatched </dict> tag."; + return false; + } + stack.pop_back(); + continue; + } + + if (token == "array") { + if (!stack.is_empty()) { + // Add subnode end enter it. + Ref<PListNode> arr = PListNode::new_array(); + if (!stack.back()->get()->push_subnode(arr, key)) { + r_err_out = "Can't push subnode, invalid parent type."; + return false; + } + stack.push_back(arr); + } else { + // Add root node. + if (!root.is_null()) { + r_err_out = "Root node already set."; + return false; + } + Ref<PListNode> arr = PListNode::new_array(); + stack.push_back(arr); + root = arr; + } + continue; + } + + if (token == "/array") { + // Exit current array. + if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) { + r_err_out = "Mismatched </array> tag."; + return false; + } + stack.pop_back(); + continue; + } + + if (token[token.length() - 1] == '/') { + token = token.substr(0, token.length() - 1); + } else { + int end_token_s = p_string.find("</", pos); + if (end_token_s == -1) { + r_err_out = vformat("Mismatched <%s> tag.", token); + return false; + } + int end_token_e = p_string.find(">", end_token_s); + pos = end_token_e; + String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2); + if (end_token != token) { + r_err_out = vformat("Mismatched <%s> and <%s> token pair.", token, end_token); + return false; + } + value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1); + } + if (token == "key") { + key = value; + } else { + Ref<PListNode> var = nullptr; + if (token == "true") { + var = PListNode::new_bool(true); + } else if (token == "false") { + var = PListNode::new_bool(false); + } else if (token == "integer") { + var = PListNode::new_int(value.to_int()); + } else if (token == "real") { + var = PListNode::new_real(value.to_float()); + } else if (token == "string") { + var = PListNode::new_string(value); + } else if (token == "data") { + var = PListNode::new_data(value); + } else if (token == "date") { + var = PListNode::new_date(value); + } else { + r_err_out = vformat("Invalid value type: %s.", token); + return false; + } + if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) { + r_err_out = "Can't push subnode, invalid parent type."; + return false; + } + } + } + if (!stack.is_empty() || !done_plist) { + r_err_out = "Unexpected end of data. Root node is not closed."; + return false; + } + return true; +} + +PackedByteArray PList::save_asn1() const { + if (root == nullptr) { + ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node."); + } + size_t size = root->get_asn1_size(1); + uint8_t len_octets = 0; + if (size < 0x80) { + len_octets = 1; + } else { + size = root->get_asn1_size(2); + if (size < 0xFFFF) { + len_octets = 2; + } else { + size = root->get_asn1_size(3); + if (size < 0xFFFFFF) { + len_octets = 3; + } else { + size = root->get_asn1_size(4); + if (size < 0xFFFFFFFF) { + len_octets = 4; + } else { + ERR_FAIL_V_MSG(PackedByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB."); + } + } + } + } + + PackedByteArray ret; + if (!root->store_asn1(ret, len_octets)) { + ERR_FAIL_V_MSG(PackedByteArray(), "PList: ASN.1 serializer error."); + } + return ret; +} + +String PList::save_text() const { + if (root == nullptr) { + ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node."); + } + + String ret; + ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"; + ret += "<plist version=\"1.0\">\n"; + + root->store_text(ret, 0); + + ret += "</plist>\n\n"; + return ret; +} + +Ref<PListNode> PList::get_root() { + return root; +} diff --git a/core/io/plist.h b/core/io/plist.h new file mode 100644 index 0000000000..7d8b8ef0b4 --- /dev/null +++ b/core/io/plist.h @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* plist.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 PLIST_H +#define PLIST_H + +// Property list file format (application/x-plist) parser, property list ASN-1 serialization. + +#include "core/crypto/crypto_core.h" +#include "core/io/file_access.h" +#include "core/os/time.h" + +class PListNode; + +class PList : public RefCounted { + friend class PListNode; + +public: + enum PLNodeType { + PL_NODE_TYPE_NIL, + PL_NODE_TYPE_STRING, + PL_NODE_TYPE_ARRAY, + PL_NODE_TYPE_DICT, + PL_NODE_TYPE_BOOLEAN, + PL_NODE_TYPE_INTEGER, + PL_NODE_TYPE_REAL, + PL_NODE_TYPE_DATA, + PL_NODE_TYPE_DATE, + }; + +private: + struct PListTrailer { + uint8_t offset_size; + uint8_t ref_size; + uint64_t object_num; + uint64_t root_offset_idx; + uint64_t offset_table_start; + }; + + PListTrailer trailer; + Ref<PListNode> root; + + uint64_t read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size); + Ref<PListNode> read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx); + +public: + PList(); + PList(const String &p_string); + + bool load_file(const String &p_filename); + bool load_string(const String &p_string, String &r_err_out); + + PackedByteArray save_asn1() const; + String save_text() const; + + Ref<PListNode> get_root(); +}; + +/*************************************************************************/ + +class PListNode : public RefCounted { + static int _asn1_size_len(uint8_t p_len_octets); + +public: + PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL; + + CharString data_string; + Vector<Ref<PListNode>> data_array; + HashMap<String, Ref<PListNode>> data_dict; + union { + int64_t data_int; + bool data_bool; + double data_real; + }; + + PList::PLNodeType get_type() const; + Variant get_value() const; + + static Ref<PListNode> new_node(const Variant &p_value); + static Ref<PListNode> new_array(); + static Ref<PListNode> new_dict(); + static Ref<PListNode> new_string(const String &p_string); + static Ref<PListNode> new_data(const String &p_string); + static Ref<PListNode> new_date(const String &p_string); + static Ref<PListNode> new_bool(bool p_bool); + static Ref<PListNode> new_int(int64_t p_int); + static Ref<PListNode> new_real(double p_real); + + bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = ""); + + size_t get_asn1_size(uint8_t p_len_octets) const; + + void store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const; + bool store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const; + void store_text(String &p_stream, uint8_t p_indent) const; + + PListNode() {} + ~PListNode() {} +}; + +#endif // PLIST_H diff --git a/core/io/resource.cpp b/core/io/resource.cpp index e0d42a274a..74f18ceee1 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -41,7 +41,12 @@ #include <stdio.h> void Resource::emit_changed() { - emit_signal(CoreStringNames::get_singleton()->changed); + if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) { + // Let the connection happen on the main thread, later, since signals are not thread-safe. + call_deferred("emit_signal", CoreStringNames::get_singleton()->changed); + } else { + emit_signal(CoreStringNames::get_singleton()->changed); + } } void Resource::_resource_path_changed() { @@ -90,6 +95,10 @@ String Resource::get_path() const { return path_cache; } +void Resource::set_path_cache(const String &p_path) { + path_cache = p_path; +} + String Resource::generate_scene_unique_id() { // Generate a unique enough hash, but still user-readable. // If it's not unique it does not matter because the saver will try again. @@ -121,6 +130,16 @@ String Resource::generate_scene_unique_id() { } void Resource::set_scene_unique_id(const String &p_id) { + bool is_valid = true; + for (int i = 0; i < p_id.length(); i++) { + if (!is_ascii_identifier_char(p_id[i])) { + is_valid = false; + scene_unique_id = Resource::generate_scene_unique_id(); + break; + } + } + + ERR_FAIL_COND_MSG(!is_valid, "The scene unique ID must contain only letters, numbers, and underscores."); scene_unique_id = p_id; } @@ -148,12 +167,22 @@ bool Resource::editor_can_reload_from_file() { } void Resource::connect_changed(const Callable &p_callable, uint32_t p_flags) { + if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) { + // Let the check and connection happen on the main thread, later, since signals are not thread-safe. + callable_mp(this, &Resource::connect_changed).call_deferred(p_callable, p_flags); + return; + } if (!is_connected(CoreStringNames::get_singleton()->changed, p_callable) || p_flags & CONNECT_REFERENCE_COUNTED) { connect(CoreStringNames::get_singleton()->changed, p_callable, p_flags); } } void Resource::disconnect_changed(const Callable &p_callable) { + if (ResourceLoader::is_within_load() && !Thread::is_main_thread()) { + // Let the check and disconnection happen on the main thread, later, since signals are not thread-safe. + callable_mp(this, &Resource::disconnect_changed).call_deferred(p_callable); + return; + } if (is_connected(CoreStringNames::get_singleton()->changed, p_callable)) { disconnect(CoreStringNames::get_singleton()->changed, p_callable); } @@ -185,6 +214,7 @@ Error Resource::copy_from(const Ref<Resource> &p_resource) { } return OK; } + void Resource::reload_from_file() { String path = get_path(); if (!path.is_resource_file()) { @@ -200,7 +230,58 @@ void Resource::reload_from_file() { copy_from(s); } -Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { +void Resource::_dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) { + switch (r_variant.get_type()) { + case Variant::ARRAY: { + Array a = r_variant; + for (int i = 0; i < a.size(); i++) { + _dupe_sub_resources(a[i], p_for_scene, p_remap_cache); + } + } break; + case Variant::DICTIONARY: { + Dictionary d = r_variant; + List<Variant> keys; + d.get_key_list(&keys); + for (Variant &k : keys) { + if (k.get_type() == Variant::OBJECT) { + // Replace in dictionary key. + Ref<Resource> sr = k; + if (sr.is_valid() && sr->is_local_to_scene()) { + if (p_remap_cache.has(sr)) { + d[p_remap_cache[sr]] = d[k]; + d.erase(k); + } else { + Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache); + d[dupe] = d[k]; + d.erase(k); + p_remap_cache[sr] = dupe; + } + } + } else { + _dupe_sub_resources(k, p_for_scene, p_remap_cache); + } + + _dupe_sub_resources(d[k], p_for_scene, p_remap_cache); + } + } break; + case Variant::OBJECT: { + Ref<Resource> sr = r_variant; + if (sr.is_valid() && sr->is_local_to_scene()) { + if (p_remap_cache.has(sr)) { + r_variant = p_remap_cache[sr]; + } else { + Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache); + r_variant = dupe; + p_remap_cache[sr] = dupe; + } + } + } break; + default: { + } + } +} + +Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) { List<PropertyInfo> plist; get_property_list(&plist); @@ -213,21 +294,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant p = get(E.name); - if (p.get_type() == Variant::OBJECT) { - Ref<Resource> sr = p; - if (sr.is_valid()) { - if (sr->is_local_to_scene()) { - if (remap_cache.has(sr)) { - p = remap_cache[sr]; - } else { - Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache); - p = dupe; - remap_cache[sr] = dupe; - } - } - } - } + Variant p = get(E.name).duplicate(true); + + _dupe_sub_resources(p, p_for_scene, p_remap_cache); r->set(E.name, p); } @@ -235,7 +304,35 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref return r; } -void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { +void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found) { + switch (p_variant.get_type()) { + case Variant::ARRAY: { + Array a = p_variant; + for (int i = 0; i < a.size(); i++) { + _find_sub_resources(a[i], p_resources_found); + } + } break; + case Variant::DICTIONARY: { + Dictionary d = p_variant; + List<Variant> keys; + d.get_key_list(&keys); + for (const Variant &k : keys) { + _find_sub_resources(k, p_resources_found); + _find_sub_resources(d[k], p_resources_found); + } + } break; + case Variant::OBJECT: { + Ref<Resource> r = p_variant; + if (r.is_valid()) { + p_resources_found.insert(r); + } + } break; + default: { + } + } +} + +void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) { List<PropertyInfo> plist; get_property_list(&plist); @@ -247,14 +344,15 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource continue; } Variant p = get(E.name); - if (p.get_type() == Variant::OBJECT) { - Ref<Resource> sr = p; - if (sr.is_valid()) { - if (sr->is_local_to_scene()) { - if (!remap_cache.has(sr)) { - sr->configure_for_local_scene(p_for_scene, remap_cache); - remap_cache[sr] = sr; - } + + HashSet<Ref<Resource>> sub_resources; + _find_sub_resources(p, sub_resources); + + for (Ref<Resource> sr : sub_resources) { + if (sr->is_local_to_scene()) { + if (!p_remap_cache.has(sr)) { + sr->configure_for_local_scene(p_for_scene, p_remap_cache); + p_remap_cache[sr] = sr; } } } @@ -326,8 +424,7 @@ RID Resource::get_rid() const { } } if (_get_extension() && _get_extension()->get_rid) { - RID ret; - ret.from_uint64(_get_extension()->get_rid(_get_extension_instance())); + RID ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance())); if (ret.is_valid()) { return ret; } @@ -338,7 +435,7 @@ RID Resource::get_rid() const { #ifdef TOOLS_ENABLED -uint32_t Resource::hash_edited_version() const { +uint32_t Resource::hash_edited_version_for_preview() const { uint32_t hash = hash_murmur3_one_32(get_edited_version()); List<PropertyInfo> plist; @@ -348,7 +445,7 @@ uint32_t Resource::hash_edited_version() const { if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { Ref<Resource> res = get(E.name); if (res.is_valid()) { - hash = hash_murmur3_one_32(res->hash_edited_version(), hash); + hash = hash_murmur3_one_32(res->hash_edited_version_for_preview(), hash); } } } @@ -445,6 +542,10 @@ void Resource::_bind_methods() { ClassDB::bind_method(D_METHOD("get_local_scene"), &Resource::get_local_scene); ClassDB::bind_method(D_METHOD("setup_local_to_scene"), &Resource::setup_local_to_scene); + ClassDB::bind_static_method("Resource", D_METHOD("generate_scene_unique_id"), &Resource::generate_scene_unique_id); + ClassDB::bind_method(D_METHOD("set_scene_unique_id", "id"), &Resource::set_scene_unique_id); + ClassDB::bind_method(D_METHOD("get_scene_unique_id"), &Resource::get_scene_unique_id); + ClassDB::bind_method(D_METHOD("emit_changed"), &Resource::emit_changed); ClassDB::bind_method(D_METHOD("duplicate", "subresources"), &Resource::duplicate, DEFVAL(false)); @@ -455,6 +556,7 @@ void Resource::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "resource_local_to_scene"), "set_local_to_scene", "is_local_to_scene"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_path", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_path", "get_path"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_name"), "set_name", "get_name"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "resource_scene_unique_id", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_scene_unique_id", "get_scene_unique_id"); MethodInfo get_rid_bind("_get_rid"); get_rid_bind.return_val.type = Variant::RID; @@ -485,12 +587,14 @@ RWLock ResourceCache::path_cache_lock; #endif void ResourceCache::clear() { - if (resources.size()) { - ERR_PRINT("Resources still in use at exit (run with --verbose for details)."); + if (!resources.is_empty()) { if (OS::get_singleton()->is_stdout_verbose()) { + ERR_PRINT(vformat("%d resources still in use at exit.", resources.size())); for (const KeyValue<String, Resource *> &E : resources) { print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class())); } + } else { + ERR_PRINT(vformat("%d resources still in use at exit (run with --verbose for details).", resources.size())); } } diff --git a/core/io/resource.h b/core/io/resource.h index a9b1a88f6b..cc8a0d4387 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -74,6 +74,9 @@ private: SelfList<Resource> remapped_list; + void _dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache); + void _find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found); + protected: virtual void _resource_path_changed(); static void _bind_methods(); @@ -103,6 +106,7 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false); String get_path() const; + virtual void set_path_cache(const String &p_path); // Set raw path without involving resource cache. _FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); } static String generate_scene_unique_id(); @@ -110,8 +114,8 @@ public: String get_scene_unique_id() const; virtual Ref<Resource> duplicate(bool p_subresources = false) const; - Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache); - void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache); + Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache); + void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache); void set_local_to_scene(bool p_enable); bool is_local_to_scene() const; @@ -121,7 +125,7 @@ public: #ifdef TOOLS_ENABLED - uint32_t hash_edited_version() const; + virtual uint32_t hash_edited_version_for_preview() const; virtual void set_last_modified_time(uint64_t p_time) { last_modified_time = p_time; } uint64_t get_last_modified_time() const { return last_modified_time; } diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index ea97e5ecce..d0a8200546 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -430,7 +430,7 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { path = remaps[path]; } - Ref<Resource> res = ResourceLoader::load(path, exttype); + Ref<Resource> res = ResourceLoader::load(path, exttype, cache_mode_for_external); if (res.is_null()) { WARN_PRINT(String("Couldn't load resource: " + path).utf8().get_data()); @@ -683,7 +683,7 @@ Error ResourceLoaderBinary::load() { } external_resources.write[i].path = path; //remap happens here, not on load because on load it can actually be used for filesystem dock resource remap - external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, ResourceFormatLoader::CACHE_MODE_REUSE); + external_resources.write[i].load_token = ResourceLoader::_load_start(path, external_resources[i].type, use_sub_threads ? ResourceLoader::LOAD_THREAD_DISTRIBUTE : ResourceLoader::LOAD_THREAD_FROM_CURRENT, cache_mode_for_external); if (!external_resources[i].load_token.is_valid()) { if (!ResourceLoader::get_abort_on_missing_resources()) { ResourceLoader::notify_dependency_error(local_path, path, external_resources[i].type); @@ -772,8 +772,12 @@ Error ResourceLoaderBinary::load() { } res = Ref<Resource>(r); - if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it + if (!path.is_empty()) { + if (cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); // If got here because the resource with same path has different type, replace it. + } else { + r->set_path_cache(path); + } } r->set_scene_unique_id(id); } @@ -1185,7 +1189,22 @@ Ref<Resource> ResourceFormatLoaderBinary::load(const String &p_path, const Strin ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Cannot open file '" + p_path + "'."); ResourceLoaderBinary loader; - loader.cache_mode = p_cache_mode; + switch (p_cache_mode) { + case CACHE_MODE_IGNORE: + case CACHE_MODE_REUSE: + case CACHE_MODE_REPLACE: + loader.cache_mode = p_cache_mode; + loader.cache_mode_for_external = CACHE_MODE_REUSE; + break; + case CACHE_MODE_IGNORE_DEEP: + loader.cache_mode = CACHE_MODE_IGNORE; + loader.cache_mode_for_external = p_cache_mode; + break; + case CACHE_MODE_REPLACE_DEEP: + loader.cache_mode = CACHE_MODE_REPLACE; + loader.cache_mode_for_external = p_cache_mode; + break; + } loader.use_sub_threads = p_use_sub_threads; loader.progress = r_progress; String path = !p_original_path.is_empty() ? p_original_path : p_path; @@ -1452,8 +1471,10 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons fw.unref(); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES); - da->remove(p_path); - da->rename(p_path + ".depren", p_path); + if (da->exists(p_path + ".depren")) { + da->remove(p_path); + da->rename(p_path + ".depren", p_path); + } return OK; } @@ -1823,8 +1844,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V f->store_32(VARIANT_ARRAY); Array a = p_property; f->store_32(uint32_t(a.size())); - for (int i = 0; i < a.size(); i++) { - write_variant(f, a[i], resource_map, external_resources, string_map); + for (const Variant &var : a) { + write_variant(f, var, resource_map, external_resources, string_map); } } break; @@ -1995,9 +2016,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant case Variant::ARRAY: { Array varray = p_variant; - int len = varray.size(); - for (int i = 0; i < len; i++) { - const Variant &v = varray.get(i); + _find_resources(varray.get_typed_script()); + for (const Variant &v : varray) { _find_resources(v); } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index e64485d404..222e633e58 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -85,6 +85,7 @@ class ResourceLoaderBinary { Error error = OK; ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; + ResourceFormatLoader::CacheMode cache_mode_for_external = ResourceFormatLoader::CACHE_MODE_REUSE; friend class ResourceFormatLoaderBinary; @@ -109,16 +110,16 @@ public: class ResourceFormatLoaderBinary : public ResourceFormatLoader { public: - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; - virtual String get_resource_script_class(const String &p_path) const; - virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes); - virtual ResourceUID::ID get_resource_uid(const String &p_path) const; - virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); - virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map); + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; + virtual String get_resource_script_class(const String &p_path) const override; + virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override; + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; + virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; + virtual Error rename_dependencies(const String &p_path, const HashMap<String, String> &p_map) override; }; class ResourceFormatSaverBinaryInstance { @@ -180,10 +181,10 @@ public: class ResourceFormatSaverBinary : public ResourceFormatSaver { public: static ResourceFormatSaverBinary *singleton; - virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0); - virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid); - virtual bool recognize(const Ref<Resource> &p_resource) const; - virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const; + virtual Error save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags = 0) override; + virtual Error set_uid(const String &p_path, ResourceUID::ID p_uid) override; + virtual bool recognize(const Ref<Resource> &p_resource) const override; + virtual void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) const override; ResourceFormatSaverBinary(); }; diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 0089544caa..dbd9e70d16 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -59,22 +59,22 @@ class ResourceFormatImporter : public ResourceFormatLoader { public: static ResourceFormatImporter *get_singleton() { return singleton; } - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const; - virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; - virtual ResourceUID::ID get_resource_uid(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual void get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const override; + virtual bool recognize_path(const String &p_path, const String &p_for_type = String()) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; + virtual ResourceUID::ID get_resource_uid(const String &p_path) const override; virtual Variant get_resource_metadata(const String &p_path) const; - virtual bool is_import_valid(const String &p_path) const; - virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); - virtual bool is_imported(const String &p_path) const { return recognize_path(p_path); } - virtual String get_import_group_file(const String &p_path) const; - virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes); - virtual bool exists(const String &p_path) const; + virtual bool is_import_valid(const String &p_path) const override; + virtual void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false) override; + virtual bool is_imported(const String &p_path) const override { return recognize_path(p_path); } + virtual String get_import_group_file(const String &p_path) const override; + virtual void get_classes_used(const String &p_path, HashSet<StringName> *r_classes) override; + virtual bool exists(const String &p_path) const override; - virtual int get_import_order(const String &p_path) const; + virtual int get_import_order(const String &p_path) const override; Error get_import_order_threads_and_importer(const String &p_path, int &r_order, bool &r_can_threads, String &r_importer) const; @@ -136,6 +136,7 @@ public: virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const = 0; virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const = 0; + virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {} virtual String get_option_group_file() const { return String(); } virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 6721ec0953..c3c37aa89d 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -36,6 +36,7 @@ #include "core/object/script_language.h" #include "core/os/condition_variable.h" #include "core/os/os.h" +#include "core/os/safe_binary_mutex.h" #include "core/string/print_string.h" #include "core/string/translation.h" #include "core/variant/variant_parser.h" @@ -187,6 +188,8 @@ void ResourceFormatLoader::_bind_methods() { BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE); BIND_ENUM_CONSTANT(CACHE_MODE_REUSE); BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE); + BIND_ENUM_CONSTANT(CACHE_MODE_IGNORE_DEEP); + BIND_ENUM_CONSTANT(CACHE_MODE_REPLACE_DEEP); GDVIRTUAL_BIND(_get_recognized_extensions); GDVIRTUAL_BIND(_recognize_path, "path", "type"); @@ -239,16 +242,20 @@ ResourceLoader::LoadToken::~LoadToken() { } Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_original_path, const String &p_type_hint, ResourceFormatLoader::CacheMode p_cache_mode, Error *r_error, bool p_use_sub_threads, float *r_progress) { + const String &original_path = p_original_path.is_empty() ? p_path : p_original_path; load_nesting++; if (load_paths_stack->size()) { thread_load_mutex.lock(); - HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1)); - if (E) { - E->value.sub_tasks.insert(p_path); + const String &parent_task_path = load_paths_stack->get(load_paths_stack->size() - 1); + HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(parent_task_path); + // Avoid double-tracking, for progress reporting, resources that boil down to a remapped path containing the real payload (e.g., imported resources). + bool is_remapped_load = original_path == parent_task_path; + if (E && !is_remapped_load) { + E->value.sub_tasks.insert(p_original_path); } thread_load_mutex.unlock(); } - load_paths_stack->push_back(p_path); + load_paths_stack->push_back(original_path); // Try all loaders and pick the first match for the type hint bool found = false; @@ -258,7 +265,7 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin continue; } found = true; - res = loader[i]->load(p_path, !p_original_path.is_empty() ? p_original_path : p_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); + res = loader[i]->load(p_path, original_path, r_error, p_use_sub_threads, r_progress, p_cache_mode); if (!res.is_null()) { break; } @@ -338,9 +345,21 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { load_task.cond_var = nullptr; } + bool ignoring = load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE || load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_IGNORE_DEEP; + bool replacing = load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE || load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE_DEEP; if (load_task.resource.is_valid()) { - if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - load_task.resource->set_path(load_task.local_path); + if (!ignoring) { + if (replacing) { + Ref<Resource> old_res = ResourceCache::get_ref(load_task.local_path); + if (old_res.is_valid() && old_res != load_task.resource) { + // If resource is already loaded, only replace its data, to avoid existing invalidating instances. + old_res->copy_from(load_task.resource); + load_task.resource = old_res; + } + } + load_task.resource->set_path(load_task.local_path, replacing); + } else { + load_task.resource->set_path_cache(load_task.local_path); } if (load_task.xl_remapped) { @@ -359,6 +378,17 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (_loaded_callback) { _loaded_callback(load_task.resource, load_task.local_path); } + } else if (!ignoring) { + Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path); + if (existing.is_valid()) { + load_task.resource = existing; + load_task.status = THREAD_LOAD_LOADED; + load_task.progress = 1.0; + + if (_loaded_callback) { + _loaded_callback(load_task.resource, load_task.local_path); + } + } } thread_load_mutex.unlock(); @@ -376,7 +406,7 @@ static String _validate_local_path(const String &p_path) { if (uid != ResourceUID::INVALID_ID) { return ResourceUID::get_singleton()->get_id_path(uid); } else if (p_path.is_relative_path()) { - return "res://" + p_path; + return ("res://" + p_path).simplify_path(); } else { return ProjectSettings::get_singleton()->localize_path(p_path); } @@ -461,7 +491,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, load_task.type_hint = p_type_hint; load_task.cache_mode = p_cache_mode; load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE; - if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { + if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { Ref<Resource> existing = ResourceCache::get_ref(local_path); if (existing.is_valid()) { //referencing is fine @@ -507,20 +537,20 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, float ResourceLoader::_dependency_get_progress(const String &p_path) { if (thread_load_tasks.has(p_path)) { ThreadLoadTask &load_task = thread_load_tasks[p_path]; + float current_progress = 0.0; int dep_count = load_task.sub_tasks.size(); if (dep_count > 0) { - float dep_progress = 0; for (const String &E : load_task.sub_tasks) { - dep_progress += _dependency_get_progress(E); + current_progress += _dependency_get_progress(E); } - dep_progress /= float(dep_count); - dep_progress *= 0.5; - dep_progress += load_task.progress * 0.5; - return dep_progress; + current_progress /= float(dep_count); + current_progress *= 0.5; + current_progress += load_task.progress * 0.5; } else { - return load_task.progress; + current_progress = load_task.progress; } - + load_task.max_reported_progress = MAX(load_task.max_reported_progress, current_progress); + return load_task.max_reported_progress; } else { return 1.0; //assume finished loading it so it no longer exists } @@ -628,16 +658,14 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro if (load_task.task_id != 0) { // Loading thread is in the worker pool. - load_task.awaited = true; thread_load_mutex.unlock(); Error err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id); if (err == ERR_BUSY) { - // The WorkerThreadPool has scheduled tasks in a way that the current load depends on - // another one in a lower stack frame. Restart such load here. When the stack is eventually - // unrolled, the original load will have been notified to go on. -#ifdef DEV_ENABLED - print_verbose("ResourceLoader: Load task happened to wait on another one deep in the call stack. Attempting to avoid deadlock by re-issuing the load now."); -#endif + // The WorkerThreadPool has reported that the current task wants to await on an older one. + // That't not allowed for safety, to avoid deadlocks. Fortunately, though, in the context of + // resource loading that means that the task to wait for can be restarted here to break the + // cycle, with as much recursion into this process as needed. + // When the stack is eventually unrolled, the original load will have been notified to go on. // CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's // an ongoing load for that resource and wait for it again. This value forces a new load. Ref<ResourceLoader::LoadToken> token = _load_start(load_task.local_path, load_task.type_hint, LOAD_THREAD_DISTRIBUTE, ResourceFormatLoader::CACHE_MODE_IGNORE); @@ -650,6 +678,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro } else { DEV_ASSERT(err == OK); thread_load_mutex.lock(); + load_task.awaited = true; } } else { // Loading thread is main or user thread. @@ -918,7 +947,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem } // Fallback to p_path if new_path does not exist. - if (!FileAccess::exists(new_path)) { + if (!FileAccess::exists(new_path + ".import") && !FileAccess::exists(new_path)) { WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path)); new_path = p_path; } @@ -1010,8 +1039,9 @@ void ResourceLoader::load_translation_remaps() { Array langs = remaps[E]; Vector<String> lang_remaps; lang_remaps.resize(langs.size()); - for (int i = 0; i < langs.size(); i++) { - lang_remaps.write[i] = langs[i]; + String *lang_remaps_ptrw = lang_remaps.ptrw(); + for (const Variant &lang : langs) { + *lang_remaps_ptrw++ = lang; } translation_remaps[String(E)] = lang_remaps; @@ -1090,7 +1120,7 @@ void ResourceLoader::set_load_callback(ResourceLoadedCallback p_callback) { ResourceLoadedCallback ResourceLoader::_loaded_callback = nullptr; -Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(String path) { +Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(const String &path) { for (int i = 0; i < loader_count; ++i) { if (loader[i]->get_script_instance() && loader[i]->get_script_instance()->get_script()->get_path() == path) { return loader[i]; @@ -1099,7 +1129,7 @@ Ref<ResourceFormatLoader> ResourceLoader::_find_custom_resource_format_loader(St return Ref<ResourceFormatLoader>(); } -bool ResourceLoader::add_custom_resource_format_loader(String script_path) { +bool ResourceLoader::add_custom_resource_format_loader(const String &script_path) { if (_find_custom_resource_format_loader(script_path).is_valid()) { return false; } @@ -1111,11 +1141,10 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader"); - ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + "."); + ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource loader, script '%s' does not inherit 'ResourceFormatLoader'.", script_path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource loader, cannot instantiate '%s'.", ibt)); Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj); crl->set_script(s); diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index 3c32a19066..5caf699d32 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -39,14 +39,19 @@ class ConditionVariable; +template <int Tag> +class SafeBinaryMutex; + class ResourceFormatLoader : public RefCounted { GDCLASS(ResourceFormatLoader, RefCounted); public: enum CacheMode { - CACHE_MODE_IGNORE, // Resource and subresources do not use path cache, no path is set into resource. - CACHE_MODE_REUSE, // Resource and subresources use patch cache, reuse existing loaded resources instead of loading from disk when available. - CACHE_MODE_REPLACE, // Resource and subresource use path cache, but replace existing loaded resources when available with information from disk. + CACHE_MODE_IGNORE, + CACHE_MODE_REUSE, + CACHE_MODE_REPLACE, + CACHE_MODE_IGNORE_DEEP, + CACHE_MODE_REPLACE_DEEP, }; protected: @@ -155,7 +160,7 @@ private: static ResourceLoadedCallback _loaded_callback; - static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(String path); + static Ref<ResourceFormatLoader> _find_custom_resource_format_loader(const String &path); struct ThreadLoadTask { WorkerThreadPool::TaskID task_id = 0; // Used if run on a worker thread from the pool. @@ -167,7 +172,8 @@ private: String remapped_path; String dependent_path; String type_hint; - float progress = 0.0; + float progress = 0.0f; + float max_reported_progress = 0.0f; ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS; ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE; Error error = OK; @@ -259,7 +265,7 @@ public: static void set_load_callback(ResourceLoadedCallback p_callback); static ResourceLoaderImport import; - static bool add_custom_resource_format_loader(String script_path); + static bool add_custom_resource_format_loader(const String &script_path); static void add_custom_loaders(); static void remove_custom_loaders(); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 564a54b913..1dc1245355 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -98,6 +98,7 @@ void ResourceFormatSaver::_bind_methods() { } Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) { + ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, "Can't save empty resource to path '" + p_path + "'."); String path = p_path; if (path.is_empty()) { path = p_resource->get_path(); @@ -120,9 +121,8 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, String local_path = ProjectSettings::get_singleton()->localize_path(path); - Ref<Resource> rwcopy = p_resource; if (p_flags & FLAG_CHANGE_PATH) { - rwcopy->set_path(local_path); + p_resource->set_path(local_path); } err = saver[i]->save(p_resource, path, p_flags); @@ -139,7 +139,7 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, #endif if (p_flags & FLAG_CHANGE_PATH) { - rwcopy->set_path(old_path); + p_resource->set_path(old_path); } if (save_callback && path.begins_with("res://")) { @@ -175,6 +175,7 @@ void ResourceSaver::set_save_callback(ResourceSavedCallback p_callback) { } void ResourceSaver::get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions) { + ERR_FAIL_COND_MSG(p_resource.is_null(), "It's not a reference to a valid Resource object."); for (int i = 0; i < saver_count; i++) { saver[i]->get_recognized_extensions(p_resource, p_extensions); } @@ -216,7 +217,7 @@ void ResourceSaver::remove_resource_format_saver(Ref<ResourceFormatSaver> p_form --saver_count; } -Ref<ResourceFormatSaver> ResourceSaver::_find_custom_resource_format_saver(String path) { +Ref<ResourceFormatSaver> ResourceSaver::_find_custom_resource_format_saver(const String &path) { for (int i = 0; i < saver_count; ++i) { if (saver[i]->get_script_instance() && saver[i]->get_script_instance()->get_script()->get_path() == path) { return saver[i]; @@ -225,7 +226,7 @@ Ref<ResourceFormatSaver> ResourceSaver::_find_custom_resource_format_saver(Strin return Ref<ResourceFormatSaver>(); } -bool ResourceSaver::add_custom_resource_format_saver(String script_path) { +bool ResourceSaver::add_custom_resource_format_saver(const String &script_path) { if (_find_custom_resource_format_saver(script_path).is_valid()) { return false; } @@ -237,11 +238,10 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) { Ref<Script> s = res; StringName ibt = s->get_instance_base_type(); bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver"); - ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + "."); + ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource saver, script '%s' does not inherit 'ResourceFormatSaver'.", script_path)); Object *obj = ClassDB::instantiate(ibt); - - ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + "."); + ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource saver, cannot instantiate '%s'.", ibt)); Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj); crl->set_script(s); diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 4828df297a..3e0821926a 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -70,7 +70,7 @@ class ResourceSaver { static ResourceSavedCallback save_callback; static ResourceSaverGetResourceIDForPath save_get_id_for_path; - static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(String path); + static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(const String &path); public: enum SaverFlags { @@ -99,7 +99,7 @@ public: static void set_save_callback(ResourceSavedCallback p_callback); static void set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback); - static bool add_custom_resource_format_saver(String script_path); + static bool add_custom_resource_format_saver(const String &script_path); static void add_custom_savers(); static void remove_custom_savers(); }; diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp index edff3e1f14..c14121a53b 100644 --- a/core/io/resource_uid.cpp +++ b/core/io/resource_uid.cpp @@ -169,14 +169,16 @@ Error ResourceUID::save_to_cache() { return OK; } -Error ResourceUID::load_from_cache() { +Error ResourceUID::load_from_cache(bool p_reset) { Ref<FileAccess> f = FileAccess::open(get_cache_file(), FileAccess::READ); if (f.is_null()) { return ERR_CANT_OPEN; } MutexLock l(mutex); - unique_ids.clear(); + if (p_reset) { + unique_ids.clear(); + } uint32_t entry_count = f->get_32(); for (uint32_t i = 0; i < entry_count; i++) { diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h index 22561c5c03..e56b89f603 100644 --- a/core/io/resource_uid.h +++ b/core/io/resource_uid.h @@ -73,7 +73,7 @@ public: String get_id_path(ID p_id) const; void remove_id(ID p_id); - Error load_from_cache(); + Error load_from_cache(bool p_reset); Error save_to_cache(); Error update_cache(); diff --git a/core/io/stream_peer_gzip.cpp b/core/io/stream_peer_gzip.cpp index 4daa71a22a..514bcf59b8 100644 --- a/core/io/stream_peer_gzip.cpp +++ b/core/io/stream_peer_gzip.cpp @@ -76,6 +76,7 @@ Error StreamPeerGZIP::start_decompression(bool p_is_deflate, int buffer_size) { Error StreamPeerGZIP::_start(bool p_compress, bool p_is_deflate, int buffer_size) { ERR_FAIL_COND_V(ctx != nullptr, ERR_ALREADY_IN_USE); + ERR_FAIL_COND_V_MSG(buffer_size <= 0, ERR_INVALID_PARAMETER, "Invalid buffer size. It should be a positive integer."); clear(); compressing = p_compress; rb.resize(nearest_shift(buffer_size - 1)); diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp index 2b9487b9e1..90a8f49a75 100644 --- a/core/io/stream_peer_tcp.cpp +++ b/core/io/stream_peer_tcp.cpp @@ -51,6 +51,7 @@ Error StreamPeerTCP::poll() { status = STATUS_ERROR; return err; } + return OK; } else if (status != STATUS_CONNECTING) { return OK; } diff --git a/core/io/translation_loader_po.h b/core/io/translation_loader_po.h index 16e7c28cbe..a695826e59 100644 --- a/core/io/translation_loader_po.h +++ b/core/io/translation_loader_po.h @@ -38,10 +38,10 @@ class TranslationLoaderPO : public ResourceFormatLoader { public: static Ref<Resource> load_translation(Ref<FileAccess> f, Error *r_error = nullptr); - virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - virtual bool handles_type(const String &p_type) const; - virtual String get_resource_type(const String &p_path) const; + virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE) override; + virtual void get_recognized_extensions(List<String> *p_extensions) const override; + virtual bool handles_type(const String &p_type) const override; + virtual String get_resource_type(const String &p_path) const override; TranslationLoaderPO() {} }; diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp index faf7d75172..06888c7cda 100644 --- a/core/io/xml_parser.cpp +++ b/core/io/xml_parser.cpp @@ -454,7 +454,7 @@ bool XMLParser::is_empty() const { } Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) { - ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA); + ERR_FAIL_COND_V(p_buffer.is_empty(), ERR_INVALID_DATA); if (data_copy) { memdelete_arr(data_copy); diff --git a/core/io/zip_io.cpp b/core/io/zip_io.cpp index a89c6253f1..972656e237 100644 --- a/core/io/zip_io.cpp +++ b/core/io/zip_io.cpp @@ -53,7 +53,7 @@ int godot_unzip_get_current_file_info(unzFile p_zip_file, unz_file_info64 &r_fil return err; } -int godot_unzip_locate_file(unzFile p_zip_file, String p_filepath, bool p_case_sensitive) { +int godot_unzip_locate_file(unzFile p_zip_file, const String &p_filepath, bool p_case_sensitive) { int err = unzGoToFirstFile(p_zip_file); while (err == UNZ_OK) { unz_file_info64 current_file_info; diff --git a/core/io/zip_io.h b/core/io/zip_io.h index c59b981373..cd5c873c4b 100644 --- a/core/io/zip_io.h +++ b/core/io/zip_io.h @@ -42,7 +42,7 @@ // Get the current file info and safely convert the full filepath to a String. int godot_unzip_get_current_file_info(unzFile p_zip_file, unz_file_info64 &r_file_info, String &r_filepath); // Try to locate the file in the archive specified by the filepath (works with large paths and Unicode). -int godot_unzip_locate_file(unzFile p_zip_file, String p_filepath, bool p_case_sensitive = true); +int godot_unzip_locate_file(unzFile p_zip_file, const String &p_filepath, bool p_case_sensitive = true); // diff --git a/core/math/a_star.compat.inc b/core/math/a_star.compat.inc new file mode 100644 index 0000000000..664d7ffd5e --- /dev/null +++ b/core/math/a_star.compat.inc @@ -0,0 +1,59 @@ +/**************************************************************************/ +/* a_star.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +Vector<int64_t> AStar3D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) { + return get_id_path(p_from_id, p_to_id, false); +} + +Vector<Vector3> AStar3D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) { + return get_point_path(p_from_id, p_to_id, false); +} + +void AStar3D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::_get_id_path_bind_compat_88047); + ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::_get_point_path_bind_compat_88047); +} + +Vector<int64_t> AStar2D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) { + return get_id_path(p_from_id, p_to_id, false); +} + +Vector<Vector2> AStar2D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) { + return get_point_path(p_from_id, p_to_id, false); +} + +void AStar2D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::_get_id_path_bind_compat_88047); + ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::_get_point_path_bind_compat_88047); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index f0f160940d..4497604947 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "a_star.h" +#include "a_star.compat.inc" #include "core/math/geometry_3d.h" #include "core/object/script_language.h" @@ -69,7 +70,7 @@ void AStar3D::add_point(int64_t p_id, const Vector3 &p_pos, real_t p_weight_scal } Vector3 AStar3D::get_point_position(int64_t p_id) const { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V_MSG(!p_exists, Vector3(), vformat("Can't get point's position. Point with id: %d doesn't exist.", p_id)); @@ -77,7 +78,7 @@ Vector3 AStar3D::get_point_position(int64_t p_id) const { } void AStar3D::set_point_position(int64_t p_id, const Vector3 &p_pos) { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's position. Point with id: %d doesn't exist.", p_id)); @@ -85,7 +86,7 @@ void AStar3D::set_point_position(int64_t p_id, const Vector3 &p_pos) { } real_t AStar3D::get_point_weight_scale(int64_t p_id) const { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V_MSG(!p_exists, 0, vformat("Can't get point's weight scale. Point with id: %d doesn't exist.", p_id)); @@ -93,7 +94,7 @@ real_t AStar3D::get_point_weight_scale(int64_t p_id) const { } void AStar3D::set_point_weight_scale(int64_t p_id, real_t p_weight_scale) { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's weight scale. Point with id: %d doesn't exist.", p_id)); ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale)); @@ -102,7 +103,7 @@ void AStar3D::set_point_weight_scale(int64_t p_id, real_t p_weight_scale) { } void AStar3D::remove_point(int64_t p_id) { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_MSG(!p_exists, vformat("Can't remove point. Point with id: %d doesn't exist.", p_id)); @@ -130,11 +131,11 @@ void AStar3D::remove_point(int64_t p_id) { void AStar3D::connect_points(int64_t p_id, int64_t p_with_id, bool bidirectional) { ERR_FAIL_COND_MSG(p_id == p_with_id, vformat("Can't connect point with id: %d to itself.", p_id)); - Point *a; + Point *a = nullptr; bool from_exists = points.lookup(p_id, a); ERR_FAIL_COND_MSG(!from_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_id)); - Point *b; + Point *b = nullptr; bool to_exists = points.lookup(p_with_id, b); ERR_FAIL_COND_MSG(!to_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_with_id)); @@ -166,11 +167,11 @@ void AStar3D::connect_points(int64_t p_id, int64_t p_with_id, bool bidirectional } void AStar3D::disconnect_points(int64_t p_id, int64_t p_with_id, bool bidirectional) { - Point *a; + Point *a = nullptr; bool a_exists = points.lookup(p_id, a); ERR_FAIL_COND_MSG(!a_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_id)); - Point *b; + Point *b = nullptr; bool b_exists = points.lookup(p_with_id, b); ERR_FAIL_COND_MSG(!b_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_with_id)); @@ -220,7 +221,7 @@ PackedInt64Array AStar3D::get_point_ids() { } Vector<int64_t> AStar3D::get_point_connections(int64_t p_id) { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V_MSG(!p_exists, Vector<int64_t>(), vformat("Can't get point's connections. Point with id: %d doesn't exist.", p_id)); @@ -319,6 +320,7 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const { } bool AStar3D::_solve(Point *begin_point, Point *end_point) { + last_closest_point = nullptr; pass++; if (!end_point->enabled) { @@ -332,11 +334,18 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) { begin_point->g_score = 0; begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); + begin_point->abs_g_score = 0; + begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id); open_list.push_back(begin_point); while (!open_list.is_empty()) { Point *p = open_list[0]; // The currently processed point. + // Find point closer to end_point, or same distance to end_point but closer to begin_point. + if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) { + last_closest_point = p; + } + if (p == end_point) { found_route = true; break; @@ -368,6 +377,8 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) { e->prev_point = p; e->g_score = tentative_g_score; e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); + e->abs_g_score = tentative_g_score; + e->abs_f_score = e->f_score - e->g_score; if (new_point) { // The position of the new points is already known. sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); @@ -386,11 +397,11 @@ real_t AStar3D::_estimate_cost(int64_t p_from_id, int64_t p_to_id) { return scost; } - Point *from_point; + Point *from_point = nullptr; bool from_exists = points.lookup(p_from_id, from_point); ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id)); - Point *to_point; + Point *to_point = nullptr; bool to_exists = points.lookup(p_to_id, to_point); ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id)); @@ -403,23 +414,23 @@ real_t AStar3D::_compute_cost(int64_t p_from_id, int64_t p_to_id) { return scost; } - Point *from_point; + Point *from_point = nullptr; bool from_exists = points.lookup(p_from_id, from_point); ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id)); - Point *to_point; + Point *to_point = nullptr; bool to_exists = points.lookup(p_to_id, to_point); ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } -Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) { - Point *a; +Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) { + Point *a = nullptr; bool from_exists = points.lookup(p_from_id, a); ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); - Point *b; + Point *b = nullptr; bool to_exists = points.lookup(p_to_id, b); ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); @@ -434,7 +445,12 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) { bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<Vector3>(); + if (!p_allow_partial_path || last_closest_point == nullptr) { + return Vector<Vector3>(); + } + + // Use closest point instead. + end_point = last_closest_point; } Point *p = end_point; @@ -463,12 +479,12 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) { return path; } -Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) { - Point *a; +Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) { + Point *a = nullptr; bool from_exists = points.lookup(p_from_id, a); ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); - Point *b; + Point *b = nullptr; bool to_exists = points.lookup(p_to_id, b); ERR_FAIL_COND_V_MSG(!to_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); @@ -483,7 +499,12 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) { bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<int64_t>(); + if (!p_allow_partial_path || last_closest_point == nullptr) { + return Vector<int64_t>(); + } + + // Use closest point instead. + end_point = last_closest_point; } Point *p = end_point; @@ -513,7 +534,7 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) { } void AStar3D::set_point_disabled(int64_t p_id, bool p_disabled) { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set if point is disabled. Point with id: %d doesn't exist.", p_id)); @@ -521,7 +542,7 @@ void AStar3D::set_point_disabled(int64_t p_id, bool p_disabled) { } bool AStar3D::is_point_disabled(int64_t p_id) const { - Point *p; + Point *p = nullptr; bool p_exists = points.lookup(p_id, p); ERR_FAIL_COND_V_MSG(!p_exists, false, vformat("Can't get if point is disabled. Point with id: %d doesn't exist.", p_id)); @@ -555,8 +576,8 @@ void AStar3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar3D::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar3D::get_closest_position_in_segment); - ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::get_point_path); - ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::get_id_path); + ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_point_path, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_id_path, DEFVAL(false)); GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") @@ -660,11 +681,11 @@ real_t AStar2D::_estimate_cost(int64_t p_from_id, int64_t p_to_id) { return scost; } - AStar3D::Point *from_point; + AStar3D::Point *from_point = nullptr; bool from_exists = astar.points.lookup(p_from_id, from_point); ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id)); - AStar3D::Point *to_point; + AStar3D::Point *to_point = nullptr; bool to_exists = astar.points.lookup(p_to_id, to_point); ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id)); @@ -677,23 +698,23 @@ real_t AStar2D::_compute_cost(int64_t p_from_id, int64_t p_to_id) { return scost; } - AStar3D::Point *from_point; + AStar3D::Point *from_point = nullptr; bool from_exists = astar.points.lookup(p_from_id, from_point); ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id)); - AStar3D::Point *to_point; + AStar3D::Point *to_point = nullptr; bool to_exists = astar.points.lookup(p_to_id, to_point); ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id)); return from_point->pos.distance_to(to_point->pos); } -Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) { - AStar3D::Point *a; +Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) { + AStar3D::Point *a = nullptr; bool from_exists = astar.points.lookup(p_from_id, a); ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id)); - AStar3D::Point *b; + AStar3D::Point *b = nullptr; bool to_exists = astar.points.lookup(p_to_id, b); ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id)); @@ -707,7 +728,12 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) { bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<Vector2>(); + if (!p_allow_partial_path || astar.last_closest_point == nullptr) { + return Vector<Vector2>(); + } + + // Use closest point instead. + end_point = astar.last_closest_point; } AStar3D::Point *p = end_point; @@ -736,12 +762,12 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) { return path; } -Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) { - AStar3D::Point *a; +Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) { + AStar3D::Point *a = nullptr; bool from_exists = astar.points.lookup(p_from_id, a); ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id)); - AStar3D::Point *b; + AStar3D::Point *b = nullptr; bool to_exists = astar.points.lookup(p_to_id, b); ERR_FAIL_COND_V_MSG(!to_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id)); @@ -756,7 +782,12 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) { bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<int64_t>(); + if (!p_allow_partial_path || astar.last_closest_point == nullptr) { + return Vector<int64_t>(); + } + + // Use closest point instead. + end_point = astar.last_closest_point; } AStar3D::Point *p = end_point; @@ -786,6 +817,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) { } bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) { + astar.last_closest_point = nullptr; astar.pass++; if (!end_point->enabled) { @@ -799,11 +831,18 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) { begin_point->g_score = 0; begin_point->f_score = _estimate_cost(begin_point->id, end_point->id); + begin_point->abs_g_score = 0; + begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id); open_list.push_back(begin_point); while (!open_list.is_empty()) { AStar3D::Point *p = open_list[0]; // The currently processed point. + // Find point closer to end_point, or same distance to end_point but closer to begin_point. + if (astar.last_closest_point == nullptr || astar.last_closest_point->abs_f_score > p->abs_f_score || (astar.last_closest_point->abs_f_score >= p->abs_f_score && astar.last_closest_point->abs_g_score > p->abs_g_score)) { + astar.last_closest_point = p; + } + if (p == end_point) { found_route = true; break; @@ -835,6 +874,8 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) { e->prev_point = p; e->g_score = tentative_g_score; e->f_score = e->g_score + _estimate_cost(e->id, end_point->id); + e->abs_g_score = tentative_g_score; + e->abs_f_score = e->f_score - e->g_score; if (new_point) { // The position of the new points is already known. sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); @@ -874,8 +915,8 @@ void AStar2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment); - ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path); - ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path); + ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_point_path, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_id_path, DEFVAL(false)); GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") diff --git a/core/math/a_star.h b/core/math/a_star.h index 0758500c8a..8e054c4789 100644 --- a/core/math/a_star.h +++ b/core/math/a_star.h @@ -60,6 +60,10 @@ class AStar3D : public RefCounted { real_t f_score = 0; uint64_t open_pass = 0; uint64_t closed_pass = 0; + + // Used for getting closest_point_of_last_pathing_call. + real_t abs_g_score = 0; + real_t abs_f_score = 0; }; struct SortPoints { @@ -109,6 +113,7 @@ class AStar3D : public RefCounted { OAHashMap<int64_t, Point *> points; HashSet<Segment, Segment> segments; + Point *last_closest_point = nullptr; bool _solve(Point *begin_point, Point *end_point); @@ -121,6 +126,12 @@ protected: GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t) +#ifndef DISABLE_DEPRECATED + Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id); + Vector<Vector3> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id); + static void _bind_compatibility_methods(); +#endif + public: int64_t get_available_point_id() const; @@ -149,8 +160,8 @@ public: int64_t get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const; Vector3 get_closest_position_in_segment(const Vector3 &p_point) const; - Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id); - Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id); + Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false); + Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false); AStar3D() {} ~AStar3D(); @@ -171,6 +182,12 @@ protected: GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t) GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t) +#ifndef DISABLE_DEPRECATED + Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id); + Vector<Vector2> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id); + static void _bind_compatibility_methods(); +#endif + public: int64_t get_available_point_id() const; @@ -199,8 +216,8 @@ public: int64_t get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const; Vector2 get_closest_position_in_segment(const Vector2 &p_point) const; - Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id); - Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id); + Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false); + Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false); AStar2D() {} ~AStar2D() {} diff --git a/core/math/a_star_grid_2d.compat.inc b/core/math/a_star_grid_2d.compat.inc new file mode 100644 index 0000000000..e7124c2477 --- /dev/null +++ b/core/math/a_star_grid_2d.compat.inc @@ -0,0 +1,48 @@ +/**************************************************************************/ +/* a_star_grid_2d.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +#include "core/variant/typed_array.h" + +TypedArray<Vector2i> AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) { + return get_id_path(p_from_id, p_to_id, false); +} + +Vector<Vector2> AStarGrid2D::_get_point_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) { + return get_point_path(p_from_id, p_to_id, false); +} + +void AStarGrid2D::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::_get_id_path_bind_compat_88047); + ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::_get_point_path_bind_compat_88047); +} + +#endif // DISABLE_DEPRECATED diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp index 379d34aa2a..f272407869 100644 --- a/core/math/a_star_grid_2d.cpp +++ b/core/math/a_star_grid_2d.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "a_star_grid_2d.h" +#include "a_star_grid_2d.compat.inc" #include "core/variant/typed_array.h" @@ -106,16 +107,45 @@ Size2 AStarGrid2D::get_cell_size() const { return cell_size; } +void AStarGrid2D::set_cell_shape(CellShape p_cell_shape) { + if (cell_shape == p_cell_shape) { + return; + } + + ERR_FAIL_INDEX(p_cell_shape, CellShape::CELL_SHAPE_MAX); + cell_shape = p_cell_shape; + dirty = true; +} + +AStarGrid2D::CellShape AStarGrid2D::get_cell_shape() const { + return cell_shape; +} + void AStarGrid2D::update() { points.clear(); const int32_t end_x = region.get_end().x; const int32_t end_y = region.get_end().y; + const Vector2 half_cell_size = cell_size / 2; for (int32_t y = region.position.y; y < end_y; y++) { LocalVector<Point> line; for (int32_t x = region.position.x; x < end_x; x++) { - line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size)); + Vector2 v = offset; + switch (cell_shape) { + case CELL_SHAPE_ISOMETRIC_RIGHT: + v += half_cell_size + Vector2(x + y, y - x) * half_cell_size; + break; + case CELL_SHAPE_ISOMETRIC_DOWN: + v += half_cell_size + Vector2(x - y, x + y) * half_cell_size; + break; + case CELL_SHAPE_SQUARE: + v += Vector2(x, y) * cell_size; + break; + default: + break; + } + line.push_back(Point(Vector2i(x, y), v)); } points.push_back(line); } @@ -417,6 +447,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) { } bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { + last_closest_point = nullptr; pass++; if (p_end_point->solid) { @@ -430,12 +461,19 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { p_begin_point->g_score = 0; p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id); + p_begin_point->abs_g_score = 0; + p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id); open_list.push_back(p_begin_point); end = p_end_point; while (!open_list.is_empty()) { Point *p = open_list[0]; // The currently processed point. + // Find point closer to end_point, or same distance to end_point but closer to begin_point. + if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) { + last_closest_point = p; + } + if (p == p_end_point) { found_route = true; break; @@ -479,6 +517,9 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) { e->g_score = tentative_g_score; e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id); + e->abs_g_score = tentative_g_score; + e->abs_f_score = e->f_score - e->g_score; + if (new_point) { // The position of the new points is already known. sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr()); } else { @@ -517,7 +558,7 @@ Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const { return _get_point_unchecked(p_id)->pos; } -Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { +Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) { ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method."); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); @@ -536,7 +577,12 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec bool found_route = _solve(begin_point, end_point); if (!found_route) { - return Vector<Vector2>(); + if (!p_allow_partial_path || last_closest_point == nullptr) { + return Vector<Vector2>(); + } + + // Use closest point instead. + end_point = last_closest_point; } Point *p = end_point; @@ -565,7 +611,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec return path; } -TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) { +TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) { ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method."); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region)); ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region)); @@ -584,7 +630,12 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V bool found_route = _solve(begin_point, end_point); if (!found_route) { - return TypedArray<Vector2i>(); + if (!p_allow_partial_path || last_closest_point == nullptr) { + return TypedArray<Vector2i>(); + } + + // Use closest point instead. + end_point = last_closest_point; } Point *p = end_point; @@ -620,6 +671,8 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_offset"), &AStarGrid2D::get_offset); ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &AStarGrid2D::set_cell_size); ClassDB::bind_method(D_METHOD("get_cell_size"), &AStarGrid2D::get_cell_size); + ClassDB::bind_method(D_METHOD("set_cell_shape", "cell_shape"), &AStarGrid2D::set_cell_shape); + ClassDB::bind_method(D_METHOD("get_cell_shape"), &AStarGrid2D::get_cell_shape); ClassDB::bind_method(D_METHOD("is_in_bounds", "x", "y"), &AStarGrid2D::is_in_bounds); ClassDB::bind_method(D_METHOD("is_in_boundsv", "id"), &AStarGrid2D::is_in_boundsv); ClassDB::bind_method(D_METHOD("is_dirty"), &AStarGrid2D::is_dirty); @@ -641,8 +694,8 @@ void AStarGrid2D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear); ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position); - ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path); - ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path); + ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_point_path, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_id_path, DEFVAL(false)); GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id") GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id") @@ -651,6 +704,7 @@ void AStarGrid2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_shape", PROPERTY_HINT_ENUM, "Square,IsometricRight,IsometricDown"), "set_cell_shape", "get_cell_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "jumping_enabled"), "set_jumping_enabled", "is_jumping_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "default_compute_heuristic", PROPERTY_HINT_ENUM, "Euclidean,Manhattan,Octile,Chebyshev"), "set_default_compute_heuristic", "get_default_compute_heuristic"); @@ -668,4 +722,9 @@ void AStarGrid2D::_bind_methods() { BIND_ENUM_CONSTANT(DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE); BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES); BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX); + + BIND_ENUM_CONSTANT(CELL_SHAPE_SQUARE); + BIND_ENUM_CONSTANT(CELL_SHAPE_ISOMETRIC_RIGHT); + BIND_ENUM_CONSTANT(CELL_SHAPE_ISOMETRIC_DOWN); + BIND_ENUM_CONSTANT(CELL_SHAPE_MAX); } diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h index 619551b754..1a9f6dcc11 100644 --- a/core/math/a_star_grid_2d.h +++ b/core/math/a_star_grid_2d.h @@ -56,11 +56,19 @@ public: HEURISTIC_MAX, }; + enum CellShape { + CELL_SHAPE_SQUARE, + CELL_SHAPE_ISOMETRIC_RIGHT, + CELL_SHAPE_ISOMETRIC_DOWN, + CELL_SHAPE_MAX, + }; + private: Rect2i region; Vector2 offset; Size2 cell_size = Size2(1, 1); bool dirty = false; + CellShape cell_shape = CELL_SHAPE_SQUARE; bool jumping_enabled = false; DiagonalMode diagonal_mode = DIAGONAL_MODE_ALWAYS; @@ -81,6 +89,10 @@ private: uint64_t open_pass = 0; uint64_t closed_pass = 0; + // Used for getting last_closest_point. + real_t abs_g_score = 0; + real_t abs_f_score = 0; + Point() {} Point(const Vector2i &p_id, const Vector2 &p_pos) : @@ -101,6 +113,7 @@ private: LocalVector<LocalVector<Point>> points; Point *end = nullptr; + Point *last_closest_point = nullptr; uint64_t pass = 1; @@ -144,6 +157,12 @@ protected: GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i) GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i) +#ifndef DISABLE_DEPRECATED + TypedArray<Vector2i> _get_id_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to); + Vector<Vector2> _get_point_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to); + static void _bind_compatibility_methods(); +#endif + public: void set_region(const Rect2i &p_region); Rect2i get_region() const; @@ -157,6 +176,9 @@ public: void set_cell_size(const Size2 &p_cell_size); Size2 get_cell_size() const; + void set_cell_shape(CellShape p_cell_shape); + CellShape get_cell_shape() const; + void update(); bool is_in_bounds(int32_t p_x, int32_t p_y) const; @@ -187,11 +209,12 @@ public: void clear(); Vector2 get_point_position(const Vector2i &p_id) const; - Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to); - TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to); + Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false); + TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false); }; VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode); VARIANT_ENUM_CAST(AStarGrid2D::Heuristic); +VARIANT_ENUM_CAST(AStarGrid2D::CellShape) #endif // A_STAR_GRID_2D_H diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp index 1071df0979..76e9e74dea 100644 --- a/core/math/aabb.cpp +++ b/core/math/aabb.cpp @@ -125,8 +125,8 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 * #endif Vector3 c1, c2; Vector3 end = position + size; - real_t near = -1e20; - real_t far = 1e20; + real_t depth_near = -1e20; + real_t depth_far = 1e20; int axis = 0; for (int i = 0; i < 3; i++) { @@ -141,14 +141,14 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 * if (c1[i] > c2[i]) { SWAP(c1, c2); } - if (c1[i] > near) { - near = c1[i]; + if (c1[i] > depth_near) { + depth_near = c1[i]; axis = i; } - if (c2[i] < far) { - far = c2[i]; + if (c2[i] < depth_far) { + depth_far = c2[i]; } - if ((near > far) || (far < 0)) { + if ((depth_near > depth_far) || (depth_far < 0)) { return false; } } diff --git a/core/math/aabb.h b/core/math/aabb.h index 859810df37..48a883e64c 100644 --- a/core/math/aabb.h +++ b/core/math/aabb.h @@ -73,7 +73,7 @@ struct _NO_DISCARD_ AABB { AABB intersection(const AABB &p_aabb) const; ///get box where two intersect, empty if no intersection occurs bool intersects_segment(const Vector3 &p_from, const Vector3 &p_to, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; bool intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip = nullptr, Vector3 *r_normal = nullptr) const; - _FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const; + _FORCE_INLINE_ bool smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t p_t0, real_t p_t1) const; _FORCE_INLINE_ bool intersects_convex_shape(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count) const; _FORCE_INLINE_ bool inside_convex_shape(const Plane *p_planes, int p_plane_count) const; @@ -101,7 +101,7 @@ struct _NO_DISCARD_ AABB { _FORCE_INLINE_ void expand_to(const Vector3 &p_vector); /** expand to contain a point if necessary */ _FORCE_INLINE_ AABB abs() const { - return AABB(Vector3(position.x + MIN(size.x, (real_t)0), position.y + MIN(size.y, (real_t)0), position.z + MIN(size.z, (real_t)0)), size.abs()); + return AABB(position + size.min(Vector3()), size.abs()); } Variant intersects_segment_bind(const Vector3 &p_from, const Vector3 &p_to) const; @@ -200,11 +200,11 @@ inline bool AABB::encloses(const AABB &p_aabb) const { return ( (src_min.x <= dst_min.x) && - (src_max.x > dst_max.x) && + (src_max.x >= dst_max.x) && (src_min.y <= dst_min.y) && - (src_max.y > dst_max.y) && + (src_max.y >= dst_max.y) && (src_min.z <= dst_min.z) && - (src_max.z > dst_max.z)); + (src_max.z >= dst_max.z)); } Vector3 AABB::get_support(const Vector3 &p_normal) const { @@ -401,7 +401,7 @@ inline real_t AABB::get_shortest_axis_size() const { return max_size; } -bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t t0, real_t t1) const { +bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real_t p_t0, real_t p_t1) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) { ERR_PRINT("AABB size is negative, this is not supported. Use AABB.abs() to get an AABB with a positive size."); @@ -452,7 +452,7 @@ bool AABB::smits_intersect_ray(const Vector3 &p_from, const Vector3 &p_dir, real if (tzmax < tmax) { tmax = tzmax; } - return ((tmin < t1) && (tmax > t0)); + return ((tmin < p_t1) && (tmax > p_t0)); } void AABB::grow_by(real_t p_amount) { diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h index d26336e9a2..e205126cdf 100644 --- a/core/math/audio_frame.h +++ b/core/math/audio_frame.h @@ -51,105 +51,123 @@ static const float AUDIO_PEAK_OFFSET = 0.0000000001f; static const float AUDIO_MIN_PEAK_DB = -200.0f; // linear_to_db(AUDIO_PEAK_OFFSET) struct AudioFrame { - //left and right samples - float l = 0.f, r = 0.f; - - _ALWAYS_INLINE_ const float &operator[](int idx) const { return idx == 0 ? l : r; } - _ALWAYS_INLINE_ float &operator[](int idx) { return idx == 0 ? l : r; } + // Left and right samples. + union { + struct { + float left; + float right; + }; +#ifndef DISABLE_DEPRECATED + struct { + float l; + float r; + }; +#endif + float levels[2] = { 0.0 }; + }; + + _ALWAYS_INLINE_ const float &operator[](int p_idx) const { + DEV_ASSERT((unsigned int)p_idx < 2); + return levels[p_idx]; + } + _ALWAYS_INLINE_ float &operator[](int p_idx) { + DEV_ASSERT((unsigned int)p_idx < 2); + return levels[p_idx]; + } - _ALWAYS_INLINE_ AudioFrame operator+(const AudioFrame &p_frame) const { return AudioFrame(l + p_frame.l, r + p_frame.r); } - _ALWAYS_INLINE_ AudioFrame operator-(const AudioFrame &p_frame) const { return AudioFrame(l - p_frame.l, r - p_frame.r); } - _ALWAYS_INLINE_ AudioFrame operator*(const AudioFrame &p_frame) const { return AudioFrame(l * p_frame.l, r * p_frame.r); } - _ALWAYS_INLINE_ AudioFrame operator/(const AudioFrame &p_frame) const { return AudioFrame(l / p_frame.l, r / p_frame.r); } + _ALWAYS_INLINE_ AudioFrame operator+(const AudioFrame &p_frame) const { return AudioFrame(left + p_frame.left, right + p_frame.right); } + _ALWAYS_INLINE_ AudioFrame operator-(const AudioFrame &p_frame) const { return AudioFrame(left - p_frame.left, right - p_frame.right); } + _ALWAYS_INLINE_ AudioFrame operator*(const AudioFrame &p_frame) const { return AudioFrame(left * p_frame.left, right * p_frame.right); } + _ALWAYS_INLINE_ AudioFrame operator/(const AudioFrame &p_frame) const { return AudioFrame(left / p_frame.left, right / p_frame.right); } - _ALWAYS_INLINE_ AudioFrame operator+(float p_sample) const { return AudioFrame(l + p_sample, r + p_sample); } - _ALWAYS_INLINE_ AudioFrame operator-(float p_sample) const { return AudioFrame(l - p_sample, r - p_sample); } - _ALWAYS_INLINE_ AudioFrame operator*(float p_sample) const { return AudioFrame(l * p_sample, r * p_sample); } - _ALWAYS_INLINE_ AudioFrame operator/(float p_sample) const { return AudioFrame(l / p_sample, r / p_sample); } + _ALWAYS_INLINE_ AudioFrame operator+(float p_sample) const { return AudioFrame(left + p_sample, right + p_sample); } + _ALWAYS_INLINE_ AudioFrame operator-(float p_sample) const { return AudioFrame(left - p_sample, right - p_sample); } + _ALWAYS_INLINE_ AudioFrame operator*(float p_sample) const { return AudioFrame(left * p_sample, right * p_sample); } + _ALWAYS_INLINE_ AudioFrame operator/(float p_sample) const { return AudioFrame(left / p_sample, right / p_sample); } _ALWAYS_INLINE_ void operator+=(const AudioFrame &p_frame) { - l += p_frame.l; - r += p_frame.r; + left += p_frame.left; + right += p_frame.right; } _ALWAYS_INLINE_ void operator-=(const AudioFrame &p_frame) { - l -= p_frame.l; - r -= p_frame.r; + left -= p_frame.left; + right -= p_frame.right; } _ALWAYS_INLINE_ void operator*=(const AudioFrame &p_frame) { - l *= p_frame.l; - r *= p_frame.r; + left *= p_frame.left; + right *= p_frame.right; } _ALWAYS_INLINE_ void operator/=(const AudioFrame &p_frame) { - l /= p_frame.l; - r /= p_frame.r; + left /= p_frame.left; + right /= p_frame.right; } _ALWAYS_INLINE_ void operator+=(float p_sample) { - l += p_sample; - r += p_sample; + left += p_sample; + right += p_sample; } _ALWAYS_INLINE_ void operator-=(float p_sample) { - l -= p_sample; - r -= p_sample; + left -= p_sample; + right -= p_sample; } _ALWAYS_INLINE_ void operator*=(float p_sample) { - l *= p_sample; - r *= p_sample; + left *= p_sample; + right *= p_sample; } _ALWAYS_INLINE_ void operator/=(float p_sample) { - l /= p_sample; - r /= p_sample; + left /= p_sample; + right /= p_sample; } _ALWAYS_INLINE_ void undenormalize() { - l = ::undenormalize(l); - r = ::undenormalize(r); + left = ::undenormalize(left); + right = ::undenormalize(right); } _FORCE_INLINE_ AudioFrame lerp(const AudioFrame &p_b, float p_t) const { AudioFrame res = *this; - res.l += (p_t * (p_b.l - l)); - res.r += (p_t * (p_b.r - r)); + res.left += (p_t * (p_b.left - left)); + res.right += (p_t * (p_b.right - right)); return res; } - _ALWAYS_INLINE_ AudioFrame(float p_l, float p_r) { - l = p_l; - r = p_r; + _ALWAYS_INLINE_ AudioFrame(float p_left, float p_right) { + left = p_left; + right = p_right; } _ALWAYS_INLINE_ AudioFrame(const AudioFrame &p_frame) { - l = p_frame.l; - r = p_frame.r; + left = p_frame.left; + right = p_frame.right; } _ALWAYS_INLINE_ void operator=(const AudioFrame &p_frame) { - l = p_frame.l; - r = p_frame.r; + left = p_frame.left; + right = p_frame.right; } _ALWAYS_INLINE_ operator Vector2() const { - return Vector2(l, r); + return Vector2(left, right); } _ALWAYS_INLINE_ AudioFrame(const Vector2 &p_v2) { - l = p_v2.x; - r = p_v2.y; + left = p_v2.x; + right = p_v2.y; } _ALWAYS_INLINE_ AudioFrame() {} }; _ALWAYS_INLINE_ AudioFrame operator*(float p_scalar, const AudioFrame &p_frame) { - return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); + return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar); } _ALWAYS_INLINE_ AudioFrame operator*(int32_t p_scalar, const AudioFrame &p_frame) { - return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); + return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar); } _ALWAYS_INLINE_ AudioFrame operator*(int64_t p_scalar, const AudioFrame &p_frame) { - return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar); + return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar); } #endif // AUDIO_FRAME_H diff --git a/core/math/basis.cpp b/core/math/basis.cpp index 9796ac59c2..34ed1c2d85 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -89,13 +89,26 @@ Basis Basis::orthogonalized() const { return c; } +// Returns true if the basis vectors are orthogonal (perpendicular), so it has no skew or shear, and can be decomposed into rotation and scale. +// See https://en.wikipedia.org/wiki/Orthogonal_basis bool Basis::is_orthogonal() const { - Basis identity; - Basis m = (*this) * transposed(); + const Vector3 x = get_column(0); + const Vector3 y = get_column(1); + const Vector3 z = get_column(2); + return Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); +} - return m.is_equal_approx(identity); +// Returns true if the basis vectors are orthonormal (orthogonal and normalized), so it has no scale, skew, or shear. +// See https://en.wikipedia.org/wiki/Orthonormal_basis +bool Basis::is_orthonormal() const { + const Vector3 x = get_column(0); + const Vector3 y = get_column(1); + const Vector3 z = get_column(2); + return Math::is_equal_approx(x.length_squared(), 1) && Math::is_equal_approx(y.length_squared(), 1) && Math::is_equal_approx(z.length_squared(), 1) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); } +// Returns true if the basis is conformal (orthogonal, uniform scale, preserves angles and distance ratios). +// See https://en.wikipedia.org/wiki/Conformal_linear_transformation bool Basis::is_conformal() const { const Vector3 x = get_column(0); const Vector3 y = get_column(1); @@ -104,6 +117,7 @@ bool Basis::is_conformal() const { return Math::is_equal_approx(x_len_sq, y.length_squared()) && Math::is_equal_approx(x_len_sq, z.length_squared()) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z)); } +// Returns true if the basis only has diagonal elements, so it may only have scale or flip, but no rotation, skew, or shear. bool Basis::is_diagonal() const { return ( Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) && @@ -111,8 +125,9 @@ bool Basis::is_diagonal() const { Math::is_zero_approx(rows[2][0]) && Math::is_zero_approx(rows[2][1])); } +// Returns true if the basis is a pure rotation matrix, so it has no scale, skew, shear, or flip. bool Basis::is_rotation() const { - return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal(); + return is_conformal() && Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON); } #ifdef MATH_CHECKS @@ -263,7 +278,7 @@ Basis Basis::scaled_orthogonal(const Vector3 &p_scale) const { return m; } -float Basis::get_uniform_scale() const { +real_t Basis::get_uniform_scale() const { return (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f; } @@ -278,7 +293,7 @@ Vector3 Basis::get_scale_abs() const { Vector3(rows[0][2], rows[1][2], rows[2][2]).length()); } -Vector3 Basis::get_scale_local() const { +Vector3 Basis::get_scale_global() const { real_t det_sign = SIGN(determinant()); return det_sign * Vector3(rows[0].length(), rows[1].length(), rows[2].length()); } @@ -670,7 +685,7 @@ void Basis::set_euler(const Vector3 &p_euler, EulerOrder p_order) { *this = zmat * ymat * xmat; } break; default: { - ERR_FAIL_MSG("Invalid order parameter for set_euler(vec3,order)"); + ERR_FAIL_MSG("Invalid Euler order parameter."); } } } @@ -707,7 +722,7 @@ Basis::operator String() const { Quaternion Basis::get_quaternion() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors."); + ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis " + operator String() + " must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors."); #endif /* Allow getting a quaternion from an unnormalized transform */ Basis m = *this; @@ -834,7 +849,7 @@ void Basis::set_quaternion(const Quaternion &p_quaternion) { void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_angle) { // Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle #ifdef MATH_CHECKS - ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized."); + ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized."); #endif Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z); real_t cosine = Math::cos(p_angle); @@ -892,7 +907,7 @@ void Basis::_set_diagonal(const Vector3 &p_diag) { rows[2][2] = p_diag.z; } -Basis Basis::lerp(const Basis &p_to, const real_t &p_weight) const { +Basis Basis::lerp(const Basis &p_to, real_t p_weight) const { Basis b; b.rows[0] = rows[0].lerp(p_to.rows[0], p_weight); b.rows[1] = rows[1].lerp(p_to.rows[1], p_weight); @@ -901,7 +916,7 @@ Basis Basis::lerp(const Basis &p_to, const real_t &p_weight) const { return b; } -Basis Basis::slerp(const Basis &p_to, const real_t &p_weight) const { +Basis Basis::slerp(const Basis &p_to, real_t p_weight) const { //consider scale Quaternion from(*this); Quaternion to(p_to); diff --git a/core/math/basis.h b/core/math/basis.h index adacd1c216..918cbc18d4 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -41,11 +41,11 @@ struct _NO_DISCARD_ Basis { Vector3(0, 0, 1) }; - _FORCE_INLINE_ const Vector3 &operator[](int axis) const { - return rows[axis]; + _FORCE_INLINE_ const Vector3 &operator[](int p_axis) const { + return rows[p_axis]; } - _FORCE_INLINE_ Vector3 &operator[](int axis) { - return rows[axis]; + _FORCE_INLINE_ Vector3 &operator[](int p_axis) { + return rows[p_axis]; } void invert(); @@ -99,25 +99,25 @@ struct _NO_DISCARD_ Basis { void scale_orthogonal(const Vector3 &p_scale); Basis scaled_orthogonal(const Vector3 &p_scale) const; - float get_uniform_scale() const; + real_t get_uniform_scale() const; Vector3 get_scale() const; Vector3 get_scale_abs() const; - Vector3 get_scale_local() const; + Vector3 get_scale_global() const; void set_axis_angle_scale(const Vector3 &p_axis, real_t p_angle, const Vector3 &p_scale); void set_euler_scale(const Vector3 &p_euler, const Vector3 &p_scale, EulerOrder p_order = EulerOrder::YXZ); void set_quaternion_scale(const Quaternion &p_quaternion, const Vector3 &p_scale); // transposed dot products - _FORCE_INLINE_ real_t tdotx(const Vector3 &v) const { - return rows[0][0] * v[0] + rows[1][0] * v[1] + rows[2][0] * v[2]; + _FORCE_INLINE_ real_t tdotx(const Vector3 &p_v) const { + return rows[0][0] * p_v[0] + rows[1][0] * p_v[1] + rows[2][0] * p_v[2]; } - _FORCE_INLINE_ real_t tdoty(const Vector3 &v) const { - return rows[0][1] * v[0] + rows[1][1] * v[1] + rows[2][1] * v[2]; + _FORCE_INLINE_ real_t tdoty(const Vector3 &p_v) const { + return rows[0][1] * p_v[0] + rows[1][1] * p_v[1] + rows[2][1] * p_v[2]; } - _FORCE_INLINE_ real_t tdotz(const Vector3 &v) const { - return rows[0][2] * v[0] + rows[1][2] * v[1] + rows[2][2] * v[2]; + _FORCE_INLINE_ real_t tdotz(const Vector3 &p_v) const { + return rows[0][2] * p_v[0] + rows[1][2] * p_v[1] + rows[2][2] * p_v[2]; } bool is_equal_approx(const Basis &p_basis) const; @@ -134,32 +134,35 @@ struct _NO_DISCARD_ Basis { _FORCE_INLINE_ Basis operator+(const Basis &p_matrix) const; _FORCE_INLINE_ void operator-=(const Basis &p_matrix); _FORCE_INLINE_ Basis operator-(const Basis &p_matrix) const; - _FORCE_INLINE_ void operator*=(const real_t p_val); - _FORCE_INLINE_ Basis operator*(const real_t p_val) const; + _FORCE_INLINE_ void operator*=(real_t p_val); + _FORCE_INLINE_ Basis operator*(real_t p_val) const; + _FORCE_INLINE_ void operator/=(real_t p_val); + _FORCE_INLINE_ Basis operator/(real_t p_val) const; bool is_orthogonal() const; + bool is_orthonormal() const; bool is_conformal() const; bool is_diagonal() const; bool is_rotation() const; - Basis lerp(const Basis &p_to, const real_t &p_weight) const; - Basis slerp(const Basis &p_to, const real_t &p_weight) const; + Basis lerp(const Basis &p_to, real_t p_weight) const; + Basis slerp(const Basis &p_to, real_t p_weight) const; void rotate_sh(real_t *p_values); operator String() const; /* create / set */ - _FORCE_INLINE_ void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - rows[0][0] = xx; - rows[0][1] = xy; - rows[0][2] = xz; - rows[1][0] = yx; - rows[1][1] = yy; - rows[1][2] = yz; - rows[2][0] = zx; - rows[2][1] = zy; - rows[2][2] = zz; + _FORCE_INLINE_ void set(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_zx, real_t p_zy, real_t p_zz) { + rows[0][0] = p_xx; + rows[0][1] = p_xy; + rows[0][2] = p_xz; + rows[1][0] = p_yx; + rows[1][1] = p_yy; + rows[1][2] = p_yz; + rows[2][0] = p_zx; + rows[2][1] = p_zy; + rows[2][2] = p_zz; } _FORCE_INLINE_ void set_columns(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z) { set_column(0, p_x); @@ -189,20 +192,20 @@ struct _NO_DISCARD_ Basis { rows[2].zero(); } - _FORCE_INLINE_ Basis transpose_xform(const Basis &m) const { + _FORCE_INLINE_ Basis transpose_xform(const Basis &p_m) const { return Basis( - rows[0].x * m[0].x + rows[1].x * m[1].x + rows[2].x * m[2].x, - rows[0].x * m[0].y + rows[1].x * m[1].y + rows[2].x * m[2].y, - rows[0].x * m[0].z + rows[1].x * m[1].z + rows[2].x * m[2].z, - rows[0].y * m[0].x + rows[1].y * m[1].x + rows[2].y * m[2].x, - rows[0].y * m[0].y + rows[1].y * m[1].y + rows[2].y * m[2].y, - rows[0].y * m[0].z + rows[1].y * m[1].z + rows[2].y * m[2].z, - rows[0].z * m[0].x + rows[1].z * m[1].x + rows[2].z * m[2].x, - rows[0].z * m[0].y + rows[1].z * m[1].y + rows[2].z * m[2].y, - rows[0].z * m[0].z + rows[1].z * m[1].z + rows[2].z * m[2].z); + rows[0].x * p_m[0].x + rows[1].x * p_m[1].x + rows[2].x * p_m[2].x, + rows[0].x * p_m[0].y + rows[1].x * p_m[1].y + rows[2].x * p_m[2].y, + rows[0].x * p_m[0].z + rows[1].x * p_m[1].z + rows[2].x * p_m[2].z, + rows[0].y * p_m[0].x + rows[1].y * p_m[1].x + rows[2].y * p_m[2].x, + rows[0].y * p_m[0].y + rows[1].y * p_m[1].y + rows[2].y * p_m[2].y, + rows[0].y * p_m[0].z + rows[1].y * p_m[1].z + rows[2].y * p_m[2].z, + rows[0].z * p_m[0].x + rows[1].z * p_m[1].x + rows[2].z * p_m[2].x, + rows[0].z * p_m[0].y + rows[1].z * p_m[1].y + rows[2].z * p_m[2].y, + rows[0].z * p_m[0].z + rows[1].z * p_m[1].z + rows[2].z * p_m[2].z); } - Basis(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz) { - set(xx, xy, xz, yx, yy, yz, zx, zy, zz); + Basis(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_zx, real_t p_zy, real_t p_zz) { + set(p_xx, p_xy, p_xz, p_yx, p_yy, p_yz, p_zx, p_zy, p_zz); } void orthonormalize(); @@ -276,18 +279,30 @@ _FORCE_INLINE_ Basis Basis::operator-(const Basis &p_matrix) const { return ret; } -_FORCE_INLINE_ void Basis::operator*=(const real_t p_val) { +_FORCE_INLINE_ void Basis::operator*=(real_t p_val) { rows[0] *= p_val; rows[1] *= p_val; rows[2] *= p_val; } -_FORCE_INLINE_ Basis Basis::operator*(const real_t p_val) const { +_FORCE_INLINE_ Basis Basis::operator*(real_t p_val) const { Basis ret(*this); ret *= p_val; return ret; } +_FORCE_INLINE_ void Basis::operator/=(real_t p_val) { + rows[0] /= p_val; + rows[1] /= p_val; + rows[2] /= p_val; +} + +_FORCE_INLINE_ Basis Basis::operator/(real_t p_val) const { + Basis ret(*this); + ret /= p_val; + return ret; +} + Vector3 Basis::xform(const Vector3 &p_vector) const { return Vector3( rows[0].dot(p_vector), diff --git a/core/math/bvh.h b/core/math/bvh.h index 8fbbd8e7fd..4815466e89 100644 --- a/core/math/bvh.h +++ b/core/math/bvh.h @@ -57,7 +57,7 @@ #define BVHTREE_CLASS BVH_Tree<T, NUM_TREES, 2, MAX_ITEMS, USER_PAIR_TEST_FUNCTION, USER_CULL_TEST_FUNCTION, USE_PAIRS, BOUNDS, POINT> #define BVH_LOCKED_FUNCTION BVHLockedFunction _lock_guard(&_mutex, BVH_THREAD_SAFE &&_thread_safe); -template <class T, int NUM_TREES = 1, bool USE_PAIRS = false, int MAX_ITEMS = 32, class USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, class USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, class BOUNDS = AABB, class POINT = Vector3, bool BVH_THREAD_SAFE = true> +template <typename T, int NUM_TREES = 1, bool USE_PAIRS = false, int MAX_ITEMS = 32, typename USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, typename USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, typename BOUNDS = AABB, typename POINT = Vector3, bool BVH_THREAD_SAFE = true> class BVH_Manager { public: // note we are using uint32_t instead of BVHHandle, losing type safety, but this diff --git a/core/math/bvh_abb.h b/core/math/bvh_abb.h index fb0207e0bd..3d32c250c9 100644 --- a/core/math/bvh_abb.h +++ b/core/math/bvh_abb.h @@ -32,7 +32,7 @@ #define BVH_ABB_H // special optimized version of axis aligned bounding box -template <class BOUNDS = AABB, class POINT = Vector3> +template <typename BOUNDS = AABB, typename POINT = Vector3> struct BVH_ABB { struct ConvexHull { // convex hulls (optional) @@ -258,7 +258,7 @@ struct BVH_ABB { } // Actually surface area metric. - float get_area() const { + real_t get_area() const { POINT d = calculate_size(); return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x); } diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc index 1964f2fa83..a1fdf78c98 100644 --- a/core/math/bvh_debug.inc +++ b/core/math/bvh_debug.inc @@ -10,7 +10,7 @@ String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const { POINT size = aabb.calculate_size(); String sz; - float vol = 0.0; + real_t vol = 0.0; for (int i = 0; i < POINT::AXIS_COUNT; ++i) { sz += "("; diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc index 2c85a63575..6da89bd027 100644 --- a/core/math/bvh_split.inc +++ b/core/math/bvh_split.inc @@ -150,7 +150,7 @@ void _split_leaf_sort_groups(int &num_a, int &num_b, uint16_t *group_a, uint16_t BVHABB_CLASS rest_aabb; - float best_size = FLT_MAX; + real_t best_size = FLT_MAX; int best_candidate = -1; // find most likely from a to move into b @@ -171,7 +171,7 @@ void _split_leaf_sort_groups(int &num_a, int &num_b, uint16_t *group_a, uint16_t groupb_aabb_new.merge(temp_bounds[group_a[check]]); // now compare the sizes - float size = groupb_aabb_new.get_area() + rest_aabb.get_area(); + real_t size = groupb_aabb_new.get_area() + rest_aabb.get_area(); if (size < best_size) { best_size = size; best_candidate = check; diff --git a/core/math/bvh_tree.h b/core/math/bvh_tree.h index ce296668db..0faa50555f 100644 --- a/core/math/bvh_tree.h +++ b/core/math/bvh_tree.h @@ -106,7 +106,7 @@ struct BVHHandle { }; // helper class to make iterative versions of recursive functions -template <class T> +template <typename T> class BVH_IterativeInfo { public: enum { @@ -152,7 +152,7 @@ public: } }; -template <class T> +template <typename T> class BVH_DummyPairTestFunction { public: static bool user_collision_check(T *p_a, T *p_b) { @@ -161,7 +161,7 @@ public: } }; -template <class T> +template <typename T> class BVH_DummyCullTestFunction { public: static bool user_cull_check(T *p_a, T *p_b) { @@ -170,7 +170,7 @@ public: } }; -template <class T, int NUM_TREES, int MAX_CHILDREN, int MAX_ITEMS, class USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, class USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, bool USE_PAIRS = false, class BOUNDS = AABB, class POINT = Vector3> +template <typename T, int NUM_TREES, int MAX_CHILDREN, int MAX_ITEMS, typename USER_PAIR_TEST_FUNCTION = BVH_DummyPairTestFunction<T>, typename USER_CULL_TEST_FUNCTION = BVH_DummyCullTestFunction<T>, bool USE_PAIRS = false, typename BOUNDS = AABB, typename POINT = Vector3> class BVH_Tree { friend class BVH; diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 68d995fe67..80662c1b07 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -77,15 +77,17 @@ subject to the following restrictions: #ifdef DEBUG_ENABLED #define CHULL_ASSERT(m_cond) \ - do { \ + if constexpr (true) { \ if (unlikely(!(m_cond))) { \ ERR_PRINT("Assertion \"" _STR(m_cond) "\" failed."); \ } \ - } while (0) + } else \ + ((void)0) #else #define CHULL_ASSERT(m_cond) \ - do { \ - } while (0) + if constexpr (true) { \ + } else \ + ((void)0) #endif #if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS) @@ -344,31 +346,31 @@ public: Rational128(int64_t p_value) { if (p_value > 0) { sign = 1; - this->numerator = p_value; + numerator = p_value; } else if (p_value < 0) { sign = -1; - this->numerator = -p_value; + numerator = -p_value; } else { sign = 0; - this->numerator = (uint64_t)0; + numerator = (uint64_t)0; } - this->denominator = (uint64_t)1; + denominator = (uint64_t)1; is_int_64 = true; } Rational128(const Int128 &p_numerator, const Int128 &p_denominator) { sign = p_numerator.get_sign(); if (sign >= 0) { - this->numerator = p_numerator; + numerator = p_numerator; } else { - this->numerator = -p_numerator; + numerator = -p_numerator; } int32_t dsign = p_denominator.get_sign(); if (dsign >= 0) { - this->denominator = p_denominator; + denominator = p_denominator; } else { sign = -sign; - this->denominator = -p_denominator; + denominator = -p_denominator; } is_int_64 = false; } diff --git a/core/math/delaunay_2d.h b/core/math/delaunay_2d.h index fc70724308..0bc67a92f6 100644 --- a/core/math/delaunay_2d.h +++ b/core/math/delaunay_2d.h @@ -64,7 +64,7 @@ public: } }; - static Triangle create_triangle(const Vector<Vector2> &p_vertices, const int &p_a, const int &p_b, const int &p_c) { + static Triangle create_triangle(const Vector<Vector2> &p_vertices, int p_a, int p_b, int p_c) { Triangle triangle = Triangle(p_a, p_b, p_c); // Get the values of the circumcircle and store them inside the triangle object. diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index 7df8c37e3c..25bd4e8d89 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -46,7 +46,8 @@ class Delaunay3D { struct Simplex; enum { - ACCEL_GRID_SIZE = 16 + ACCEL_GRID_SIZE = 16, + QUANTIZATION_MAX = 1 << 16 // A power of two smaller than the 23 bit significand of a float. }; struct GridPos { Vector3i pos; @@ -173,38 +174,25 @@ class Delaunay3D { R128 radius2 = rel2_x * rel2_x + rel2_y * rel2_y + rel2_z * rel2_z; - return radius2 < (p_simplex.circum_r2 - R128(0.00001)); + return radius2 < (p_simplex.circum_r2 - R128(0.0000000001)); + // When this tolerance is too big, it can result in overlapping simplices. + // When it's too small, large amounts of planar simplices are created. } static bool simplex_is_coplanar(const Vector3 *p_points, const Simplex &p_simplex) { - Plane p(p_points[p_simplex.points[0]], p_points[p_simplex.points[1]], p_points[p_simplex.points[2]]); - if (ABS(p.distance_to(p_points[p_simplex.points[3]])) < CMP_EPSILON) { - return true; + // Checking every possible distance like this is overkill, but only checking + // one is not enough. If the simplex is almost planar then the vectors p1-p2 + // and p1-p3 can be practically collinear, which makes Plane unreliable. + for (uint32_t i = 0; i < 4; i++) { + Plane p(p_points[p_simplex.points[i]], p_points[p_simplex.points[(i + 1) % 4]], p_points[p_simplex.points[(i + 2) % 4]]); + // This tolerance should not be smaller than the one used with + // Plane::has_point() when creating the LightmapGI probe BSP tree. + if (ABS(p.distance_to(p_points[p_simplex.points[(i + 3) % 4]])) < 0.001) { + return true; + } } - Projection cm; - - cm.columns[0][0] = p_points[p_simplex.points[0]].x; - cm.columns[0][1] = p_points[p_simplex.points[1]].x; - cm.columns[0][2] = p_points[p_simplex.points[2]].x; - cm.columns[0][3] = p_points[p_simplex.points[3]].x; - - cm.columns[1][0] = p_points[p_simplex.points[0]].y; - cm.columns[1][1] = p_points[p_simplex.points[1]].y; - cm.columns[1][2] = p_points[p_simplex.points[2]].y; - cm.columns[1][3] = p_points[p_simplex.points[3]].y; - - cm.columns[2][0] = p_points[p_simplex.points[0]].z; - cm.columns[2][1] = p_points[p_simplex.points[1]].z; - cm.columns[2][2] = p_points[p_simplex.points[2]].z; - cm.columns[2][3] = p_points[p_simplex.points[3]].z; - - cm.columns[3][0] = 1.0; - cm.columns[3][1] = 1.0; - cm.columns[3][2] = 1.0; - cm.columns[3][3] = 1.0; - - return ABS(cm.determinant()) <= CMP_EPSILON; + return false; } public: @@ -215,9 +203,10 @@ public: static Vector<OutputSimplex> tetrahedralize(const Vector<Vector3> &p_points) { uint32_t point_count = p_points.size(); Vector3 *points = (Vector3 *)memalloc(sizeof(Vector3) * (point_count + 4)); + const Vector3 *src_points = p_points.ptr(); + Vector3 proportions; { - const Vector3 *src_points = p_points.ptr(); AABB rect; for (uint32_t i = 0; i < point_count; i++) { Vector3 point = src_points[i]; @@ -226,17 +215,25 @@ public: } else { rect.expand_to(point); } - points[i] = point; } + real_t longest_axis = rect.size[rect.get_longest_axis_index()]; + proportions = Vector3(longest_axis, longest_axis, longest_axis) / rect.size; + for (uint32_t i = 0; i < point_count; i++) { - points[i] = (points[i] - rect.position) / rect.size; + // Scale points to the unit cube to better utilize R128 precision + // and quantize to stabilize triangulation over a wide range of + // distances. + points[i] = Vector3(Vector3i((src_points[i] - rect.position) / longest_axis * QUANTIZATION_MAX)) / QUANTIZATION_MAX; } - float delta_max = Math::sqrt(2.0) * 20.0; + const real_t delta_max = Math::sqrt(2.0) * 100.0; Vector3 center = Vector3(0.5, 0.5, 0.5); - // any simplex that contains everything is good + // The larger the root simplex is, the more likely it is that the + // triangulation is convex. If it's not absolutely huge, there can + // be missing simplices that are not created for the outermost faces + // of the point cloud if the point density is very low there. points[point_count + 0] = center + Vector3(0, 1, 0) * delta_max; points[point_count + 1] = center + Vector3(0, -1, 1) * delta_max; points[point_count + 2] = center + Vector3(1, -1, -1) * delta_max; @@ -271,7 +268,7 @@ public: for (uint32_t i = 0; i < point_count; i++) { bool unique = true; for (uint32_t j = i + 1; j < point_count; j++) { - if (points[i].is_equal_approx(points[j])) { + if (points[i] == points[j]) { unique = false; break; } @@ -280,10 +277,8 @@ public: continue; } - Vector3i grid_pos = Vector3i(points[i] * ACCEL_GRID_SIZE); - grid_pos.x = CLAMP(grid_pos.x, 0, ACCEL_GRID_SIZE - 1); - grid_pos.y = CLAMP(grid_pos.y, 0, ACCEL_GRID_SIZE - 1); - grid_pos.z = CLAMP(grid_pos.z, 0, ACCEL_GRID_SIZE - 1); + Vector3i grid_pos = Vector3i(points[i] * proportions * ACCEL_GRID_SIZE); + grid_pos = grid_pos.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1)); for (List<Simplex *>::Element *E = acceleration_grid[grid_pos.x][grid_pos.y][grid_pos.z].front(); E;) { List<Simplex *>::Element *N = E->next(); //may be deleted @@ -302,6 +297,9 @@ public: Triangle t = Triangle(simplex->points[triangle_order[k][0]], simplex->points[triangle_order[k][1]], simplex->points[triangle_order[k][2]]); uint32_t *p = triangles_inserted.lookup_ptr(t); if (p) { + // This Delaunay implementation uses the Bowyer-Watson algorithm. + // The rule is that you don't reuse any triangles that were + // shared by any of the retriangulated simplices. triangles[*p].bad = true; } else { triangles_inserted.insert(t, triangles.size()); @@ -309,7 +307,6 @@ public: } } - //remove simplex and continue simplex_list.erase(simplex->SE); for (const GridPos &gp : simplex->grid_positions) { @@ -334,17 +331,12 @@ public: center.y = double(new_simplex->circum_center_y); center.z = double(new_simplex->circum_center_z); - float radius2 = Math::sqrt(double(new_simplex->circum_r2)); - radius2 += 0.0001; // + const real_t radius2 = Math::sqrt(double(new_simplex->circum_r2)) + 0.0001; Vector3 extents = Vector3(radius2, radius2, radius2); - Vector3i from = Vector3i((center - extents) * ACCEL_GRID_SIZE); - Vector3i to = Vector3i((center + extents) * ACCEL_GRID_SIZE); - from.x = CLAMP(from.x, 0, ACCEL_GRID_SIZE - 1); - from.y = CLAMP(from.y, 0, ACCEL_GRID_SIZE - 1); - from.z = CLAMP(from.z, 0, ACCEL_GRID_SIZE - 1); - to.x = CLAMP(to.x, 0, ACCEL_GRID_SIZE - 1); - to.y = CLAMP(to.y, 0, ACCEL_GRID_SIZE - 1); - to.z = CLAMP(to.z, 0, ACCEL_GRID_SIZE - 1); + Vector3i from = Vector3i((center - extents) * proportions * ACCEL_GRID_SIZE); + Vector3i to = Vector3i((center + extents) * proportions * ACCEL_GRID_SIZE); + from = from.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1)); + to = to.clamp(Vector3i(), Vector3i(ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1, ACCEL_GRID_SIZE - 1)); for (int32_t x = from.x; x <= to.x; x++) { for (int32_t y = from.y; y <= to.y; y++) { @@ -377,7 +369,7 @@ public: break; } } - if (invalid || simplex_is_coplanar(points, *simplex)) { + if (invalid || simplex_is_coplanar(src_points, *simplex)) { memdelete(simplex); continue; } diff --git a/core/math/disjoint_set.h b/core/math/disjoint_set.h index 2ece991fea..4348da992d 100644 --- a/core/math/disjoint_set.h +++ b/core/math/disjoint_set.h @@ -35,7 +35,7 @@ #include "core/templates/vector.h" /* This DisjointSet class uses Find with path compression and Union by rank */ -template <typename T, class H = HashMapHasherDefault, class C = HashMapComparatorDefault<T>, class AL = DefaultAllocator> +template <typename T, typename H = HashMapHasherDefault, typename C = HashMapComparatorDefault<T>, typename AL = DefaultAllocator> class DisjointSet { struct Element { T object; @@ -65,14 +65,14 @@ public: /* FUNCTIONS */ -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> DisjointSet<T, H, C, AL>::~DisjointSet() { for (KeyValue<T, Element *> &E : elements) { memdelete_allocator<Element, AL>(E.value); } } -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> typename DisjointSet<T, H, C, AL>::Element *DisjointSet<T, H, C, AL>::get_parent(Element *element) { if (element->parent != element) { element->parent = get_parent(element->parent); @@ -81,7 +81,7 @@ typename DisjointSet<T, H, C, AL>::Element *DisjointSet<T, H, C, AL>::get_parent return element->parent; } -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> typename DisjointSet<T, H, C, AL>::Element *DisjointSet<T, H, C, AL>::insert_or_get(T object) { typename MapT::Iterator itr = elements.find(object); if (itr != nullptr) { @@ -96,7 +96,7 @@ typename DisjointSet<T, H, C, AL>::Element *DisjointSet<T, H, C, AL>::insert_or_ return new_element; } -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> void DisjointSet<T, H, C, AL>::create_union(T a, T b) { Element *x = insert_or_get(a); Element *y = insert_or_get(b); @@ -121,7 +121,7 @@ void DisjointSet<T, H, C, AL>::create_union(T a, T b) { } } -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> void DisjointSet<T, H, C, AL>::get_representatives(Vector<T> &out_representatives) { for (KeyValue<T, Element *> &E : elements) { Element *element = E.value; @@ -131,7 +131,7 @@ void DisjointSet<T, H, C, AL>::get_representatives(Vector<T> &out_representative } } -template <typename T, class H, class C, class AL> +template <typename T, typename H, typename C, typename AL> void DisjointSet<T, H, C, AL>::get_members(Vector<T> &out_members, T representative) { typename MapT::Iterator rep_itr = elements.find(representative); ERR_FAIL_NULL(rep_itr); diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h index dbc1cb31de..26fc517f7f 100644 --- a/core/math/dynamic_bvh.h +++ b/core/math/dynamic_bvh.h @@ -305,11 +305,11 @@ public: virtual ~DefaultQueryResult() {} }; - template <class QueryResult> + template <typename QueryResult> _FORCE_INLINE_ void aabb_query(const AABB &p_aabb, QueryResult &r_result); - template <class QueryResult> + template <typename QueryResult> _FORCE_INLINE_ void convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result); - template <class QueryResult> + template <typename QueryResult> _FORCE_INLINE_ void ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result); void set_index(uint32_t p_index); @@ -318,7 +318,7 @@ public: ~DynamicBVH(); }; -template <class QueryResult> +template <typename QueryResult> void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { if (!bvh_root) { return; @@ -328,7 +328,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { volume.min = p_box.position; volume.max = p_box.position + p_box.size; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -343,7 +344,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -361,7 +363,7 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) { } while (depth > 0); } -template <class QueryResult> +template <typename QueryResult> void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Vector3 *p_points, int p_point_count, QueryResult &r_result) { if (!bvh_root) { return; @@ -374,17 +376,13 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve volume.min = p_points[0]; volume.max = p_points[0]; } else { - volume.min.x = MIN(volume.min.x, p_points[i].x); - volume.min.y = MIN(volume.min.y, p_points[i].y); - volume.min.z = MIN(volume.min.z, p_points[i].z); - - volume.max.x = MAX(volume.max.x, p_points[i].x); - volume.max.y = MAX(volume.max.y, p_points[i].y); - volume.max.z = MAX(volume.max.z, p_points[i].z); + volume.min = volume.min.min(p_points[i]); + volume.max = volume.max.max(p_points[i]); } } - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -399,7 +397,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } @@ -416,7 +415,7 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve } } while (depth > 0); } -template <class QueryResult> +template <typename QueryResult> void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResult &r_result) { if (!bvh_root) { return; @@ -436,7 +435,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu Vector3 bounds[2]; - const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *)); + const Node **stack = alloca_stack; stack[0] = bvh_root; int32_t depth = 1; int32_t threshold = ALLOCA_STACK_SIZE - 2; @@ -456,7 +456,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu if (depth > threshold) { if (aux_stack.is_empty()) { aux_stack.resize(ALLOCA_STACK_SIZE * 2); - memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *)); + alloca_stack = nullptr; } else { aux_stack.resize(aux_stack.size() * 2); } diff --git a/core/math/expression.cpp b/core/math/expression.cpp index d1ec987d56..636c2c16bf 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -1494,7 +1494,7 @@ Error Expression::parse(const String &p_expression, const Vector<String> &p_inpu return OK; } -Variant Expression::execute(Array p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) { +Variant Expression::execute(const Array &p_inputs, Object *p_base, bool p_show_error, bool p_const_calls_only) { ERR_FAIL_COND_V_MSG(error_set, Variant(), "There was previously a parse error: " + error_str + "."); execution_error = false; diff --git a/core/math/expression.h b/core/math/expression.h index 175db4e25e..46bc3618df 100644 --- a/core/math/expression.h +++ b/core/math/expression.h @@ -243,7 +243,7 @@ private: } }; - template <class T> + template <typename T> T *alloc_node() { T *node = memnew(T); node->next = nodes; @@ -264,7 +264,7 @@ protected: public: Error parse(const String &p_expression, const Vector<String> &p_input_names = Vector<String>()); - Variant execute(Array p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true, bool p_const_calls_only = false); + Variant execute(const Array &p_inputs = Array(), Object *p_base = nullptr, bool p_show_error = true, bool p_const_calls_only = false); bool has_execute_failed() const; String get_error_text() const; diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp index 74cb92539a..d60619b27f 100644 --- a/core/math/geometry_2d.cpp +++ b/core/math/geometry_2d.cpp @@ -30,14 +30,14 @@ #include "geometry_2d.h" -#include "thirdparty/misc/clipper.hpp" +#include "thirdparty/clipper2/include/clipper2/clipper.h" #include "thirdparty/misc/polypartition.h" #define STB_RECT_PACK_IMPLEMENTATION #include "thirdparty/misc/stb_rect_pack.h" -#define SCALE_FACTOR 100000.0 // Based on CMP_EPSILON. +#define PRECISION 5 // Based on CMP_EPSILON. -Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(Vector<Point2> polygon) { +Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(const Vector<Point2> &polygon) { Vector<Vector<Vector2>> decomp; List<TPPLPoly> in_poly, out_poly; @@ -93,7 +93,7 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re // For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a // 256x8192 atlas (won't work anywhere). - ERR_FAIL_COND(p_rects.size() == 0); + ERR_FAIL_COND(p_rects.is_empty()); for (int i = 0; i < p_rects.size(); i++) { ERR_FAIL_COND(p_rects[i].width <= 0); ERR_FAIL_COND(p_rects[i].height <= 0); @@ -196,58 +196,59 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re } Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation p_op, const Vector<Point2> &p_polypath_a, const Vector<Point2> &p_polypath_b, bool is_a_open) { - using namespace ClipperLib; + using namespace Clipper2Lib; - ClipType op = ctUnion; + ClipType op = ClipType::Union; switch (p_op) { case OPERATION_UNION: - op = ctUnion; + op = ClipType::Union; break; case OPERATION_DIFFERENCE: - op = ctDifference; + op = ClipType::Difference; break; case OPERATION_INTERSECTION: - op = ctIntersection; + op = ClipType::Intersection; break; case OPERATION_XOR: - op = ctXor; + op = ClipType::Xor; break; } - Path path_a, path_b; - // Need to scale points (Clipper's requirement for robust computation). + PathD path_a(p_polypath_a.size()); for (int i = 0; i != p_polypath_a.size(); ++i) { - path_a << IntPoint(p_polypath_a[i].x * (real_t)SCALE_FACTOR, p_polypath_a[i].y * (real_t)SCALE_FACTOR); + path_a[i] = PointD(p_polypath_a[i].x, p_polypath_a[i].y); } + PathD path_b(p_polypath_b.size()); for (int i = 0; i != p_polypath_b.size(); ++i) { - path_b << IntPoint(p_polypath_b[i].x * (real_t)SCALE_FACTOR, p_polypath_b[i].y * (real_t)SCALE_FACTOR); + path_b[i] = PointD(p_polypath_b[i].x, p_polypath_b[i].y); } - Clipper clp; - clp.AddPath(path_a, ptSubject, !is_a_open); // Forward compatible with Clipper 10.0.0. - clp.AddPath(path_b, ptClip, true); // Polylines cannot be set as clip. - Paths paths; + ClipperD clp(PRECISION); // Scale points up internally to attain the desired precision. + clp.PreserveCollinear(false); // Remove redundant vertices. + if (is_a_open) { + clp.AddOpenSubject({ path_a }); + } else { + clp.AddSubject({ path_a }); + } + clp.AddClip({ path_b }); + + PathsD paths; if (is_a_open) { - PolyTree tree; // Needed to populate polylines. - clp.Execute(op, tree); - OpenPathsFromPolyTree(tree, paths); + PolyTreeD tree; // Needed to populate polylines. + clp.Execute(op, FillRule::EvenOdd, tree, paths); } else { - clp.Execute(op, paths); // Works on closed polygons only. + clp.Execute(op, FillRule::EvenOdd, paths); // Works on closed polygons only. } - // Have to scale points down now. + Vector<Vector<Point2>> polypaths; + for (PathsD::size_type i = 0; i < paths.size(); ++i) { + const PathD &path = paths[i]; - for (Paths::size_type i = 0; i < paths.size(); ++i) { Vector<Vector2> polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast<real_t>(scaled_path[j].X) / (real_t)SCALE_FACTOR, - static_cast<real_t>(scaled_path[j].Y) / (real_t)SCALE_FACTOR)); + for (PathsD::size_type j = 0; j < path.size(); ++j) { + polypath.push_back(Point2(static_cast<real_t>(path[j].x), static_cast<real_t>(path[j].y))); } polypaths.push_back(polypath); } @@ -255,67 +256,61 @@ Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation } Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_polypath, real_t p_delta, PolyJoinType p_join_type, PolyEndType p_end_type) { - using namespace ClipperLib; + using namespace Clipper2Lib; - JoinType jt = jtSquare; + JoinType jt = JoinType::Square; switch (p_join_type) { case JOIN_SQUARE: - jt = jtSquare; + jt = JoinType::Square; break; case JOIN_ROUND: - jt = jtRound; + jt = JoinType::Round; break; case JOIN_MITER: - jt = jtMiter; + jt = JoinType::Miter; break; } - EndType et = etClosedPolygon; + EndType et = EndType::Polygon; switch (p_end_type) { case END_POLYGON: - et = etClosedPolygon; + et = EndType::Polygon; break; case END_JOINED: - et = etClosedLine; + et = EndType::Joined; break; case END_BUTT: - et = etOpenButt; + et = EndType::Butt; break; case END_SQUARE: - et = etOpenSquare; + et = EndType::Square; break; case END_ROUND: - et = etOpenRound; + et = EndType::Round; break; } - ClipperOffset co(2.0, 0.25f * (real_t)SCALE_FACTOR); // Defaults from ClipperOffset. - Path path; - // Need to scale points (Clipper's requirement for robust computation). + PathD polypath(p_polypath.size()); for (int i = 0; i != p_polypath.size(); ++i) { - path << IntPoint(p_polypath[i].x * (real_t)SCALE_FACTOR, p_polypath[i].y * (real_t)SCALE_FACTOR); + polypath[i] = PointD(p_polypath[i].x, p_polypath[i].y); } - co.AddPath(path, jt, et); - Paths paths; - co.Execute(paths, p_delta * (real_t)SCALE_FACTOR); // Inflate/deflate. + // Inflate/deflate. + PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, PRECISION, 0.0); + // Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults, + // and the PRECISION is used to scale points up internally, to attain the desired precision. - // Have to scale points down now. Vector<Vector<Point2>> polypaths; + for (PathsD::size_type i = 0; i < paths.size(); ++i) { + const PathD &path = paths[i]; - for (Paths::size_type i = 0; i < paths.size(); ++i) { - Vector<Vector2> polypath; - - const Path &scaled_path = paths[i]; - - for (Paths::size_type j = 0; j < scaled_path.size(); ++j) { - polypath.push_back(Point2( - static_cast<real_t>(scaled_path[j].X) / (real_t)SCALE_FACTOR, - static_cast<real_t>(scaled_path[j].Y) / (real_t)SCALE_FACTOR)); + Vector<Vector2> polypath2; + for (PathsD::size_type j = 0; j < path.size(); ++j) { + polypath2.push_back(Point2(static_cast<real_t>(path[j].x), static_cast<real_t>(path[j].y))); } - polypaths.push_back(polypath); + polypaths.push_back(polypath2); } return polypaths; } diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h index b37fce9e9c..1502b2807c 100644 --- a/core/math/geometry_2d.h +++ b/core/math/geometry_2d.h @@ -119,6 +119,10 @@ public: } } + static real_t get_distance_to_segment(const Vector2 &p_point, const Vector2 *p_segment) { + return p_point.distance_to(get_closest_point_to_segment(p_point, p_segment)); + } + static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) { Vector2 an = a - s; Vector2 bn = b - s; @@ -249,6 +253,28 @@ public: return -1; } + static bool segment_intersects_rect(const Vector2 &p_from, const Vector2 &p_to, const Rect2 &p_rect) { + if (p_rect.has_point(p_from) || p_rect.has_point(p_to)) { + return true; + } + + const Vector2 rect_points[4] = { + p_rect.position, + p_rect.position + Vector2(p_rect.size.x, 0), + p_rect.position + p_rect.size, + p_rect.position + Vector2(0, p_rect.size.y) + }; + + // Check if any of the rect's edges intersect the segment. + for (int i = 0; i < 4; i++) { + if (segment_intersects_segment(p_from, p_to, rect_points[i], rect_points[(i + 1) % 4], nullptr)) { + return true; + } + } + + return false; + } + enum PolyBooleanOperation { OPERATION_UNION, OPERATION_DIFFERENCE, @@ -351,10 +377,8 @@ public: Vector2 further_away_opposite(1e20, 1e20); for (int i = 0; i < c; i++) { - further_away.x = MAX(p[i].x, further_away.x); - further_away.y = MAX(p[i].y, further_away.y); - further_away_opposite.x = MIN(p[i].x, further_away_opposite.x); - further_away_opposite.y = MIN(p[i].y, further_away_opposite.y); + further_away = further_away.max(p[i]); + further_away_opposite = further_away_opposite.min(p[i]); } // Make point outside that won't intersect with points in segment from p_point. @@ -463,7 +487,7 @@ public: return points; } - static Vector<Vector<Vector2>> decompose_polygon_in_convex(Vector<Point2> polygon); + static Vector<Vector<Vector2>> decompose_polygon_in_convex(const Vector<Point2> &polygon); static void make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_result, Size2i &r_size); static Vector<Vector3i> partial_pack_rects(const Vector<Vector2i> &p_sizes, const Size2i &p_atlas_size); diff --git a/core/math/geometry_3d.cpp b/core/math/geometry_3d.cpp index 9dd88485f9..4d55455166 100644 --- a/core/math/geometry_3d.cpp +++ b/core/math/geometry_3d.cpp @@ -393,7 +393,7 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i return; } -#define vert(m_idx) Vector3(((m_idx)&4) >> 2, ((m_idx)&2) >> 1, (m_idx)&1) +#define vert(m_idx) Vector3(((m_idx) & 4) >> 2, ((m_idx) & 2) >> 1, (m_idx) & 1) static const uint8_t indices[6][4] = { { 7, 6, 4, 5 }, @@ -449,7 +449,7 @@ static inline void _build_faces(uint8_t ***p_cell_status, int x, int y, int z, i } } -Vector<Face3> Geometry3D::wrap_geometry(Vector<Face3> p_array, real_t *p_error) { +Vector<Face3> Geometry3D::wrap_geometry(const Vector<Face3> &p_array, real_t *p_error) { int face_count = p_array.size(); const Face3 *faces = p_array.ptr(); constexpr double min_size = 1.0; diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 99c554fe05..ff39d82595 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -31,6 +31,7 @@ #ifndef GEOMETRY_3D_H #define GEOMETRY_3D_H +#include "core/math/delaunay_3d.h" #include "core/math/face3.h" #include "core/object/object.h" #include "core/templates/local_vector.h" @@ -532,8 +533,23 @@ public: return clipped; } + static Vector<int32_t> tetrahedralize_delaunay(const Vector<Vector3> &p_points) { + Vector<Delaunay3D::OutputSimplex> tetr = Delaunay3D::tetrahedralize(p_points); + Vector<int32_t> tetrahedrons; + + tetrahedrons.resize(4 * tetr.size()); + int32_t *ptr = tetrahedrons.ptrw(); + for (int i = 0; i < tetr.size(); i++) { + *ptr++ = tetr[i].points[0]; + *ptr++ = tetr[i].points[1]; + *ptr++ = tetr[i].points[2]; + *ptr++ = tetr[i].points[3]; + } + return tetrahedrons; + } + // Create a "wrap" that encloses the given geometry. - static Vector<Face3> wrap_geometry(Vector<Face3> p_array, real_t *p_error = nullptr); + static Vector<Face3> wrap_geometry(const Vector<Face3> &p_array, real_t *p_error = nullptr); struct MeshData { struct Face { @@ -578,7 +594,7 @@ public: max = x2; \ } - _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) { + _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, real_t d, Vector3 maxbox) { int q; Vector3 vmin, vmax; for (q = 0; q <= 2; q++) { @@ -662,8 +678,7 @@ public: return false; \ } - /*======================== Z-tests ========================*/ - +/*======================== Z-tests ========================*/ #define AXISTEST_Z12(a, b, fa, fb) \ p1 = a * v1.x - b * v1.y; \ p2 = a * v2.x - b * v2.y; \ @@ -702,21 +717,19 @@ public: /* 2) normal of the triangle */ /* 3) crossproduct(edge from tri, {x,y,z}-directin) */ /* this gives 3x3=9 more tests */ - Vector3 v0, v1, v2; - float min, max, d, p0, p1, p2, rad, fex, fey, fez; - Vector3 normal, e0, e1, e2; + real_t min, max, p0, p1, p2, rad, fex, fey, fez; /* This is the fastest branch on Sun */ /* move everything so that the boxcenter is in (0,0,0) */ - v0 = triverts[0] - boxcenter; - v1 = triverts[1] - boxcenter; - v2 = triverts[2] - boxcenter; + const Vector3 v0 = triverts[0] - boxcenter; + const Vector3 v1 = triverts[1] - boxcenter; + const Vector3 v2 = triverts[2] - boxcenter; /* compute triangle edges */ - e0 = v1 - v0; /* tri edge 0 */ - e1 = v2 - v1; /* tri edge 1 */ - e2 = v0 - v2; /* tri edge 2 */ + const Vector3 e0 = v1 - v0; /* tri edge 0 */ + const Vector3 e1 = v2 - v1; /* tri edge 1 */ + const Vector3 e2 = v0 - v2; /* tri edge 2 */ /* Bullet 3: */ /* test the 9 tests first (this was faster) */ @@ -768,8 +781,8 @@ public: /* Bullet 2: */ /* test if the box intersects the plane of the triangle */ /* compute plane equation of triangle: normal*x+d=0 */ - normal = e0.cross(e1); - d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ + const Vector3 normal = e0.cross(e1); + const real_t d = -normal.dot(v0); /* plane eq: normal.x+d=0 */ return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */ } @@ -777,51 +790,51 @@ public: static Vector<int8_t> generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative); static Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) { - Vector3 v0 = p_b - p_a; - Vector3 v1 = p_c - p_a; - Vector3 v2 = p_pos - p_a; - - float d00 = v0.dot(v0); - float d01 = v0.dot(v1); - float d11 = v1.dot(v1); - float d20 = v2.dot(v0); - float d21 = v2.dot(v1); - float denom = (d00 * d11 - d01 * d01); + const Vector3 v0 = p_b - p_a; + const Vector3 v1 = p_c - p_a; + const Vector3 v2 = p_pos - p_a; + + const real_t d00 = v0.dot(v0); + const real_t d01 = v0.dot(v1); + const real_t d11 = v1.dot(v1); + const real_t d20 = v2.dot(v0); + const real_t d21 = v2.dot(v1); + const real_t denom = (d00 * d11 - d01 * d01); if (denom == 0) { return Vector3(); //invalid triangle, return empty } - float v = (d11 * d20 - d01 * d21) / denom; - float w = (d00 * d21 - d01 * d20) / denom; - float u = 1.0f - v - w; + const real_t v = (d11 * d20 - d01 * d21) / denom; + const real_t w = (d00 * d21 - d01 * d20) / denom; + const real_t u = 1.0f - v - w; return Vector3(u, v, w); } static Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) { - Vector3 vap = p_pos - p_a; - Vector3 vbp = p_pos - p_b; + const Vector3 vap = p_pos - p_a; + const Vector3 vbp = p_pos - p_b; - Vector3 vab = p_b - p_a; - Vector3 vac = p_c - p_a; - Vector3 vad = p_d - p_a; + const Vector3 vab = p_b - p_a; + const Vector3 vac = p_c - p_a; + const Vector3 vad = p_d - p_a; - Vector3 vbc = p_c - p_b; - Vector3 vbd = p_d - p_b; + const Vector3 vbc = p_c - p_b; + const Vector3 vbd = p_d - p_b; // ScTP computes the scalar triple product #define STP(m_a, m_b, m_c) ((m_a).dot((m_b).cross((m_c)))) - float va6 = STP(vbp, vbd, vbc); - float vb6 = STP(vap, vac, vad); - float vc6 = STP(vap, vad, vab); - float vd6 = STP(vap, vab, vac); - float v6 = 1 / STP(vab, vac, vad); + const real_t va6 = STP(vbp, vbd, vbc); + const real_t vb6 = STP(vap, vac, vad); + const real_t vc6 = STP(vap, vad, vab); + const real_t vd6 = STP(vap, vab, vac); + const real_t v6 = 1 / STP(vab, vac, vad); return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6); #undef STP } _FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) { // https://twitter.com/Stubbesaurus/status/937994790553227264 - Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f); + const Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f); Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); - float t = CLAMP(-n.z, 0.0f, 1.0f); + const real_t t = CLAMP(-n.z, 0.0f, 1.0f); n.x += n.x >= 0 ? -t : t; n.y += n.y >= 0 ? -t : t; return n.normalized(); diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 366ccca4cb..3060f31970 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -198,6 +198,22 @@ public: #endif } + // These methods assume (p_num + p_den) doesn't overflow. + static _ALWAYS_INLINE_ int32_t division_round_up(int32_t p_num, int32_t p_den) { + int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1; + return (p_num + p_den + offset) / p_den; + } + static _ALWAYS_INLINE_ uint32_t division_round_up(uint32_t p_num, uint32_t p_den) { + return (p_num + p_den - 1) / p_den; + } + static _ALWAYS_INLINE_ int64_t division_round_up(int64_t p_num, int64_t p_den) { + int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1; + return (p_num + p_den + offset) / p_den; + } + static _ALWAYS_INLINE_ uint64_t division_round_up(uint64_t p_num, uint64_t p_den) { + return (p_num + p_den - 1) / p_den; + } + static _ALWAYS_INLINE_ bool is_finite(double p_val) { return isfinite(p_val); } static _ALWAYS_INLINE_ bool is_finite(float p_val) { return isfinite(p_val); } diff --git a/core/math/projection.cpp b/core/math/projection.cpp index 9d5dc8b4d6..d0ca7c5684 100644 --- a/core/math/projection.cpp +++ b/core/math/projection.cpp @@ -37,7 +37,7 @@ #include "core/math/transform_3d.h" #include "core/string/ustring.h" -float Projection::determinant() const { +real_t Projection::determinant() const { return columns[0][3] * columns[1][2] * columns[2][1] * columns[3][0] - columns[0][2] * columns[1][3] * columns[2][1] * columns[3][0] - columns[0][3] * columns[1][1] * columns[2][2] * columns[3][0] + columns[0][1] * columns[1][3] * columns[2][2] * columns[3][0] + columns[0][2] * columns[1][1] * columns[2][3] * columns[3][0] - columns[0][1] * columns[1][2] * columns[2][3] * columns[3][0] - @@ -719,7 +719,8 @@ Projection Projection::operator*(const Projection &p_matrix) const { return new_matrix; } -void Projection::set_depth_correction(bool p_flip_y) { +void Projection::set_depth_correction(bool p_flip_y, bool p_reverse_z, bool p_remap_z) { + // p_remap_z is used to convert from OpenGL-style clip space (-1 - 1) to Vulkan style (0 - 1). real_t *m = &columns[0][0]; m[0] = 1; @@ -732,11 +733,11 @@ void Projection::set_depth_correction(bool p_flip_y) { m[7] = 0.0; m[8] = 0.0; m[9] = 0.0; - m[10] = 0.5; + m[10] = p_remap_z ? (p_reverse_z ? -0.5 : 0.5) : (p_reverse_z ? -1.0 : 1.0); m[11] = 0.0; m[12] = 0.0; m[13] = 0.0; - m[14] = 0.5; + m[14] = p_remap_z ? 0.5 : 0.0; m[15] = 1.0; } @@ -831,13 +832,13 @@ real_t Projection::get_fov() const { } } -float Projection::get_lod_multiplier() const { +real_t Projection::get_lod_multiplier() const { if (is_orthogonal()) { return get_viewport_half_extents().x; } else { - float zn = get_z_near(); - float width = get_viewport_half_extents().x * 2.0; - return 1.0 / (zn / width); + const real_t zn = get_z_near(); + const real_t width = get_viewport_half_extents().x * 2.0f; + return 1.0f / (zn / width); } // Usage is lod_size / (lod_distance * multiplier) < threshold diff --git a/core/math/projection.h b/core/math/projection.h index b3a9cff002..f3ed9d7b1c 100644 --- a/core/math/projection.h +++ b/core/math/projection.h @@ -34,7 +34,7 @@ #include "core/math/vector3.h" #include "core/math/vector4.h" -template <class T> +template <typename T> class Vector; struct AABB; @@ -55,21 +55,21 @@ struct _NO_DISCARD_ Projection { Vector4 columns[4]; - _FORCE_INLINE_ const Vector4 &operator[](const int p_axis) const { + _FORCE_INLINE_ const Vector4 &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 4); return columns[p_axis]; } - _FORCE_INLINE_ Vector4 &operator[](const int p_axis) { + _FORCE_INLINE_ Vector4 &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 4); return columns[p_axis]; } - float determinant() const; + real_t determinant() const; void set_identity(); void set_zero(); void set_light_bias(); - void set_depth_correction(bool p_flip_y = true); + void set_depth_correction(bool p_flip_y = true, bool p_reverse_z = true, bool p_remap_z = true); void set_light_atlas_rect(const Rect2 &p_rect); void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false); @@ -148,7 +148,7 @@ struct _NO_DISCARD_ Projection { return !(*this == p_cam); } - float get_lod_multiplier() const; + real_t get_lod_multiplier() const; Projection(); Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w); diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp index e4ad17c8ef..08eac14b76 100644 --- a/core/math/quaternion.cpp +++ b/core/math/quaternion.cpp @@ -41,7 +41,7 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const { Vector3 Quaternion::get_euler(EulerOrder p_order) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion " + operator String() + " must be normalized."); #endif return Basis(*this).get_euler(p_order); } @@ -88,7 +88,7 @@ bool Quaternion::is_normalized() const { Quaternion Quaternion::inverse() const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion " + operator String() + " must be normalized."); #endif return Quaternion(-x, -y, -z, w); } @@ -110,10 +110,10 @@ Quaternion Quaternion::exp() const { return Quaternion(src_v, theta); } -Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const { +Quaternion Quaternion::slerp(const Quaternion &p_to, real_t p_weight) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized."); #endif Quaternion to1; real_t omega, cosom, sinom, scale0, scale1; @@ -151,10 +151,10 @@ Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) con scale0 * w + scale1 * to1.w); } -Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const { +Quaternion Quaternion::slerpni(const Quaternion &p_to, real_t p_weight) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized."); + ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized."); #endif const Quaternion &from = *this; @@ -175,10 +175,10 @@ Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) c invFactor * from.w + newFactor * p_to.w); } -Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const { +Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized."); + ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized."); #endif Quaternion from_q = *this; Quaternion pre_q = p_pre_a; @@ -225,11 +225,11 @@ Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const return q1.slerp(q2, p_weight); } -Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight, - const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const { +Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight, + real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized."); - ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized."); + ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized."); #endif Quaternion from_q = *this; Quaternion pre_q = p_pre_a; @@ -294,7 +294,7 @@ real_t Quaternion::get_angle() const { Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) { #ifdef MATH_CHECKS - ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized."); + ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized."); #endif real_t d = p_axis.length(); if (d == 0) { diff --git a/core/math/quaternion.h b/core/math/quaternion.h index ea952304a5..868a2916f5 100644 --- a/core/math/quaternion.h +++ b/core/math/quaternion.h @@ -33,8 +33,7 @@ #include "core/math/math_funcs.h" #include "core/math/vector3.h" - -class String; +#include "core/string/ustring.h" struct _NO_DISCARD_ Quaternion { union { @@ -47,11 +46,11 @@ struct _NO_DISCARD_ Quaternion { real_t components[4] = { 0, 0, 0, 1.0 }; }; - _FORCE_INLINE_ real_t &operator[](int idx) { - return components[idx]; + _FORCE_INLINE_ real_t &operator[](int p_idx) { + return components[p_idx]; } - _FORCE_INLINE_ const real_t &operator[](int idx) const { - return components[idx]; + _FORCE_INLINE_ const real_t &operator[](int p_idx) const { + return components[p_idx]; } _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Quaternion &p_quaternion) const; @@ -69,10 +68,10 @@ struct _NO_DISCARD_ Quaternion { Vector3 get_euler(EulerOrder p_order = EulerOrder::YXZ) const; static Quaternion from_euler(const Vector3 &p_euler); - Quaternion slerp(const Quaternion &p_to, const real_t &p_weight) const; - Quaternion slerpni(const Quaternion &p_to, const real_t &p_weight) const; - Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const; - Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const; + Quaternion slerp(const Quaternion &p_to, real_t p_weight) const; + Quaternion slerpni(const Quaternion &p_to, real_t p_weight) const; + Quaternion spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight) const; + Quaternion spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const; Vector3 get_axis() const; real_t get_angle() const; @@ -88,28 +87,28 @@ struct _NO_DISCARD_ Quaternion { void operator*=(const Quaternion &p_q); Quaternion operator*(const Quaternion &p_q) const; - _FORCE_INLINE_ Vector3 xform(const Vector3 &v) const { + _FORCE_INLINE_ Vector3 xform(const Vector3 &p_v) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized."); + ERR_FAIL_COND_V_MSG(!is_normalized(), p_v, "The quaternion " + operator String() + " must be normalized."); #endif Vector3 u(x, y, z); - Vector3 uv = u.cross(v); - return v + ((uv * w) + u.cross(uv)) * ((real_t)2); + Vector3 uv = u.cross(p_v); + return p_v + ((uv * w) + u.cross(uv)) * ((real_t)2); } - _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &v) const { - return inverse().xform(v); + _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_v) const { + return inverse().xform(p_v); } _FORCE_INLINE_ void operator+=(const Quaternion &p_q); _FORCE_INLINE_ void operator-=(const Quaternion &p_q); - _FORCE_INLINE_ void operator*=(const real_t &s); - _FORCE_INLINE_ void operator/=(const real_t &s); - _FORCE_INLINE_ Quaternion operator+(const Quaternion &q2) const; - _FORCE_INLINE_ Quaternion operator-(const Quaternion &q2) const; + _FORCE_INLINE_ void operator*=(real_t p_s); + _FORCE_INLINE_ void operator/=(real_t p_s); + _FORCE_INLINE_ Quaternion operator+(const Quaternion &p_q2) const; + _FORCE_INLINE_ Quaternion operator-(const Quaternion &p_q2) const; _FORCE_INLINE_ Quaternion operator-() const; - _FORCE_INLINE_ Quaternion operator*(const real_t &s) const; - _FORCE_INLINE_ Quaternion operator/(const real_t &s) const; + _FORCE_INLINE_ Quaternion operator*(real_t p_s) const; + _FORCE_INLINE_ Quaternion operator/(real_t p_s) const; _FORCE_INLINE_ bool operator==(const Quaternion &p_quaternion) const; _FORCE_INLINE_ bool operator!=(const Quaternion &p_quaternion) const; @@ -141,9 +140,9 @@ struct _NO_DISCARD_ Quaternion { w = p_q.w; } - Quaternion(const Vector3 &v0, const Vector3 &v1) { // Shortest arc. - Vector3 c = v0.cross(v1); - real_t d = v0.dot(v1); + Quaternion(const Vector3 &p_v0, const Vector3 &p_v1) { // Shortest arc. + Vector3 c = p_v0.cross(p_v1); + real_t d = p_v0.dot(p_v1); if (d < -1.0f + (real_t)CMP_EPSILON) { x = 0; @@ -184,25 +183,25 @@ void Quaternion::operator-=(const Quaternion &p_q) { w -= p_q.w; } -void Quaternion::operator*=(const real_t &s) { - x *= s; - y *= s; - z *= s; - w *= s; +void Quaternion::operator*=(real_t p_s) { + x *= p_s; + y *= p_s; + z *= p_s; + w *= p_s; } -void Quaternion::operator/=(const real_t &s) { - *this *= 1.0f / s; +void Quaternion::operator/=(real_t p_s) { + *this *= 1.0f / p_s; } -Quaternion Quaternion::operator+(const Quaternion &q2) const { +Quaternion Quaternion::operator+(const Quaternion &p_q2) const { const Quaternion &q1 = *this; - return Quaternion(q1.x + q2.x, q1.y + q2.y, q1.z + q2.z, q1.w + q2.w); + return Quaternion(q1.x + p_q2.x, q1.y + p_q2.y, q1.z + p_q2.z, q1.w + p_q2.w); } -Quaternion Quaternion::operator-(const Quaternion &q2) const { +Quaternion Quaternion::operator-(const Quaternion &p_q2) const { const Quaternion &q1 = *this; - return Quaternion(q1.x - q2.x, q1.y - q2.y, q1.z - q2.z, q1.w - q2.w); + return Quaternion(q1.x - p_q2.x, q1.y - p_q2.y, q1.z - p_q2.z, q1.w - p_q2.w); } Quaternion Quaternion::operator-() const { @@ -210,12 +209,12 @@ Quaternion Quaternion::operator-() const { return Quaternion(-q2.x, -q2.y, -q2.z, -q2.w); } -Quaternion Quaternion::operator*(const real_t &s) const { - return Quaternion(x * s, y * s, z * s, w * s); +Quaternion Quaternion::operator*(real_t p_s) const { + return Quaternion(x * p_s, y * p_s, z * p_s, w * p_s); } -Quaternion Quaternion::operator/(const real_t &s) const { - return *this * (1.0f / s); +Quaternion Quaternion::operator/(real_t p_s) const { + return *this * (1.0f / p_s); } bool Quaternion::operator==(const Quaternion &p_quaternion) const { @@ -226,7 +225,7 @@ bool Quaternion::operator!=(const Quaternion &p_quaternion) const { return x != p_quaternion.x || y != p_quaternion.y || z != p_quaternion.z || w != p_quaternion.w; } -_FORCE_INLINE_ Quaternion operator*(const real_t &p_real, const Quaternion &p_quaternion) { +_FORCE_INLINE_ Quaternion operator*(real_t p_real, const Quaternion &p_quaternion) { return p_quaternion * p_real; } diff --git a/core/math/random_number_generator.cpp b/core/math/random_number_generator.cpp index c3f36b32a5..226d748c52 100644 --- a/core/math/random_number_generator.cpp +++ b/core/math/random_number_generator.cpp @@ -42,6 +42,7 @@ void RandomNumberGenerator::_bind_methods() { ClassDB::bind_method(D_METHOD("randfn", "mean", "deviation"), &RandomNumberGenerator::randfn, DEFVAL(0.0), DEFVAL(1.0)); ClassDB::bind_method(D_METHOD("randf_range", "from", "to"), &RandomNumberGenerator::randf_range); ClassDB::bind_method(D_METHOD("randi_range", "from", "to"), &RandomNumberGenerator::randi_range); + ClassDB::bind_method(D_METHOD("rand_weighted", "weights"), &RandomNumberGenerator::rand_weighted); ClassDB::bind_method(D_METHOD("randomize"), &RandomNumberGenerator::randomize); ADD_PROPERTY(PropertyInfo(Variant::INT, "seed"), "set_seed", "get_seed"); diff --git a/core/math/random_number_generator.h b/core/math/random_number_generator.h index e1c353d439..7ec4cdffb0 100644 --- a/core/math/random_number_generator.h +++ b/core/math/random_number_generator.h @@ -57,6 +57,8 @@ public: _FORCE_INLINE_ real_t randfn(real_t p_mean = 0.0, real_t p_deviation = 1.0) { return randbase.randfn(p_mean, p_deviation); } _FORCE_INLINE_ int randi_range(int p_from, int p_to) { return randbase.random(p_from, p_to); } + _FORCE_INLINE_ int64_t rand_weighted(const Vector<float> &p_weights) { return randbase.rand_weighted(p_weights); } + RandomNumberGenerator() { randbase.randomize(); } }; diff --git a/core/math/random_pcg.cpp b/core/math/random_pcg.cpp index 45a9285ddd..55787a0b57 100644 --- a/core/math/random_pcg.cpp +++ b/core/math/random_pcg.cpp @@ -31,6 +31,7 @@ #include "random_pcg.h" #include "core/os/os.h" +#include "core/templates/vector.h" RandomPCG::RandomPCG(uint64_t p_seed, uint64_t p_inc) : pcg(), @@ -42,6 +43,26 @@ void RandomPCG::randomize() { seed(((uint64_t)OS::get_singleton()->get_unix_time() + OS::get_singleton()->get_ticks_usec()) * pcg.state + PCG_DEFAULT_INC_64); } +int64_t RandomPCG::rand_weighted(const Vector<float> &p_weights) { + ERR_FAIL_COND_V_MSG(p_weights.is_empty(), -1, "Weights array is empty."); + int64_t weights_size = p_weights.size(); + const float *weights = p_weights.ptr(); + float weights_sum = 0.0; + for (int64_t i = 0; i < weights_size; ++i) { + weights_sum += weights[i]; + } + + float remaining_distance = randf() * weights_sum; + for (int64_t i = 0; i < weights_size; ++i) { + remaining_distance -= weights[i]; + if (remaining_distance < 0) { + return i; + } + } + + return -1; +} + double RandomPCG::random(double p_from, double p_to) { return randd() * (p_to - p_from) + p_from; } diff --git a/core/math/random_pcg.h b/core/math/random_pcg.h index cc22b23b70..6bad70059f 100644 --- a/core/math/random_pcg.h +++ b/core/math/random_pcg.h @@ -59,6 +59,9 @@ static int __bsr_clz32(uint32_t x) { #define LDEXPF(s, e) ldexp(s, e) #endif +template <typename T> +class Vector; + class RandomPCG { pcg32_random_t pcg; uint64_t current_seed = 0; // The seed the current generator state started from. @@ -87,6 +90,8 @@ public: return pcg32_boundedrand_r(&pcg, bounds); } + int64_t rand_weighted(const Vector<float> &p_weights); + // Obtaining floating point numbers in [0, 1] range with "good enough" uniformity. // These functions sample the output of rand() as the fraction part of an infinite binary number, // with some tricks applied to reduce ops and branching: diff --git a/core/math/rect2.h b/core/math/rect2.h index 6ccb76cd10..7f410feb1c 100644 --- a/core/math/rect2.h +++ b/core/math/rect2.h @@ -51,7 +51,7 @@ struct _NO_DISCARD_ Rect2 { _FORCE_INLINE_ Vector2 get_center() const { return position + (size * 0.5f); } - inline bool intersects(const Rect2 &p_rect, const bool p_include_borders = false) const { + inline bool intersects(const Rect2 &p_rect, bool p_include_borders = false) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0 || p_rect.size.x < 0 || p_rect.size.y < 0)) { ERR_PRINT("Rect2 size is negative, this is not supported. Use Rect2.abs() to get a Rect2 with a positive size."); @@ -152,14 +152,12 @@ struct _NO_DISCARD_ Rect2 { return Rect2(); } - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); + new_rect.position = p_rect.position.max(position); Point2 p_rect_end = p_rect.position + p_rect.size; Point2 end = position + size; - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + new_rect.size = p_rect_end.min(end) - new_rect.position; return new_rect; } @@ -172,11 +170,9 @@ struct _NO_DISCARD_ Rect2 { #endif Rect2 new_rect; - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); + new_rect.position = p_rect.position.min(position); - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + new_rect.size = (p_rect.position + p_rect.size).max(position + size); new_rect.size = new_rect.size - new_rect.position; // Make relative again. @@ -282,7 +278,11 @@ struct _NO_DISCARD_ Rect2 { } _FORCE_INLINE_ Rect2 abs() const { - return Rect2(Point2(position.x + MIN(size.x, (real_t)0), position.y + MIN(size.y, (real_t)0)), size.abs()); + return Rect2(position + size.min(Point2()), size.abs()); + } + + _FORCE_INLINE_ Rect2 round() const { + return Rect2(position.round(), size.round()); } Vector2 get_support(const Vector2 &p_normal) const { @@ -307,14 +307,14 @@ struct _NO_DISCARD_ Rect2 { i_f = i; Vector2 r = (b - a); - float l = r.length(); + const real_t l = r.length(); if (l == 0.0f) { continue; } // Check inside. Vector2 tg = r.orthogonal(); - float s = tg.dot(center) - tg.dot(a); + const real_t s = tg.dot(center) - tg.dot(a); if (s < 0.0f) { side_plus++; } else { @@ -330,8 +330,8 @@ struct _NO_DISCARD_ Rect2 { Vector2 t13 = (position - a) * ir; Vector2 t24 = (end - a) * ir; - float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y)); - float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y)); + const real_t tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y)); + const real_t tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y)); // if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us if (tmax < 0 || tmin > tmax || tmin >= l) { diff --git a/core/math/rect2i.h b/core/math/rect2i.h index 205b2c7198..64806414c7 100644 --- a/core/math/rect2i.h +++ b/core/math/rect2i.h @@ -95,14 +95,12 @@ struct _NO_DISCARD_ Rect2i { return Rect2i(); } - new_rect.position.x = MAX(p_rect.position.x, position.x); - new_rect.position.y = MAX(p_rect.position.y, position.y); + new_rect.position = p_rect.position.max(position); Point2i p_rect_end = p_rect.position + p_rect.size; Point2i end = position + size; - new_rect.size.x = MIN(p_rect_end.x, end.x) - new_rect.position.x; - new_rect.size.y = MIN(p_rect_end.y, end.y) - new_rect.position.y; + new_rect.size = p_rect_end.min(end) - new_rect.position; return new_rect; } @@ -115,11 +113,9 @@ struct _NO_DISCARD_ Rect2i { #endif Rect2i new_rect; - new_rect.position.x = MIN(p_rect.position.x, position.x); - new_rect.position.y = MIN(p_rect.position.y, position.y); + new_rect.position = p_rect.position.min(position); - new_rect.size.x = MAX(p_rect.position.x + p_rect.size.x, position.x + size.x); - new_rect.size.y = MAX(p_rect.position.y + p_rect.size.y, position.y + size.y); + new_rect.size = (p_rect.position + p_rect.size).max(position + size); new_rect.size = new_rect.size - new_rect.position; // Make relative again. @@ -217,7 +213,7 @@ struct _NO_DISCARD_ Rect2i { } _FORCE_INLINE_ Rect2i abs() const { - return Rect2i(Point2i(position.x + MIN(size.x, 0), position.y + MIN(size.y, 0)), size.abs()); + return Rect2i(position + size.min(Point2i()), size.abs()); } _FORCE_INLINE_ void set_end(const Vector2i &p_end) { diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp index bc4682fd90..f6525fe5ca 100644 --- a/core/math/transform_2d.cpp +++ b/core/math/transform_2d.cpp @@ -65,7 +65,7 @@ Transform2D Transform2D::affine_inverse() const { return inv; } -void Transform2D::rotate(const real_t p_angle) { +void Transform2D::rotate(real_t p_angle) { *this = Transform2D(p_angle, Vector2()) * (*this); } @@ -74,7 +74,7 @@ real_t Transform2D::get_skew() const { return Math::acos(columns[0].normalized().dot(SIGN(det) * columns[1].normalized())) - (real_t)Math_PI * 0.5f; } -void Transform2D::set_skew(const real_t p_angle) { +void Transform2D::set_skew(real_t p_angle) { real_t det = determinant(); columns[1] = SIGN(det) * columns[0].rotated(((real_t)Math_PI * 0.5f + p_angle)).normalized() * columns[1].length(); } @@ -83,7 +83,7 @@ real_t Transform2D::get_rotation() const { return Math::atan2(columns[0].y, columns[0].x); } -void Transform2D::set_rotation(const real_t p_rot) { +void Transform2D::set_rotation(real_t p_rot) { Size2 scale = get_scale(); real_t cr = Math::cos(p_rot); real_t sr = Math::sin(p_rot); @@ -94,7 +94,7 @@ void Transform2D::set_rotation(const real_t p_rot) { set_scale(scale); } -Transform2D::Transform2D(const real_t p_rot, const Vector2 &p_pos) { +Transform2D::Transform2D(real_t p_rot, const Vector2 &p_pos) { real_t cr = Math::cos(p_rot); real_t sr = Math::sin(p_rot); columns[0][0] = cr; @@ -104,7 +104,7 @@ Transform2D::Transform2D(const real_t p_rot, const Vector2 &p_pos) { columns[2] = p_pos; } -Transform2D::Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos) { +Transform2D::Transform2D(real_t p_rot, const Size2 &p_scale, real_t p_skew, const Vector2 &p_pos) { columns[0][0] = Math::cos(p_rot) * p_scale.x; columns[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; columns[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; @@ -136,7 +136,7 @@ void Transform2D::scale_basis(const Size2 &p_scale) { columns[1][1] *= p_scale.y; } -void Transform2D::translate_local(const real_t p_tx, const real_t p_ty) { +void Transform2D::translate_local(real_t p_tx, real_t p_ty) { translate_local(Vector2(p_tx, p_ty)); } @@ -261,12 +261,12 @@ Transform2D Transform2D::translated_local(const Vector2 &p_offset) const { return Transform2D(columns[0], columns[1], columns[2] + basis_xform(p_offset)); } -Transform2D Transform2D::rotated(const real_t p_angle) const { +Transform2D Transform2D::rotated(real_t p_angle) const { // Equivalent to left multiplication return Transform2D(p_angle, Vector2()) * (*this); } -Transform2D Transform2D::rotated_local(const real_t p_angle) const { +Transform2D Transform2D::rotated_local(real_t p_angle) const { // Equivalent to right multiplication return (*this) * Transform2D(p_angle, Vector2()); // Could be optimized, because origin transform can be skipped. } @@ -275,7 +275,7 @@ real_t Transform2D::determinant() const { return columns[0].x * columns[1].y - columns[0].y * columns[1].x; } -Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const real_t p_weight) const { +Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, real_t p_weight) const { return Transform2D( Math::lerp_angle(get_rotation(), p_transform.get_rotation(), p_weight), get_scale().lerp(p_transform.get_scale(), p_weight), @@ -283,18 +283,30 @@ Transform2D Transform2D::interpolate_with(const Transform2D &p_transform, const get_origin().lerp(p_transform.get_origin(), p_weight)); } -void Transform2D::operator*=(const real_t p_val) { +void Transform2D::operator*=(real_t p_val) { columns[0] *= p_val; columns[1] *= p_val; columns[2] *= p_val; } -Transform2D Transform2D::operator*(const real_t p_val) const { +Transform2D Transform2D::operator*(real_t p_val) const { Transform2D ret(*this); ret *= p_val; return ret; } +void Transform2D::operator/=(real_t p_val) { + columns[0] /= p_val; + columns[1] /= p_val; + columns[2] /= p_val; +} + +Transform2D Transform2D::operator/(real_t p_val) const { + Transform2D ret(*this); + ret /= p_val; + return ret; +} + Transform2D::operator String() const { return "[X: " + columns[0].operator String() + ", Y: " + columns[1].operator String() + diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index dd1a33c5d5..4ec2dc119c 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -52,8 +52,8 @@ struct _NO_DISCARD_ Transform2D { Vector2 columns[3]; - _FORCE_INLINE_ real_t tdotx(const Vector2 &v) const { return columns[0][0] * v.x + columns[1][0] * v.y; } - _FORCE_INLINE_ real_t tdoty(const Vector2 &v) const { return columns[0][1] * v.x + columns[1][1] * v.y; } + _FORCE_INLINE_ real_t tdotx(const Vector2 &p_v) const { return columns[0][0] * p_v.x + columns[1][0] * p_v.y; } + _FORCE_INLINE_ real_t tdoty(const Vector2 &p_v) const { return columns[0][1] * p_v.x + columns[1][1] * p_v.y; } const Vector2 &operator[](int p_idx) const { return columns[p_idx]; } Vector2 &operator[](int p_idx) { return columns[p_idx]; } @@ -64,17 +64,17 @@ struct _NO_DISCARD_ Transform2D { void affine_invert(); Transform2D affine_inverse() const; - void set_rotation(const real_t p_rot); + void set_rotation(real_t p_rot); real_t get_rotation() const; real_t get_skew() const; - void set_skew(const real_t p_angle); - _FORCE_INLINE_ void set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale); - _FORCE_INLINE_ void set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew); - void rotate(const real_t p_angle); + void set_skew(real_t p_angle); + _FORCE_INLINE_ void set_rotation_and_scale(real_t p_rot, const Size2 &p_scale); + _FORCE_INLINE_ void set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, real_t p_skew); + void rotate(real_t p_angle); void scale(const Size2 &p_scale); void scale_basis(const Size2 &p_scale); - void translate_local(const real_t p_tx, const real_t p_ty); + void translate_local(real_t p_tx, real_t p_ty); void translate_local(const Vector2 &p_translation); real_t determinant() const; @@ -89,8 +89,8 @@ struct _NO_DISCARD_ Transform2D { Transform2D scaled_local(const Size2 &p_scale) const; Transform2D translated(const Vector2 &p_offset) const; Transform2D translated_local(const Vector2 &p_offset) const; - Transform2D rotated(const real_t p_angle) const; - Transform2D rotated_local(const real_t p_angle) const; + Transform2D rotated(real_t p_angle) const; + Transform2D rotated_local(real_t p_angle) const; Transform2D untranslated() const; @@ -107,10 +107,12 @@ struct _NO_DISCARD_ Transform2D { void operator*=(const Transform2D &p_transform); Transform2D operator*(const Transform2D &p_transform) const; - void operator*=(const real_t p_val); - Transform2D operator*(const real_t p_val) const; + void operator*=(real_t p_val); + Transform2D operator*(real_t p_val) const; + void operator/=(real_t p_val); + Transform2D operator/(real_t p_val) const; - Transform2D interpolate_with(const Transform2D &p_transform, const real_t p_c) const; + Transform2D interpolate_with(const Transform2D &p_transform, real_t p_c) const; _FORCE_INLINE_ Vector2 basis_xform(const Vector2 &p_vec) const; _FORCE_INLINE_ Vector2 basis_xform_inv(const Vector2 &p_vec) const; @@ -123,13 +125,13 @@ struct _NO_DISCARD_ Transform2D { operator String() const; - Transform2D(const real_t xx, const real_t xy, const real_t yx, const real_t yy, const real_t ox, const real_t oy) { - columns[0][0] = xx; - columns[0][1] = xy; - columns[1][0] = yx; - columns[1][1] = yy; - columns[2][0] = ox; - columns[2][1] = oy; + Transform2D(real_t p_xx, real_t p_xy, real_t p_yx, real_t p_yy, real_t p_ox, real_t p_oy) { + columns[0][0] = p_xx; + columns[0][1] = p_xy; + columns[1][0] = p_yx; + columns[1][1] = p_yy; + columns[2][0] = p_ox; + columns[2][1] = p_oy; } Transform2D(const Vector2 &p_x, const Vector2 &p_y, const Vector2 &p_origin) { @@ -138,9 +140,9 @@ struct _NO_DISCARD_ Transform2D { columns[2] = p_origin; } - Transform2D(const real_t p_rot, const Vector2 &p_pos); + Transform2D(real_t p_rot, const Vector2 &p_pos); - Transform2D(const real_t p_rot, const Size2 &p_scale, const real_t p_skew, const Vector2 &p_pos); + Transform2D(real_t p_rot, const Size2 &p_scale, real_t p_skew, const Vector2 &p_pos); Transform2D() { columns[0][0] = 1.0; @@ -188,14 +190,14 @@ Rect2 Transform2D::xform(const Rect2 &p_rect) const { return new_rect; } -void Transform2D::set_rotation_and_scale(const real_t p_rot, const Size2 &p_scale) { +void Transform2D::set_rotation_and_scale(real_t p_rot, const Size2 &p_scale) { columns[0][0] = Math::cos(p_rot) * p_scale.x; columns[1][1] = Math::cos(p_rot) * p_scale.y; columns[1][0] = -Math::sin(p_rot) * p_scale.y; columns[0][1] = Math::sin(p_rot) * p_scale.x; } -void Transform2D::set_rotation_scale_and_skew(const real_t p_rot, const Size2 &p_scale, const real_t p_skew) { +void Transform2D::set_rotation_scale_and_skew(real_t p_rot, const Size2 &p_scale, real_t p_skew) { columns[0][0] = Math::cos(p_rot) * p_scale.x; columns[1][1] = Math::cos(p_rot + p_skew) * p_scale.y; columns[1][0] = -Math::sin(p_rot + p_skew) * p_scale.y; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index cdc94676c9..2c91a7604b 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -197,17 +197,28 @@ Transform3D Transform3D::operator*(const Transform3D &p_transform) const { return t; } -void Transform3D::operator*=(const real_t p_val) { +void Transform3D::operator*=(real_t p_val) { origin *= p_val; basis *= p_val; } -Transform3D Transform3D::operator*(const real_t p_val) const { +Transform3D Transform3D::operator*(real_t p_val) const { Transform3D ret(*this); ret *= p_val; return ret; } +void Transform3D::operator/=(real_t p_val) { + basis /= p_val; + origin /= p_val; +} + +Transform3D Transform3D::operator/(real_t p_val) const { + Transform3D ret(*this); + ret /= p_val; + return ret; +} + Transform3D::operator String() const { return "[X: " + basis.get_column(0).operator String() + ", Y: " + basis.get_column(1).operator String() + @@ -227,7 +238,7 @@ Transform3D::Transform3D(const Vector3 &p_x, const Vector3 &p_y, const Vector3 & basis.set_column(2, p_z); } -Transform3D::Transform3D(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz) { - basis = Basis(xx, xy, xz, yx, yy, yz, zx, zy, zz); - origin = Vector3(ox, oy, oz); +Transform3D::Transform3D(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_zx, real_t p_zy, real_t p_zz, real_t p_ox, real_t p_oy, real_t p_oz) { + basis = Basis(p_xx, p_xy, p_xz, p_yx, p_yy, p_yz, p_zx, p_zy, p_zz); + origin = Vector3(p_ox, p_oy, p_oz); } diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index 70141a3dbe..7d89b86c75 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -102,8 +102,10 @@ struct _NO_DISCARD_ Transform3D { void operator*=(const Transform3D &p_transform); Transform3D operator*(const Transform3D &p_transform) const; - void operator*=(const real_t p_val); - Transform3D operator*(const real_t p_val) const; + void operator*=(real_t p_val); + Transform3D operator*(real_t p_val) const; + void operator/=(real_t p_val); + Transform3D operator/(real_t p_val) const; Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const; @@ -113,11 +115,11 @@ struct _NO_DISCARD_ Transform3D { basis.xform(v)); } - void set(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t tx, real_t ty, real_t tz) { - basis.set(xx, xy, xz, yx, yy, yz, zx, zy, zz); - origin.x = tx; - origin.y = ty; - origin.z = tz; + void set(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_zx, real_t p_zy, real_t p_zz, real_t p_tx, real_t p_ty, real_t p_tz) { + basis.set(p_xx, p_xy, p_xz, p_yx, p_yy, p_yz, p_zx, p_zy, p_zz); + origin.x = p_tx; + origin.y = p_ty; + origin.z = p_tz; } operator String() const; @@ -125,7 +127,7 @@ struct _NO_DISCARD_ Transform3D { Transform3D() {} Transform3D(const Basis &p_basis, const Vector3 &p_origin = Vector3()); Transform3D(const Vector3 &p_x, const Vector3 &p_y, const Vector3 &p_z, const Vector3 &p_origin); - Transform3D(real_t xx, real_t xy, real_t xz, real_t yx, real_t yy, real_t yz, real_t zx, real_t zy, real_t zz, real_t ox, real_t oy, real_t oz); + Transform3D(real_t p_xx, real_t p_xy, real_t p_xz, real_t p_yx, real_t p_yy, real_t p_yz, real_t p_zx, real_t p_zy, real_t p_zz, real_t p_ox, real_t p_oy, real_t p_oz); }; _FORCE_INLINE_ Vector3 Transform3D::xform(const Vector3 &p_vector) const { diff --git a/core/math/transform_interpolator.cpp b/core/math/transform_interpolator.cpp new file mode 100644 index 0000000000..7cfe880b5a --- /dev/null +++ b/core/math/transform_interpolator.cpp @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* transform_interpolator.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "transform_interpolator.h" + +#include "core/math/transform_2d.h" + +void TransformInterpolator::interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction) { + // Extract parameters. + Vector2 p1 = p_prev.get_origin(); + Vector2 p2 = p_curr.get_origin(); + + // Special case for physics interpolation, if flipping, don't interpolate basis. + // If the determinant polarity changes, the handedness of the coordinate system changes. + if (_sign(p_prev.determinant()) != _sign(p_curr.determinant())) { + r_result.columns[0] = p_curr.columns[0]; + r_result.columns[1] = p_curr.columns[1]; + r_result.set_origin(p1.lerp(p2, p_fraction)); + return; + } + + real_t r1 = p_prev.get_rotation(); + real_t r2 = p_curr.get_rotation(); + + Size2 s1 = p_prev.get_scale(); + Size2 s2 = p_curr.get_scale(); + + // Slerp rotation. + Vector2 v1(Math::cos(r1), Math::sin(r1)); + Vector2 v2(Math::cos(r2), Math::sin(r2)); + + real_t dot = v1.dot(v2); + + dot = CLAMP(dot, -1, 1); + + Vector2 v; + + if (dot > 0.9995f) { + v = v1.lerp(v2, p_fraction).normalized(); // Linearly interpolate to avoid numerical precision issues. + } else { + real_t angle = p_fraction * Math::acos(dot); + Vector2 v3 = (v2 - v1 * dot).normalized(); + v = v1 * Math::cos(angle) + v3 * Math::sin(angle); + } + + // Construct matrix. + r_result = Transform2D(Math::atan2(v.y, v.x), p1.lerp(p2, p_fraction)); + r_result.scale_basis(s1.lerp(s2, p_fraction)); +} diff --git a/core/math/transform_interpolator.h b/core/math/transform_interpolator.h new file mode 100644 index 0000000000..a9bce2bd7f --- /dev/null +++ b/core/math/transform_interpolator.h @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* transform_interpolator.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 TRANSFORM_INTERPOLATOR_H +#define TRANSFORM_INTERPOLATOR_H + +#include "core/math/math_defs.h" + +struct Transform2D; + +class TransformInterpolator { +private: + static bool _sign(real_t p_val) { return p_val >= 0; } + +public: + static void interpolate_transform_2d(const Transform2D &p_prev, const Transform2D &p_curr, Transform2D &r_result, real_t p_fraction); +}; + +#endif // TRANSFORM_INTERPOLATOR_H diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index df8c804243..198fd85d20 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -37,7 +37,7 @@ real_t Vector2::angle() const { return Math::atan2(y, x); } -Vector2 Vector2::from_angle(const real_t p_angle) { +Vector2 Vector2::from_angle(real_t p_angle) { return Vector2(Math::cos(p_angle), Math::sin(p_angle)); } @@ -109,7 +109,7 @@ Vector2 Vector2::round() const { return Vector2(Math::round(x), Math::round(y)); } -Vector2 Vector2::rotated(const real_t p_by) const { +Vector2 Vector2::rotated(real_t p_by) const { real_t sine = Math::sin(p_by); real_t cosi = Math::cos(p_by); return Vector2( @@ -117,7 +117,7 @@ Vector2 Vector2::rotated(const real_t p_by) const { x * sine + y * cosi); } -Vector2 Vector2::posmod(const real_t p_mod) const { +Vector2 Vector2::posmod(real_t p_mod) const { return Vector2(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod)); } @@ -141,7 +141,7 @@ Vector2 Vector2::snapped(const Vector2 &p_step) const { Math::snapped(y, p_step.y)); } -Vector2 Vector2::limit_length(const real_t p_len) const { +Vector2 Vector2::limit_length(real_t p_len) const { const real_t l = length(); Vector2 v = *this; if (l > 0 && p_len < l) { @@ -152,7 +152,7 @@ Vector2 Vector2::limit_length(const real_t p_len) const { return v; } -Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const { +Vector2 Vector2::move_toward(const Vector2 &p_to, real_t p_delta) const { Vector2 v = *this; Vector2 vd = p_to - v; real_t len = vd.length(); @@ -162,9 +162,9 @@ Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const { // slide returns the component of the vector along the given plane, specified by its normal vector. Vector2 Vector2::slide(const Vector2 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized."); + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 " + p_normal.operator String() + "must be normalized."); #endif - return *this - p_normal * this->dot(p_normal); + return *this - p_normal * dot(p_normal); } Vector2 Vector2::bounce(const Vector2 &p_normal) const { @@ -173,9 +173,9 @@ Vector2 Vector2::bounce(const Vector2 &p_normal) const { Vector2 Vector2::reflect(const Vector2 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized."); + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 " + p_normal.operator String() + "must be normalized."); #endif - return 2.0f * p_normal * this->dot(p_normal) - *this; + return 2.0f * p_normal * dot(p_normal) - *this; } bool Vector2::is_equal_approx(const Vector2 &p_v) const { diff --git a/core/math/vector2.h b/core/math/vector2.h index b9d7709acd..6ad003edd1 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -60,13 +60,13 @@ struct _NO_DISCARD_ Vector2 { real_t coord[2] = { 0 }; }; - _FORCE_INLINE_ real_t &operator[](int p_idx) { - DEV_ASSERT((unsigned int)p_idx < 2); - return coord[p_idx]; + _FORCE_INLINE_ real_t &operator[](int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 2); + return coord[p_axis]; } - _FORCE_INLINE_ const real_t &operator[](int p_idx) const { - DEV_ASSERT((unsigned int)p_idx < 2); - return coord[p_idx]; + _FORCE_INLINE_ const real_t &operator[](int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 2); + return coord[p_axis]; } _FORCE_INLINE_ Vector2::Axis min_axis_index() const { @@ -83,7 +83,7 @@ struct _NO_DISCARD_ Vector2 { real_t length() const; real_t length_squared() const; - Vector2 limit_length(const real_t p_len = 1.0) const; + Vector2 limit_length(real_t p_len = 1.0) const; Vector2 min(const Vector2 &p_vector2) const { return Vector2(MIN(x, p_vector2.x), MIN(y, p_vector2.y)); @@ -101,20 +101,20 @@ struct _NO_DISCARD_ Vector2 { real_t dot(const Vector2 &p_other) const; real_t cross(const Vector2 &p_other) const; - Vector2 posmod(const real_t p_mod) const; + Vector2 posmod(real_t p_mod) const; Vector2 posmodv(const Vector2 &p_modv) const; Vector2 project(const Vector2 &p_to) const; - Vector2 plane_project(const real_t p_d, const Vector2 &p_vec) const; + Vector2 plane_project(real_t p_d, const Vector2 &p_vec) const; - _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, const real_t p_weight) const; - _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, const real_t p_weight) const; - _FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const; - _FORCE_INLINE_ Vector2 cubic_interpolate_in_time(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const; - _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; - _FORCE_INLINE_ Vector2 bezier_derivative(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const; + _FORCE_INLINE_ Vector2 lerp(const Vector2 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector2 slerp(const Vector2 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector2 cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const; + _FORCE_INLINE_ Vector2 cubic_interpolate_in_time(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const; + _FORCE_INLINE_ Vector2 bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, real_t p_t) const; + _FORCE_INLINE_ Vector2 bezier_derivative(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, real_t p_t) const; - Vector2 move_toward(const Vector2 &p_to, const real_t p_delta) const; + Vector2 move_toward(const Vector2 &p_to, real_t p_delta) const; Vector2 slide(const Vector2 &p_normal) const; Vector2 bounce(const Vector2 &p_normal) const; @@ -130,16 +130,16 @@ struct _NO_DISCARD_ Vector2 { void operator-=(const Vector2 &p_v); Vector2 operator*(const Vector2 &p_v1) const; - Vector2 operator*(const real_t &rvalue) const; - void operator*=(const real_t &rvalue); - void operator*=(const Vector2 &rvalue) { *this = *this * rvalue; } + Vector2 operator*(real_t p_rvalue) const; + void operator*=(real_t p_rvalue); + void operator*=(const Vector2 &p_rvalue) { *this = *this * p_rvalue; } Vector2 operator/(const Vector2 &p_v1) const; - Vector2 operator/(const real_t &rvalue) const; + Vector2 operator/(real_t p_rvalue) const; - void operator/=(const real_t &rvalue); - void operator/=(const Vector2 &rvalue) { *this = *this / rvalue; } + void operator/=(real_t p_rvalue); + void operator/=(const Vector2 &p_rvalue) { *this = *this / p_rvalue; } Vector2 operator-() const; @@ -152,13 +152,13 @@ struct _NO_DISCARD_ Vector2 { bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } real_t angle() const; - static Vector2 from_angle(const real_t p_angle); + static Vector2 from_angle(real_t p_angle); _FORCE_INLINE_ Vector2 abs() const { return Vector2(Math::abs(x), Math::abs(y)); } - Vector2 rotated(const real_t p_by) const; + Vector2 rotated(real_t p_by) const; Vector2 orthogonal() const { return Vector2(y, -x); } @@ -175,13 +175,13 @@ struct _NO_DISCARD_ Vector2 { operator Vector2i() const; _FORCE_INLINE_ Vector2() {} - _FORCE_INLINE_ Vector2(const real_t p_x, const real_t p_y) { + _FORCE_INLINE_ Vector2(real_t p_x, real_t p_y) { x = p_x; y = p_y; } }; -_FORCE_INLINE_ Vector2 Vector2::plane_project(const real_t p_d, const Vector2 &p_vec) const { +_FORCE_INLINE_ Vector2 Vector2::plane_project(real_t p_d, const Vector2 &p_vec) const { return p_vec - *this * (dot(p_vec) - p_d); } @@ -207,26 +207,26 @@ _FORCE_INLINE_ Vector2 Vector2::operator*(const Vector2 &p_v1) const { return Vector2(x * p_v1.x, y * p_v1.y); } -_FORCE_INLINE_ Vector2 Vector2::operator*(const real_t &rvalue) const { - return Vector2(x * rvalue, y * rvalue); +_FORCE_INLINE_ Vector2 Vector2::operator*(real_t p_rvalue) const { + return Vector2(x * p_rvalue, y * p_rvalue); } -_FORCE_INLINE_ void Vector2::operator*=(const real_t &rvalue) { - x *= rvalue; - y *= rvalue; +_FORCE_INLINE_ void Vector2::operator*=(real_t p_rvalue) { + x *= p_rvalue; + y *= p_rvalue; } _FORCE_INLINE_ Vector2 Vector2::operator/(const Vector2 &p_v1) const { return Vector2(x / p_v1.x, y / p_v1.y); } -_FORCE_INLINE_ Vector2 Vector2::operator/(const real_t &rvalue) const { - return Vector2(x / rvalue, y / rvalue); +_FORCE_INLINE_ Vector2 Vector2::operator/(real_t p_rvalue) const { + return Vector2(x / p_rvalue, y / p_rvalue); } -_FORCE_INLINE_ void Vector2::operator/=(const real_t &rvalue) { - x /= rvalue; - y /= rvalue; +_FORCE_INLINE_ void Vector2::operator/=(real_t p_rvalue) { + x /= p_rvalue; + y /= p_rvalue; } _FORCE_INLINE_ Vector2 Vector2::operator-() const { @@ -241,14 +241,14 @@ _FORCE_INLINE_ bool Vector2::operator!=(const Vector2 &p_vec2) const { return x != p_vec2.x || y != p_vec2.y; } -Vector2 Vector2::lerp(const Vector2 &p_to, const real_t p_weight) const { +Vector2 Vector2::lerp(const Vector2 &p_to, real_t p_weight) const { Vector2 res = *this; res.x = Math::lerp(res.x, p_to.x, p_weight); res.y = Math::lerp(res.y, p_to.y, p_weight); return res; } -Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const { +Vector2 Vector2::slerp(const Vector2 &p_to, real_t p_weight) const { real_t start_length_sq = length_squared(); real_t end_length_sq = p_to.length_squared(); if (unlikely(start_length_sq == 0.0f || end_length_sq == 0.0f)) { @@ -261,28 +261,28 @@ Vector2 Vector2::slerp(const Vector2 &p_to, const real_t p_weight) const { return rotated(angle * p_weight) * (result_length / start_length); } -Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight) const { +Vector2 Vector2::cubic_interpolate(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight) const { Vector2 res = *this; res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); return res; } -Vector2 Vector2::cubic_interpolate_in_time(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const { +Vector2 Vector2::cubic_interpolate_in_time(const Vector2 &p_b, const Vector2 &p_pre_a, const Vector2 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const { Vector2 res = *this; res.x = Math::cubic_interpolate_in_time(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight, p_b_t, p_pre_a_t, p_post_b_t); res.y = Math::cubic_interpolate_in_time(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight, p_b_t, p_pre_a_t, p_post_b_t); return res; } -Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const { +Vector2 Vector2::bezier_interpolate(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, real_t p_t) const { Vector2 res = *this; res.x = Math::bezier_interpolate(res.x, p_control_1.x, p_control_2.x, p_end.x, p_t); res.y = Math::bezier_interpolate(res.y, p_control_1.y, p_control_2.y, p_end.y, p_t); return res; } -Vector2 Vector2::bezier_derivative(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, const real_t p_t) const { +Vector2 Vector2::bezier_derivative(const Vector2 &p_control_1, const Vector2 &p_control_2, const Vector2 &p_end, real_t p_t) const { Vector2 res = *this; res.x = Math::bezier_derivative(res.x, p_control_1.x, p_control_2.x, p_end.x, p_t); res.y = Math::bezier_derivative(res.y, p_control_1.y, p_control_2.y, p_end.y, p_t); @@ -298,19 +298,19 @@ Vector2 Vector2::direction_to(const Vector2 &p_to) const { // Multiplication operators required to workaround issues with LLVM using implicit conversion // to Vector2i instead for integers where it should not. -_FORCE_INLINE_ Vector2 operator*(const float p_scalar, const Vector2 &p_vec) { +_FORCE_INLINE_ Vector2 operator*(float p_scalar, const Vector2 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2 operator*(const double p_scalar, const Vector2 &p_vec) { +_FORCE_INLINE_ Vector2 operator*(double p_scalar, const Vector2 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2 operator*(const int32_t p_scalar, const Vector2 &p_vec) { +_FORCE_INLINE_ Vector2 operator*(int32_t p_scalar, const Vector2 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector2 operator*(const int64_t p_scalar, const Vector2 &p_vec) { +_FORCE_INLINE_ Vector2 operator*(int64_t p_scalar, const Vector2 &p_vec) { return p_vec * p_scalar; } diff --git a/core/math/vector2i.cpp b/core/math/vector2i.cpp index 9c7da668ff..ba79d439dd 100644 --- a/core/math/vector2i.cpp +++ b/core/math/vector2i.cpp @@ -75,39 +75,39 @@ Vector2i Vector2i::operator*(const Vector2i &p_v1) const { return Vector2i(x * p_v1.x, y * p_v1.y); } -Vector2i Vector2i::operator*(const int32_t &rvalue) const { - return Vector2i(x * rvalue, y * rvalue); +Vector2i Vector2i::operator*(int32_t p_rvalue) const { + return Vector2i(x * p_rvalue, y * p_rvalue); } -void Vector2i::operator*=(const int32_t &rvalue) { - x *= rvalue; - y *= rvalue; +void Vector2i::operator*=(int32_t p_rvalue) { + x *= p_rvalue; + y *= p_rvalue; } Vector2i Vector2i::operator/(const Vector2i &p_v1) const { return Vector2i(x / p_v1.x, y / p_v1.y); } -Vector2i Vector2i::operator/(const int32_t &rvalue) const { - return Vector2i(x / rvalue, y / rvalue); +Vector2i Vector2i::operator/(int32_t p_rvalue) const { + return Vector2i(x / p_rvalue, y / p_rvalue); } -void Vector2i::operator/=(const int32_t &rvalue) { - x /= rvalue; - y /= rvalue; +void Vector2i::operator/=(int32_t p_rvalue) { + x /= p_rvalue; + y /= p_rvalue; } Vector2i Vector2i::operator%(const Vector2i &p_v1) const { return Vector2i(x % p_v1.x, y % p_v1.y); } -Vector2i Vector2i::operator%(const int32_t &rvalue) const { - return Vector2i(x % rvalue, y % rvalue); +Vector2i Vector2i::operator%(int32_t p_rvalue) const { + return Vector2i(x % p_rvalue, y % p_rvalue); } -void Vector2i::operator%=(const int32_t &rvalue) { - x %= rvalue; - y %= rvalue; +void Vector2i::operator%=(int32_t p_rvalue) { + x %= p_rvalue; + y %= p_rvalue; } Vector2i Vector2i::operator-() const { diff --git a/core/math/vector2i.h b/core/math/vector2i.h index e6850347c3..aa29263a65 100644 --- a/core/math/vector2i.h +++ b/core/math/vector2i.h @@ -60,13 +60,13 @@ struct _NO_DISCARD_ Vector2i { int32_t coord[2] = { 0 }; }; - _FORCE_INLINE_ int32_t &operator[](int p_idx) { - DEV_ASSERT((unsigned int)p_idx < 2); - return coord[p_idx]; + _FORCE_INLINE_ int32_t &operator[](int p_axis) { + DEV_ASSERT((unsigned int)p_axis < 2); + return coord[p_axis]; } - _FORCE_INLINE_ const int32_t &operator[](int p_idx) const { - DEV_ASSERT((unsigned int)p_idx < 2); - return coord[p_idx]; + _FORCE_INLINE_ const int32_t &operator[](int p_axis) const { + DEV_ASSERT((unsigned int)p_axis < 2); + return coord[p_axis]; } _FORCE_INLINE_ Vector2i::Axis min_axis_index() const { @@ -85,22 +85,30 @@ struct _NO_DISCARD_ Vector2i { return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y)); } + double distance_to(const Vector2i &p_to) const { + return (p_to - *this).length(); + } + + int64_t distance_squared_to(const Vector2i &p_to) const { + return (p_to - *this).length_squared(); + } + Vector2i operator+(const Vector2i &p_v) const; void operator+=(const Vector2i &p_v); Vector2i operator-(const Vector2i &p_v) const; void operator-=(const Vector2i &p_v); Vector2i operator*(const Vector2i &p_v1) const; - Vector2i operator*(const int32_t &rvalue) const; - void operator*=(const int32_t &rvalue); + Vector2i operator*(int32_t p_rvalue) const; + void operator*=(int32_t p_rvalue); Vector2i operator/(const Vector2i &p_v1) const; - Vector2i operator/(const int32_t &rvalue) const; - void operator/=(const int32_t &rvalue); + Vector2i operator/(int32_t p_rvalue) const; + void operator/=(int32_t p_rvalue); Vector2i operator%(const Vector2i &p_v1) const; - Vector2i operator%(const int32_t &rvalue) const; - void operator%=(const int32_t &rvalue); + Vector2i operator%(int32_t p_rvalue) const; + void operator%=(int32_t p_rvalue); Vector2i operator-() const; bool operator<(const Vector2i &p_vec2) const { return (x == p_vec2.x) ? (y < p_vec2.y) : (x < p_vec2.x); } @@ -125,7 +133,7 @@ struct _NO_DISCARD_ Vector2i { operator Vector2() const; inline Vector2i() {} - inline Vector2i(const int32_t p_x, const int32_t p_y) { + inline Vector2i(int32_t p_x, int32_t p_y) { x = p_x; y = p_y; } @@ -133,19 +141,19 @@ struct _NO_DISCARD_ Vector2i { // Multiplication operators required to workaround issues with LLVM using implicit conversion. -_FORCE_INLINE_ Vector2i operator*(const int32_t p_scalar, const Vector2i &p_vector) { +_FORCE_INLINE_ Vector2i operator*(int32_t p_scalar, const Vector2i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const int64_t p_scalar, const Vector2i &p_vector) { +_FORCE_INLINE_ Vector2i operator*(int64_t p_scalar, const Vector2i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const float p_scalar, const Vector2i &p_vector) { +_FORCE_INLINE_ Vector2i operator*(float p_scalar, const Vector2i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector2i operator*(const double p_scalar, const Vector2i &p_vector) { +_FORCE_INLINE_ Vector2i operator*(double p_scalar, const Vector2i &p_vector) { return p_vector * p_scalar; } diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp index ae009fc4ef..fad5f2c0fb 100644 --- a/core/math/vector3.cpp +++ b/core/math/vector3.cpp @@ -35,11 +35,11 @@ #include "core/math/vector3i.h" #include "core/string/ustring.h" -void Vector3::rotate(const Vector3 &p_axis, const real_t p_angle) { +void Vector3::rotate(const Vector3 &p_axis, real_t p_angle) { *this = Basis(p_axis, p_angle).xform(*this); } -Vector3 Vector3::rotated(const Vector3 &p_axis, const real_t p_angle) const { +Vector3 Vector3::rotated(const Vector3 &p_axis, real_t p_angle) const { Vector3 r = *this; r.rotate(p_axis, p_angle); return r; @@ -52,19 +52,19 @@ Vector3 Vector3::clamp(const Vector3 &p_min, const Vector3 &p_max) const { CLAMP(z, p_min.z, p_max.z)); } -void Vector3::snap(const Vector3 p_step) { +void Vector3::snap(const Vector3 &p_step) { x = Math::snapped(x, p_step.x); y = Math::snapped(y, p_step.y); z = Math::snapped(z, p_step.z); } -Vector3 Vector3::snapped(const Vector3 p_step) const { +Vector3 Vector3::snapped(const Vector3 &p_step) const { Vector3 v = *this; v.snap(p_step); return v; } -Vector3 Vector3::limit_length(const real_t p_len) const { +Vector3 Vector3::limit_length(real_t p_len) const { const real_t l = length(); Vector3 v = *this; if (l > 0 && p_len < l) { @@ -75,7 +75,7 @@ Vector3 Vector3::limit_length(const real_t p_len) const { return v; } -Vector3 Vector3::move_toward(const Vector3 &p_to, const real_t p_delta) const { +Vector3 Vector3::move_toward(const Vector3 &p_to, real_t p_delta) const { Vector3 v = *this; Vector3 vd = p_to - v; real_t len = vd.length(); @@ -101,25 +101,25 @@ Vector2 Vector3::octahedron_encode() const { Vector3 Vector3::octahedron_decode(const Vector2 &p_oct) { Vector2 f(p_oct.x * 2.0f - 1.0f, p_oct.y * 2.0f - 1.0f); Vector3 n(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y)); - float t = CLAMP(-n.z, 0.0f, 1.0f); + const real_t t = CLAMP(-n.z, 0.0f, 1.0f); n.x += n.x >= 0 ? -t : t; n.y += n.y >= 0 ? -t : t; return n.normalized(); } -Vector2 Vector3::octahedron_tangent_encode(const float sign) const { - const float bias = 1.0f / 32767.0f; - Vector2 res = this->octahedron_encode(); +Vector2 Vector3::octahedron_tangent_encode(float p_sign) const { + const real_t bias = 1.0f / (real_t)32767.0f; + Vector2 res = octahedron_encode(); res.y = MAX(res.y, bias); res.y = res.y * 0.5f + 0.5f; - res.y = sign >= 0.0f ? res.y : 1 - res.y; + res.y = p_sign >= 0.0f ? res.y : 1 - res.y; return res; } -Vector3 Vector3::octahedron_tangent_decode(const Vector2 &p_oct, float *sign) { +Vector3 Vector3::octahedron_tangent_decode(const Vector2 &p_oct, float *r_sign) { Vector2 oct_compressed = p_oct; oct_compressed.y = oct_compressed.y * 2 - 1; - *sign = oct_compressed.y >= 0.0f ? 1.0f : -1.0f; + *r_sign = oct_compressed.y >= 0.0f ? 1.0f : -1.0f; oct_compressed.y = Math::abs(oct_compressed.y); Vector3 res = Vector3::octahedron_decode(oct_compressed); return res; diff --git a/core/math/vector3.h b/core/math/vector3.h index 18943a820f..f5d16984d9 100644 --- a/core/math/vector3.h +++ b/core/math/vector3.h @@ -33,8 +33,8 @@ #include "core/error/error_macros.h" #include "core/math/math_funcs.h" +#include "core/string/ustring.h" -class String; struct Basis; struct Vector2; struct Vector3i; @@ -58,12 +58,12 @@ struct _NO_DISCARD_ Vector3 { real_t coord[3] = { 0 }; }; - _FORCE_INLINE_ const real_t &operator[](const int p_axis) const { + _FORCE_INLINE_ const real_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 3); return coord[p_axis]; } - _FORCE_INLINE_ real_t &operator[](const int p_axis) { + _FORCE_INLINE_ real_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 3); return coord[p_axis]; } @@ -91,31 +91,31 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ Vector3 normalized() const; _FORCE_INLINE_ bool is_normalized() const; _FORCE_INLINE_ Vector3 inverse() const; - Vector3 limit_length(const real_t p_len = 1.0) const; + Vector3 limit_length(real_t p_len = 1.0) const; _FORCE_INLINE_ void zero(); - void snap(const Vector3 p_val); - Vector3 snapped(const Vector3 p_val) const; + void snap(const Vector3 &p_step); + Vector3 snapped(const Vector3 &p_step) const; - void rotate(const Vector3 &p_axis, const real_t p_angle); - Vector3 rotated(const Vector3 &p_axis, const real_t p_angle) const; + void rotate(const Vector3 &p_axis, real_t p_angle); + Vector3 rotated(const Vector3 &p_axis, real_t p_angle) const; /* Static Methods between 2 vector3s */ - _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, const real_t p_weight) const; - _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, const real_t p_weight) const; - _FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const; - _FORCE_INLINE_ Vector3 cubic_interpolate_in_time(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const; - _FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const; - _FORCE_INLINE_ Vector3 bezier_derivative(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const; + _FORCE_INLINE_ Vector3 lerp(const Vector3 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector3 slerp(const Vector3 &p_to, real_t p_weight) const; + _FORCE_INLINE_ Vector3 cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const; + _FORCE_INLINE_ Vector3 cubic_interpolate_in_time(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const; + _FORCE_INLINE_ Vector3 bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, real_t p_t) const; + _FORCE_INLINE_ Vector3 bezier_derivative(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, real_t p_t) const; - Vector3 move_toward(const Vector3 &p_to, const real_t p_delta) const; + Vector3 move_toward(const Vector3 &p_to, real_t p_delta) const; Vector2 octahedron_encode() const; static Vector3 octahedron_decode(const Vector2 &p_oct); - Vector2 octahedron_tangent_encode(const float sign) const; - static Vector3 octahedron_tangent_decode(const Vector2 &p_oct, float *sign); + Vector2 octahedron_tangent_encode(float p_sign) const; + static Vector3 octahedron_tangent_decode(const Vector2 &p_oct, float *r_sign); _FORCE_INLINE_ Vector3 cross(const Vector3 &p_with) const; _FORCE_INLINE_ real_t dot(const Vector3 &p_with) const; @@ -131,7 +131,7 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ real_t distance_to(const Vector3 &p_to) const; _FORCE_INLINE_ real_t distance_squared_to(const Vector3 &p_to) const; - _FORCE_INLINE_ Vector3 posmod(const real_t p_mod) const; + _FORCE_INLINE_ Vector3 posmod(real_t p_mod) const; _FORCE_INLINE_ Vector3 posmodv(const Vector3 &p_modv) const; _FORCE_INLINE_ Vector3 project(const Vector3 &p_to) const; @@ -158,10 +158,10 @@ struct _NO_DISCARD_ Vector3 { _FORCE_INLINE_ Vector3 &operator/=(const Vector3 &p_v); _FORCE_INLINE_ Vector3 operator/(const Vector3 &p_v) const; - _FORCE_INLINE_ Vector3 &operator*=(const real_t p_scalar); - _FORCE_INLINE_ Vector3 operator*(const real_t p_scalar) const; - _FORCE_INLINE_ Vector3 &operator/=(const real_t p_scalar); - _FORCE_INLINE_ Vector3 operator/(const real_t p_scalar) const; + _FORCE_INLINE_ Vector3 &operator*=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator*(real_t p_scalar) const; + _FORCE_INLINE_ Vector3 &operator/=(real_t p_scalar); + _FORCE_INLINE_ Vector3 operator/(real_t p_scalar) const; _FORCE_INLINE_ Vector3 operator-() const; @@ -176,7 +176,7 @@ struct _NO_DISCARD_ Vector3 { operator Vector3i() const; _FORCE_INLINE_ Vector3() {} - _FORCE_INLINE_ Vector3(const real_t p_x, const real_t p_y, const real_t p_z) { + _FORCE_INLINE_ Vector3(real_t p_x, real_t p_y, real_t p_z) { x = p_x; y = p_y; z = p_z; @@ -216,7 +216,7 @@ Vector3 Vector3::round() const { return Vector3(Math::round(x), Math::round(y), Math::round(z)); } -Vector3 Vector3::lerp(const Vector3 &p_to, const real_t p_weight) const { +Vector3 Vector3::lerp(const Vector3 &p_to, real_t p_weight) const { Vector3 res = *this; res.x = Math::lerp(res.x, p_to.x, p_weight); res.y = Math::lerp(res.y, p_to.y, p_weight); @@ -224,7 +224,7 @@ Vector3 Vector3::lerp(const Vector3 &p_to, const real_t p_weight) const { return res; } -Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const { +Vector3 Vector3::slerp(const Vector3 &p_to, real_t p_weight) const { // This method seems more complicated than it really is, since we write out // the internals of some methods for efficiency (mainly, checking length). real_t start_length_sq = length_squared(); @@ -246,7 +246,7 @@ Vector3 Vector3::slerp(const Vector3 &p_to, const real_t p_weight) const { return rotated(axis, angle * p_weight) * (result_length / start_length); } -Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight) const { +Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight) const { Vector3 res = *this; res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); @@ -254,7 +254,7 @@ Vector3 Vector3::cubic_interpolate(const Vector3 &p_b, const Vector3 &p_pre_a, c return res; } -Vector3 Vector3::cubic_interpolate_in_time(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const { +Vector3 Vector3::cubic_interpolate_in_time(const Vector3 &p_b, const Vector3 &p_pre_a, const Vector3 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const { Vector3 res = *this; res.x = Math::cubic_interpolate_in_time(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight, p_b_t, p_pre_a_t, p_post_b_t); res.y = Math::cubic_interpolate_in_time(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight, p_b_t, p_pre_a_t, p_post_b_t); @@ -262,7 +262,7 @@ Vector3 Vector3::cubic_interpolate_in_time(const Vector3 &p_b, const Vector3 &p_ return res; } -Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const { +Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, real_t p_t) const { Vector3 res = *this; res.x = Math::bezier_interpolate(res.x, p_control_1.x, p_control_2.x, p_end.x, p_t); res.y = Math::bezier_interpolate(res.y, p_control_1.y, p_control_2.y, p_end.y, p_t); @@ -270,7 +270,7 @@ Vector3 Vector3::bezier_interpolate(const Vector3 &p_control_1, const Vector3 &p return res; } -Vector3 Vector3::bezier_derivative(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, const real_t p_t) const { +Vector3 Vector3::bezier_derivative(const Vector3 &p_control_1, const Vector3 &p_control_2, const Vector3 &p_end, real_t p_t) const { Vector3 res = *this; res.x = Math::bezier_derivative(res.x, p_control_1.x, p_control_2.x, p_end.x, p_t); res.y = Math::bezier_derivative(res.y, p_control_1.y, p_control_2.y, p_end.y, p_t); @@ -286,7 +286,7 @@ real_t Vector3::distance_squared_to(const Vector3 &p_to) const { return (p_to - *this).length_squared(); } -Vector3 Vector3::posmod(const real_t p_mod) const { +Vector3 Vector3::posmod(real_t p_mod) const { return Vector3(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod)); } @@ -361,7 +361,7 @@ Vector3 Vector3::operator/(const Vector3 &p_v) const { return Vector3(x / p_v.x, y / p_v.y, z / p_v.z); } -Vector3 &Vector3::operator*=(const real_t p_scalar) { +Vector3 &Vector3::operator*=(real_t p_scalar) { x *= p_scalar; y *= p_scalar; z *= p_scalar; @@ -371,34 +371,34 @@ Vector3 &Vector3::operator*=(const real_t p_scalar) { // Multiplication operators required to workaround issues with LLVM using implicit conversion // to Vector3i instead for integers where it should not. -_FORCE_INLINE_ Vector3 operator*(const float p_scalar, const Vector3 &p_vec) { +_FORCE_INLINE_ Vector3 operator*(float p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector3 operator*(const double p_scalar, const Vector3 &p_vec) { +_FORCE_INLINE_ Vector3 operator*(double p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector3 operator*(const int32_t p_scalar, const Vector3 &p_vec) { +_FORCE_INLINE_ Vector3 operator*(int32_t p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector3 operator*(const int64_t p_scalar, const Vector3 &p_vec) { +_FORCE_INLINE_ Vector3 operator*(int64_t p_scalar, const Vector3 &p_vec) { return p_vec * p_scalar; } -Vector3 Vector3::operator*(const real_t p_scalar) const { +Vector3 Vector3::operator*(real_t p_scalar) const { return Vector3(x * p_scalar, y * p_scalar, z * p_scalar); } -Vector3 &Vector3::operator/=(const real_t p_scalar) { +Vector3 &Vector3::operator/=(real_t p_scalar) { x /= p_scalar; y /= p_scalar; z /= p_scalar; return *this; } -Vector3 Vector3::operator/(const real_t p_scalar) const { +Vector3 Vector3::operator/(real_t p_scalar) const { return Vector3(x / p_scalar, y / p_scalar, z / p_scalar); } @@ -512,9 +512,9 @@ void Vector3::zero() { // slide returns the component of the vector along the given plane, specified by its normal vector. Vector3 Vector3::slide(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized."); + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 " + p_normal.operator String() + " must be normalized."); #endif - return *this - p_normal * this->dot(p_normal); + return *this - p_normal * dot(p_normal); } Vector3 Vector3::bounce(const Vector3 &p_normal) const { @@ -523,9 +523,9 @@ Vector3 Vector3::bounce(const Vector3 &p_normal) const { Vector3 Vector3::reflect(const Vector3 &p_normal) const { #ifdef MATH_CHECKS - ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized."); + ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 " + p_normal.operator String() + " must be normalized."); #endif - return 2.0f * p_normal * this->dot(p_normal) - *this; + return 2.0f * p_normal * dot(p_normal) - *this; } #endif // VECTOR3_H diff --git a/core/math/vector3i.h b/core/math/vector3i.h index 53d3829a99..a9f298bff1 100644 --- a/core/math/vector3i.h +++ b/core/math/vector3i.h @@ -56,12 +56,12 @@ struct _NO_DISCARD_ Vector3i { int32_t coord[3] = { 0 }; }; - _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const { + _FORCE_INLINE_ const int32_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 3); return coord[p_axis]; } - _FORCE_INLINE_ int32_t &operator[](const int p_axis) { + _FORCE_INLINE_ int32_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 3); return coord[p_axis]; } @@ -87,6 +87,9 @@ struct _NO_DISCARD_ Vector3i { Vector3i clamp(const Vector3i &p_min, const Vector3i &p_max) const; Vector3i snapped(const Vector3i &p_step) const; + _FORCE_INLINE_ double distance_to(const Vector3i &p_to) const; + _FORCE_INLINE_ int64_t distance_squared_to(const Vector3i &p_to) const; + /* Operators */ _FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v); @@ -100,12 +103,12 @@ struct _NO_DISCARD_ Vector3i { _FORCE_INLINE_ Vector3i &operator%=(const Vector3i &p_v); _FORCE_INLINE_ Vector3i operator%(const Vector3i &p_v) const; - _FORCE_INLINE_ Vector3i &operator*=(const int32_t p_scalar); - _FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar) const; - _FORCE_INLINE_ Vector3i &operator/=(const int32_t p_scalar); - _FORCE_INLINE_ Vector3i operator/(const int32_t p_scalar) const; - _FORCE_INLINE_ Vector3i &operator%=(const int32_t p_scalar); - _FORCE_INLINE_ Vector3i operator%(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator*=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator*(int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator/=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator/(int32_t p_scalar) const; + _FORCE_INLINE_ Vector3i &operator%=(int32_t p_scalar); + _FORCE_INLINE_ Vector3i operator%(int32_t p_scalar) const; _FORCE_INLINE_ Vector3i operator-() const; @@ -120,7 +123,7 @@ struct _NO_DISCARD_ Vector3i { operator Vector3() const; _FORCE_INLINE_ Vector3i() {} - _FORCE_INLINE_ Vector3i(const int32_t p_x, const int32_t p_y, const int32_t p_z) { + _FORCE_INLINE_ Vector3i(int32_t p_x, int32_t p_y, int32_t p_z) { x = p_x; y = p_y; z = p_z; @@ -143,6 +146,14 @@ Vector3i Vector3i::sign() const { return Vector3i(SIGN(x), SIGN(y), SIGN(z)); } +double Vector3i::distance_to(const Vector3i &p_to) const { + return (p_to - *this).length(); +} + +int64_t Vector3i::distance_squared_to(const Vector3i &p_to) const { + return (p_to - *this).length_squared(); +} + /* Operators */ Vector3i &Vector3i::operator+=(const Vector3i &p_v) { @@ -200,54 +211,54 @@ Vector3i Vector3i::operator%(const Vector3i &p_v) const { return Vector3i(x % p_v.x, y % p_v.y, z % p_v.z); } -Vector3i &Vector3i::operator*=(const int32_t p_scalar) { +Vector3i &Vector3i::operator*=(int32_t p_scalar) { x *= p_scalar; y *= p_scalar; z *= p_scalar; return *this; } -Vector3i Vector3i::operator*(const int32_t p_scalar) const { +Vector3i Vector3i::operator*(int32_t p_scalar) const { return Vector3i(x * p_scalar, y * p_scalar, z * p_scalar); } // Multiplication operators required to workaround issues with LLVM using implicit conversion. -_FORCE_INLINE_ Vector3i operator*(const int32_t p_scalar, const Vector3i &p_vector) { +_FORCE_INLINE_ Vector3i operator*(int32_t p_scalar, const Vector3i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector3i operator*(const int64_t p_scalar, const Vector3i &p_vector) { +_FORCE_INLINE_ Vector3i operator*(int64_t p_scalar, const Vector3i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector3i operator*(const float p_scalar, const Vector3i &p_vector) { +_FORCE_INLINE_ Vector3i operator*(float p_scalar, const Vector3i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector3i operator*(const double p_scalar, const Vector3i &p_vector) { +_FORCE_INLINE_ Vector3i operator*(double p_scalar, const Vector3i &p_vector) { return p_vector * p_scalar; } -Vector3i &Vector3i::operator/=(const int32_t p_scalar) { +Vector3i &Vector3i::operator/=(int32_t p_scalar) { x /= p_scalar; y /= p_scalar; z /= p_scalar; return *this; } -Vector3i Vector3i::operator/(const int32_t p_scalar) const { +Vector3i Vector3i::operator/(int32_t p_scalar) const { return Vector3i(x / p_scalar, y / p_scalar, z / p_scalar); } -Vector3i &Vector3i::operator%=(const int32_t p_scalar) { +Vector3i &Vector3i::operator%=(int32_t p_scalar) { x %= p_scalar; y %= p_scalar; z %= p_scalar; return *this; } -Vector3i Vector3i::operator%(const int32_t p_scalar) const { +Vector3i Vector3i::operator%(int32_t p_scalar) const { return Vector3i(x % p_scalar, y % p_scalar, z % p_scalar); } diff --git a/core/math/vector4.cpp b/core/math/vector4.cpp index 5566b63714..e6f6dee42c 100644 --- a/core/math/vector4.cpp +++ b/core/math/vector4.cpp @@ -129,7 +129,7 @@ Vector4 Vector4::round() const { return Vector4(Math::round(x), Math::round(y), Math::round(z), Math::round(w)); } -Vector4 Vector4::lerp(const Vector4 &p_to, const real_t p_weight) const { +Vector4 Vector4::lerp(const Vector4 &p_to, real_t p_weight) const { Vector4 res = *this; res.x = Math::lerp(res.x, p_to.x, p_weight); res.y = Math::lerp(res.y, p_to.y, p_weight); @@ -138,7 +138,7 @@ Vector4 Vector4::lerp(const Vector4 &p_to, const real_t p_weight) const { return res; } -Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const { +Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, real_t p_weight) const { Vector4 res = *this; res.x = Math::cubic_interpolate(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight); res.y = Math::cubic_interpolate(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight); @@ -147,7 +147,7 @@ Vector4 Vector4::cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, c return res; } -Vector4 Vector4::cubic_interpolate_in_time(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const { +Vector4 Vector4::cubic_interpolate_in_time(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const { Vector4 res = *this; res.x = Math::cubic_interpolate_in_time(res.x, p_b.x, p_pre_a.x, p_post_b.x, p_weight, p_b_t, p_pre_a_t, p_post_b_t); res.y = Math::cubic_interpolate_in_time(res.y, p_b.y, p_pre_a.y, p_post_b.y, p_weight, p_b_t, p_pre_a_t, p_post_b_t); @@ -156,7 +156,7 @@ Vector4 Vector4::cubic_interpolate_in_time(const Vector4 &p_b, const Vector4 &p_ return res; } -Vector4 Vector4::posmod(const real_t p_mod) const { +Vector4 Vector4::posmod(real_t p_mod) const { return Vector4(Math::fposmod(x, p_mod), Math::fposmod(y, p_mod), Math::fposmod(z, p_mod), Math::fposmod(w, p_mod)); } diff --git a/core/math/vector4.h b/core/math/vector4.h index f16b040317..4dba3126cb 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -56,11 +56,11 @@ struct _NO_DISCARD_ Vector4 { real_t components[4] = { 0, 0, 0, 0 }; }; - _FORCE_INLINE_ real_t &operator[](const int p_axis) { + _FORCE_INLINE_ real_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 4); return components[p_axis]; } - _FORCE_INLINE_ const real_t &operator[](const int p_axis) const { + _FORCE_INLINE_ const real_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 4); return components[p_axis]; } @@ -94,11 +94,11 @@ struct _NO_DISCARD_ Vector4 { Vector4 floor() const; Vector4 ceil() const; Vector4 round() const; - Vector4 lerp(const Vector4 &p_to, const real_t p_weight) const; - Vector4 cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight) const; - Vector4 cubic_interpolate_in_time(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, const real_t p_weight, const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const; + Vector4 lerp(const Vector4 &p_to, real_t p_weight) const; + Vector4 cubic_interpolate(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, real_t p_weight) const; + Vector4 cubic_interpolate_in_time(const Vector4 &p_b, const Vector4 &p_pre_a, const Vector4 &p_post_b, real_t p_weight, real_t p_b_t, real_t p_pre_a_t, real_t p_post_b_t) const; - Vector4 posmod(const real_t p_mod) const; + Vector4 posmod(real_t p_mod) const; Vector4 posmodv(const Vector4 &p_modv) const; void snap(const Vector4 &p_step); Vector4 snapped(const Vector4 &p_step) const; @@ -111,15 +111,15 @@ struct _NO_DISCARD_ Vector4 { _FORCE_INLINE_ void operator-=(const Vector4 &p_vec4); _FORCE_INLINE_ void operator*=(const Vector4 &p_vec4); _FORCE_INLINE_ void operator/=(const Vector4 &p_vec4); - _FORCE_INLINE_ void operator*=(const real_t &s); - _FORCE_INLINE_ void operator/=(const real_t &s); + _FORCE_INLINE_ void operator*=(real_t p_s); + _FORCE_INLINE_ void operator/=(real_t p_s); _FORCE_INLINE_ Vector4 operator+(const Vector4 &p_vec4) const; _FORCE_INLINE_ Vector4 operator-(const Vector4 &p_vec4) const; _FORCE_INLINE_ Vector4 operator*(const Vector4 &p_vec4) const; _FORCE_INLINE_ Vector4 operator/(const Vector4 &p_vec4) const; _FORCE_INLINE_ Vector4 operator-() const; - _FORCE_INLINE_ Vector4 operator*(const real_t &s) const; - _FORCE_INLINE_ Vector4 operator/(const real_t &s) const; + _FORCE_INLINE_ Vector4 operator*(real_t p_s) const; + _FORCE_INLINE_ Vector4 operator/(real_t p_s) const; _FORCE_INLINE_ bool operator==(const Vector4 &p_vec4) const; _FORCE_INLINE_ bool operator!=(const Vector4 &p_vec4) const; @@ -189,15 +189,15 @@ void Vector4::operator/=(const Vector4 &p_vec4) { z /= p_vec4.z; w /= p_vec4.w; } -void Vector4::operator*=(const real_t &s) { - x *= s; - y *= s; - z *= s; - w *= s; +void Vector4::operator*=(real_t p_s) { + x *= p_s; + y *= p_s; + z *= p_s; + w *= p_s; } -void Vector4::operator/=(const real_t &s) { - *this *= 1.0f / s; +void Vector4::operator/=(real_t p_s) { + *this *= 1.0f / p_s; } Vector4 Vector4::operator+(const Vector4 &p_vec4) const { @@ -220,12 +220,12 @@ Vector4 Vector4::operator-() const { return Vector4(-x, -y, -z, -w); } -Vector4 Vector4::operator*(const real_t &s) const { - return Vector4(x * s, y * s, z * s, w * s); +Vector4 Vector4::operator*(real_t p_s) const { + return Vector4(x * p_s, y * p_s, z * p_s, w * p_s); } -Vector4 Vector4::operator/(const real_t &s) const { - return *this * (1.0f / s); +Vector4 Vector4::operator/(real_t p_s) const { + return *this * (1.0f / p_s); } bool Vector4::operator==(const Vector4 &p_vec4) const { @@ -288,19 +288,19 @@ bool Vector4::operator>=(const Vector4 &p_v) const { return x > p_v.x; } -_FORCE_INLINE_ Vector4 operator*(const float p_scalar, const Vector4 &p_vec) { +_FORCE_INLINE_ Vector4 operator*(float p_scalar, const Vector4 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector4 operator*(const double p_scalar, const Vector4 &p_vec) { +_FORCE_INLINE_ Vector4 operator*(double p_scalar, const Vector4 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector4 operator*(const int32_t p_scalar, const Vector4 &p_vec) { +_FORCE_INLINE_ Vector4 operator*(int32_t p_scalar, const Vector4 &p_vec) { return p_vec * p_scalar; } -_FORCE_INLINE_ Vector4 operator*(const int64_t p_scalar, const Vector4 &p_vec) { +_FORCE_INLINE_ Vector4 operator*(int64_t p_scalar, const Vector4 &p_vec) { return p_vec * p_scalar; } diff --git a/core/math/vector4i.h b/core/math/vector4i.h index b815aa8e76..5a96d98d18 100644 --- a/core/math/vector4i.h +++ b/core/math/vector4i.h @@ -58,12 +58,12 @@ struct _NO_DISCARD_ Vector4i { int32_t coord[4] = { 0 }; }; - _FORCE_INLINE_ const int32_t &operator[](const int p_axis) const { + _FORCE_INLINE_ const int32_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 4); return coord[p_axis]; } - _FORCE_INLINE_ int32_t &operator[](const int p_axis) { + _FORCE_INLINE_ int32_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 4); return coord[p_axis]; } @@ -84,6 +84,9 @@ struct _NO_DISCARD_ Vector4i { _FORCE_INLINE_ void zero(); + _FORCE_INLINE_ double distance_to(const Vector4i &p_to) const; + _FORCE_INLINE_ int64_t distance_squared_to(const Vector4i &p_to) const; + _FORCE_INLINE_ Vector4i abs() const; _FORCE_INLINE_ Vector4i sign() const; Vector4i clamp(const Vector4i &p_min, const Vector4i &p_max) const; @@ -102,12 +105,12 @@ struct _NO_DISCARD_ Vector4i { _FORCE_INLINE_ Vector4i &operator%=(const Vector4i &p_v); _FORCE_INLINE_ Vector4i operator%(const Vector4i &p_v) const; - _FORCE_INLINE_ Vector4i &operator*=(const int32_t p_scalar); - _FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar) const; - _FORCE_INLINE_ Vector4i &operator/=(const int32_t p_scalar); - _FORCE_INLINE_ Vector4i operator/(const int32_t p_scalar) const; - _FORCE_INLINE_ Vector4i &operator%=(const int32_t p_scalar); - _FORCE_INLINE_ Vector4i operator%(const int32_t p_scalar) const; + _FORCE_INLINE_ Vector4i &operator*=(int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator*(int32_t p_scalar) const; + _FORCE_INLINE_ Vector4i &operator/=(int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator/(int32_t p_scalar) const; + _FORCE_INLINE_ Vector4i &operator%=(int32_t p_scalar); + _FORCE_INLINE_ Vector4i operator%(int32_t p_scalar) const; _FORCE_INLINE_ Vector4i operator-() const; @@ -123,7 +126,7 @@ struct _NO_DISCARD_ Vector4i { _FORCE_INLINE_ Vector4i() {} Vector4i(const Vector4 &p_vec4); - _FORCE_INLINE_ Vector4i(const int32_t p_x, const int32_t p_y, const int32_t p_z, const int32_t p_w) { + _FORCE_INLINE_ Vector4i(int32_t p_x, int32_t p_y, int32_t p_z, int32_t p_w) { x = p_x; y = p_y; z = p_z; @@ -139,6 +142,14 @@ double Vector4i::length() const { return Math::sqrt((double)length_squared()); } +double Vector4i::distance_to(const Vector4i &p_to) const { + return (p_to - *this).length(); +} + +int64_t Vector4i::distance_squared_to(const Vector4i &p_to) const { + return (p_to - *this).length_squared(); +} + Vector4i Vector4i::abs() const { return Vector4i(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w)); } @@ -209,7 +220,7 @@ Vector4i Vector4i::operator%(const Vector4i &p_v) const { return Vector4i(x % p_v.x, y % p_v.y, z % p_v.z, w % p_v.w); } -Vector4i &Vector4i::operator*=(const int32_t p_scalar) { +Vector4i &Vector4i::operator*=(int32_t p_scalar) { x *= p_scalar; y *= p_scalar; z *= p_scalar; @@ -217,29 +228,29 @@ Vector4i &Vector4i::operator*=(const int32_t p_scalar) { return *this; } -Vector4i Vector4i::operator*(const int32_t p_scalar) const { +Vector4i Vector4i::operator*(int32_t p_scalar) const { return Vector4i(x * p_scalar, y * p_scalar, z * p_scalar, w * p_scalar); } // Multiplication operators required to workaround issues with LLVM using implicit conversion. -_FORCE_INLINE_ Vector4i operator*(const int32_t p_scalar, const Vector4i &p_vector) { +_FORCE_INLINE_ Vector4i operator*(int32_t p_scalar, const Vector4i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector4i operator*(const int64_t p_scalar, const Vector4i &p_vector) { +_FORCE_INLINE_ Vector4i operator*(int64_t p_scalar, const Vector4i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector4i operator*(const float p_scalar, const Vector4i &p_vector) { +_FORCE_INLINE_ Vector4i operator*(float p_scalar, const Vector4i &p_vector) { return p_vector * p_scalar; } -_FORCE_INLINE_ Vector4i operator*(const double p_scalar, const Vector4i &p_vector) { +_FORCE_INLINE_ Vector4i operator*(double p_scalar, const Vector4i &p_vector) { return p_vector * p_scalar; } -Vector4i &Vector4i::operator/=(const int32_t p_scalar) { +Vector4i &Vector4i::operator/=(int32_t p_scalar) { x /= p_scalar; y /= p_scalar; z /= p_scalar; @@ -247,11 +258,11 @@ Vector4i &Vector4i::operator/=(const int32_t p_scalar) { return *this; } -Vector4i Vector4i::operator/(const int32_t p_scalar) const { +Vector4i Vector4i::operator/(int32_t p_scalar) const { return Vector4i(x / p_scalar, y / p_scalar, z / p_scalar, w / p_scalar); } -Vector4i &Vector4i::operator%=(const int32_t p_scalar) { +Vector4i &Vector4i::operator%=(int32_t p_scalar) { x %= p_scalar; y %= p_scalar; z %= p_scalar; @@ -259,7 +270,7 @@ Vector4i &Vector4i::operator%=(const int32_t p_scalar) { return *this; } -Vector4i Vector4i::operator%(const int32_t p_scalar) const { +Vector4i Vector4i::operator%(int32_t p_scalar) const { return Vector4i(x % p_scalar, y % p_scalar, z % p_scalar, w % p_scalar); } diff --git a/core/object/SCsub b/core/object/SCsub index dc116aeb19..7c00bb719e 100644 --- a/core/object/SCsub +++ b/core/object/SCsub @@ -3,9 +3,8 @@ Import("env") import make_virtuals -from platform_methods import run_in_subprocess -env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", run_in_subprocess(make_virtuals.run)) +env.CommandNoCache(["gdvirtual.gen.inc"], "make_virtuals.py", env.Run(make_virtuals.run)) env_object = env.Clone() diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index db78b982e4..1b29e1778a 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -77,45 +77,42 @@ public: virtual uint32_t hash() const; }; -template <class T, class... P> +template <typename T, typename... P> class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif void (T::*method)(P...); } data; public: virtual ObjectID get_object() const { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif 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 { -#ifdef DEBUG_ENABLED 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); } CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } }; -template <class T, class... P> +template <typename T, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, @@ -131,46 +128,43 @@ Callable create_custom_callable_function_pointer(T *p_instance, // VERSION WITH RETURN -template <class T, class R, class... P> +template <typename T, typename R, typename... P> class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif R(T::*method) (P...); } data; public: virtual ObjectID get_object() const { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif 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 { -#ifdef DEBUG_ENABLED 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); } CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } }; -template <class T, class R, class... P> +template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, @@ -186,46 +180,43 @@ Callable create_custom_callable_function_pointer(T *p_instance, // CONST VERSION WITH RETURN -template <class T, class R, class... P> +template <typename T, typename R, typename... P> class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { struct Data { T *instance; -#ifdef DEBUG_ENABLED uint64_t object_id; -#endif R(T::*method) (P...) const; } data; public: virtual ObjectID get_object() const override { -#ifdef DEBUG_ENABLED if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { return ObjectID(); } -#endif 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 { -#ifdef DEBUG_ENABLED 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); } CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; -#ifdef DEBUG_ENABLED data.object_id = p_instance->get_instance_id(); -#endif data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } }; -template <class T, class R, class... P> +template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, @@ -247,7 +238,7 @@ Callable create_custom_callable_function_pointer(T *p_instance, // STATIC VERSIONS -template <class... P> +template <typename... P> class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { struct Data { void (*method)(P...); @@ -262,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(); @@ -274,7 +270,7 @@ public: } }; -template <class T, class... P> +template <typename T, typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, @@ -288,7 +284,7 @@ Callable create_custom_callable_static_function_pointer( return Callable(ccmp); } -template <class R, class... P> +template <typename R, typename... P> class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { struct Data { R(*method) @@ -304,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); } @@ -315,7 +316,7 @@ public: } }; -template <class R, class... P> +template <typename R, typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index bf1bd0de93..7ef1ce74ed 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -31,6 +31,7 @@ #include "class_db.h" #include "core/config/engine.h" +#include "core/core_string_names.h" #include "core/io/resource_loader.h" #include "core/object/script_language.h" #include "core/os/mutex.h" @@ -69,6 +70,151 @@ HashMap<StringName, ClassDB::ClassInfo> ClassDB::classes; HashMap<StringName, StringName> ClassDB::resource_base_extensions; HashMap<StringName, StringName> ClassDB::compat_classes; +#ifdef TOOLS_ENABLED +HashMap<StringName, ObjectGDExtension> ClassDB::placeholder_extensions; + +class PlaceholderExtensionInstance { + StringName class_name; + HashMap<StringName, Variant> properties; + +public: + PlaceholderExtensionInstance(const StringName &p_class_name) { + class_name = p_class_name; + } + + ~PlaceholderExtensionInstance() {} + + void set(const StringName &p_name, const Variant &p_value) { + bool is_default_valid = false; + Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid); + + // If there's a default value, then we know it's a valid property. + if (is_default_valid) { + properties[p_name] = p_value; + } + } + + Variant get(const StringName &p_name) { + const Variant *value = properties.getptr(p_name); + Variant ret; + + if (value) { + ret = *value; + } else { + bool is_default_valid = false; + Variant default_value = ClassDB::class_get_default_property_value(class_name, p_name, &is_default_valid); + if (is_default_valid) { + ret = default_value; + } + } + + return ret; + } + + static GDExtensionBool placeholder_instance_set(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionConstVariantPtr p_value) { + PlaceholderExtensionInstance *self = (PlaceholderExtensionInstance *)p_instance; + const StringName &name = *(StringName *)p_name; + const Variant &value = *(const Variant *)p_value; + + self->set(name, value); + + // We have to return true so Godot doesn't try to call the real setter function. + return true; + } + + static GDExtensionBool placeholder_instance_get(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) { + PlaceholderExtensionInstance *self = (PlaceholderExtensionInstance *)p_instance; + const StringName &name = *(StringName *)p_name; + Variant *value = (Variant *)r_ret; + + *value = self->get(name); + + // We have to return true so Godot doesn't try to call the real getter function. + return true; + } + + static const GDExtensionPropertyInfo *placeholder_instance_get_property_list(GDExtensionClassInstancePtr p_instance, uint32_t *r_count) { + *r_count = 0; + return nullptr; + } + + static void placeholder_instance_free_property_list(GDExtensionClassInstancePtr p_instance, const GDExtensionPropertyInfo *p_list) { + } + + static GDExtensionBool placeholder_instance_property_can_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name) { + return false; + } + + static GDExtensionBool placeholder_instance_property_get_revert(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, GDExtensionVariantPtr r_ret) { + return false; + } + + static GDExtensionBool placeholder_instance_validate_property(GDExtensionClassInstancePtr p_instance, GDExtensionPropertyInfo *p_property) { + return false; + } + + static void placeholder_instance_notification(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) { + } + + static void placeholder_instance_to_string(GDExtensionClassInstancePtr p_instance, GDExtensionBool *r_is_valid, GDExtensionStringPtr p_out) { + *r_is_valid = true; + } + + static void placeholder_instance_reference(GDExtensionClassInstancePtr p_instance) { + } + + static void placeholder_instance_unreference(GDExtensionClassInstancePtr p_instance) { + } + + static uint64_t placeholder_instance_get_rid(GDExtensionClassInstancePtr p_instance) { + return 0; + } + + static GDExtensionObjectPtr placeholder_class_create_instance(void *p_class_userdata) { + ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata; + + // Find the closest native parent. + ClassDB::ClassInfo *native_parent = ti->inherits_ptr; + while (native_parent->gdextension) { + native_parent = native_parent->inherits_ptr; + } + ERR_FAIL_NULL_V(native_parent->creation_func, nullptr); + + // Construct a placeholder. + Object *obj = native_parent->creation_func(); + + // ClassDB::set_object_extension_instance() won't be called for placeholders. + // We need need to make sure that all the things it would have done (even if + // done in a different way to support placeholders) will also be done here. + + obj->_extension = ClassDB::get_placeholder_extension(ti->name); + obj->_extension_instance = memnew(PlaceholderExtensionInstance(ti->name)); + +#ifdef TOOLS_ENABLED + if (obj->_extension->track_instance) { + obj->_extension->track_instance(obj->_extension->tracking_userdata, obj); + } +#endif + + return obj; + } + + static GDExtensionObjectPtr placeholder_class_recreate_instance(void *p_class_userdata, GDExtensionObjectPtr p_object) { + ClassDB::ClassInfo *ti = (ClassDB::ClassInfo *)p_class_userdata; + return memnew(PlaceholderExtensionInstance(ti->name)); + } + + static void placeholder_class_free_instance(void *p_class_userdata, GDExtensionClassInstancePtr p_instance) { + PlaceholderExtensionInstance *instance = (PlaceholderExtensionInstance *)p_instance; + memdelete(instance); + } + + static GDExtensionClassCallVirtual placeholder_class_get_virtual(void *p_class_userdata, GDExtensionConstStringNamePtr p_name) { + return nullptr; + } +}; +#endif + bool ClassDB::_is_parent_class(const StringName &p_class, const StringName &p_inherits) { if (!classes.has(p_class)) { return false; @@ -345,7 +491,7 @@ StringName ClassDB::get_compatibility_class(const StringName &p_class) { return StringName(); } -Object *ClassDB::instantiate(const StringName &p_class) { +Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require_real_class) { ClassInfo *ti; { OBJTYPE_RLOCK; @@ -366,18 +512,119 @@ Object *ClassDB::instantiate(const StringName &p_class) { } #endif if (ti->gdextension && ti->gdextension->create_instance) { - Object *obj = (Object *)ti->gdextension->create_instance(ti->gdextension->class_userdata); + ObjectGDExtension *extension = ti->gdextension; #ifdef TOOLS_ENABLED - if (ti->gdextension->track_instance) { - ti->gdextension->track_instance(ti->gdextension->tracking_userdata, obj); + if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { + extension = get_placeholder_extension(ti->name); } #endif - return obj; + return (Object *)extension->create_instance(extension->class_userdata); } else { +#ifdef TOOLS_ENABLED + if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) { + if (!ti->inherits_ptr || !ti->inherits_ptr->creation_func) { + ERR_PRINT_ONCE(vformat("Cannot make a placeholder instance of runtime class %s because its parent cannot be constructed.", ti->name)); + } else { + ObjectGDExtension *extension = get_placeholder_extension(ti->name); + return (Object *)extension->create_instance(extension->class_userdata); + } + } +#endif + return ti->creation_func(); } } +Object *ClassDB::instantiate(const StringName &p_class) { + return _instantiate_internal(p_class); +} + +Object *ClassDB::instantiate_no_placeholders(const StringName &p_class) { + return _instantiate_internal(p_class, true); +} + +#ifdef TOOLS_ENABLED +ObjectGDExtension *ClassDB::get_placeholder_extension(const StringName &p_class) { + ObjectGDExtension *placeholder_extension = placeholder_extensions.getptr(p_class); + if (placeholder_extension) { + return placeholder_extension; + } + + ClassInfo *ti; + { + OBJTYPE_RLOCK; + ti = classes.getptr(p_class); + if (!ti || ti->disabled || !ti->creation_func || (ti->gdextension && !ti->gdextension->create_instance)) { + if (compat_classes.has(p_class)) { + ti = classes.getptr(compat_classes[p_class]); + } + } + ERR_FAIL_NULL_V_MSG(ti, nullptr, "Cannot get class '" + String(p_class) + "'."); + ERR_FAIL_COND_V_MSG(ti->disabled, nullptr, "Class '" + String(p_class) + "' is disabled."); + } + + // Make a "fake" extension to act as a placeholder. + placeholder_extensions[p_class] = ObjectGDExtension(); + placeholder_extension = placeholder_extensions.getptr(p_class); + + placeholder_extension->is_runtime = true; + placeholder_extension->is_placeholder = true; + + if (ti->gdextension) { + placeholder_extension->library = ti->gdextension->library; + placeholder_extension->parent = ti->gdextension->parent; + placeholder_extension->children = ti->gdextension->children; + placeholder_extension->parent_class_name = ti->gdextension->parent_class_name; + placeholder_extension->class_name = ti->gdextension->class_name; + placeholder_extension->editor_class = ti->gdextension->editor_class; + placeholder_extension->reloadable = ti->gdextension->reloadable; + placeholder_extension->is_virtual = ti->gdextension->is_virtual; + placeholder_extension->is_abstract = ti->gdextension->is_abstract; + placeholder_extension->is_exposed = ti->gdextension->is_exposed; + + placeholder_extension->tracking_userdata = ti->gdextension->tracking_userdata; + placeholder_extension->track_instance = ti->gdextension->track_instance; + placeholder_extension->untrack_instance = ti->gdextension->untrack_instance; + } else { + placeholder_extension->library = nullptr; + placeholder_extension->parent = nullptr; + placeholder_extension->parent_class_name = ti->inherits; + placeholder_extension->class_name = ti->name; + placeholder_extension->editor_class = ti->api == API_EDITOR; + placeholder_extension->reloadable = false; + placeholder_extension->is_virtual = ti->is_virtual; + placeholder_extension->is_abstract = false; + placeholder_extension->is_exposed = ti->exposed; + } + + placeholder_extension->set = &PlaceholderExtensionInstance::placeholder_instance_set; + placeholder_extension->get = &PlaceholderExtensionInstance::placeholder_instance_get; + placeholder_extension->get_property_list = &PlaceholderExtensionInstance::placeholder_instance_get_property_list; + placeholder_extension->free_property_list = &PlaceholderExtensionInstance::placeholder_instance_free_property_list; + placeholder_extension->property_can_revert = &PlaceholderExtensionInstance::placeholder_instance_property_can_revert; + placeholder_extension->property_get_revert = &PlaceholderExtensionInstance::placeholder_instance_property_get_revert; + placeholder_extension->validate_property = &PlaceholderExtensionInstance::placeholder_instance_validate_property; +#ifndef DISABLE_DEPRECATED + placeholder_extension->notification = nullptr; +#endif // DISABLE_DEPRECATED + placeholder_extension->notification2 = &PlaceholderExtensionInstance::placeholder_instance_notification; + placeholder_extension->to_string = &PlaceholderExtensionInstance::placeholder_instance_to_string; + placeholder_extension->reference = &PlaceholderExtensionInstance::placeholder_instance_reference; + placeholder_extension->unreference = &PlaceholderExtensionInstance::placeholder_instance_unreference; + placeholder_extension->get_rid = &PlaceholderExtensionInstance::placeholder_instance_get_rid; + + placeholder_extension->class_userdata = ti; + placeholder_extension->create_instance = &PlaceholderExtensionInstance::placeholder_class_create_instance; + placeholder_extension->free_instance = &PlaceholderExtensionInstance::placeholder_class_free_instance; + placeholder_extension->get_virtual = &PlaceholderExtensionInstance::placeholder_class_get_virtual; + placeholder_extension->get_virtual_call_data = nullptr; + placeholder_extension->call_virtual_with_data = nullptr; + placeholder_extension->recreate_instance = &PlaceholderExtensionInstance::placeholder_class_recreate_instance; + + return placeholder_extension; +} +#endif + void ClassDB::set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance) { ERR_FAIL_NULL(p_object); ClassInfo *ti; @@ -396,6 +643,12 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName & p_object->_extension = ti->gdextension; p_object->_extension_instance = p_instance; + +#ifdef TOOLS_ENABLED + if (p_object->_extension->track_instance) { + p_object->_extension->track_instance(p_object->_extension->tracking_userdata, p_object); + } +#endif } bool ClassDB::can_instantiate(const StringName &p_class) { @@ -1299,6 +1552,12 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia check = check->inherits_ptr; } + // The "free()" method is special, so we assume it exists and return a Callable. + if (p_property == CoreStringNames::get_singleton()->_free) { + r_value = Callable(p_object, p_property); + return true; + } + return false; } @@ -1418,6 +1677,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); } @@ -1608,6 +1892,28 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p #endif } +void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) { + ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); + +#ifdef DEBUG_METHODS_ENABLED + PackedStringArray arg_names; + + MethodInfo mi; + mi.name = *reinterpret_cast<StringName *>(p_method_info->name); + mi.return_val = PropertyInfo(p_method_info->return_value); + mi.return_val_metadata = p_method_info->return_value_metadata; + mi.flags = p_method_info->method_flags; + for (int i = 0; i < (int)p_method_info->argument_count; i++) { + PropertyInfo arg(p_method_info->arguments[i]); + mi.arguments.push_back(arg); + mi.arguments_metadata.push_back(p_method_info->arguments_metadata[i]); + arg_names.push_back(arg.name); + } + + add_virtual_method(p_class, mi, true, arg_names); +#endif +} + void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { OBJTYPE_WLOCK; @@ -1687,7 +1993,7 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con c = Engine::get_singleton()->get_singleton_object(p_class); cleanup_c = false; } else if (ClassDB::can_instantiate(p_class) && !ClassDB::is_virtual(p_class)) { // Keep this condition in sync with doc_tools.cpp get_documentation_default_value. - c = ClassDB::instantiate(p_class); + c = ClassDB::instantiate_no_placeholders(p_class); cleanup_c = true; } @@ -1784,6 +2090,9 @@ void ClassDB::register_extension_class(ObjectGDExtension *p_extension) { } } c.reloadable = p_extension->reloadable; +#ifdef TOOLS_ENABLED + c.is_runtime = p_extension->is_runtime; +#endif classes[p_extension->class_name] = c; } @@ -1797,6 +2106,11 @@ void ClassDB::unregister_extension_class(const StringName &p_class, bool p_free_ } } classes.erase(p_class); + default_values_cached.erase(p_class); + default_values.erase(p_class); +#ifdef TOOLS_ENABLED + placeholder_extensions.erase(p_class); +#endif } HashMap<StringName, ClassDB::NativeStruct> ClassDB::native_structs; diff --git a/core/object/class_db.h b/core/object/class_db.h index 7a4ee1afa4..37a864c109 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -133,13 +133,14 @@ public: bool exposed = false; bool reloadable = false; bool is_virtual = false; + bool is_runtime = false; Object *(*creation_func)() = nullptr; ClassInfo() {} ~ClassInfo() {} }; - template <class T> + template <typename T> static Object *creator() { return memnew(T); } @@ -149,6 +150,10 @@ public: static HashMap<StringName, StringName> resource_base_extensions; static HashMap<StringName, StringName> compat_classes; +#ifdef TOOLS_ENABLED + static HashMap<StringName, ObjectGDExtension> placeholder_extensions; +#endif + #ifdef DEBUG_METHODS_ENABLED static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, bool p_compatibility, const MethodDefinition &method_name, const Variant **p_defs, int p_defcount); #else @@ -178,17 +183,19 @@ private: static MethodBind *_bind_vararg_method(MethodBind *p_bind, const StringName &p_name, const Vector<Variant> &p_default_args, bool p_compatibility); static void _bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility); + static Object *_instantiate_internal(const StringName &p_class, bool p_require_real_class = false); + public: // DO NOT USE THIS!!!!!! NEEDS TO BE PUBLIC BUT DO NOT USE NO MATTER WHAT!!! - template <class T> + template <typename T> static void _add_class() { _add_class2(T::get_class_static(), T::get_parent_class_static()); } - template <class T> + template <typename T> static void register_class(bool p_virtual = false) { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -200,10 +207,10 @@ public: T::register_custom_data_to_otdb(); } - template <class T> + template <typename T> static void register_abstract_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -213,10 +220,10 @@ public: //nothing } - template <class T> + template <typename T> static void register_internal_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -228,18 +235,35 @@ public: T::register_custom_data_to_otdb(); } + template <typename T> + static void register_runtime_class() { + GLOBAL_LOCK_FUNCTION; + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); + T::initialize_class(); + ClassInfo *t = classes.getptr(T::get_class_static()); + ERR_FAIL_NULL(t); + ERR_FAIL_COND_MSG(t->inherits_ptr && !t->inherits_ptr->creation_func, vformat("Cannot register runtime class '%s' that descends from an abstract parent class.", T::get_class_static())); + t->creation_func = &creator<T>; + t->exposed = true; + t->is_virtual = false; + t->is_runtime = true; + t->class_ptr = T::get_class_ptr_static(); + t->api = current_api; + T::register_custom_data_to_otdb(); + } + static void register_extension_class(ObjectGDExtension *p_extension); static void unregister_extension_class(const StringName &p_class, bool p_free_method_binds = true); - template <class T> + template <typename T> static Object *_create_ptr_func() { return T::create(); } - template <class T> + template <typename T> static void register_custom_instance_class() { GLOBAL_LOCK_FUNCTION; - static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS."); + static_assert(std::is_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS."); T::initialize_class(); ClassInfo *t = classes.getptr(T::get_class_static()); ERR_FAIL_NULL(t); @@ -253,6 +277,7 @@ public: static void get_class_list(List<StringName> *p_classes); #ifdef TOOLS_ENABLED static void get_extensions_class_list(List<StringName> *p_classes); + static ObjectGDExtension *get_placeholder_extension(const StringName &p_class); #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); @@ -264,6 +289,7 @@ public: static bool can_instantiate(const StringName &p_class); static bool is_virtual(const StringName &p_class); static Object *instantiate(const StringName &p_class); + static Object *instantiate_no_placeholders(const StringName &p_class); static void set_object_extension_instance(Object *p_object, const StringName &p_class, GDExtensionClassInstancePtr p_instance); static APIType get_api_type(const StringName &p_class); @@ -288,7 +314,7 @@ public: using return_type = R; }; - template <class N, class M, typename... VarArgs> + template <typename N, typename M, typename... VarArgs> static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; @@ -296,13 +322,13 @@ public: argptrs[i] = &args[i]; } MethodBind *bind = create_method_bind(p_method); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - template <class N, class M, typename... VarArgs> + template <typename N, typename M, typename... VarArgs> static MethodBind *bind_static_method(const StringName &p_class, N p_method_name, M p_method, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; @@ -311,13 +337,13 @@ public: } MethodBind *bind = create_static_method_bind(p_method); bind->set_instance_class(p_class); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - template <class N, class M, typename... VarArgs> + template <typename N, typename M, typename... VarArgs> static MethodBind *bind_compatibility_method(N p_method_name, M p_method, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; @@ -325,13 +351,13 @@ public: argptrs[i] = &args[i]; } MethodBind *bind = create_method_bind(p_method); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - template <class N, class M, typename... VarArgs> + template <typename N, typename M, typename... VarArgs> static MethodBind *bind_compatibility_static_method(const StringName &p_class, N p_method_name, M p_method, VarArgs... p_args) { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; @@ -340,33 +366,33 @@ public: } MethodBind *bind = create_static_method_bind(p_method); bind->set_instance_class(p_class); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } - template <class M> + template <typename M> static MethodBind *bind_vararg_method(uint32_t p_flags, const StringName &p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) { GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); ERR_FAIL_NULL_V(bind, nullptr); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return _bind_vararg_method(bind, p_name, p_default_args, false); } - template <class M> + template <typename M> static MethodBind *bind_compatibility_vararg_method(uint32_t p_flags, const StringName &p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const Vector<Variant> &p_default_args = Vector<Variant>(), bool p_return_nil_is_variant = true) { GLOBAL_LOCK_FUNCTION; MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); ERR_FAIL_NULL_V(bind, nullptr); - if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) { + if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) { bind->set_return_type_is_raw_object_ptr(true); } return _bind_vararg_method(bind, p_name, p_default_args, true); @@ -404,12 +430,14 @@ 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); static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false); static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false); + static void add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info); static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false); static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false); @@ -470,13 +498,13 @@ _FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err) { arr.push_back(p_err); } -template <class... P> +template <typename... P> _FORCE_INLINE_ void errarray_add_str(Vector<Error> &arr, const Error &p_err, P... p_args) { arr.push_back(p_err); errarray_add_str(arr, p_args...); } -template <class... P> +template <typename... P> _FORCE_INLINE_ Vector<Error> errarray(P... p_args) { Vector<Error> arr; errarray_add_str(arr, p_args...); @@ -509,6 +537,11 @@ _FORCE_INLINE_ Vector<Error> errarray(P... p_args) { ::ClassDB::register_internal_class<m_class>(); \ } +#define GDREGISTER_RUNTIME_CLASS(m_class) \ + if (m_class::_class_is_enabled) { \ + ::ClassDB::register_runtime_class<m_class>(); \ + } + #define GDREGISTER_NATIVE_STRUCT(m_class, m_code) ClassDB::register_native_struct(#m_class, m_code, sizeof(m_class)) #include "core/disabled_classes.gen.h" diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 79a8df6c8a..7a85753072 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -1,78 +1,74 @@ -proto = """ -#define GDVIRTUAL$VER($RET m_name $ARG) \\ -StringName _gdvirtual_##m_name##_sn = #m_name;\\ -mutable bool _gdvirtual_##m_name##_initialized = false;\\ -mutable void* _gdvirtual_##m_name = nullptr;\\ -template<bool required>\\ -_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ - ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - if (_script_instance) {\\ - Callable::CallError ce; \\ - $CALLSIARGS\\ - $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ - if (ce.error == Callable::CallError::CALL_OK) {\\ - $CALLSIRET\\ +proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\ + StringName _gdvirtual_##m_name##_sn = #m_name;\\ + mutable bool _gdvirtual_##m_name##_initialized = false;\\ + mutable void *_gdvirtual_##m_name = nullptr;\\ + template <bool required>\\ + _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\ + ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ + if (_script_instance) {\\ + Callable::CallError ce;\\ + $CALLSIARGS\\ + $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\ + if (ce.error == Callable::CallError::CALL_OK) {\\ + $CALLSIRET\\ + return true;\\ + }\\ + }\\ + if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ + _gdvirtual_##m_name = nullptr;\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + } else if (_get_extension()->get_virtual) {\\ + _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + }\\ + GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + if (_gdvirtual_##m_name) {\\ + $CALLPTRARGS\\ + $CALLPTRRETDEF\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + $CALLPTRRET\\ + } else {\\ + ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\ + $CALLPTRRET\\ + }\\ return true;\\ - } \\ + }\\ + if (required) {\\ + ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ + $RVOID\\ + }\\ + return false;\\ }\\ - if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ - _gdvirtual_##m_name = nullptr;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\ - _gdvirtual_##m_name##_initialized = true;\\ - }\\ - if (_gdvirtual_##m_name) {\\ - $CALLPTRARGS\\ - $CALLPTRRETDEF\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\ - $CALLPTRRET\\ - } else {\\ - ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\ - $CALLPTRRET\\ - }\\ - return true;\\ - }\\ - \\ - if (required) {\\ - ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\ - $RVOID\\ - }\\ -\\ - return false;\\ -}\\ -_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ - ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - 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;\\ - if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ - _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - } else if (_get_extension()->get_virtual) {\\ - _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ - }\\ - GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\ - _gdvirtual_##m_name##_initialized = true;\\ - }\\ - if (_gdvirtual_##m_name) {\\ - return true;\\ + _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\ + ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\ + 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;\\ + if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\ + _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + } else if (_get_extension()->get_virtual) {\\ + _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\ + }\\ + GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\ + _gdvirtual_##m_name##_initialized = true;\\ + }\\ + if (_gdvirtual_##m_name) {\\ + return true;\\ + }\\ + return false;\\ }\\ - return false;\\ -}\\ -\\ -_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\ - MethodInfo method_info;\\ - method_info.name = #m_name;\\ - method_info.flags = METHOD_FLAG_VIRTUAL;\\ - $FILL_METHOD_INFO\\ - return method_info;\\ -} + _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\ + MethodInfo method_info;\\ + method_info.name = #m_name;\\ + method_info.flags = $METHOD_FLAGS;\\ + $FILL_METHOD_INFO\\ + return method_info;\\ + } """ @@ -83,22 +79,23 @@ def generate_version(argcount, const=False, returns=False): method_info = "" if returns: sproto += "R" - s = s.replace("$RET", "m_ret, ") + s = s.replace("$RET", "m_ret,") s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;") - method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" - method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n" + method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n" + method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;" else: - s = s.replace("$RET", "") - s = s.replace("$RVOID", "") - s = s.replace("$CALLPTRRETDEF", "") + s = s.replace("$RET ", "") + s = s.replace("\t\t\t$RVOID\\\n", "") + s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "") if const: sproto += "C" s = s.replace("$CONST", "const") - method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n" + s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST") else: - s = s.replace("$CONST", "") + s = s.replace("$CONST ", "") + s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL") s = s.replace("$VER", sproto) argtext = "" @@ -108,9 +105,9 @@ def generate_version(argcount, const=False, returns=False): callptrargsptr = "" if argcount > 0: argtext += ", " - callsiargs = "Variant vargs[" + str(argcount) + "]={" - callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={" - callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={" + callsiargs = f"Variant vargs[{argcount}] = {{ " + callsiargptrs = f"\t\t\tconst Variant *vargptrs[{argcount}] = {{ " + callptrargsptr = f"\t\t\tGDExtensionConstTypePtr argptrs[{argcount}] = {{ " callptrargs = "" for i in range(argcount): if i > 0: @@ -118,52 +115,55 @@ def generate_version(argcount, const=False, returns=False): callargtext += ", " callsiargs += ", " callsiargptrs += ", " - callptrargs += "\t\t" + callptrargs += "\t\t\t" callptrargsptr += ", " - argtext += "m_type" + str(i + 1) - callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1) - callsiargs += "Variant(arg" + str(i + 1) + ")" - callsiargptrs += "&vargs[" + str(i) + "]" + argtext += f"m_type{i + 1}" + callargtext += f"m_type{i + 1} arg{i + 1}" + callsiargs += f"Variant(arg{i + 1})" + callsiargptrs += f"&vargs[{i}]" callptrargs += ( - "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n" - ) - callptrargsptr += "&argval" + str(i + 1) - method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n" - method_info += ( - "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n" + f"PtrToArg<m_type{i + 1}>::EncodeT argval{i + 1} = (PtrToArg<m_type{i + 1}>::EncodeT)arg{i + 1};\\\n" ) + callptrargsptr += f"&argval{i + 1}" + if method_info: + method_info += "\\\n\t\t" + method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n" + method_info += f"\t\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type{i + 1}>::METADATA);" if argcount: - callsiargs += "};\\\n" - callsiargptrs += "};\\\n" + callsiargs += " };\\\n" + callsiargptrs += " };" s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs) - s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount)) - callptrargsptr += "};\\\n" + s = s.replace("$CALLSIARGPASS", f"(const Variant **)vargptrs, {argcount}") + callptrargsptr += " };" s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr) - s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)") + s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr *>(argptrs)") else: - s = s.replace("$CALLSIARGS", "") + s = s.replace("\t\t\t$CALLSIARGS\\\n", "") s = s.replace("$CALLSIARGPASS", "nullptr, 0") - s = s.replace("$CALLPTRARGS", "") + s = s.replace("\t\t\t$CALLPTRARGS\\\n", "") s = s.replace("$CALLPTRARGPASS", "nullptr") if returns: if argcount > 0: - callargtext += "," - callargtext += " m_ret& r_ret" + callargtext += ", " + callargtext += "m_ret &r_ret" s = s.replace("$CALLSIBEGIN", "Variant ret = ") s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);") s = s.replace("$CALLPTRRETPASS", "&ret") s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;") else: s = s.replace("$CALLSIBEGIN", "") - s = s.replace("$CALLSIRET", "") + s = s.replace("\t\t\t\t$CALLSIRET\\\n", "") s = s.replace("$CALLPTRRETPASS", "nullptr") - s = s.replace("$CALLPTRRET", "") + s = s.replace("\t\t\t\t$CALLPTRRET\\\n", "") - s = s.replace("$ARG", argtext) + s = s.replace(" $ARG", argtext) s = s.replace("$CALLARGS", callargtext) - s = s.replace("$FILL_METHOD_INFO", method_info) + if method_info: + s = s.replace("$FILL_METHOD_INFO", method_info) + else: + s = s.replace("\t\t$FILL_METHOD_INFO\\\n", method_info) return s @@ -171,21 +171,21 @@ def generate_version(argcount, const=False, returns=False): def run(target, source, env): max_versions = 12 - txt = """ + txt = """/* THIS FILE IS GENERATED DO NOT EDIT */ #ifndef GDVIRTUAL_GEN_H #define GDVIRTUAL_GEN_H #include "core/object/script_instance.h" #ifdef TOOLS_ENABLED -#define GDVIRTUAL_TRACK(m_virtual, m_initialized) \\ - if (_get_extension()->reloadable) {\\ - VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\ - tracker->method = (void **)&m_virtual;\\ - tracker->initialized = &m_initialized;\\ - tracker->next = virtual_method_list;\\ - virtual_method_list = tracker;\\ - } +#define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\ + if (_get_extension()->reloadable) {\\ + VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\ + tracker->method = (void **)&m_virtual;\\ + tracker->initialized = &m_initialized;\\ + tracker->next = virtual_method_list;\\ + virtual_method_list = tracker;\\ + } #else #define GDVIRTUAL_TRACK(m_virtual, m_initialized) #endif @@ -193,19 +193,13 @@ def run(target, source, env): """ for i in range(max_versions + 1): - txt += "/* " + str(i) + " Arguments */\n\n" + txt += f"/* {i} Arguments */\n\n" txt += generate_version(i, False, False) txt += generate_version(i, False, True) txt += generate_version(i, True, False) txt += generate_version(i, True, True) - txt += "#endif" + txt += "#endif // GDVIRTUAL_GEN_H\n" - with open(target[0], "w") as f: + with open(str(target[0]), "w", encoding="utf-8", newline="\n") as f: f.write(txt) - - -if __name__ == "__main__": - from platform_methods import subprocess_main - - subprocess_main(globals()) diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp index de71295ee5..90536e58ff 100644 --- a/core/object/message_queue.cpp +++ b/core/object/message_queue.cpp @@ -35,15 +35,17 @@ #include "core/object/class_db.h" #include "core/object/script_language.h" +#include <stdio.h> + #ifdef DEV_ENABLED // 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) { \ - DEV_ASSERT(!this->is_current_thread_override); \ - mutex.lock(); \ - } else { \ - DEV_ASSERT(this->is_current_thread_override); \ +#define LOCK_MUTEX \ + if (this != MessageQueue::thread_singleton) { \ + DEV_ASSERT(!is_current_thread_override); \ + mutex.lock(); \ + } else { \ + DEV_ASSERT(is_current_thread_override); \ } #else #define LOCK_MUTEX \ @@ -93,7 +95,7 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) { if (pages_used == max_pages) { - ERR_PRINT("Failed method: " + p_callable + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed method: %s. Message queue out of memory. %s\n", String(p_callable).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; return ERR_OUT_OF_MEMORY; @@ -144,7 +146,7 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant if (ObjectDB::get_instance(p_id)) { type = ObjectDB::get_instance(p_id)->get_class(); } - ERR_PRINT("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed set: %s: %s target ID: %s. Message queue out of memory. %s\n", type.utf8().get_data(), String(p_prop).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; @@ -181,7 +183,7 @@ Error CallQueue::push_notification(ObjectID p_id, int p_notification) { if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) { if (pages_used == max_pages) { - ERR_PRINT("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text); + fprintf(stderr, "Failed notification: %d target ID: %s. Message queue out of memory. %s\n", p_notification, itos(p_id).utf8().get_data(), error_text.utf8().get_data()); statistics(); UNLOCK_MUTEX; return ERR_OUT_OF_MEMORY; @@ -254,7 +256,7 @@ Error CallQueue::_transfer_messages_to_main_queue() { // Any other possibly existing source page needs to be added. if (mq->pages_used + (pages_used - src_page) > mq->max_pages) { - ERR_PRINT("Failed appending thread queue. Message queue out of memory. " + mq->error_text); + fprintf(stderr, "Failed appending thread queue. Message queue out of memory. %s\n", mq->error_text.utf8().get_data()); mq->statistics(); mq->mutex.unlock(); return ERR_OUT_OF_MEMORY; @@ -460,8 +462,8 @@ void CallQueue::statistics() { } break; } if (null_target) { - //object was deleted - print_line("Object was deleted while awaiting a callback"); + // Object was deleted. + fprintf(stdout, "Object was deleted while awaiting a callback.\n"); null_count++; } @@ -479,19 +481,19 @@ void CallQueue::statistics() { } } - print_line("TOTAL PAGES: " + itos(pages_used) + " (" + itos(pages_used * PAGE_SIZE_BYTES) + " bytes)."); - print_line("NULL count: " + itos(null_count)); + fprintf(stdout, "TOTAL PAGES: %d (%d bytes).\n", pages_used, pages_used * PAGE_SIZE_BYTES); + fprintf(stdout, "NULL count: %d.\n", null_count); for (const KeyValue<StringName, int> &E : set_count) { - print_line("SET " + E.key + ": " + itos(E.value)); + fprintf(stdout, "SET %s: %d.\n", String(E.key).utf8().get_data(), E.value); } for (const KeyValue<Callable, int> &E : call_count) { - print_line("CALL " + E.key + ": " + itos(E.value)); + fprintf(stdout, "CALL %s: %d.\n", String(E.key).utf8().get_data(), E.value); } for (const KeyValue<int, int> &E : notify_count) { - print_line("NOTIFY " + itos(E.key) + ": " + itos(E.value)); + fprintf(stdout, "NOTIFY %d: %d.\n", E.key, E.value); } UNLOCK_MUTEX; diff --git a/core/object/method_bind.h b/core/object/method_bind.h index d67fd003c8..e97f4abc6a 100644 --- a/core/object/method_bind.h +++ b/core/object/method_bind.h @@ -140,7 +140,7 @@ public: }; // MethodBindVarArg base CRTP -template <class Derived, class T, class R, bool should_returns> +template <typename Derived, typename T, typename R, bool should_returns> class MethodBindVarArgBase : public MethodBind { protected: R(T::*method) @@ -219,12 +219,15 @@ private: }; // variadic, no return -template <class T> +template <typename T> class MethodBindVarArgT : public MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false> { friend class MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>; public: virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == MethodBind::get_instance_class(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgT<T>, T, void, false>::method)(p_args, p_arg_count, r_error); return {}; } @@ -242,7 +245,7 @@ private: } }; -template <class T> +template <typename T> MethodBind *create_vararg_method_bind(void (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { MethodBind *a = memnew((MethodBindVarArgT<T>)(p_method, p_info, p_return_nil_is_variant)); a->set_instance_class(T::get_class_static()); @@ -250,7 +253,7 @@ MethodBind *create_vararg_method_bind(void (T::*p_method)(const Variant **, int, } // variadic, return -template <class T, class R> +template <typename T, typename R> class MethodBindVarArgTR : public MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true> { friend class MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>; @@ -261,6 +264,9 @@ public: #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == MethodBind::get_instance_class(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif return (static_cast<T *>(p_object)->*MethodBindVarArgBase<MethodBindVarArgTR<T, R>, T, R, true>::method)(p_args, p_arg_count, r_error); } @@ -281,7 +287,7 @@ private: } }; -template <class T, class R> +template <typename T, typename R> MethodBind *create_vararg_method_bind(R (T::*p_method)(const Variant **, int, Callable::CallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { MethodBind *a = memnew((MethodBindVarArgTR<T, R>)(p_method, p_info, p_return_nil_is_variant)); a->set_instance_class(T::get_class_static()); @@ -299,9 +305,9 @@ class __UnexistingClass; // no return, not const #ifdef TYPED_METHOD_BIND -template <class T, class... P> +template <typename T, typename... P> #else -template <class... P> +template <typename... P> #endif class MethodBindT : public MethodBind { void (MB_T::*method)(P...); @@ -329,6 +335,9 @@ public: #endif virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_variant_args_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else @@ -338,6 +347,9 @@ public: } virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_validated_object_instance_args(static_cast<T *>(p_object), method, p_args); #else @@ -346,6 +358,9 @@ public: } virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_ptr_args<T, P...>(static_cast<T *>(p_object), method, p_args); #else @@ -360,7 +375,7 @@ public: } }; -template <class T, class... P> +template <typename T, typename... P> MethodBind *create_method_bind(void (T::*p_method)(P...)) { #ifdef TYPED_METHOD_BIND MethodBind *a = memnew((MethodBindT<T, P...>)(p_method)); @@ -374,9 +389,9 @@ MethodBind *create_method_bind(void (T::*p_method)(P...)) { // no return, const #ifdef TYPED_METHOD_BIND -template <class T, class... P> +template <typename T, typename... P> #else -template <class... P> +template <typename... P> #endif class MethodBindTC : public MethodBind { void (MB_T::*method)(P...) const; @@ -404,6 +419,9 @@ public: #endif virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), Variant(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_variant_argsc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, r_error, get_default_arguments()); #else @@ -413,6 +431,9 @@ public: } virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_validated_object_instance_argsc(static_cast<T *>(p_object), method, p_args); #else @@ -421,6 +442,9 @@ public: } virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_ptr_argsc<T, P...>(static_cast<T *>(p_object), method, p_args); #else @@ -436,7 +460,7 @@ public: } }; -template <class T, class... P> +template <typename T, typename... P> MethodBind *create_method_bind(void (T::*p_method)(P...) const) { #ifdef TYPED_METHOD_BIND MethodBind *a = memnew((MethodBindTC<T, P...>)(p_method)); @@ -450,9 +474,9 @@ MethodBind *create_method_bind(void (T::*p_method)(P...) const) { // return, not const #ifdef TYPED_METHOD_BIND -template <class T, class R, class... P> +template <typename T, typename R, typename... P> #else -template <class R, class... P> +template <typename R, typename... P> #endif class MethodBindTR : public MethodBind { R(MB_T::*method) @@ -490,6 +514,9 @@ public: virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { Variant ret; +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), ret, vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_variant_args_ret_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #else @@ -499,6 +526,9 @@ public: } virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_validated_object_instance_args_ret(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -507,6 +537,9 @@ public: } virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_ptr_args_ret<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -522,7 +555,7 @@ public: } }; -template <class T, class R, class... P> +template <typename T, typename R, typename... P> MethodBind *create_method_bind(R (T::*p_method)(P...)) { #ifdef TYPED_METHOD_BIND MethodBind *a = memnew((MethodBindTR<T, R, P...>)(p_method)); @@ -537,9 +570,9 @@ MethodBind *create_method_bind(R (T::*p_method)(P...)) { // return, const #ifdef TYPED_METHOD_BIND -template <class T, class R, class... P> +template <typename T, typename R, typename... P> #else -template <class R, class... P> +template <typename R, typename... P> #endif class MethodBindTRC : public MethodBind { R(MB_T::*method) @@ -577,6 +610,9 @@ public: virtual Variant call(Object *p_object, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) const override { Variant ret; +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_V_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), ret, vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_variant_args_retc_dv(static_cast<T *>(p_object), method, p_args, p_arg_count, ret, r_error, get_default_arguments()); #else @@ -586,6 +622,9 @@ public: } virtual void validated_call(Object *p_object, const Variant **p_args, Variant *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_validated_object_instance_args_retc(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -594,6 +633,9 @@ public: } virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) const override { +#ifdef TOOLS_ENABLED + ERR_FAIL_COND_MSG(p_object && p_object->is_extension_placeholder() && p_object->get_class_name() == get_instance_class(), vformat("Cannot call method bind '%s' on placeholder instance.", MethodBind::get_name())); +#endif #ifdef TYPED_METHOD_BIND call_with_ptr_args_retc<T, R, P...>(static_cast<T *>(p_object), method, p_args, r_ret); #else @@ -610,7 +652,7 @@ public: } }; -template <class T, class R, class... P> +template <typename T, typename R, typename... P> MethodBind *create_method_bind(R (T::*p_method)(P...) const) { #ifdef TYPED_METHOD_BIND MethodBind *a = memnew((MethodBindTRC<T, R, P...>)(p_method)); @@ -625,7 +667,7 @@ MethodBind *create_method_bind(R (T::*p_method)(P...) const) { // no return -template <class... P> +template <typename... P> class MethodBindTS : public MethodBind { void (*function)(P...); @@ -675,7 +717,7 @@ public: } }; -template <class... P> +template <typename... P> MethodBind *create_static_method_bind(void (*p_method)(P...)) { MethodBind *a = memnew((MethodBindTS<P...>)(p_method)); return a; @@ -683,7 +725,7 @@ MethodBind *create_static_method_bind(void (*p_method)(P...)) { // return -template <class R, class... P> +template <typename R, typename... P> class MethodBindTRS : public MethodBind { R(*function) (P...); @@ -742,7 +784,7 @@ public: } }; -template <class R, class... P> +template <typename R, typename... P> MethodBind *create_static_method_bind(R (*p_method)(P...)) { MethodBind *a = memnew((MethodBindTRS<R, P...>)(p_method)); return a; diff --git a/core/object/object.compat.inc b/core/object/object.compat.inc new file mode 100644 index 0000000000..bf1e99fc9b --- /dev/null +++ b/core/object/object.compat.inc @@ -0,0 +1,40 @@ +/**************************************************************************/ +/* object.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +#include "core/object/class_db.h" + +void Object::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL("")); +} + +#endif diff --git a/core/object/object.cpp b/core/object/object.cpp index 2e5b897bce..f8d2feb5a8 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "object.h" +#include "object.compat.inc" #include "core/core_string_names.h" #include "core/extension/gdextension_manager.h" @@ -141,16 +142,16 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { args = p_dict["args"]; } - for (int i = 0; i < args.size(); i++) { - Dictionary d = args[i]; + for (const Variant &arg : args) { + Dictionary d = arg; mi.arguments.push_back(PropertyInfo::from_dict(d)); } Array defargs; if (p_dict.has("default_args")) { defargs = p_dict["default_args"]; } - for (int i = 0; i < defargs.size(); i++) { - mi.default_arguments.push_back(defargs[i]); + for (const Variant &defarg : defargs) { + mi.default_arguments.push_back(defarg); } if (p_dict.has("return")) { @@ -198,6 +199,7 @@ bool Object::_predelete() { notification(NOTIFICATION_PREDELETE, true); if (_predelete_ok) { _class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage. + notification(NOTIFICATION_PREDELETE_CLEANUP, true); } return _predelete_ok; } @@ -491,14 +493,22 @@ void Object::get_property_list(List<PropertyInfo> *p_list, bool p_reversed) cons ClassDB::get_property_list(current_extension->class_name, p_list, true, this); if (current_extension->get_property_list) { - uint32_t pcount; - const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount); - for (uint32_t i = 0; i < pcount; i++) { - p_list->push_back(PropertyInfo(pinfo[i])); - } - if (current_extension->free_property_list) { - current_extension->free_property_list(_extension_instance, pinfo); +#ifdef TOOLS_ENABLED + // If this is a placeholder, we can't call into the GDExtension on the parent class, + // because we don't have a real instance of the class to give it. + if (likely(!_extension->is_placeholder)) { +#endif + uint32_t pcount; + const GDExtensionPropertyInfo *pinfo = current_extension->get_property_list(_extension_instance, &pcount); + for (uint32_t i = 0; i < pcount; i++) { + p_list->push_back(PropertyInfo(pinfo[i])); + } + if (current_extension->free_property_list) { + current_extension->free_property_list(_extension_instance, pinfo); + } +#ifdef TOOLS_ENABLED } +#endif } current_extension = current_extension->parent; @@ -678,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; @@ -727,7 +790,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_ r_error.expected = 0; return Variant(); } - if (Object::cast_to<RefCounted>(this)) { + if (is_ref_counted()) { r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD; ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference."); } @@ -1037,10 +1100,19 @@ bool Object::_has_user_signal(const StringName &p_name) const { return signal_map[p_name].user.name.length() > 0; } -struct _ObjectSignalDisconnectData { - StringName signal; - Callable callable; -}; +void Object::_remove_user_signal(const StringName &p_name) { + SignalData *s = signal_map.getptr(p_name); + ERR_FAIL_NULL_MSG(s, "Provided signal does not exist."); + ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal)."); + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + Object *target = slot_kv.key.get_object(); + if (likely(target)) { + target->connections.erase(slot_kv.value.cE); + } + } + + signal_map.erase(p_name); +} Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) { if (unlikely(p_argcount < 1)) { @@ -1090,26 +1162,43 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int // which is needed in certain edge cases; e.g., https://github.com/godotengine/godot/issues/73889. Ref<RefCounted> rc = Ref<RefCounted>(Object::cast_to<RefCounted>(this)); - List<_ObjectSignalDisconnectData> disconnect_data; - // Ensure that disconnecting the signal or even deleting the object // will not affect the signal calling. - LocalVector<Connection> slot_conns; - slot_conns.resize(s->slot_map.size()); - { - uint32_t idx = 0; - for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { - slot_conns[idx++] = slot_kv.value.conn; + Callable *slot_callables = (Callable *)alloca(sizeof(Callable) * s->slot_map.size()); + uint32_t *slot_flags = (uint32_t *)alloca(sizeof(uint32_t) * s->slot_map.size()); + uint32_t slot_count = 0; + + for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) { + memnew_placement(&slot_callables[slot_count], Callable(slot_kv.value.conn.callable)); + slot_flags[slot_count] = slot_kv.value.conn.flags; + ++slot_count; + } + + DEV_ASSERT(slot_count == s->slot_map.size()); + + // Disconnect all one-shot connections before emitting to prevent recursion. + for (uint32_t i = 0; i < slot_count; ++i) { + bool disconnect = slot_flags[i] & CONNECT_ONE_SHOT; +#ifdef TOOLS_ENABLED + if (disconnect && (slot_flags[i] & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { + // This signal was connected from the editor, and is being edited. Just don't disconnect for now. + disconnect = false; + } +#endif + if (disconnect) { + _disconnect(p_name, slot_callables[i]); } - DEV_ASSERT(idx == s->slot_map.size()); } OBJ_DEBUG_LOCK Error err = OK; - for (const Connection &c : slot_conns) { - if (!c.callable.is_valid()) { + for (uint32_t i = 0; i < slot_count; ++i) { + const Callable &callable = slot_callables[i]; + const uint32_t &flags = slot_flags[i]; + + if (!callable.is_valid()) { // Target might have been deleted during signal callback, this is expected and OK. continue; } @@ -1117,51 +1206,34 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int const Variant **args = p_args; int argc = p_argcount; - if (c.flags & CONNECT_DEFERRED) { - MessageQueue::get_singleton()->push_callablep(c.callable, args, argc, true); + if (flags & CONNECT_DEFERRED) { + MessageQueue::get_singleton()->push_callablep(callable, args, argc, true); } else { Callable::CallError ce; _emitting = true; Variant ret; - c.callable.callp(args, argc, ret, ce); + callable.callp(args, argc, ret, ce); _emitting = false; if (ce.error != Callable::CallError::CALL_OK) { #ifdef DEBUG_ENABLED - if (c.flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) { + if (flags & CONNECT_PERSIST && Engine::get_singleton()->is_editor_hint() && (script.is_null() || !Ref<Script>(script)->is_tool())) { continue; } #endif - Object *target = c.callable.get_object(); + Object *target = 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) + "."); + ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(callable, args, argc, ce) + "."); err = ERR_METHOD_NOT_FOUND; } } } - - bool disconnect = c.flags & CONNECT_ONE_SHOT; -#ifdef TOOLS_ENABLED - if (disconnect && (c.flags & CONNECT_PERSIST) && Engine::get_singleton()->is_editor_hint()) { - //this signal was connected from the editor, and is being edited. just don't disconnect for now - disconnect = false; - } -#endif - if (disconnect) { - _ObjectSignalDisconnectData dd; - dd.signal = p_name; - dd.callable = c.callable; - disconnect_data.push_back(dd); - } } - while (!disconnect_data.is_empty()) { - const _ObjectSignalDisconnectData &dd = disconnect_data.front()->get(); - - _disconnect(dd.signal, dd.callable); - disconnect_data.pop_front(); + for (uint32_t i = 0; i < slot_count; ++i) { + slot_callables[i].~Callable(); } return err; @@ -1175,8 +1247,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { MethodInfo mi; mi.name = p_name; - for (int i = 0; i < p_args.size(); i++) { - Dictionary d = p_args[i]; + for (const Variant &arg : p_args) { + Dictionary d = arg; PropertyInfo param; if (d.has("name")) { @@ -1190,6 +1262,10 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) { } add_user_signal(mi); + + if (signal_map.has(p_name)) { + signal_map.getptr(p_name)->removable = true; + } } TypedArray<Dictionary> Object::_get_signal_list() const { @@ -1346,12 +1422,10 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui s = &signal_map[p_signal]; } - Callable target = p_callable; - //compare with the base callable, so binds can be ignored - if (s->slot_map.has(*target.get_base_comparator())) { + if (s->slot_map.has(*p_callable.get_base_comparator())) { if (p_flags & CONNECT_REFERENCE_COUNTED) { - s->slot_map[*target.get_base_comparator()].reference_count++; + s->slot_map[*p_callable.get_base_comparator()].reference_count++; return OK; } else { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object."); @@ -1363,7 +1437,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui SignalData::Slot slot; Connection conn; - conn.callable = target; + conn.callable = p_callable; conn.signal = ::Signal(this, p_signal); conn.flags = p_flags; slot.conn = conn; @@ -1375,7 +1449,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui } //use callable version as key, so binds can be ignored - s->slot_map[*target.get_base_comparator()] = slot; + s->slot_map[*p_callable.get_base_comparator()] = slot; return OK; } @@ -1396,9 +1470,7 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + "."); } - Callable target = p_callable; - - return s->slot_map.has(*target.get_base_comparator()); + return s->slot_map.has(*p_callable.get_base_comparator()); } void Object::disconnect(const StringName &p_signal, const Callable &p_callable) { @@ -1467,6 +1539,7 @@ void Object::initialize_class() { } ClassDB::_add_class<Object>(); _bind_methods(); + _bind_compatibility_methods(); initialized = true; } @@ -1475,11 +1548,16 @@ String Object::tr(const StringName &p_message, const StringName &p_context) cons return p_message; } - if (Engine::get_singleton()->is_editor_hint()) { + if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) { + String tr_msg = TranslationServer::get_singleton()->extractable_translate(p_message, p_context); + if (!tr_msg.is_empty() && tr_msg != p_message) { + return tr_msg; + } + return TranslationServer::get_singleton()->tool_translate(p_message, p_context); - } else { - return TranslationServer::get_singleton()->translate(p_message, p_context); } + + return TranslationServer::get_singleton()->translate(p_message, p_context); } String Object::tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1491,11 +1569,16 @@ String Object::tr_n(const StringName &p_message, const StringName &p_message_plu return p_message_plural; } - if (Engine::get_singleton()->is_editor_hint()) { + if (Engine::get_singleton()->is_editor_hint() || Engine::get_singleton()->is_project_manager_hint()) { + String tr_msg = TranslationServer::get_singleton()->extractable_translate_plural(p_message, p_message_plural, p_n, p_context); + if (!tr_msg.is_empty() && tr_msg != p_message && tr_msg != p_message_plural) { + return tr_msg; + } + return TranslationServer::get_singleton()->tool_translate_plural(p_message, p_message_plural, p_n, p_context); - } else { - return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context); } + + return TranslationServer::get_singleton()->translate_plural(p_message, p_message_plural, p_n, p_context); } void Object::_clear_internal_resource_paths(const Variant &p_var) { @@ -1520,8 +1603,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { } break; case Variant::ARRAY: { Array a = p_var; - for (int i = 0; i < a.size(); i++) { - _clear_internal_resource_paths(a[i]); + for (const Variant &var : a) { + _clear_internal_resource_paths(var); } } break; @@ -1596,6 +1679,7 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array())); ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal); + ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::_remove_user_signal); { MethodInfo mi; @@ -1627,6 +1711,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); @@ -1642,8 +1728,8 @@ void Object::_bind_methods() { ClassDB::bind_method(D_METHOD("set_message_translation", "enable"), &Object::set_message_translation); ClassDB::bind_method(D_METHOD("can_translate_messages"), &Object::can_translate_messages); - ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL("")); - ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL("")); + ClassDB::bind_method(D_METHOD("tr", "message", "context"), &Object::tr, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("tr_n", "message", "plural_message", "n", "context"), &Object::tr_n, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("is_queued_for_deletion"), &Object::is_queued_for_deletion); ClassDB::bind_method(D_METHOD("cancel_free"), &Object::cancel_free); @@ -1686,6 +1772,7 @@ void Object::_bind_methods() { BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE); BIND_CONSTANT(NOTIFICATION_PREDELETE); + BIND_CONSTANT(NOTIFICATION_EXTENSION_RELOADED); BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); @@ -1794,6 +1881,16 @@ uint32_t Object::get_edited_version() const { #endif StringName Object::get_class_name_for_extension(const GDExtension *p_library) const { +#ifdef TOOLS_ENABLED + // If this is the library this extension comes from and it's a placeholder, we + // have to return the closest native parent's class name, so that it doesn't try to + // use this like the real object. + if (unlikely(_extension && _extension->library == p_library && _extension->is_placeholder)) { + const StringName *class_name = _get_class_namev(); + return *class_name; + } +#endif + // Only return the class name per the extension if it matches the given p_library. if (_extension && _extension->library == p_library) { return _extension->class_name; @@ -1921,13 +2018,15 @@ void Object::clear_internal_extension() { // Clear the instance bindings. _instance_binding_mutex.lock(); - if (_instance_bindings[0].free_callback) { - _instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding); + if (_instance_bindings) { + if (_instance_bindings[0].free_callback) { + _instance_bindings[0].free_callback(_instance_bindings[0].token, this, _instance_bindings[0].binding); + } + _instance_bindings[0].binding = nullptr; + _instance_bindings[0].token = nullptr; + _instance_bindings[0].free_callback = nullptr; + _instance_bindings[0].reference_callback = nullptr; } - _instance_bindings[0].binding = nullptr; - _instance_bindings[0].token = nullptr; - _instance_bindings[0].free_callback = nullptr; - _instance_bindings[0].reference_callback = nullptr; _instance_binding_mutex.unlock(); // Clear the virtual methods. @@ -2066,15 +2165,17 @@ void ObjectDB::debug_objects(DebugFunc p_func) { spin_lock.unlock(); } +#ifdef TOOLS_ENABLED void Object::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; if (p_idx == 0) { - if (p_function == "connect" || p_function == "is_connected" || p_function == "disconnect" || p_function == "emit_signal" || p_function == "has_signal") { + if (pf == "connect" || pf == "is_connected" || pf == "disconnect" || pf == "emit_signal" || pf == "has_signal") { List<MethodInfo> signals; get_signal_list(&signals); for (const MethodInfo &E : signals) { r_options->push_back(E.name.quote()); } - } else if (p_function == "call" || p_function == "call_deferred" || p_function == "callv" || p_function == "has_method") { + } else if (pf == "call" || pf == "call_deferred" || pf == "callv" || pf == "has_method") { List<MethodInfo> methods; get_method_list(&methods); for (const MethodInfo &E : methods) { @@ -2083,7 +2184,7 @@ void Object::get_argument_options(const StringName &p_function, int p_idx, List< } r_options->push_back(E.name.quote()); } - } else if (p_function == "set" || p_function == "set_deferred" || p_function == "get") { + } else if (pf == "set" || pf == "set_deferred" || pf == "get") { List<PropertyInfo> properties; get_property_list(&properties); for (const PropertyInfo &E : properties) { @@ -2091,13 +2192,13 @@ void Object::get_argument_options(const StringName &p_function, int p_idx, List< r_options->push_back(E.name.quote()); } } - } else if (p_function == "set_meta" || p_function == "get_meta" || p_function == "has_meta" || p_function == "remove_meta") { + } else if (pf == "set_meta" || pf == "get_meta" || pf == "has_meta" || pf == "remove_meta") { for (const KeyValue<StringName, Variant> &K : metadata) { r_options->push_back(String(K.key).quote()); } } } else if (p_idx == 2) { - if (p_function == "connect") { + if (pf == "connect") { // Ideally, the constants should be inferred by the parameter. // But a parameter's PropertyInfo does not store the enum they come from, so this will do for now. List<StringName> constants; @@ -2108,6 +2209,7 @@ void Object::get_argument_options(const StringName &p_function, int p_idx, List< } } } +#endif SpinLock ObjectDB::spin_lock; uint32_t ObjectDB::slot_count = 0; @@ -2225,8 +2327,9 @@ void ObjectDB::cleanup() { extra_info = " - Resource path: " + String(resource_get_path->call(obj, nullptr, 0, call_error)); } - uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_VALIDATOR_BITS) | (object_slots[i].is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0); - print_line("Leaked instance: " + String(obj->get_class()) + ":" + itos(id) + extra_info); + uint64_t id = uint64_t(i) | (uint64_t(object_slots[i].validator) << OBJECTDB_SLOT_MAX_COUNT_BITS) | (object_slots[i].is_ref_counted ? OBJECTDB_REFERENCE_BIT : 0); + DEV_ASSERT(id == (uint64_t)obj->get_instance_id()); // We could just use the id from the object, but this check may help catching memory corruption catastrophes. + print_line("Leaked instance: " + String(obj->get_class()) + ":" + uitos(id) + extra_info); count--; } diff --git a/core/object/object.h b/core/object/object.h index a444db0f70..915c3a8c25 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -72,12 +72,12 @@ enum PropertyHint { PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_OBJECT_ID, PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose - PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts) + PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, // Deprecated. PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send PROPERTY_HINT_NODE_PATH_VALID_TYPES, PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog - PROPERTY_HINT_INT_IS_OBJECTID, + PROPERTY_HINT_INT_IS_OBJECTID, // Deprecated. PROPERTY_HINT_INT_IS_POINTER, PROPERTY_HINT_ARRAY_TYPE, PROPERTY_HINT_LOCALE_ID, @@ -105,7 +105,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_SCRIPT_VARIABLE = 1 << 12, PROPERTY_USAGE_STORE_IF_NULL = 1 << 13, PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 1 << 14, - PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15, + PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15, // Deprecated. PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16, PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17, PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array. @@ -115,7 +115,7 @@ enum PropertyUsageFlags { PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 22, PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 23, PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 24, // Used in inspector to increment property when keyed in animation player. - PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // when loading, the resource for this property can be set at the end of loading. + PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // Deprecated. PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor. PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected. PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector. @@ -165,7 +165,7 @@ struct PropertyInfo { PropertyInfo() {} - PropertyInfo(const Variant::Type p_type, const String p_name, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", const uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) : + PropertyInfo(const Variant::Type p_type, const String &p_name, const PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", const uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = StringName()) : type(p_type), name(p_name), hint(p_hint), @@ -235,7 +235,7 @@ struct MethodInfo { return arguments_metadata.size() > p_arg ? arguments_metadata[p_arg] : 0; } - inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; } + inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id && name == p_method.name; } inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); } operator Dictionary() const; @@ -317,6 +317,10 @@ struct ObjectGDExtension { bool is_virtual = false; bool is_abstract = false; bool is_exposed = true; +#ifdef TOOLS_ENABLED + bool is_runtime = false; + bool is_placeholder = false; +#endif GDExtensionClassSet set; GDExtensionClassGet get; GDExtensionClassGetPropertyList get_property_list; @@ -354,8 +358,8 @@ struct ObjectGDExtension { #ifdef TOOLS_ENABLED void *tracking_userdata = nullptr; - void (*track_instance)(void *p_userdata, void *p_instance); - void (*untrack_instance)(void *p_userdata, void *p_instance); + void (*track_instance)(void *p_userdata, void *p_instance) = nullptr; + void (*untrack_instance)(void *p_userdata, void *p_instance) = nullptr; #endif }; @@ -615,6 +619,7 @@ private: MethodInfo user; HashMap<Callable, Slot, HashableHasher<Callable>> slot_map; + bool removable = false; }; HashMap<StringName, SignalData> signal_map; @@ -642,6 +647,7 @@ private: void _add_user_signal(const String &p_name, const Array &p_args = Array()); bool _has_user_signal(const StringName &p_name) const; + void _remove_user_signal(const StringName &p_name); Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error); TypedArray<Dictionary> _get_signal_list() const; TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const; @@ -650,13 +656,14 @@ 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); friend class RefCounted; bool type_is_reference = false; - std::mutex _instance_binding_mutex; + BinaryMutex _instance_binding_mutex; struct InstanceBinding { void *binding = nullptr; void *token = nullptr; @@ -698,7 +705,11 @@ protected: virtual void _notificationv(int p_notification, bool p_reversed) {} static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + static void _bind_compatibility_methods(); +#else static void _bind_compatibility_methods() {} +#endif bool _set(const StringName &p_name, const Variant &p_property) { return false; }; bool _get(const StringName &p_name, Variant &r_property) const { return false; }; void _get_property_list(List<PropertyInfo> *p_list) const {}; @@ -755,6 +766,7 @@ protected: void _clear_internal_resource_paths(const Variant &p_var); friend class ClassDB; + friend class PlaceholderExtensionInstance; bool _disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force = false); @@ -787,20 +799,22 @@ public: void detach_from_objectdb(); _FORCE_INLINE_ ObjectID get_instance_id() const { return _instance_id; } - template <class T> + template <typename T> static T *cast_to(Object *p_object) { - return dynamic_cast<T *>(p_object); + return p_object ? dynamic_cast<T *>(p_object) : nullptr; } - template <class T> + template <typename T> static const T *cast_to(const Object *p_object) { - return dynamic_cast<const T *>(p_object); + return p_object ? dynamic_cast<const T *>(p_object) : nullptr; } enum { NOTIFICATION_POSTINITIALIZE = 0, NOTIFICATION_PREDELETE = 1, NOTIFICATION_EXTENSION_RELOADED = 2, + // Internal notification to send after NOTIFICATION_PREDELETE, not bound to scripting. + NOTIFICATION_PREDELETE_CLEANUP = 3, }; /* TYPE API */ @@ -854,6 +868,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); @@ -945,8 +960,6 @@ public: Variant::Type get_static_property_type(const StringName &p_property, bool *r_valid = nullptr) const; Variant::Type get_static_property_type_indexed(const Vector<StringName> &p_path, bool *r_valid = nullptr) const; - virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; - // Translate message (internationalization). String tr(const StringName &p_message, const StringName &p_context = "") const; String tr_n(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; @@ -958,6 +971,7 @@ public: _FORCE_INLINE_ bool can_translate_messages() const { return _can_translate; } #ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; void editor_set_section_unfold(const String &p_section, bool p_unfolded); bool editor_is_section_unfolded(const String &p_section); const HashSet<String> &editor_get_section_folding() const { return editor_section_folding; } @@ -975,6 +989,7 @@ public: #ifdef TOOLS_ENABLED void clear_internal_extension(); void reset_internal_extension(ObjectGDExtension *p_extension); + bool is_extension_placeholder() const { return _extension && _extension->is_placeholder; } #endif void clear_internal_resource_paths(); diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h index 228373d662..5b358135c4 100644 --- a/core/object/ref_counted.h +++ b/core/object/ref_counted.h @@ -53,7 +53,7 @@ public: ~RefCounted() {} }; -template <class T> +template <typename T> class Ref { T *reference = nullptr; @@ -117,7 +117,7 @@ public: ref(p_from); } - template <class T_Other> + template <typename T_Other> void operator=(const Ref<T_Other> &p_from) { RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); if (!refb) { @@ -149,7 +149,7 @@ public: } } - template <class T_Other> + template <typename T_Other> void reference_ptr(T_Other *p_ptr) { if (reference == p_ptr) { return; @@ -166,7 +166,7 @@ public: ref(p_from); } - template <class T_Other> + template <typename T_Other> Ref(const Ref<T_Other> &p_from) { RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr())); if (!refb) { @@ -212,8 +212,9 @@ public: reference = nullptr; } - void instantiate() { - ref(memnew(T)); + template <typename... VarArgs> + void instantiate(VarArgs... p_params) { + ref(memnew(T(p_params...))); } Ref() {} @@ -239,7 +240,7 @@ public: WeakRef() {} }; -template <class T> +template <typename T> struct PtrToArg<Ref<T>> { _FORCE_INLINE_ static Ref<T> convert(const void *p_ptr) { if (p_ptr == nullptr) { @@ -257,7 +258,7 @@ struct PtrToArg<Ref<T>> { } }; -template <class T> +template <typename T> struct PtrToArg<const Ref<T> &> { typedef Ref<T> EncodeT; @@ -270,7 +271,7 @@ struct PtrToArg<const Ref<T> &> { } }; -template <class T> +template <typename T> struct GetTypeInfo<Ref<T>> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -280,7 +281,7 @@ struct GetTypeInfo<Ref<T>> { } }; -template <class T> +template <typename T> struct GetTypeInfo<const Ref<T> &> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -290,13 +291,13 @@ struct GetTypeInfo<const Ref<T> &> { } }; -template <class T> +template <typename T> struct VariantInternalAccessor<Ref<T>> { static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); } static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); } }; -template <class T> +template <typename T> struct VariantInternalAccessor<const Ref<T> &> { static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); } static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); } 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 df978a25ea..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> @@ -76,7 +78,7 @@ public: } //this is used by script languages that keep a reference counter of their own - //you can make make Ref<> not die when it reaches zero, so deleting the reference + //you can make Ref<> not die when it reaches zero, so deleting the reference //depends entirely from the script virtual void refcount_incremented() {} diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 011f4203ea..bd3199ca0a 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -34,22 +34,24 @@ #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" +#include "core/io/resource_loader.h" #include <stdint.h> ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES]; int ScriptServer::_language_count = 0; +bool ScriptServer::languages_ready = false; +Mutex ScriptServer::languages_mutex; bool ScriptServer::scripting_enabled = true; bool ScriptServer::reload_scripts_on_save = false; -SafeFlag ScriptServer::languages_finished; // Used until GH-76581 is fixed properly. ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr; void Script::_notification(int p_what) { switch (p_what) { case NOTIFICATION_POSTINITIALIZE: { if (EngineDebugger::is_active()) { - EngineDebugger::get_script_debugger()->set_break_language(get_language()); + callable_mp(this, &Script::_set_debugger_break_language).call_deferred(); } } break; } @@ -101,6 +103,28 @@ Dictionary Script::_get_script_constant_map() { return ret; } +void Script::_set_debugger_break_language() { + if (EngineDebugger::is_active()) { + EngineDebugger::get_script_debugger()->set_break_language(get_language()); + } +} + +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 { @@ -137,6 +161,8 @@ void Script::_bind_methods() { ClassDB::bind_method(D_METHOD("get_base_script"), &Script::get_base_script); ClassDB::bind_method(D_METHOD("get_instance_base_type"), &Script::get_instance_base_type); + ClassDB::bind_method(D_METHOD("get_global_name"), &Script::get_global_name); + ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal); ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list); @@ -151,6 +177,24 @@ void Script::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING, "source_code", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_source_code", "get_source_code"); } +void Script::reload_from_file() { +#ifdef TOOLS_ENABLED + // Replicates how the ScriptEditor reloads script resources, which generally handles it. + // However, when scripts are to be reloaded but aren't open in the internal editor, we go through here instead. + const Ref<Script> rel = ResourceLoader::load(ResourceLoader::path_remap(get_path()), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); + if (rel.is_null()) { + return; + } + + set_source_code(rel->get_source_code()); + set_last_modified_time(rel->get_last_modified_time()); + + reload(); +#else + Resource::reload_from_file(); +#endif +} + void ScriptServer::set_scripting_enabled(bool p_enabled) { scripting_enabled = p_enabled; } @@ -160,12 +204,25 @@ bool ScriptServer::is_scripting_enabled() { } ScriptLanguage *ScriptServer::get_language(int p_idx) { + MutexLock lock(languages_mutex); ERR_FAIL_INDEX_V(p_idx, _language_count, nullptr); - return _languages[p_idx]; } +ScriptLanguage *ScriptServer::get_language_for_extension(const String &p_extension) { + MutexLock lock(languages_mutex); + + for (int i = 0; i < _language_count; i++) { + if (_languages[i] && _languages[i]->get_extension() == p_extension) { + return _languages[i]; + } + } + + return nullptr; +} + Error ScriptServer::register_language(ScriptLanguage *p_language) { + MutexLock lock(languages_mutex); ERR_FAIL_NULL_V(p_language, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V_MSG(_language_count >= MAX_LANGUAGES, ERR_UNAVAILABLE, "Script languages limit has been reach, cannot register more."); for (int i = 0; i < _language_count; i++) { @@ -179,6 +236,8 @@ Error ScriptServer::register_language(ScriptLanguage *p_language) { } Error ScriptServer::unregister_language(const ScriptLanguage *p_language) { + MutexLock lock(languages_mutex); + for (int i = 0; i < _language_count; i++) { if (_languages[i] == p_language) { _language_count--; @@ -198,8 +257,8 @@ void ScriptServer::init_languages() { if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) { Array script_classes = GLOBAL_GET("_global_script_classes"); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary c = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary c = script_class; if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { continue; } @@ -210,8 +269,8 @@ void ScriptServer::init_languages() { #endif Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary c = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary c = script_class; if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) { continue; } @@ -219,17 +278,53 @@ void ScriptServer::init_languages() { } } - for (int i = 0; i < _language_count; i++) { - _languages[i]->init(); + HashSet<ScriptLanguage *> langs_to_init; + { + MutexLock lock(languages_mutex); + for (int i = 0; i < _language_count; i++) { + if (_languages[i]) { + langs_to_init.insert(_languages[i]); + } + } + } + + for (ScriptLanguage *E : langs_to_init) { + E->init(); + } + + { + MutexLock lock(languages_mutex); + languages_ready = true; } } void ScriptServer::finish_languages() { - for (int i = 0; i < _language_count; i++) { - _languages[i]->finish(); + HashSet<ScriptLanguage *> langs_to_finish; + + { + MutexLock lock(languages_mutex); + for (int i = 0; i < _language_count; i++) { + if (_languages[i]) { + langs_to_finish.insert(_languages[i]); + } + } + } + + for (ScriptLanguage *E : langs_to_finish) { + E->finish(); + } + + { + MutexLock lock(languages_mutex); + languages_ready = false; } + global_classes_clear(); - languages_finished.set(); +} + +bool ScriptServer::are_languages_initialized() { + MutexLock lock(languages_mutex); + return languages_ready; } void ScriptServer::set_reload_scripts_on_save(bool p_enable) { @@ -241,7 +336,8 @@ bool ScriptServer::is_reload_scripts_on_save_enabled() { } void ScriptServer::thread_enter() { - if (!languages_finished.is_set()) { + MutexLock lock(languages_mutex); + if (!languages_ready) { return; } for (int i = 0; i < _language_count; i++) { @@ -250,7 +346,8 @@ void ScriptServer::thread_enter() { } void ScriptServer::thread_exit() { - if (!languages_finished.is_set()) { + MutexLock lock(languages_mutex); + if (!languages_ready) { return; } for (int i = 0; i < _language_count; i++) { @@ -269,12 +366,24 @@ void ScriptServer::global_classes_clear() { void ScriptServer::add_global_class(const StringName &p_class, const StringName &p_base, const StringName &p_language, const String &p_path) { ERR_FAIL_COND_MSG(p_class == p_base || (global_classes.has(p_base) && get_global_class_native_base(p_base) == p_class), "Cyclic inheritance in script class."); - GlobalScriptClass g; - g.language = p_language; - g.path = p_path; - g.base = p_base; - global_classes[p_class] = g; - inheriters_cache_dirty = true; + GlobalScriptClass *existing = global_classes.getptr(p_class); + if (existing) { + // Update an existing class (only set dirty if something changed). + if (existing->base != p_base || existing->path != p_path || existing->language != p_language) { + existing->base = p_base; + existing->path = p_path; + existing->language = p_language; + inheriters_cache_dirty = true; + } + } else { + // Add new class. + GlobalScriptClass g; + g.language = p_language; + g.path = p_path; + g.base = p_base; + global_classes[p_class] = g; + inheriters_cache_dirty = true; + } } void ScriptServer::remove_global_class(const StringName &p_class) { @@ -360,8 +469,8 @@ void ScriptServer::save_global_classes() { Dictionary class_icons; Array script_classes = ProjectSettings::get_singleton()->get_global_class_list(); - for (int i = 0; i < script_classes.size(); i++) { - Dictionary d = script_classes[i]; + for (const Variant &script_class : script_classes) { + Dictionary d = script_class; if (!d.has("name") || !d.has("icon")) { continue; } @@ -479,6 +588,13 @@ TypedArray<int> ScriptLanguage::CodeCompletionOption::get_option_cached_characte return charac; } +void ScriptLanguage::_bind_methods() { + BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_AUTO); + BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_PASCAL_CASE); + BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_SNAKE_CASE); + BIND_ENUM_CONSTANT(SCRIPT_NAME_CASING_KEBAB_CASE); +} + bool PlaceHolderScriptInstance::set(const StringName &p_name, const Variant &p_value) { if (script->is_placeholder_fallback_enabled()) { return false; @@ -538,9 +654,6 @@ void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properti } else { for (const PropertyInfo &E : properties) { PropertyInfo pinfo = E; - if (!values.has(pinfo.name)) { - pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE; - } p_properties->push_back(E); } } @@ -592,6 +705,10 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) { HashSet<StringName> new_values; for (const PropertyInfo &E : p_properties) { + if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY)) { + continue; + } + StringName n = E.name; new_values.insert(n); @@ -651,7 +768,12 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, } } if (!found) { - properties.push_back(PropertyInfo(p_value.get_type(), p_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); + PropertyHint hint = PROPERTY_HINT_NONE; + const Object *obj = p_value.get_validated_object(); + if (obj && obj->is_class("Node")) { + hint = PROPERTY_HINT_NODE_TYPE; + } + properties.push_back(PropertyInfo(p_value.get_type(), p_name, hint, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE)); } } diff --git a/core/object/script_language.h b/core/object/script_language.h index 3e4041d173..223f114150 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -52,9 +52,11 @@ class ScriptServer { static ScriptLanguage *_languages[MAX_LANGUAGES]; static int _language_count; + static bool languages_ready; + static Mutex languages_mutex; + static bool scripting_enabled; static bool reload_scripts_on_save; - static SafeFlag languages_finished; // Used until GH-76581 is fixed properly. struct GlobalScriptClass { StringName language; @@ -73,6 +75,7 @@ public: static bool is_scripting_enabled(); _FORCE_INLINE_ static int get_language_count() { return _language_count; } static ScriptLanguage *get_language(int p_idx); + static ScriptLanguage *get_language_for_extension(const String &p_extension); static Error register_language(ScriptLanguage *p_language); static Error unregister_language(const ScriptLanguage *p_language); @@ -98,8 +101,7 @@ public: static void init_languages(); static void finish_languages(); - - static bool are_languages_finished() { return languages_finished.is_set(); } + static bool are_languages_initialized(); }; class PlaceHolderScriptInstance; @@ -122,7 +124,11 @@ protected: TypedArray<Dictionary> _get_script_signal_list(); Dictionary _get_script_constant_map(); + void _set_debugger_break_language(); + public: + virtual void reload_from_file() override; + virtual bool can_instantiate() const = 0; virtual Ref<Script> get_base_script() const = 0; //for script inheritance @@ -149,6 +155,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; @@ -191,6 +199,10 @@ public: class ScriptLanguage : public Object { GDCLASS(ScriptLanguage, Object) + +protected: + static void _bind_methods(); + public: virtual String get_name() const = 0; @@ -222,6 +234,13 @@ public: TEMPLATE_PROJECT }; + enum ScriptNameCasing { + SCRIPT_NAME_CASING_AUTO, + SCRIPT_NAME_CASING_PASCAL_CASE, + SCRIPT_NAME_CASING_SNAKE_CASE, + SCRIPT_NAME_CASING_KEBAB_CASE, + }; + struct ScriptTemplate { String inherit = "Object"; String name; @@ -237,12 +256,12 @@ public: void get_core_type_words(List<String> *p_core_type_words) const; virtual void get_reserved_words(List<String> *p_words) const = 0; - virtual bool is_control_flow_keyword(String p_string) const = 0; + virtual bool is_control_flow_keyword(const 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>(); } + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) { return Vector<ScriptTemplate>(); } virtual bool is_using_templates() { return false; } virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } @@ -255,8 +274,10 @@ public: virtual bool can_inherit_from_file() const { return false; } virtual int find_function(const String &p_function, const String &p_code) const = 0; virtual String make_function(const String &p_class, const String &p_name, const PackedStringArray &p_args) const = 0; + virtual bool can_make_function() const { return true; } virtual Error open_in_external_editor(const Ref<Script> &p_script, int p_line, int p_col) { return ERR_UNAVAILABLE; } virtual bool overrides_external_editor() { return false; } + virtual ScriptNameCasing preferred_file_name_casing() const { return SCRIPT_NAME_CASING_SNAKE_CASE; } // Keep enums in sync with: // scene/gui/code_edit.h - CodeEdit::CodeCompletionKind @@ -370,6 +391,7 @@ public: virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); } virtual void reload_all_scripts() = 0; + virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) = 0; virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0; /* LOADER FUNCTIONS */ @@ -383,10 +405,12 @@ public: uint64_t call_count; uint64_t total_time; uint64_t self_time; + uint64_t internal_time; }; virtual void profiling_start() = 0; virtual void profiling_stop() = 0; + virtual void profiling_set_save_native_calls(bool p_enable) = 0; virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0; @@ -399,6 +423,8 @@ public: virtual ~ScriptLanguage() {} }; +VARIANT_ENUM_CAST(ScriptLanguage::ScriptNameCasing); + extern uint8_t script_encryption_key[32]; class PlaceHolderScriptInstance : public ScriptInstance { @@ -422,6 +448,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 e326baf7eb..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); @@ -107,10 +110,12 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_supports_builtin_mode); GDVIRTUAL_BIND(_supports_documentation); GDVIRTUAL_BIND(_can_inherit_from_file); - GDVIRTUAL_BIND(_find_function, "class_name", "function_name"); + GDVIRTUAL_BIND(_find_function, "function", "code"); GDVIRTUAL_BIND(_make_function, "class_name", "function_name", "function_args"); + GDVIRTUAL_BIND(_can_make_function); GDVIRTUAL_BIND(_open_in_external_editor, "script", "line", "column"); GDVIRTUAL_BIND(_overrides_external_editor); + GDVIRTUAL_BIND(_preferred_file_name_casing); GDVIRTUAL_BIND(_complete_code, "code", "path", "owner"); GDVIRTUAL_BIND(_lookup_code, "code", "symbol", "path", "owner"); @@ -145,6 +150,7 @@ void ScriptLanguageExtension::_bind_methods() { GDVIRTUAL_BIND(_profiling_start); GDVIRTUAL_BIND(_profiling_stop); + GDVIRTUAL_BIND(_profiling_set_save_native_calls, "enable"); GDVIRTUAL_BIND(_profiling_get_accumulated_data, "info_array", "info_max"); GDVIRTUAL_BIND(_profiling_get_frame_data, "info_array", "info_max"); diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 00ab1cd6c0..cc6b729ae8 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; @@ -229,7 +242,7 @@ public: p_words->push_back(ret[i]); } } - EXBIND1RC(bool, is_control_flow_keyword, String) + EXBIND1RC(bool, is_control_flow_keyword, const String &) GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters) @@ -265,7 +278,7 @@ public: GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName) - virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override { + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret); Vector<ScriptTemplate> stret; @@ -306,8 +319,8 @@ public: } if (r_errors != nullptr && ret.has("errors")) { Array errors = ret["errors"]; - for (int i = 0; i < errors.size(); i++) { - Dictionary err = errors[i]; + for (const Variant &error : errors) { + Dictionary err = error; ERR_CONTINUE(!err.has("line")); ERR_CONTINUE(!err.has("column")); ERR_CONTINUE(!err.has("message")); @@ -326,8 +339,8 @@ public: if (r_warnings != nullptr && ret.has("warnings")) { ERR_FAIL_COND_V(!ret.has("warnings"), false); Array warnings = ret["warnings"]; - for (int i = 0; i < warnings.size(); i++) { - Dictionary warn = warnings[i]; + for (const Variant &warning : warnings) { + Dictionary warn = warning; ERR_CONTINUE(!warn.has("start_line")); ERR_CONTINUE(!warn.has("end_line")); ERR_CONTINUE(!warn.has("leftmost_column")); @@ -373,8 +386,10 @@ public: EXBIND2RC(int, find_function, const String &, const String &) EXBIND3RC(String, make_function, const String &, const String &, const PackedStringArray &) + EXBIND0RC(bool, can_make_function) EXBIND3R(Error, open_in_external_editor, const Ref<Script> &, int, int) EXBIND0R(bool, overrides_external_editor) + EXBIND0RC(ScriptNameCasing, preferred_file_name_casing) GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *) @@ -387,8 +402,8 @@ public: if (r_options != nullptr && ret.has("options")) { Array options = ret["options"]; - for (int i = 0; i < options.size(); i++) { - Dictionary op = options[i]; + for (const Variant &var : options) { + Dictionary op = var; CodeCompletionOption option; ERR_CONTINUE(!op.has("kind")); option.kind = CodeCompletionKind(int(op["kind"])); @@ -487,8 +502,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -507,8 +522,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -534,8 +549,8 @@ public: } if (p_values != nullptr && ret.has("values")) { Array values = ret["values"]; - for (int i = 0; i < values.size(); i++) { - p_values->push_back(values[i]); + for (const Variant &value : values) { + p_values->push_back(value); } } } @@ -547,9 +562,9 @@ public: TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret); Vector<StackInfo> sret; - for (int i = 0; i < ret.size(); i++) { + for (const Variant &var : ret) { StackInfo si; - Dictionary d = ret[i]; + Dictionary d = var; ERR_CONTINUE(!d.has("file")); ERR_CONTINUE(!d.has("func")); ERR_CONTINUE(!d.has("line")); @@ -562,6 +577,7 @@ public: } EXBIND0(reload_all_scripts) + EXBIND2(reload_scripts, const Array &, bool) EXBIND2(reload_tool_script, const Ref<Script> &, bool) /* LOADER FUNCTIONS */ @@ -579,8 +595,8 @@ public: virtual void get_public_functions(List<MethodInfo> *p_functions) const override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret); - for (int i = 0; i < ret.size(); i++) { - MethodInfo mi = MethodInfo::from_dict(ret[i]); + for (const Variant &var : ret) { + MethodInfo mi = MethodInfo::from_dict(var); p_functions->push_back(mi); } } @@ -599,14 +615,15 @@ public: virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret); - for (int i = 0; i < ret.size(); i++) { - MethodInfo mi = MethodInfo::from_dict(ret[i]); + for (const Variant &var : ret) { + MethodInfo mi = MethodInfo::from_dict(var); p_annotations->push_back(mi); } } EXBIND0(profiling_start) EXBIND0(profiling_stop) + EXBIND1(profiling_set_save_native_calls, bool) GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int) @@ -652,11 +669,17 @@ VARIANT_ENUM_CAST(ScriptLanguageExtension::CodeCompletionLocation) class ScriptInstanceExtension : public ScriptInstance { public: - const GDExtensionScriptInstanceInfo2 *native_info; + const GDExtensionScriptInstanceInfo3 *native_info; + +#ifndef DISABLE_DEPRECATED bool free_native_info = false; - struct { + struct DeprecatedNativeInfo { GDExtensionScriptInstanceNotification notification_func = nullptr; - } deprecated_native_info; + GDExtensionScriptInstanceFreePropertyList free_property_list_func = nullptr; + GDExtensionScriptInstanceFreeMethodList free_method_list_func = nullptr; + }; + DeprecatedNativeInfo *deprecated_native_info = nullptr; +#endif // DISABLE_DEPRECATED GDExtensionScriptInstanceDataPtr instance = nullptr; @@ -703,7 +726,11 @@ public: p_list->push_back(PropertyInfo(pinfo[i])); } if (native_info->free_property_list_func) { - native_info->free_property_list_func(instance, pinfo); + native_info->free_property_list_func(instance, pinfo, pcount); +#ifndef DISABLE_DEPRECATED + } else if (deprecated_native_info && deprecated_native_info->free_property_list_func) { + deprecated_native_info->free_property_list_func(instance, pinfo); +#endif // DISABLE_DEPRECATED } } } @@ -778,7 +805,11 @@ public: p_list->push_back(MethodInfo(minfo[i])); } if (native_info->free_method_list_func) { - native_info->free_method_list_func(instance, minfo); + native_info->free_method_list_func(instance, minfo, mcount); +#ifndef DISABLE_DEPRECATED + } else if (deprecated_native_info && deprecated_native_info->free_method_list_func) { + deprecated_native_info->free_method_list_func(instance, minfo); +#endif // DISABLE_DEPRECATED } } } @@ -789,6 +820,19 @@ public: return false; } + virtual int get_method_argument_count(const StringName &p_method, bool *r_is_valid = nullptr) const override { + if (native_info->get_method_argument_count_func) { + GDExtensionBool is_valid = 0; + GDExtensionInt ret = native_info->get_method_argument_count_func(instance, (GDExtensionStringNamePtr)&p_method, &is_valid); + if (r_is_valid) { + *r_is_valid = is_valid != 0; + } + return ret; + } + // 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) { @@ -805,8 +849,8 @@ public: if (native_info->notification_func) { native_info->notification_func(instance, p_notification, p_reversed); #ifndef DISABLE_DEPRECATED - } else if (deprecated_native_info.notification_func) { - deprecated_native_info.notification_func(instance, p_notification); + } else if (deprecated_native_info && deprecated_native_info->notification_func) { + deprecated_native_info->notification_func(instance, p_notification); #endif // DISABLE_DEPRECATED } } @@ -876,15 +920,19 @@ public: return reinterpret_cast<ScriptLanguage *>(lang); } return nullptr; - ; } virtual ~ScriptInstanceExtension() { if (native_info->free_func) { native_info->free_func(instance); } +#ifndef DISABLE_DEPRECATED if (free_native_info) { - memfree(const_cast<GDExtensionScriptInstanceInfo2 *>(native_info)); + memfree(const_cast<GDExtensionScriptInstanceInfo3 *>(native_info)); + } + if (deprecated_native_info) { + memfree(deprecated_native_info); } +#endif // DISABLE_DEPRECATED } #if defined(__GNUC__) && !defined(__clang__) diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index a8f2ac5bfe..6a1385e268 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -71,7 +71,14 @@ bool UndoRedo::_redo(bool p_execute) { } current_action++; - _process_operation_list(actions.write[current_action].do_ops.front(), p_execute); + + List<Operation>::Element *start_doops_element = actions.write[current_action].do_ops.front(); + while (merge_total > 0 && start_doops_element) { + start_doops_element = start_doops_element->next(); + merge_total--; + } + + _process_operation_list(start_doops_element, p_execute); version++; emit_signal(SNAME("version_changed")); @@ -104,6 +111,12 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back } } + if (p_mode == MERGE_ALL) { + merge_total = actions.write[current_action + 1].do_ops.size(); + } else { + merge_total = 0; + } + actions.write[actions.size() - 1].last_tick = ticks; // Revert reverse from previous commit. @@ -121,6 +134,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back actions.push_back(new_action); merge_mode = MERGE_DISABLE; + merge_total = 0; } } @@ -301,6 +315,8 @@ void UndoRedo::commit_action(bool p_execute) { return; //still nested } + bool add_message = !merging; + if (merging) { version--; merging = false; @@ -314,7 +330,15 @@ void UndoRedo::commit_action(bool p_execute) { _redo(p_execute); // perform action committing--; - if (callback && actions.size() > 0) { + if (max_steps > 0) { + // Clear early steps. + + while (actions.size() > max_steps) { + _pop_history_tail(); + } + } + + if (add_message && callback && actions.size() > 0) { callback(callback_ud, actions[actions.size() - 1].name); } } @@ -463,10 +487,22 @@ bool UndoRedo::has_redo() const { return (current_action + 1) < actions.size(); } +bool UndoRedo::is_merging() const { + return merging; +} + uint64_t UndoRedo::get_version() const { return version; } +void UndoRedo::set_max_steps(int p_max_steps) { + max_steps = p_max_steps; +} + +int UndoRedo::get_max_steps() const { + return max_steps; +} + void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud) { callback = p_callback; callback_ud = p_ud; @@ -511,9 +547,13 @@ void UndoRedo::_bind_methods() { ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo); ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo); ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version); + ClassDB::bind_method(D_METHOD("set_max_steps", "max_steps"), &UndoRedo::set_max_steps); + ClassDB::bind_method(D_METHOD("get_max_steps"), &UndoRedo::get_max_steps); ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo); ClassDB::bind_method(D_METHOD("undo"), &UndoRedo::undo); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_steps", PROPERTY_HINT_RANGE, "0,50,1,or_greater"), "set_max_steps", "get_max_steps"); + ADD_SIGNAL(MethodInfo("version_changed")); BIND_ENUM_CONSTANT(MERGE_DISABLE); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 74a6bea732..19d178635c 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -80,9 +80,11 @@ private: int current_action = -1; bool force_keep_in_merge_ends = false; int action_level = 0; + int max_steps = 0; MergeMode merge_mode = MERGE_DISABLE; bool merging = false; uint64_t version = 1; + int merge_total = 0; void _pop_history_tail(); void _process_operation_list(List<Operation>::Element *E, bool p_execute); @@ -131,8 +133,13 @@ public: bool has_undo() const; bool has_redo() const; + bool is_merging() const; + uint64_t get_version() const; + void set_max_steps(int p_max_steps); + int get_max_steps() const; + void set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud); void set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud); diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp index 2fcd0867e6..9c9e0fa899 100644 --- a/core/object/worker_thread_pool.cpp +++ b/core/object/worker_thread_pool.cpp @@ -30,8 +30,12 @@ #include "worker_thread_pool.h" +#include "core/object/script_language.h" #include "core/os/os.h" #include "core/os/thread_safe.h" +#include "core/templates/command_queue_mt.h" + +WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1; void WorkerThreadPool::Task::free_template_userdata() { ERR_FAIL_NULL(template_userdata); @@ -42,35 +46,38 @@ void WorkerThreadPool::Task::free_template_userdata() { WorkerThreadPool *WorkerThreadPool::singleton = nullptr; -void WorkerThreadPool::_process_task_queue() { - task_mutex.lock(); - Task *task = task_queue.first()->self(); - task_queue.remove(task_queue.first()); - task_mutex.unlock(); - _process_task(task); -} +thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr; void WorkerThreadPool::_process_task(Task *p_task) { - bool low_priority = p_task->low_priority; - int pool_thread_index = -1; - Task *prev_low_prio_task = nullptr; // In case this is recursively called. +#ifdef THREADS_ENABLED + int pool_thread_index = thread_ids[Thread::get_caller_id()]; + ThreadData &curr_thread = threads[pool_thread_index]; + Task *prev_task = nullptr; // In case this is recursively called. + bool safe_for_nodes_backup = is_current_thread_safe_for_nodes(); - if (!use_native_low_priority_threads) { + { // Tasks must start with this unset. They are free to set-and-forget otherwise. set_current_thread_safe_for_nodes(false); - pool_thread_index = thread_ids[Thread::get_caller_id()]; - ThreadData &curr_thread = threads[pool_thread_index]; + // Since the WorkerThreadPool is started before the script server, + // its pre-created threads can't have ScriptServer::thread_enter() called on them early. + // Therefore, we do it late at the first opportunity, so in case the task + // about to be run uses scripting, guarantees are held. task_mutex.lock(); + if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) { + task_mutex.unlock(); + ScriptServer::thread_enter(); + task_mutex.lock(); + curr_thread.ready_for_scripting = true; + } p_task->pool_thread_index = pool_thread_index; - if (low_priority) { - low_priority_tasks_running++; - prev_low_prio_task = curr_thread.current_low_prio_task; - curr_thread.current_low_prio_task = p_task; - } else { - curr_thread.current_low_prio_task = nullptr; + prev_task = curr_thread.current_task; + curr_thread.current_task = p_task; + if (p_task->pending_notify_yield_over) { + curr_thread.yield_is_over = true; } task_mutex.unlock(); } +#endif if (p_task->group) { // Handling a group @@ -102,33 +109,24 @@ void WorkerThreadPool::_process_task(Task *p_task) { memdelete(p_task->template_userdata); // This is no longer needed at this point, so get rid of it. } - if (low_priority && use_native_low_priority_threads) { - p_task->completed = true; - p_task->done_semaphore.post(); - if (do_post) { - p_task->group->completed.set_to(true); - } - } else { - if (do_post) { - p_task->group->done_semaphore.post(); - p_task->group->completed.set_to(true); - } - uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. - uint32_t finished_users = p_task->group->finished.increment(); - - if (finished_users == max_users) { - // Get rid of the group, because nobody else is using it. - task_mutex.lock(); - group_allocator.free(p_task->group); - task_mutex.unlock(); - } - - // For groups, tasks get rid of themselves. + if (do_post) { + p_task->group->done_semaphore.post(); + p_task->group->completed.set_to(true); + } + uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. + uint32_t finished_users = p_task->group->finished.increment(); + if (finished_users == max_users) { + // Get rid of the group, because nobody else is using it. task_mutex.lock(); - task_allocator.free(p_task); + group_allocator.free(p_task->group); task_mutex.unlock(); } + + // For groups, tasks get rid of themselves. + + task_mutex.lock(); + task_allocator.free(p_task); } else { if (p_task->native_func) { p_task->native_func(p_task->native_func_userdata); @@ -141,88 +139,164 @@ void WorkerThreadPool::_process_task(Task *p_task) { task_mutex.lock(); p_task->completed = true; - for (uint8_t i = 0; i < p_task->waiting; i++) { - p_task->done_semaphore.post(); + p_task->pool_thread_index = -1; + if (p_task->waiting_user) { + p_task->done_semaphore.post(p_task->waiting_user); } - if (!use_native_low_priority_threads) { - p_task->pool_thread_index = -1; + // Let awaiters know. + for (uint32_t i = 0; i < threads.size(); i++) { + if (threads[i].awaited_task == p_task) { + threads[i].cond_var.notify_one(); + threads[i].signaled = true; + } } - task_mutex.unlock(); // Keep mutex down to here since on unlock the task may be freed. } - // Task may have been freed by now (all callers notified). - p_task = nullptr; - - if (!use_native_low_priority_threads) { - bool post = false; - task_mutex.lock(); - ThreadData &curr_thread = threads[pool_thread_index]; - curr_thread.current_low_prio_task = prev_low_prio_task; - if (low_priority) { +#ifdef THREADS_ENABLED + { + curr_thread.current_task = prev_task; + if (p_task->low_priority) { low_priority_threads_used--; - low_priority_tasks_running--; - // A low prioriry task was freed, so see if we can move a pending one to the high priority queue. - if (_try_promote_low_priority_task()) { - post = true; - } - if (low_priority_tasks_awaiting_others == low_priority_tasks_running) { - _prevent_low_prio_saturation_deadlock(); + if (_try_promote_low_priority_task()) { + if (prev_task) { // Otherwise, this thread will catch it. + _notify_threads(&curr_thread, 1, 0); + } } } + task_mutex.unlock(); - if (post) { - task_available_semaphore.post(); - } } + + set_current_thread_safe_for_nodes(safe_for_nodes_backup); +#endif } void WorkerThreadPool::_thread_function(void *p_user) { + ThreadData *thread_data = (ThreadData *)p_user; while (true) { - singleton->task_available_semaphore.wait(); - if (singleton->exit_threads) { - break; + Task *task_to_process = nullptr; + { + MutexLock lock(singleton->task_mutex); + if (singleton->exit_threads) { + return; + } + thread_data->signaled = false; + + if (singleton->task_queue.first()) { + task_to_process = singleton->task_queue.first()->self(); + singleton->task_queue.remove(singleton->task_queue.first()); + } else { + thread_data->cond_var.wait(lock); + DEV_ASSERT(singleton->exit_threads || thread_data->signaled); + } } - singleton->_process_task_queue(); - } -} -void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) { - Task *task = (Task *)p_user; - singleton->_process_task(task); + if (task_to_process) { + singleton->_process_task(task_to_process); + } + } } -void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) { +void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority) { // Fall back to processing on the calling thread if there are no worker threads. // Separated into its own variable to make it easier to extend this logic // in custom builds. bool process_on_calling_thread = threads.size() == 0; if (process_on_calling_thread) { - _process_task(p_task); + task_mutex.unlock(); + for (uint32_t i = 0; i < p_count; i++) { + _process_task(p_tasks[i]); + } return; } - task_mutex.lock(); - p_task->low_priority = !p_high_priority; - if (!p_high_priority && use_native_low_priority_threads) { - p_task->low_priority_thread = native_thread_allocator.alloc(); - task_mutex.unlock(); + uint32_t to_process = 0; + uint32_t to_promote = 0; + + ThreadData *caller_pool_thread = thread_ids.has(Thread::get_caller_id()) ? &threads[thread_ids[Thread::get_caller_id()]] : nullptr; + + for (uint32_t i = 0; i < p_count; i++) { + p_tasks[i]->low_priority = !p_high_priority; + if (p_high_priority || low_priority_threads_used < max_low_priority_threads) { + task_queue.add_last(&p_tasks[i]->task_elem); + if (!p_high_priority) { + low_priority_threads_used++; + } + to_process++; + } else { + // Too many threads using low priority, must go to queue. + low_priority_task_queue.add_last(&p_tasks[i]->task_elem); + to_promote++; + } + } + + _notify_threads(caller_pool_thread, to_process, to_promote); + + task_mutex.unlock(); +} - if (p_task->group) { - p_task->group->low_priority_native_tasks.push_back(p_task); +void WorkerThreadPool::_notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count) { + uint32_t to_process = p_process_count; + uint32_t to_promote = p_promote_count; + + // This is where which threads are awaken is decided according to the workload. + // Threads that will anyway have a chance to check the situation and process/promote tasks + // are excluded from being notified. Others will be tried anyway to try to distribute load. + // The current thread, if is a pool thread, is also excluded depending on the promoting/processing + // needs because it will anyway loop again. However, it will contribute to decreasing the count, + // which helps reducing sync traffic. + + uint32_t thread_count = threads.size(); + + // First round: + // 1. For processing: notify threads that are not running tasks, to keep the stacks as shallow as possible. + // 2. For promoting: since it's exclusive with processing, we fin threads able to promote low-prio tasks now. + for (uint32_t i = 0; + i < thread_count && (to_process || to_promote); + i++, notify_index = (notify_index + 1) % thread_count) { + ThreadData &th = threads[notify_index]; + + if (th.signaled) { + continue; } - p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread. - } else if (p_high_priority || low_priority_threads_used < max_low_priority_threads) { - task_queue.add_last(&p_task->task_elem); - if (!p_high_priority) { - low_priority_threads_used++; + if (th.current_task) { + // Good thread for promoting low-prio? + if (to_promote && th.awaited_task && th.current_task->low_priority) { + if (likely(&th != p_current_thread_data)) { + th.cond_var.notify_one(); + } + th.signaled = true; + to_promote--; + } + } else { + if (to_process) { + if (likely(&th != p_current_thread_data)) { + th.cond_var.notify_one(); + } + th.signaled = true; + to_process--; + } + } + } + + // Second round: + // For processing: if the first round wasn't enough, let's try now with threads processing tasks but currently awaiting. + for (uint32_t i = 0; + i < thread_count && to_process; + i++, notify_index = (notify_index + 1) % thread_count) { + ThreadData &th = threads[notify_index]; + + if (th.signaled) { + continue; + } + if (th.awaited_task) { + if (likely(&th != p_current_thread_data)) { + th.cond_var.notify_one(); + } + th.signaled = true; + to_process--; } - task_mutex.unlock(); - task_available_semaphore.post(); - } else { - // Too many threads using low priority, must go to queue. - low_priority_task_queue.add_last(&p_task->task_elem); - task_mutex.unlock(); } } @@ -238,23 +312,6 @@ bool WorkerThreadPool::_try_promote_low_priority_task() { } } -void WorkerThreadPool::_prevent_low_prio_saturation_deadlock() { - if (low_priority_tasks_awaiting_others == low_priority_tasks_running) { -#ifdef DEV_ENABLED - print_verbose("WorkerThreadPool: Low-prio slots saturated with tasks all waiting for other low-prio tasks. Attempting to avoid deadlock by scheduling one extra task."); -#endif - // In order not to create dependency cycles, we can only schedule the next one. - // We'll keep doing the same until the deadlock is broken, - SelfList<Task> *to_promote = low_priority_task_queue.first(); - if (to_promote) { - low_priority_task_queue.remove(to_promote); - task_queue.add_last(to_promote); - low_priority_threads_used++; - task_available_semaphore.post(); - } - } -} - WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) { return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description); } @@ -264,15 +321,15 @@ WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable, // Get a free task Task *task = task_allocator.alloc(); TaskID id = last_task++; + task->self = id; task->callable = p_callable; task->native_func = p_func; task->native_func_userdata = p_userdata; task->description = p_description; task->template_userdata = p_template_userdata; tasks.insert(id, task); - task_mutex.unlock(); - _post_task(task, p_high_priority); + _post_tasks_and_unlock(&task, 1, p_high_priority); return id; } @@ -304,106 +361,154 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) { } Task *task = *taskp; - if (!task->completed) { - if (!use_native_low_priority_threads && task->pool_thread_index != -1) { // Otherwise, it's not running yet. - int caller_pool_th_index = thread_ids.has(Thread::get_caller_id()) ? thread_ids[Thread::get_caller_id()] : -1; - if (caller_pool_th_index == task->pool_thread_index) { - // Deadlock prevention. - // Waiting for a task run on this same thread? That means the task to be awaited started waiting as well - // and another task was run to make use of the thread in the meantime, with enough bad luck as to - // the need to wait for the original task arose in turn. - // In other words, the task we want to wait for is buried in the stack. - // Let's report the caller about the issue to it handles as it sees fit. - task_mutex.unlock(); - return ERR_BUSY; - } + if (task->completed) { + if (task->waiting_pool == 0 && task->waiting_user == 0) { + tasks.erase(p_task_id); + task_allocator.free(task); + } + task_mutex.unlock(); + return OK; + } + + ThreadData *caller_pool_thread = thread_ids.has(Thread::get_caller_id()) ? &threads[thread_ids[Thread::get_caller_id()]] : nullptr; + if (caller_pool_thread && p_task_id <= caller_pool_thread->current_task->self) { + // Deadlock prevention: + // When a pool thread wants to wait for an older task, the following situations can happen: + // 1. Awaited task is deep in the stack of the awaiter. + // 2. A group of awaiter threads end up depending on some tasks buried in the stack + // of their worker threads in such a way that progress can't be made. + // Both would entail a deadlock. Some may be handled here in the WorkerThreadPool + // with some extra logic and bookkeeping. However, there would still be unavoidable + // cases of deadlock because of the way waiting threads process outstanding tasks. + // Taking into account there's no feasible solution for every possible case + // with the current design, we just simply reject attempts to await on older tasks, + // with a specific error code that signals the situation so the caller can handle it. + task_mutex.unlock(); + return ERR_BUSY; + } + + if (caller_pool_thread) { + task->waiting_pool++; + } else { + task->waiting_user++; + } + + task_mutex.unlock(); + + if (caller_pool_thread) { + _wait_collaboratively(caller_pool_thread, task); + task->waiting_pool--; + if (task->waiting_pool == 0 && task->waiting_user == 0) { + tasks.erase(p_task_id); + task_allocator.free(task); + } + } else { + task->done_semaphore.wait(); + task_mutex.lock(); + task->waiting_user--; + if (task->waiting_pool == 0 && task->waiting_user == 0) { + tasks.erase(p_task_id); + task_allocator.free(task); } + task_mutex.unlock(); + } + + return OK; +} + +void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) { + // Keep processing tasks until the condition to stop waiting is met. - task->waiting++; - - bool is_low_prio_waiting_for_another = false; - if (!use_native_low_priority_threads) { - // Deadlock prevention: - // If all low-prio tasks are waiting for other low-prio tasks and there are no more free low-prio slots, - // we have a no progressable situation. We can apply a workaround, consisting in promoting an awaited queued - // low-prio task to the schedule queue so it can run and break the "impasse". - // NOTE: A similar reasoning could be made about high priority tasks, but there are usually much more - // than low-prio. Therefore, a deadlock there would only happen when dealing with a very complex task graph - // or when there are too few worker threads (limited platforms or exotic settings). If that turns out to be - // an issue in the real world, a further fix can be applied against that. - if (task->low_priority) { - bool awaiter_is_a_low_prio_task = thread_ids.has(Thread::get_caller_id()) && threads[thread_ids[Thread::get_caller_id()]].current_low_prio_task; - if (awaiter_is_a_low_prio_task) { - is_low_prio_waiting_for_another = true; - low_priority_tasks_awaiting_others++; - if (low_priority_tasks_awaiting_others == low_priority_tasks_running) { - _prevent_low_prio_saturation_deadlock(); +#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed) + + while (true) { + Task *task_to_process = nullptr; + { + MutexLock lock(task_mutex); + bool was_signaled = p_caller_pool_thread->signaled; + p_caller_pool_thread->signaled = false; + + if (IS_WAIT_OVER) { + p_caller_pool_thread->yield_is_over = false; + if (!exit_threads && was_signaled) { + // This thread was awaken for some additional reason, but it's about to exit. + // Let's find out what may be pending and forward the requests. + uint32_t to_process = task_queue.first() ? 1 : 0; + uint32_t to_promote = p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0; + if (to_process || to_promote) { + // This thread must be left alone since it won't loop again. + p_caller_pool_thread->signaled = true; + _notify_threads(p_caller_pool_thread, to_process, to_promote); } } + + break; } - } - task_mutex.unlock(); + if (!exit_threads) { + if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) { + if (_try_promote_low_priority_task()) { + _notify_threads(p_caller_pool_thread, 1, 0); + } + } - if (use_native_low_priority_threads && task->low_priority) { - task->done_semaphore.wait(); - } else { - bool current_is_pool_thread = thread_ids.has(Thread::get_caller_id()); - if (current_is_pool_thread) { - // We are an actual process thread, we must not be blocked so continue processing stuff if available. - bool must_exit = false; - while (true) { - if (task->done_semaphore.try_wait()) { - // If done, exit - break; + if (singleton->task_queue.first()) { + task_to_process = task_queue.first()->self(); + task_queue.remove(task_queue.first()); + } + + if (!task_to_process) { + p_caller_pool_thread->awaited_task = p_task; + + if (flushing_cmd_queue) { + flushing_cmd_queue->unlock(); } - if (!must_exit) { - if (task_available_semaphore.try_wait()) { - if (exit_threads) { - must_exit = true; - } else { - // Solve tasks while they are around. - bool safe_for_nodes_backup = is_current_thread_safe_for_nodes(); - _process_task_queue(); - set_current_thread_safe_for_nodes(safe_for_nodes_backup); - continue; - } - } else if (!use_native_low_priority_threads && task->low_priority) { - // A low prioriry task started waiting, so see if we can move a pending one to the high priority queue. - task_mutex.lock(); - bool post = _try_promote_low_priority_task(); - task_mutex.unlock(); - if (post) { - task_available_semaphore.post(); - } - } + p_caller_pool_thread->cond_var.wait(lock); + if (flushing_cmd_queue) { + flushing_cmd_queue->lock(); } - OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance. + + DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER); + p_caller_pool_thread->awaited_task = nullptr; } - } else { - task->done_semaphore.wait(); } } - task_mutex.lock(); - if (is_low_prio_waiting_for_another) { - low_priority_tasks_awaiting_others--; + if (task_to_process) { + _process_task(task_to_process); } - - task->waiting--; } +} - if (task->waiting == 0) { - if (use_native_low_priority_threads && task->low_priority) { - task->low_priority_thread->wait_to_finish(); - native_thread_allocator.free(task->low_priority_thread); +void WorkerThreadPool::yield() { + int th_index = get_thread_index(); + ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread."); + _wait_collaboratively(&threads[th_index], ThreadData::YIELDING); +} + +void WorkerThreadPool::notify_yield_over(TaskID p_task_id) { + task_mutex.lock(); + Task **taskp = tasks.getptr(p_task_id); + if (!taskp) { + task_mutex.unlock(); + ERR_FAIL_MSG("Invalid Task ID."); + } + Task *task = *taskp; + if (task->pool_thread_index == -1) { // Completed or not started yet. + if (!task->completed) { + // This avoids a race condition where a task is created and yield-over called before it's processed. + task->pending_notify_yield_over = true; } - tasks.erase(p_task_id); - task_allocator.free(task); + task_mutex.unlock(); + return; } + ThreadData &td = threads[task->pool_thread_index]; + td.yield_is_over = true; + td.signaled = true; + td.cond_var.notify_one(); + task_mutex.unlock(); - return OK; } WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) { @@ -446,11 +551,8 @@ WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_ca } groups[id] = group; - task_mutex.unlock(); - for (int i = 0; i < p_tasks; i++) { - _post_task(tasks_posted[i], p_high_priority); - } + _post_tasks_and_unlock(tasks_posted, p_tasks, p_high_priority); return id; } @@ -487,28 +589,24 @@ bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const { } void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { +#ifdef THREADS_ENABLED task_mutex.lock(); Group **groupp = groups.getptr(p_group); task_mutex.unlock(); if (!groupp) { - ERR_FAIL_MSG("Invalid Group ID"); + ERR_FAIL_MSG("Invalid Group ID."); } - Group *group = *groupp; - if (group->low_priority_native_tasks.size() > 0) { - for (Task *task : group->low_priority_native_tasks) { - task->low_priority_thread->wait_to_finish(); - task_mutex.lock(); - native_thread_allocator.free(task->low_priority_thread); - task_allocator.free(task); - task_mutex.unlock(); - } + { + Group *group = *groupp; - task_mutex.lock(); - group_allocator.free(group); - task_mutex.unlock(); - } else { + if (flushing_cmd_queue) { + flushing_cmd_queue->unlock(); + } group->done_semaphore.wait(); + if (flushing_cmd_queue) { + flushing_cmd_queue->lock(); + } uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment. uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later. @@ -524,21 +622,31 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) { task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread. groups.erase(p_group); task_mutex.unlock(); +#endif } -void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_priority, float p_low_priority_task_ratio) { +int WorkerThreadPool::get_thread_index() { + Thread::ID tid = Thread::get_caller_id(); + return singleton->thread_ids.has(tid) ? singleton->thread_ids[tid] : -1; +} + +void WorkerThreadPool::thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue) { + ERR_FAIL_COND(flushing_cmd_queue != nullptr); + flushing_cmd_queue = p_queue; +} + +void WorkerThreadPool::thread_exit_command_queue_mt_flush() { + ERR_FAIL_NULL(flushing_cmd_queue); + flushing_cmd_queue = nullptr; +} + +void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio) { ERR_FAIL_COND(threads.size() > 0); if (p_thread_count < 0) { p_thread_count = OS::get_singleton()->get_default_thread_pool_size(); } - if (p_use_native_threads_low_priority) { - max_low_priority_threads = 0; - } else { - max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1); - } - - use_native_low_priority_threads = p_use_native_threads_low_priority; + max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1); threads.resize(p_thread_count); @@ -554,24 +662,33 @@ void WorkerThreadPool::finish() { return; } - task_mutex.lock(); - SelfList<Task> *E = low_priority_task_queue.first(); - while (E) { - print_error("Task waiting was never re-claimed: " + E->self()->description); - E = E->next(); + { + MutexLock lock(task_mutex); + SelfList<Task> *E = low_priority_task_queue.first(); + while (E) { + print_error("Task waiting was never re-claimed: " + E->self()->description); + E = E->next(); + } } - task_mutex.unlock(); - - exit_threads = true; - for (uint32_t i = 0; i < threads.size(); i++) { - task_available_semaphore.post(); + { + MutexLock lock(task_mutex); + exit_threads = true; + } + for (ThreadData &data : threads) { + data.cond_var.notify_one(); } - for (ThreadData &data : threads) { data.thread.wait_to_finish(); } + { + MutexLock lock(task_mutex); + for (KeyValue<TaskID, Task *> &E : tasks) { + task_allocator.free(E.value); + } + } + threads.clear(); } diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h index d4d9387765..a9cf260a0f 100644 --- a/core/object/worker_thread_pool.h +++ b/core/object/worker_thread_pool.h @@ -31,6 +31,7 @@ #ifndef WORKER_THREAD_POOL_H #define WORKER_THREAD_POOL_H +#include "core/os/condition_variable.h" #include "core/os/memory.h" #include "core/os/os.h" #include "core/os/semaphore.h" @@ -40,6 +41,8 @@ #include "core/templates/rid.h" #include "core/templates/safe_refcount.h" +class CommandQueueMT; + class WorkerThreadPool : public Object { GDCLASS(WorkerThreadPool, Object) public: @@ -60,7 +63,7 @@ private: }; struct Group { - GroupID self; + GroupID self = -1; SafeNumeric<uint32_t> index; SafeNumeric<uint32_t> completed_index; uint32_t max = 0; @@ -68,78 +71,104 @@ private: SafeFlag completed; SafeNumeric<uint32_t> finished; uint32_t tasks_used = 0; - TightLocalVector<Task *> low_priority_native_tasks; }; struct Task { + TaskID self = -1; Callable callable; void (*native_func)(void *) = nullptr; void (*native_group_func)(void *, uint32_t) = nullptr; void *native_func_userdata = nullptr; String description; - Semaphore done_semaphore; - bool completed = false; + Semaphore done_semaphore; // For user threads awaiting. + bool completed : 1; + bool pending_notify_yield_over : 1; Group *group = nullptr; SelfList<Task> task_elem; - uint32_t waiting = 0; + uint32_t waiting_pool = 0; + uint32_t waiting_user = 0; bool low_priority = false; BaseTemplateUserdata *template_userdata = nullptr; - Thread *low_priority_thread = nullptr; int pool_thread_index = -1; void free_template_userdata(); Task() : + completed(false), + pending_notify_yield_over(false), task_elem(this) {} }; - PagedAllocator<Task> task_allocator; - PagedAllocator<Group> group_allocator; - PagedAllocator<Thread> native_thread_allocator; + static const uint32_t TASKS_PAGE_SIZE = 1024; + static const uint32_t GROUPS_PAGE_SIZE = 256; + + PagedAllocator<Task, false, TASKS_PAGE_SIZE> task_allocator; + PagedAllocator<Group, false, GROUPS_PAGE_SIZE> group_allocator; SelfList<Task>::List low_priority_task_queue; SelfList<Task>::List task_queue; - Mutex task_mutex; - Semaphore task_available_semaphore; + BinaryMutex task_mutex; struct ThreadData { - uint32_t index; + static Task *const YIELDING; // Too bad constexpr doesn't work here. + + uint32_t index = 0; Thread thread; - Task *current_low_prio_task = nullptr; + bool ready_for_scripting : 1; + bool signaled : 1; + bool yield_is_over : 1; + Task *current_task = nullptr; + Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING). + ConditionVariable cond_var; + + ThreadData() : + ready_for_scripting(false), + signaled(false), + yield_is_over(false) {} }; TightLocalVector<ThreadData> threads; bool exit_threads = false; HashMap<Thread::ID, int> thread_ids; - HashMap<TaskID, Task *> tasks; - HashMap<GroupID, Group *> groups; + HashMap< + TaskID, + Task *, + HashMapHasherDefault, + HashMapComparatorDefault<TaskID>, + PagedAllocator<HashMapElement<TaskID, Task *>, false, TASKS_PAGE_SIZE>> + tasks; + HashMap< + GroupID, + Group *, + HashMapHasherDefault, + HashMapComparatorDefault<GroupID>, + PagedAllocator<HashMapElement<GroupID, Group *>, false, GROUPS_PAGE_SIZE>> + groups; - bool use_native_low_priority_threads = false; uint32_t max_low_priority_threads = 0; uint32_t low_priority_threads_used = 0; - uint32_t low_priority_tasks_running = 0; - uint32_t low_priority_tasks_awaiting_others = 0; + uint32_t notify_index = 0; // For rotating across threads, no help distributing load. uint64_t last_task = 1; static void _thread_function(void *p_user); - static void _native_low_priority_thread_function(void *p_user); - void _process_task_queue(); void _process_task(Task *task); - void _post_task(Task *p_task, bool p_high_priority); + void _post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority); + void _notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count); bool _try_promote_low_priority_task(); - void _prevent_low_prio_saturation_deadlock(); static WorkerThreadPool *singleton; + static thread_local CommandQueueMT *flushing_cmd_queue; + TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description); GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description); - template <class C, class M, class U> + template <typename C, typename M, typename U> struct TaskUserData : public BaseTemplateUserdata { C *instance; M method; @@ -149,7 +178,7 @@ private: } }; - template <class C, class M, class U> + template <typename C, typename M, typename U> struct GroupUserData : public BaseTemplateUserdata { C *instance; M method; @@ -159,11 +188,13 @@ private: } }; + void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task); + protected: static void _bind_methods(); public: - template <class C, class M, class U> + template <typename C, typename M, typename U> TaskID add_template_task(C *p_instance, M p_method, U p_userdata, bool p_high_priority = false, const String &p_description = String()) { typedef TaskUserData<C, M, U> TUD; TUD *ud = memnew(TUD); @@ -178,7 +209,10 @@ public: bool is_task_completed(TaskID p_task_id) const; Error wait_for_task_completion(TaskID p_task_id); - template <class C, class M, class U> + void yield(); + void notify_yield_over(TaskID p_task_id); + + template <typename C, typename M, typename U> GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) { typedef GroupUserData<C, M, U> GroupUD; GroupUD *ud = memnew(GroupUD); @@ -196,7 +230,12 @@ public: _FORCE_INLINE_ int get_thread_count() const { return threads.size(); } static WorkerThreadPool *get_singleton() { return singleton; } - void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3); + static int get_thread_index(); + + static void thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue); + static void thread_exit_command_queue_mt_flush(); + + void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3); void finish(); WorkerThreadPool(); ~WorkerThreadPool(); diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h index 6037ff327d..fa1355e98c 100644 --- a/core/os/condition_variable.h +++ b/core/os/condition_variable.h @@ -31,7 +31,18 @@ #ifndef CONDITION_VARIABLE_H #define CONDITION_VARIABLE_H +#include "core/os/mutex.h" + +#ifdef THREADS_ENABLED + +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.condition_variable.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <condition_variable> +#define THREADING_NAMESPACE std +#endif // An object one or multiple threads can wait on a be notified by some other. // Normally, you want to use a semaphore for such scenarios, but when the @@ -40,12 +51,12 @@ // own mutex to tie the wait-notify to some other behavior, you need to use this. class ConditionVariable { - mutable std::condition_variable condition; + mutable THREADING_NAMESPACE::condition_variable condition; public: - template <class BinaryMutexT> + template <typename BinaryMutexT> _ALWAYS_INLINE_ void wait(const MutexLock<BinaryMutexT> &p_lock) const { - condition.wait(const_cast<std::unique_lock<std::mutex> &>(p_lock.lock)); + condition.wait(const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(p_lock.lock)); } _ALWAYS_INLINE_ void notify_one() const { @@ -57,4 +68,16 @@ public: } }; +#else // No threads. + +class ConditionVariable { +public: + template <typename BinaryMutexT> + void wait(const MutexLock<BinaryMutexT> &p_lock) const {} + void notify_one() const {} + void notify_all() const {} +}; + +#endif // THREADS_ENABLED + #endif // CONDITION_VARIABLE_H diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp index 6078882839..973c216d15 100644 --- a/core/os/keyboard.cpp +++ b/core/os/keyboard.cpp @@ -410,7 +410,7 @@ Key find_keycode(const String &p_codestr) { return keycode; } - String last_part = code_parts[code_parts.size() - 1]; + const String &last_part = code_parts[code_parts.size() - 1]; const _KeyCodeText *kct = &_keycodes[0]; while (kct->text) { @@ -422,7 +422,7 @@ Key find_keycode(const String &p_codestr) { } for (int part = 0; part < code_parts.size() - 1; part++) { - String code_part = code_parts[part]; + const String &code_part = code_parts[part]; if (code_part.nocasecmp_to(find_keycode_name(Key::SHIFT)) == 0) { keycode |= KeyModifierMask::SHIFT; } else if (code_part.nocasecmp_to(find_keycode_name(Key::CTRL)) == 0) { diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 785972d31d..2051973336 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -260,6 +260,12 @@ enum class KeyModifierMask { GROUP_SWITCH = (1 << 30) }; +enum class KeyLocation { + UNSPECIFIED, + LEFT, + RIGHT +}; + // To avoid having unnecessary operators, only define the ones that are needed. constexpr Key operator-(uint32_t a, Key b) { diff --git a/core/os/main_loop.h b/core/os/main_loop.h index b45eb38aeb..e48541d074 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -62,6 +62,7 @@ public: }; virtual void initialize(); + virtual void iteration_prepare() {} virtual bool physics_process(double p_time); virtual bool process(double p_time); virtual void finalize(); diff --git a/core/os/memory.cpp b/core/os/memory.cpp index 5f6216a5f1..32c316e58e 100644 --- a/core/os/memory.cpp +++ b/core/os/memory.cpp @@ -72,23 +72,23 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) { bool prepad = p_pad_align; #endif - void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0)); + void *mem = malloc(p_bytes + (prepad ? DATA_OFFSET : 0)); ERR_FAIL_NULL_V(mem, nullptr); alloc_count.increment(); if (prepad) { - uint64_t *s = (uint64_t *)mem; - *s = p_bytes; - uint8_t *s8 = (uint8_t *)mem; + uint64_t *s = (uint64_t *)(s8 + SIZE_OFFSET); + *s = p_bytes; + #ifdef DEBUG_ENABLED uint64_t new_mem_usage = mem_usage.add(p_bytes); max_usage.exchange_if_greater(new_mem_usage); #endif - return s8 + PAD_ALIGN; + return s8 + DATA_OFFSET; } else { return mem; } @@ -108,8 +108,8 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { #endif if (prepad) { - mem -= PAD_ALIGN; - uint64_t *s = (uint64_t *)mem; + mem -= DATA_OFFSET; + uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET); #ifdef DEBUG_ENABLED if (p_bytes > *s) { @@ -126,14 +126,14 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) { } else { *s = p_bytes; - mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN); + mem = (uint8_t *)realloc(mem, p_bytes + DATA_OFFSET); ERR_FAIL_NULL_V(mem, nullptr); - s = (uint64_t *)mem; + s = (uint64_t *)(mem + SIZE_OFFSET); *s = p_bytes; - return mem + PAD_ALIGN; + return mem + DATA_OFFSET; } } else { mem = (uint8_t *)realloc(mem, p_bytes); @@ -158,10 +158,10 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) { alloc_count.decrement(); if (prepad) { - mem -= PAD_ALIGN; + mem -= DATA_OFFSET; #ifdef DEBUG_ENABLED - uint64_t *s = (uint64_t *)mem; + uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET); mem_usage.sub(*s); #endif diff --git a/core/os/memory.h b/core/os/memory.h index a0524b0ea2..d03e08d785 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -38,10 +38,6 @@ #include <new> #include <type_traits> -#ifndef PAD_ALIGN -#define PAD_ALIGN 16 //must always be greater than this at much -#endif - class Memory { #ifdef DEBUG_ENABLED static SafeNumeric<uint64_t> mem_usage; @@ -51,6 +47,17 @@ class Memory { static SafeNumeric<uint64_t> alloc_count; public: + // Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t + // ┌─────────────────┬──┬────────────────┬──┬───────────... + // │ uint64_t │░░│ uint64_t │░░│ T[] + // │ alloc size │░░│ element count │░░│ data + // └─────────────────┴──┴────────────────┴──┴───────────... + // Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET + + static constexpr size_t SIZE_OFFSET = 0; + static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t))); + static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t))); + static void *alloc_static(size_t p_bytes, bool p_pad_align = false); static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false); static void free_static(void *p_ptr, bool p_pad_align = false); @@ -85,7 +92,7 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d _ALWAYS_INLINE_ void postinitialize_handler(void *) {} -template <class T> +template <typename T> _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) { postinitialize_handler(p_obj); return p_obj; @@ -100,24 +107,24 @@ _ALWAYS_INLINE_ bool predelete_handler(void *) { return true; } -template <class T> +template <typename T> void memdelete(T *p_class) { if (!predelete_handler(p_class)) { return; // doesn't want to be deleted } - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { p_class->~T(); } Memory::free_static(p_class, false); } -template <class T, class A> +template <typename T, typename A> void memdelete_allocator(T *p_class) { if (!predelete_handler(p_class)) { return; // doesn't want to be deleted } - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { p_class->~T(); } @@ -133,6 +140,10 @@ void memdelete_allocator(T *p_class) { #define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count) +_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) { + return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET); +} + template <typename T> T *memnew_arr_template(size_t p_elements) { if (p_elements == 0) { @@ -142,12 +153,14 @@ T *memnew_arr_template(size_t p_elements) { same strategy used by std::vector, and the Vector class, so it should be safe.*/ size_t len = sizeof(T) * p_elements; - uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true); + uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true); T *failptr = nullptr; //get rid of a warning ERR_FAIL_NULL_V(mem, failptr); - *(mem - 1) = p_elements; - if (!std::is_trivially_constructible<T>::value) { + uint64_t *_elem_count_ptr = _get_element_count_ptr(mem); + *(_elem_count_ptr) = p_elements; + + if constexpr (!std::is_trivially_constructible_v<T>) { T *elems = (T *)mem; /* call operator new */ @@ -166,16 +179,18 @@ T *memnew_arr_template(size_t p_elements) { template <typename T> size_t memarr_len(const T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; - return *(ptr - 1); + uint8_t *ptr = (uint8_t *)p_class; + uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr); + return *(_elem_count_ptr); } template <typename T> void memdelete_arr(T *p_class) { - uint64_t *ptr = (uint64_t *)p_class; + uint8_t *ptr = (uint8_t *)p_class; - if (!std::is_trivially_destructible<T>::value) { - uint64_t elem_count = *(ptr - 1); + if constexpr (!std::is_trivially_destructible_v<T>) { + uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr); + uint64_t elem_count = *(_elem_count_ptr); for (uint64_t i = 0; i < elem_count; i++) { p_class[i].~T(); @@ -198,10 +213,10 @@ struct _GlobalNilClass { static _GlobalNil _nil; }; -template <class T> +template <typename T> class DefaultTypedAllocator { public: - template <class... Args> + template <typename... Args> _FORCE_INLINE_ T *new_allocation(const Args &&...p_args) { return memnew(T(p_args...)); } _FORCE_INLINE_ void delete_allocation(T *p_allocation) { memdelete(p_allocation); } }; diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index 037851661b..6870c84b49 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -42,9 +42,10 @@ void MIDIDriver::set_singleton() { singleton = this; } -void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length) { +void MIDIDriver::receive_input_packet(int device_index, uint64_t timestamp, uint8_t *data, uint32_t length) { Ref<InputEventMIDI> event; event.instantiate(); + event->set_device(device_index); uint32_t param_position = 1; if (length >= 1) { diff --git a/core/os/midi_driver.h b/core/os/midi_driver.h index 6ad21c319e..cad3d8189e 100644 --- a/core/os/midi_driver.h +++ b/core/os/midi_driver.h @@ -51,7 +51,7 @@ public: virtual PackedStringArray get_connected_inputs(); - static void receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_t length); + static void receive_input_packet(int device_index, uint64_t timestamp, uint8_t *data, uint32_t length); MIDIDriver(); virtual ~MIDIDriver() {} diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp index 7dbb60590b..9a8a2a2961 100644 --- a/core/os/mutex.cpp +++ b/core/os/mutex.cpp @@ -40,7 +40,11 @@ void _global_unlock() { _global_mutex.unlock(); } -template class MutexImpl<std::recursive_mutex>; -template class MutexImpl<std::mutex>; -template class MutexLock<MutexImpl<std::recursive_mutex>>; -template class MutexLock<MutexImpl<std::mutex>>; +#ifdef THREADS_ENABLED + +template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>; +template class MutexImpl<THREADING_NAMESPACE::mutex>; +template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>; +template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>; + +#endif diff --git a/core/os/mutex.h b/core/os/mutex.h index cee0f8af74..3e7aa81bc1 100644 --- a/core/os/mutex.h +++ b/core/os/mutex.h @@ -34,12 +34,21 @@ #include "core/error/error_macros.h" #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <mutex> +#define THREADING_NAMESPACE std +#endif -template <class MutexT> +#ifdef THREADS_ENABLED + +template <typename MutexT> class MutexLock; -template <class StdMutexT> +template <typename StdMutexT> class MutexImpl { friend class MutexLock<MutexImpl<StdMutexT>>; @@ -61,91 +70,45 @@ public: } }; -// A very special kind of mutex, used in scenarios where these -// requirements hold at the same time: -// - Must be used with a condition variable (only binary mutexes are suitable). -// - Must have recursive semnantics (or simulate, as this one does). -// The implementation keeps the lock count in TS. Therefore, only -// one object of each version of the template can exists; hence the Tag argument. -// Tags must be unique across the Godot codebase. -// Also, don't forget to declare the thread_local variable on each use. -template <int Tag> -class SafeBinaryMutex { - friend class MutexLock<SafeBinaryMutex>; - - using StdMutexType = std::mutex; +template <typename MutexT> +class MutexLock { + friend class ConditionVariable; - mutable std::mutex mutex; - static thread_local uint32_t count; + THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock; public: - _ALWAYS_INLINE_ void lock() const { - if (++count == 1) { - mutex.lock(); - } - } + explicit MutexLock(const MutexT &p_mutex) : + lock(p_mutex.mutex) {} +}; - _ALWAYS_INLINE_ void unlock() const { - DEV_ASSERT(count); - if (--count == 0) { - mutex.unlock(); - } - } +using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use +using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care - _ALWAYS_INLINE_ bool try_lock() const { - if (count) { - count++; - return true; - } else { - if (mutex.try_lock()) { - count++; - return true; - } else { - return false; - } - } - } +extern template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>; +extern template class MutexImpl<THREADING_NAMESPACE::mutex>; +extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>; +extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>; - ~SafeBinaryMutex() { - DEV_ASSERT(!count); - } -}; - -template <class MutexT> -class MutexLock { - friend class ConditionVariable; +#else // No threads. - std::unique_lock<typename MutexT::StdMutexType> lock; +class MutexImpl { + mutable THREADING_NAMESPACE::mutex mutex; public: - _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) : - lock(p_mutex.mutex){}; + void lock() const {} + void unlock() const {} + bool try_lock() const { return true; } }; -// This specialization is needed so manual locking and MutexLock can be used -// at the same time on a SafeBinaryMutex. -template <int Tag> -class MutexLock<SafeBinaryMutex<Tag>> { - friend class ConditionVariable; - - std::unique_lock<std::mutex> lock; - +template <typename MutexT> +class MutexLock { public: - _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) : - lock(p_mutex.mutex) { - SafeBinaryMutex<Tag>::count++; - }; - _ALWAYS_INLINE_ ~MutexLock() { - SafeBinaryMutex<Tag>::count--; - }; + MutexLock(const MutexT &p_mutex) {} }; -using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use -using BinaryMutex = MutexImpl<std::mutex>; // Non-recursive, handle with care +using Mutex = MutexImpl; +using BinaryMutex = MutexImpl; -extern template class MutexImpl<std::recursive_mutex>; -extern template class MutexImpl<std::mutex>; -extern template class MutexLock<MutexImpl<std::recursive_mutex>>; -extern template class MutexLock<MutexImpl<std::mutex>>; +#endif // THREADS_ENABLED #endif // MUTEX_H diff --git a/core/os/os.cpp b/core/os/os.cpp index 991b179e1f..8582888740 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -39,7 +39,15 @@ #include "core/version_generated.gen.h" #include <stdarg.h> + +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.thread.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <thread> +#define THREADING_NAMESPACE std +#endif OS *OS::singleton = nullptr; uint64_t OS::target_ticks = 0; @@ -290,7 +298,7 @@ String OS::get_system_dir(SystemDir p_dir, bool p_shared_storage) const { return "."; } -Error OS::shell_open(String p_uri) { +Error OS::shell_open(const String &p_uri) { return ERR_UNAVAILABLE; } @@ -359,7 +367,7 @@ String OS::get_unique_id() const { } int OS::get_processor_count() const { - return std::thread::hardware_concurrency(); + return THREADING_NAMESPACE::thread::hardware_concurrency(); } String OS::get_processor_name() const { @@ -490,6 +498,18 @@ bool OS::has_feature(const String &p_feature) { } #endif +#if defined(IOS_SIMULATOR) + if (p_feature == "simulator") { + return true; + } +#endif + +#ifdef THREADS_ENABLED + if (p_feature == "threads") { + return true; + } +#endif + if (_check_internal_feature_support(p_feature)) { return true; } @@ -612,17 +632,22 @@ String OS::get_benchmark_file() { return benchmark_file; } -void OS::benchmark_begin_measure(const String &p_what) { +void OS::benchmark_begin_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec(); + Pair<String, String> mark_key(p_context, p_what); + ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what)); + + benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec(); #endif } -void OS::benchmark_end_measure(const String &p_what) { +void OS::benchmark_end_measure(const String &p_context, const String &p_what) { #ifdef TOOLS_ENABLED - uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what]; - double total_f = double(total) / double(1000000); + Pair<String, String> mark_key(p_context, p_what); + ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what)); - startup_benchmark_json[p_what] = total_f; + uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key]; + double total_f = double(total) / double(1000000); + benchmark_marks_final[mark_key] = total_f; #endif } @@ -631,19 +656,33 @@ void OS::benchmark_dump() { if (!use_benchmark) { return; } + if (!benchmark_file.is_empty()) { Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE); if (f.is_valid()) { + Dictionary benchmark_marks; + for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) { + const String mark_key = vformat("[%s] %s", E.key.first, E.key.second); + benchmark_marks[mark_key] = E.value; + } + Ref<JSON> json; json.instantiate(); - f->store_string(json->stringify(startup_benchmark_json, "\t", false, true)); + f->store_string(json->stringify(benchmark_marks, "\t", false, true)); } } else { - List<Variant> keys; - startup_benchmark_json.get_key_list(&keys); + HashMap<String, String> results; + for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) { + if (E.key.first == "Startup" && !results.has(E.key.first)) { + results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first. + } + + results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000)); + } + print_line("BENCHMARK:"); - for (const Variant &K : keys) { - print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec."); + for (const KeyValue<String, String> &E : results) { + print_line(vformat("\t[%s]\n%s", E.key, E.value)); } } #endif diff --git a/core/os/os.h b/core/os/os.h index cc5ebe1bc8..069a3876af 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -56,7 +56,8 @@ class OS { bool _verbose_stdout = false; bool _debug_stdout = false; String _local_clipboard; - int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure + // Assume success by default, all failure cases need to set EXIT_FAILURE explicitly. + int _exit_code = EXIT_SUCCESS; bool _allow_hidpi = false; bool _allow_layered = false; bool _stdout_enabled = true; @@ -79,14 +80,14 @@ class OS { // For tracking benchmark data bool use_benchmark = false; String benchmark_file; - HashMap<String, uint64_t> start_benchmark_from; - Dictionary startup_benchmark_json; + HashMap<Pair<String, String>, uint64_t, PairHash<String, String>> benchmark_marks_from; + HashMap<Pair<String, String>, double, PairHash<String, String>> benchmark_marks_final; protected: void _set_logger(CompositeLogger *p_logger); public: - typedef void (*ImeCallback)(void *p_inp, String p_text, Point2 p_selection); + typedef void (*ImeCallback)(void *p_inp, const String &p_text, Point2 p_selection); typedef bool (*HasServerFeatureCallback)(const String &p_feature); enum RenderThreadMode { @@ -109,8 +110,8 @@ protected: virtual void initialize() = 0; virtual void initialize_joypads() = 0; - void set_current_rendering_driver_name(String p_driver_name) { _current_rendering_driver_name = p_driver_name; } - void set_current_rendering_method(String p_name) { _current_rendering_method = p_name; } + void set_current_rendering_driver_name(const String &p_driver_name) { _current_rendering_driver_name = p_driver_name; } + void set_current_rendering_method(const String &p_name) { _current_rendering_method = p_name; } void set_display_driver_id(int p_display_driver_id) { _display_driver_id = p_display_driver_id; } @@ -152,9 +153,16 @@ public: virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); - virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) { return ERR_UNAVAILABLE; } + struct GDExtensionData { + bool also_set_library_path = false; + String *r_resolved_path = nullptr; + bool generate_temp_files = false; + PackedStringArray *library_dependencies = nullptr; + }; + + virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, GDExtensionData *p_data = nullptr) { return ERR_UNAVAILABLE; } virtual Error close_dynamic_library(void *p_library_handle) { return ERR_UNAVAILABLE; } - virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String p_name, void *&p_symbol_handle, bool p_optional = false) { return ERR_UNAVAILABLE; } + virtual Error get_dynamic_library_symbol_handle(void *p_library_handle, const String &p_name, void *&p_symbol_handle, bool p_optional = false) { return ERR_UNAVAILABLE; } virtual void set_low_processor_usage_mode(bool p_enabled); virtual bool is_in_low_processor_usage_mode() const; @@ -169,14 +177,16 @@ public: virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); }; virtual String get_executable_path() const; virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0; + virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) { return Dictionary(); } virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0; virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); }; virtual Error kill(const ProcessID &p_pid) = 0; virtual int get_process_id() const; virtual bool is_process_running(const ProcessID &p_pid) const = 0; + virtual int get_process_exit_code(const ProcessID &p_pid) const = 0; virtual void vibrate_handheld(int p_duration_ms = 500) {} - virtual Error shell_open(String p_uri); + virtual Error shell_open(const String &p_uri); virtual Error shell_show_in_file_manager(String p_path, bool p_open_folder = true); virtual Error set_cwd(const String &p_cwd); @@ -313,8 +323,8 @@ public: bool is_use_benchmark_set(); void set_benchmark_file(const String &p_benchmark_file); String get_benchmark_file(); - virtual void benchmark_begin_measure(const String &p_what); - virtual void benchmark_end_measure(const String &p_what); + virtual void benchmark_begin_measure(const String &p_context, const String &p_what); + virtual void benchmark_end_measure(const String &p_context, const String &p_what); virtual void benchmark_dump(); virtual void process_and_drop_events() {} diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp index acbaed4ce8..9a993cd14f 100644 --- a/core/os/pool_allocator.cpp +++ b/core/os/pool_allocator.cpp @@ -36,12 +36,13 @@ #include "core/string/print_string.h" #define COMPACT_CHUNK(m_entry, m_to_pos) \ - do { \ + if constexpr (true) { \ void *_dst = &((unsigned char *)pool)[m_to_pos]; \ void *_src = &((unsigned char *)pool)[(m_entry).pos]; \ memmove(_dst, _src, aligned((m_entry).len)); \ (m_entry).pos = m_to_pos; \ - } while (0); + } else \ + ((void)0) void PoolAllocator::mt_lock() const { } diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h index a232fcb1ce..e5bb6aec2b 100644 --- a/core/os/rw_lock.h +++ b/core/os/rw_lock.h @@ -33,10 +33,17 @@ #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.shared_mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <shared_mutex> +#define THREADING_NAMESPACE std +#endif class RWLock { - mutable std::shared_timed_mutex mutex; + mutable THREADING_NAMESPACE::shared_timed_mutex mutex; public: // Lock the RWLock, block if locked by someone else. diff --git a/core/os/safe_binary_mutex.h b/core/os/safe_binary_mutex.h new file mode 100644 index 0000000000..1e98cc074c --- /dev/null +++ b/core/os/safe_binary_mutex.h @@ -0,0 +1,124 @@ +/**************************************************************************/ +/* safe_binary_mutex.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 SAFE_BINARY_MUTEX_H +#define SAFE_BINARY_MUTEX_H + +#include "core/error/error_macros.h" +#include "core/os/mutex.h" +#include "core/typedefs.h" + +#ifdef THREADS_ENABLED + +// A very special kind of mutex, used in scenarios where these +// requirements hold at the same time: +// - Must be used with a condition variable (only binary mutexes are suitable). +// - Must have recursive semnantics (or simulate, as this one does). +// The implementation keeps the lock count in TS. Therefore, only +// one object of each version of the template can exists; hence the Tag argument. +// Tags must be unique across the Godot codebase. +// Also, don't forget to declare the thread_local variable on each use. +template <int Tag> +class SafeBinaryMutex { + friend class MutexLock<SafeBinaryMutex>; + + using StdMutexType = THREADING_NAMESPACE::mutex; + + mutable THREADING_NAMESPACE::mutex mutex; + static thread_local uint32_t count; + +public: + _ALWAYS_INLINE_ void lock() const { + if (++count == 1) { + mutex.lock(); + } + } + + _ALWAYS_INLINE_ void unlock() const { + DEV_ASSERT(count); + if (--count == 0) { + mutex.unlock(); + } + } + + _ALWAYS_INLINE_ bool try_lock() const { + if (count) { + count++; + return true; + } else { + if (mutex.try_lock()) { + count++; + return true; + } else { + return false; + } + } + } + + ~SafeBinaryMutex() { + DEV_ASSERT(!count); + } +}; + +// This specialization is needed so manual locking and MutexLock can be used +// at the same time on a SafeBinaryMutex. +template <int Tag> +class MutexLock<SafeBinaryMutex<Tag>> { + friend class ConditionVariable; + + THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock; + +public: + _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) : + lock(p_mutex.mutex) { + SafeBinaryMutex<Tag>::count++; + }; + _ALWAYS_INLINE_ ~MutexLock() { + SafeBinaryMutex<Tag>::count--; + }; +}; + +#else // No threads. + +template <int Tag> +class SafeBinaryMutex : public MutexImpl { + static thread_local uint32_t count; +}; + +template <int Tag> +class MutexLock<SafeBinaryMutex<Tag>> { +public: + MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {} + ~MutexLock() {} +}; + +#endif // THREADS_ENABLED + +#endif // SAFE_BINARY_MUTEX_H diff --git a/core/os/semaphore.h b/core/os/semaphore.h index 66dfb3ee02..19ef1dedc0 100644 --- a/core/os/semaphore.h +++ b/core/os/semaphore.h @@ -31,33 +31,47 @@ #ifndef SEMAPHORE_H #define SEMAPHORE_H +#include <cstdint> + +#ifdef THREADS_ENABLED + #include "core/error/error_list.h" #include "core/typedefs.h" #ifdef DEBUG_ENABLED #include "core/error/error_macros.h" #endif +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.condition_variable.h" +#include "thirdparty/mingw-std-threads/mingw.mutex.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <condition_variable> #include <mutex> +#define THREADING_NAMESPACE std +#endif class Semaphore { private: - mutable std::mutex mutex; - mutable std::condition_variable condition; + mutable THREADING_NAMESPACE::mutex mutex; + mutable THREADING_NAMESPACE::condition_variable condition; mutable uint32_t count = 0; // Initialized as locked. #ifdef DEBUG_ENABLED mutable uint32_t awaiters = 0; #endif public: - _ALWAYS_INLINE_ void post() const { + _ALWAYS_INLINE_ void post(uint32_t p_count = 1) const { std::lock_guard lock(mutex); - count++; - condition.notify_one(); + count += p_count; + for (uint32_t i = 0; i < p_count; ++i) { + condition.notify_one(); + } } _ALWAYS_INLINE_ void wait() const { - std::unique_lock lock(mutex); + THREADING_NAMESPACE::unique_lock lock(mutex); #ifdef DEBUG_ENABLED ++awaiters; #endif @@ -116,10 +130,23 @@ public: "A Semaphore object is being destroyed while one or more threads are still waiting on it.\n" "Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup."); // And now, the hacky countermeasure (i.e., leak the condition variable). - new (&condition) std::condition_variable(); + new (&condition) THREADING_NAMESPACE::condition_variable(); } } #endif }; +#else // No threads. + +class Semaphore { +public: + void post(uint32_t p_count = 1) const {} + void wait() const {} + bool try_wait() const { + return true; + } +}; + +#endif // THREADS_ENABLED + #endif // SEMAPHORE_H diff --git a/core/os/shared_object.h b/core/os/shared_object.h new file mode 100644 index 0000000000..88423bed13 --- /dev/null +++ b/core/os/shared_object.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* shared_object.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 SHARED_OBJECT_H +#define SHARED_OBJECT_H + +#include "core/string/ustring.h" +#include "core/templates/vector.h" + +struct SharedObject { + String path; + Vector<String> tags; + String target; + + SharedObject(const String &p_path, const Vector<String> &p_tags, const String &p_target) : + path(p_path), + tags(p_tags), + target(p_target) { + } + + SharedObject() {} +}; + +#endif // SHARED_OBJECT_H diff --git a/core/os/spin_lock.h b/core/os/spin_lock.h index 93ea782b60..d386cd5890 100644 --- a/core/os/spin_lock.h +++ b/core/os/spin_lock.h @@ -33,6 +33,25 @@ #include "core/typedefs.h" +#if defined(__APPLE__) + +#include <os/lock.h> + +class SpinLock { + mutable os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT; + +public: + _ALWAYS_INLINE_ void lock() const { + os_unfair_lock_lock(&_lock); + } + + _ALWAYS_INLINE_ void unlock() const { + os_unfair_lock_unlock(&_lock); + } +}; + +#else + #include <atomic> class SpinLock { @@ -49,4 +68,6 @@ public: } }; +#endif // __APPLE__ + #endif // SPIN_LOCK_H diff --git a/core/os/thread.cpp b/core/os/thread.cpp index 03e2c5409d..afc74364f6 100644 --- a/core/os/thread.cpp +++ b/core/os/thread.cpp @@ -33,19 +33,22 @@ #include "thread.h" +#ifdef THREADS_ENABLED #include "core/object/script_language.h" #include "core/templates/safe_refcount.h" -Thread::PlatformFunctions Thread::platform_functions; - SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1. thread_local Thread::ID Thread::caller_id = Thread::UNASSIGNED_ID; +#endif + +Thread::PlatformFunctions Thread::platform_functions; void Thread::_set_platform_functions(const PlatformFunctions &p_functions) { platform_functions = p_functions; } +#ifdef THREADS_ENABLED void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) { Thread::caller_id = p_caller_id; if (platform_functions.set_priority) { @@ -69,8 +72,7 @@ void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_cal Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) { ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it."); id = id_counter.increment(); - std::thread new_thread(&Thread::callback, id, p_settings, p_callback, p_user); - thread.swap(new_thread); + thread = THREADING_NAMESPACE::thread(&Thread::callback, id, p_settings, p_callback, p_user); return id; } @@ -82,8 +84,7 @@ void Thread::wait_to_finish() { ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started."); ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait."); thread.join(); - std::thread empty_thread; - thread.swap(empty_thread); + thread = THREADING_NAMESPACE::thread(); id = UNASSIGNED_ID; } @@ -109,4 +110,6 @@ Thread::~Thread() { } } +#endif // THREADS_ENABLED + #endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/os/thread.h b/core/os/thread.h index 3e307adfff..a0ecc24c91 100644 --- a/core/os/thread.h +++ b/core/os/thread.h @@ -42,10 +42,19 @@ #include "core/templates/safe_refcount.h" #include "core/typedefs.h" +#ifdef MINGW_ENABLED +#define MINGW_STDTHREAD_REDUNDANCY_WARNING +#include "thirdparty/mingw-std-threads/mingw.thread.h" +#define THREADING_NAMESPACE mingw_stdthread +#else #include <thread> +#define THREADING_NAMESPACE std +#endif class String; +#ifdef THREADS_ENABLED + class Thread { public: typedef void (*Callback)(void *p_userdata); @@ -79,15 +88,15 @@ public: private: friend class Main; + static PlatformFunctions platform_functions; + ID id = UNASSIGNED_ID; static SafeNumeric<uint64_t> id_counter; static thread_local ID caller_id; - std::thread thread; + THREADING_NAMESPACE::thread thread; static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata); - static PlatformFunctions platform_functions; - static void make_main_thread() { caller_id = MAIN_ID; } static void release_main_thread() { caller_id = UNASSIGNED_ID; } @@ -118,6 +127,64 @@ public: ~Thread(); }; +#else // No threads. + +class Thread { +public: + typedef void (*Callback)(void *p_userdata); + + typedef uint64_t ID; + + enum : ID { + UNASSIGNED_ID = 0, + MAIN_ID = 1 + }; + + enum Priority { + PRIORITY_LOW, + PRIORITY_NORMAL, + PRIORITY_HIGH + }; + + struct Settings { + Priority priority; + Settings() { priority = PRIORITY_NORMAL; } + }; + + struct PlatformFunctions { + Error (*set_name)(const String &) = nullptr; + void (*set_priority)(Thread::Priority) = nullptr; + void (*init)() = nullptr; + void (*wrapper)(Thread::Callback, void *) = nullptr; + void (*term)() = nullptr; + }; + +private: + friend class Main; + + static PlatformFunctions platform_functions; + + static void make_main_thread() {} + static void release_main_thread() {} + +public: + static void _set_platform_functions(const PlatformFunctions &p_functions); + + _FORCE_INLINE_ ID get_id() const { return 0; } + _FORCE_INLINE_ static ID get_caller_id() { return MAIN_ID; } + _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; } + + _FORCE_INLINE_ static bool is_main_thread() { return true; } + + static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; } + + void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {} + bool is_started() const { return false; } + void wait_to_finish() {} +}; + +#endif // THREADS_ENABLED + #endif // THREAD_H #endif // PLATFORM_THREAD_OVERRIDE diff --git a/core/os/time.cpp b/core/os/time.cpp index bad5cc2e4f..d1d3588d09 100644 --- a/core/os/time.cpp +++ b/core/os/time.cpp @@ -189,9 +189,6 @@ static const uint8_t MONTH_DAYS_TABLE[2][12] = { Time *Time::singleton = nullptr; Time *Time::get_singleton() { - if (!singleton) { - memnew(Time); - } return singleton; } @@ -258,7 +255,7 @@ String Time::get_time_string_from_unix_time(int64_t p_unix_time_val) const { return vformat("%02d:%02d:%02d", hour, minute, second); } -Dictionary Time::get_datetime_dict_from_datetime_string(String p_datetime, bool p_weekday) const { +Dictionary Time::get_datetime_dict_from_datetime_string(const String &p_datetime, bool p_weekday) const { PARSE_ISO8601_STRING(Dictionary()) Dictionary dict; dict[YEAR_KEY] = year; @@ -276,7 +273,7 @@ Dictionary Time::get_datetime_dict_from_datetime_string(String p_datetime, bool return dict; } -String Time::get_datetime_string_from_datetime_dict(const Dictionary p_datetime, bool p_use_space) const { +String Time::get_datetime_string_from_datetime_dict(const Dictionary &p_datetime, bool p_use_space) const { ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), "", "Invalid datetime Dictionary: Dictionary is empty."); EXTRACT_FROM_DICTIONARY VALIDATE_YMDHMS("") @@ -290,7 +287,7 @@ String Time::get_datetime_string_from_datetime_dict(const Dictionary p_datetime, return timestamp; } -int64_t Time::get_unix_time_from_datetime_dict(const Dictionary p_datetime) const { +int64_t Time::get_unix_time_from_datetime_dict(const Dictionary &p_datetime) const { ERR_FAIL_COND_V_MSG(p_datetime.is_empty(), 0, "Invalid datetime Dictionary: Dictionary is empty"); EXTRACT_FROM_DICTIONARY VALIDATE_YMDHMS(0) @@ -298,7 +295,7 @@ int64_t Time::get_unix_time_from_datetime_dict(const Dictionary p_datetime) cons return day_number * SECONDS_PER_DAY + hour * 3600 + minute * 60 + second; } -int64_t Time::get_unix_time_from_datetime_string(String p_datetime) const { +int64_t Time::get_unix_time_from_datetime_string(const String &p_datetime) const { PARSE_ISO8601_STRING(-1) VALIDATE_YMDHMS(0) YMD_TO_DAY_NUMBER diff --git a/core/os/time.h b/core/os/time.h index ccd2d92b8b..6550cd905e 100644 --- a/core/os/time.h +++ b/core/os/time.h @@ -59,10 +59,10 @@ public: String get_datetime_string_from_unix_time(int64_t p_unix_time_val, bool p_use_space = false) const; String get_date_string_from_unix_time(int64_t p_unix_time_val) const; String get_time_string_from_unix_time(int64_t p_unix_time_val) const; - Dictionary get_datetime_dict_from_datetime_string(String p_datetime, bool p_weekday = true) const; - String get_datetime_string_from_datetime_dict(const Dictionary p_datetime, bool p_use_space = false) const; - int64_t get_unix_time_from_datetime_dict(const Dictionary p_datetime) const; - int64_t get_unix_time_from_datetime_string(String p_datetime) const; + Dictionary get_datetime_dict_from_datetime_string(const String &p_datetime, bool p_weekday = true) const; + String get_datetime_string_from_datetime_dict(const Dictionary &p_datetime, bool p_use_space = false) const; + int64_t get_unix_time_from_datetime_dict(const Dictionary &p_datetime) const; + int64_t get_unix_time_from_datetime_string(const String &p_datetime) const; String get_offset_string_from_offset_minutes(int64_t p_offset_minutes) const; // Methods that get information from OS. diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 4ad9dd43c4..4c1ed8a69a 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -102,6 +102,7 @@ static core_bind::Marshalls *_marshalls = nullptr; static core_bind::EngineDebugger *_engine_debugger = nullptr; static IP *ip = nullptr; +static Time *_time = nullptr; static core_bind::Geometry2D *_geometry_2d = nullptr; static core_bind::Geometry3D *_geometry_3d = nullptr; @@ -120,13 +121,15 @@ static ResourceUID *resource_uid = nullptr; static bool _is_core_extensions_registered = false; void register_core_types() { - OS::get_singleton()->benchmark_begin_measure("register_core_types"); + OS::get_singleton()->benchmark_begin_measure("Core", "Register Types"); + //consistency check static_assert(sizeof(Callable) <= 16); ObjectDB::setup(); StringName::setup(); + _time = memnew(Time); ResourceLoader::initialize(); register_global_constants(); @@ -296,7 +299,7 @@ void register_core_types() { worker_thread_pool = memnew(WorkerThreadPool); - OS::get_singleton()->benchmark_end_measure("register_core_types"); + OS::get_singleton()->benchmark_end_measure("Core", "Register Types"); } void register_core_settings() { @@ -306,11 +309,12 @@ void register_core_settings() { GLOBAL_DEF(PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt"), ""); GLOBAL_DEF("threading/worker_pool/max_threads", -1); - GLOBAL_DEF("threading/worker_pool/use_system_threads_for_low_priority_tasks", true); GLOBAL_DEF("threading/worker_pool/low_priority_thread_ratio", 0.3); } void register_core_singletons() { + OS::get_singleton()->benchmark_begin_measure("Core", "Register Singletons"); + GDREGISTER_CLASS(ProjectSettings); GDREGISTER_ABSTRACT_CLASS(IP); GDREGISTER_CLASS(core_bind::Geometry2D); @@ -346,25 +350,35 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool)); + + OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons"); } void register_core_extensions() { + OS::get_singleton()->benchmark_begin_measure("Core", "Register Extensions"); + // Hardcoded for now. GDExtension::initialize_gdextensions(); gdextension_manager->load_extensions(); gdextension_manager->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); _is_core_extensions_registered = true; + + OS::get_singleton()->benchmark_end_measure("Core", "Register Extensions"); } void unregister_core_extensions() { + OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Extensions"); + if (_is_core_extensions_registered) { gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); } GDExtension::finalize_gdextensions(); + + OS::get_singleton()->benchmark_end_measure("Core", "Unregister Extensions"); } void unregister_core_types() { - OS::get_singleton()->benchmark_begin_measure("unregister_core_types"); + OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Types"); // Destroy singletons in reverse order to ensure dependencies are not broken. @@ -424,6 +438,7 @@ void unregister_core_types() { ResourceLoader::finalize(); ClassDB::cleanup_defaults(); + memdelete(_time); ObjectDB::cleanup(); Variant::unregister_types(); @@ -435,5 +450,5 @@ void unregister_core_types() { CoreStringNames::free(); StringName::cleanup(); - OS::get_singleton()->benchmark_end_measure("unregister_core_types"); + OS::get_singleton()->benchmark_end_measure("Core", "Unregister Types"); } diff --git a/core/string/char_range.inc b/core/string/char_range.inc index be5516e243..b7d6bbdb61 100644 --- a/core/string/char_range.inc +++ b/core/string/char_range.inc @@ -1,5 +1,5 @@ /**************************************************************************/ -/* color_names.inc */ +/* char_range.inc */ /**************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -38,7 +38,7 @@ struct CharRange { char32_t end; }; -static CharRange xid_start[] = { +inline constexpr CharRange xid_start[] = { { 0x41, 0x5a }, { 0x5f, 0x5f }, { 0x61, 0x7a }, @@ -689,10 +689,9 @@ static CharRange xid_start[] = { { 0x2ceb0, 0x2ebe0 }, { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, - { 0x0, 0x0 }, }; -static CharRange xid_continue[] = { +inline constexpr CharRange xid_continue[] = { { 0x30, 0x39 }, { 0x41, 0x5a }, { 0x5f, 0x5f }, @@ -1450,7 +1449,1316 @@ static CharRange xid_continue[] = { { 0x2f800, 0x2fa1d }, { 0x30000, 0x3134a }, { 0xe0100, 0xe01ef }, - { 0x0, 0x0 }, +}; + +inline constexpr CharRange uppercase_letter[] = { + { 0x41, 0x5a }, + { 0xc0, 0xd6 }, + { 0xd8, 0xde }, + { 0x100, 0x100 }, + { 0x102, 0x102 }, + { 0x104, 0x104 }, + { 0x106, 0x106 }, + { 0x108, 0x108 }, + { 0x10a, 0x10a }, + { 0x10c, 0x10c }, + { 0x10e, 0x10e }, + { 0x110, 0x110 }, + { 0x112, 0x112 }, + { 0x114, 0x114 }, + { 0x116, 0x116 }, + { 0x118, 0x118 }, + { 0x11a, 0x11a }, + { 0x11c, 0x11c }, + { 0x11e, 0x11e }, + { 0x120, 0x120 }, + { 0x122, 0x122 }, + { 0x124, 0x124 }, + { 0x126, 0x126 }, + { 0x128, 0x128 }, + { 0x12a, 0x12a }, + { 0x12c, 0x12c }, + { 0x12e, 0x12e }, + { 0x130, 0x130 }, + { 0x132, 0x132 }, + { 0x134, 0x134 }, + { 0x136, 0x136 }, + { 0x139, 0x139 }, + { 0x13b, 0x13b }, + { 0x13d, 0x13d }, + { 0x13f, 0x13f }, + { 0x141, 0x141 }, + { 0x143, 0x143 }, + { 0x145, 0x145 }, + { 0x147, 0x147 }, + { 0x14a, 0x14a }, + { 0x14c, 0x14c }, + { 0x14e, 0x14e }, + { 0x150, 0x150 }, + { 0x152, 0x152 }, + { 0x154, 0x154 }, + { 0x156, 0x156 }, + { 0x158, 0x158 }, + { 0x15a, 0x15a }, + { 0x15c, 0x15c }, + { 0x15e, 0x15e }, + { 0x160, 0x160 }, + { 0x162, 0x162 }, + { 0x164, 0x164 }, + { 0x166, 0x166 }, + { 0x168, 0x168 }, + { 0x16a, 0x16a }, + { 0x16c, 0x16c }, + { 0x16e, 0x16e }, + { 0x170, 0x170 }, + { 0x172, 0x172 }, + { 0x174, 0x174 }, + { 0x176, 0x176 }, + { 0x178, 0x179 }, + { 0x17b, 0x17b }, + { 0x17d, 0x17d }, + { 0x181, 0x182 }, + { 0x184, 0x184 }, + { 0x186, 0x187 }, + { 0x189, 0x18b }, + { 0x18e, 0x191 }, + { 0x193, 0x194 }, + { 0x196, 0x198 }, + { 0x19c, 0x19d }, + { 0x19f, 0x1a0 }, + { 0x1a2, 0x1a2 }, + { 0x1a4, 0x1a4 }, + { 0x1a6, 0x1a7 }, + { 0x1a9, 0x1a9 }, + { 0x1ac, 0x1ac }, + { 0x1ae, 0x1af }, + { 0x1b1, 0x1b3 }, + { 0x1b5, 0x1b5 }, + { 0x1b7, 0x1b8 }, + { 0x1bc, 0x1bc }, + { 0x1c4, 0x1c4 }, + { 0x1c7, 0x1c7 }, + { 0x1ca, 0x1ca }, + { 0x1cd, 0x1cd }, + { 0x1cf, 0x1cf }, + { 0x1d1, 0x1d1 }, + { 0x1d3, 0x1d3 }, + { 0x1d5, 0x1d5 }, + { 0x1d7, 0x1d7 }, + { 0x1d9, 0x1d9 }, + { 0x1db, 0x1db }, + { 0x1de, 0x1de }, + { 0x1e0, 0x1e0 }, + { 0x1e2, 0x1e2 }, + { 0x1e4, 0x1e4 }, + { 0x1e6, 0x1e6 }, + { 0x1e8, 0x1e8 }, + { 0x1ea, 0x1ea }, + { 0x1ec, 0x1ec }, + { 0x1ee, 0x1ee }, + { 0x1f1, 0x1f1 }, + { 0x1f4, 0x1f4 }, + { 0x1f6, 0x1f8 }, + { 0x1fa, 0x1fa }, + { 0x1fc, 0x1fc }, + { 0x1fe, 0x1fe }, + { 0x200, 0x200 }, + { 0x202, 0x202 }, + { 0x204, 0x204 }, + { 0x206, 0x206 }, + { 0x208, 0x208 }, + { 0x20a, 0x20a }, + { 0x20c, 0x20c }, + { 0x20e, 0x20e }, + { 0x210, 0x210 }, + { 0x212, 0x212 }, + { 0x214, 0x214 }, + { 0x216, 0x216 }, + { 0x218, 0x218 }, + { 0x21a, 0x21a }, + { 0x21c, 0x21c }, + { 0x21e, 0x21e }, + { 0x220, 0x220 }, + { 0x222, 0x222 }, + { 0x224, 0x224 }, + { 0x226, 0x226 }, + { 0x228, 0x228 }, + { 0x22a, 0x22a }, + { 0x22c, 0x22c }, + { 0x22e, 0x22e }, + { 0x230, 0x230 }, + { 0x232, 0x232 }, + { 0x23a, 0x23b }, + { 0x23d, 0x23e }, + { 0x241, 0x241 }, + { 0x243, 0x246 }, + { 0x248, 0x248 }, + { 0x24a, 0x24a }, + { 0x24c, 0x24c }, + { 0x24e, 0x24e }, + { 0x370, 0x370 }, + { 0x372, 0x372 }, + { 0x376, 0x376 }, + { 0x37f, 0x37f }, + { 0x386, 0x386 }, + { 0x388, 0x38a }, + { 0x38c, 0x38c }, + { 0x38e, 0x38f }, + { 0x391, 0x3a1 }, + { 0x3a3, 0x3ab }, + { 0x3cf, 0x3cf }, + { 0x3d2, 0x3d4 }, + { 0x3d8, 0x3d8 }, + { 0x3da, 0x3da }, + { 0x3dc, 0x3dc }, + { 0x3de, 0x3de }, + { 0x3e0, 0x3e0 }, + { 0x3e2, 0x3e2 }, + { 0x3e4, 0x3e4 }, + { 0x3e6, 0x3e6 }, + { 0x3e8, 0x3e8 }, + { 0x3ea, 0x3ea }, + { 0x3ec, 0x3ec }, + { 0x3ee, 0x3ee }, + { 0x3f4, 0x3f4 }, + { 0x3f7, 0x3f7 }, + { 0x3f9, 0x3fa }, + { 0x3fd, 0x42f }, + { 0x460, 0x460 }, + { 0x462, 0x462 }, + { 0x464, 0x464 }, + { 0x466, 0x466 }, + { 0x468, 0x468 }, + { 0x46a, 0x46a }, + { 0x46c, 0x46c }, + { 0x46e, 0x46e }, + { 0x470, 0x470 }, + { 0x472, 0x472 }, + { 0x474, 0x474 }, + { 0x476, 0x476 }, + { 0x478, 0x478 }, + { 0x47a, 0x47a }, + { 0x47c, 0x47c }, + { 0x47e, 0x47e }, + { 0x480, 0x480 }, + { 0x48a, 0x48a }, + { 0x48c, 0x48c }, + { 0x48e, 0x48e }, + { 0x490, 0x490 }, + { 0x492, 0x492 }, + { 0x494, 0x494 }, + { 0x496, 0x496 }, + { 0x498, 0x498 }, + { 0x49a, 0x49a }, + { 0x49c, 0x49c }, + { 0x49e, 0x49e }, + { 0x4a0, 0x4a0 }, + { 0x4a2, 0x4a2 }, + { 0x4a4, 0x4a4 }, + { 0x4a6, 0x4a6 }, + { 0x4a8, 0x4a8 }, + { 0x4aa, 0x4aa }, + { 0x4ac, 0x4ac }, + { 0x4ae, 0x4ae }, + { 0x4b0, 0x4b0 }, + { 0x4b2, 0x4b2 }, + { 0x4b4, 0x4b4 }, + { 0x4b6, 0x4b6 }, + { 0x4b8, 0x4b8 }, + { 0x4ba, 0x4ba }, + { 0x4bc, 0x4bc }, + { 0x4be, 0x4be }, + { 0x4c0, 0x4c1 }, + { 0x4c3, 0x4c3 }, + { 0x4c5, 0x4c5 }, + { 0x4c7, 0x4c7 }, + { 0x4c9, 0x4c9 }, + { 0x4cb, 0x4cb }, + { 0x4cd, 0x4cd }, + { 0x4d0, 0x4d0 }, + { 0x4d2, 0x4d2 }, + { 0x4d4, 0x4d4 }, + { 0x4d6, 0x4d6 }, + { 0x4d8, 0x4d8 }, + { 0x4da, 0x4da }, + { 0x4dc, 0x4dc }, + { 0x4de, 0x4de }, + { 0x4e0, 0x4e0 }, + { 0x4e2, 0x4e2 }, + { 0x4e4, 0x4e4 }, + { 0x4e6, 0x4e6 }, + { 0x4e8, 0x4e8 }, + { 0x4ea, 0x4ea }, + { 0x4ec, 0x4ec }, + { 0x4ee, 0x4ee }, + { 0x4f0, 0x4f0 }, + { 0x4f2, 0x4f2 }, + { 0x4f4, 0x4f4 }, + { 0x4f6, 0x4f6 }, + { 0x4f8, 0x4f8 }, + { 0x4fa, 0x4fa }, + { 0x4fc, 0x4fc }, + { 0x4fe, 0x4fe }, + { 0x500, 0x500 }, + { 0x502, 0x502 }, + { 0x504, 0x504 }, + { 0x506, 0x506 }, + { 0x508, 0x508 }, + { 0x50a, 0x50a }, + { 0x50c, 0x50c }, + { 0x50e, 0x50e }, + { 0x510, 0x510 }, + { 0x512, 0x512 }, + { 0x514, 0x514 }, + { 0x516, 0x516 }, + { 0x518, 0x518 }, + { 0x51a, 0x51a }, + { 0x51c, 0x51c }, + { 0x51e, 0x51e }, + { 0x520, 0x520 }, + { 0x522, 0x522 }, + { 0x524, 0x524 }, + { 0x526, 0x526 }, + { 0x528, 0x528 }, + { 0x52a, 0x52a }, + { 0x52c, 0x52c }, + { 0x52e, 0x52e }, + { 0x531, 0x556 }, + { 0x10a0, 0x10c5 }, + { 0x10c7, 0x10c7 }, + { 0x10cd, 0x10cd }, + { 0x13a0, 0x13f5 }, + { 0x1c90, 0x1cba }, + { 0x1cbd, 0x1cbf }, + { 0x1e00, 0x1e00 }, + { 0x1e02, 0x1e02 }, + { 0x1e04, 0x1e04 }, + { 0x1e06, 0x1e06 }, + { 0x1e08, 0x1e08 }, + { 0x1e0a, 0x1e0a }, + { 0x1e0c, 0x1e0c }, + { 0x1e0e, 0x1e0e }, + { 0x1e10, 0x1e10 }, + { 0x1e12, 0x1e12 }, + { 0x1e14, 0x1e14 }, + { 0x1e16, 0x1e16 }, + { 0x1e18, 0x1e18 }, + { 0x1e1a, 0x1e1a }, + { 0x1e1c, 0x1e1c }, + { 0x1e1e, 0x1e1e }, + { 0x1e20, 0x1e20 }, + { 0x1e22, 0x1e22 }, + { 0x1e24, 0x1e24 }, + { 0x1e26, 0x1e26 }, + { 0x1e28, 0x1e28 }, + { 0x1e2a, 0x1e2a }, + { 0x1e2c, 0x1e2c }, + { 0x1e2e, 0x1e2e }, + { 0x1e30, 0x1e30 }, + { 0x1e32, 0x1e32 }, + { 0x1e34, 0x1e34 }, + { 0x1e36, 0x1e36 }, + { 0x1e38, 0x1e38 }, + { 0x1e3a, 0x1e3a }, + { 0x1e3c, 0x1e3c }, + { 0x1e3e, 0x1e3e }, + { 0x1e40, 0x1e40 }, + { 0x1e42, 0x1e42 }, + { 0x1e44, 0x1e44 }, + { 0x1e46, 0x1e46 }, + { 0x1e48, 0x1e48 }, + { 0x1e4a, 0x1e4a }, + { 0x1e4c, 0x1e4c }, + { 0x1e4e, 0x1e4e }, + { 0x1e50, 0x1e50 }, + { 0x1e52, 0x1e52 }, + { 0x1e54, 0x1e54 }, + { 0x1e56, 0x1e56 }, + { 0x1e58, 0x1e58 }, + { 0x1e5a, 0x1e5a }, + { 0x1e5c, 0x1e5c }, + { 0x1e5e, 0x1e5e }, + { 0x1e60, 0x1e60 }, + { 0x1e62, 0x1e62 }, + { 0x1e64, 0x1e64 }, + { 0x1e66, 0x1e66 }, + { 0x1e68, 0x1e68 }, + { 0x1e6a, 0x1e6a }, + { 0x1e6c, 0x1e6c }, + { 0x1e6e, 0x1e6e }, + { 0x1e70, 0x1e70 }, + { 0x1e72, 0x1e72 }, + { 0x1e74, 0x1e74 }, + { 0x1e76, 0x1e76 }, + { 0x1e78, 0x1e78 }, + { 0x1e7a, 0x1e7a }, + { 0x1e7c, 0x1e7c }, + { 0x1e7e, 0x1e7e }, + { 0x1e80, 0x1e80 }, + { 0x1e82, 0x1e82 }, + { 0x1e84, 0x1e84 }, + { 0x1e86, 0x1e86 }, + { 0x1e88, 0x1e88 }, + { 0x1e8a, 0x1e8a }, + { 0x1e8c, 0x1e8c }, + { 0x1e8e, 0x1e8e }, + { 0x1e90, 0x1e90 }, + { 0x1e92, 0x1e92 }, + { 0x1e94, 0x1e94 }, + { 0x1e9e, 0x1e9e }, + { 0x1ea0, 0x1ea0 }, + { 0x1ea2, 0x1ea2 }, + { 0x1ea4, 0x1ea4 }, + { 0x1ea6, 0x1ea6 }, + { 0x1ea8, 0x1ea8 }, + { 0x1eaa, 0x1eaa }, + { 0x1eac, 0x1eac }, + { 0x1eae, 0x1eae }, + { 0x1eb0, 0x1eb0 }, + { 0x1eb2, 0x1eb2 }, + { 0x1eb4, 0x1eb4 }, + { 0x1eb6, 0x1eb6 }, + { 0x1eb8, 0x1eb8 }, + { 0x1eba, 0x1eba }, + { 0x1ebc, 0x1ebc }, + { 0x1ebe, 0x1ebe }, + { 0x1ec0, 0x1ec0 }, + { 0x1ec2, 0x1ec2 }, + { 0x1ec4, 0x1ec4 }, + { 0x1ec6, 0x1ec6 }, + { 0x1ec8, 0x1ec8 }, + { 0x1eca, 0x1eca }, + { 0x1ecc, 0x1ecc }, + { 0x1ece, 0x1ece }, + { 0x1ed0, 0x1ed0 }, + { 0x1ed2, 0x1ed2 }, + { 0x1ed4, 0x1ed4 }, + { 0x1ed6, 0x1ed6 }, + { 0x1ed8, 0x1ed8 }, + { 0x1eda, 0x1eda }, + { 0x1edc, 0x1edc }, + { 0x1ede, 0x1ede }, + { 0x1ee0, 0x1ee0 }, + { 0x1ee2, 0x1ee2 }, + { 0x1ee4, 0x1ee4 }, + { 0x1ee6, 0x1ee6 }, + { 0x1ee8, 0x1ee8 }, + { 0x1eea, 0x1eea }, + { 0x1eec, 0x1eec }, + { 0x1eee, 0x1eee }, + { 0x1ef0, 0x1ef0 }, + { 0x1ef2, 0x1ef2 }, + { 0x1ef4, 0x1ef4 }, + { 0x1ef6, 0x1ef6 }, + { 0x1ef8, 0x1ef8 }, + { 0x1efa, 0x1efa }, + { 0x1efc, 0x1efc }, + { 0x1efe, 0x1efe }, + { 0x1f08, 0x1f0f }, + { 0x1f18, 0x1f1d }, + { 0x1f28, 0x1f2f }, + { 0x1f38, 0x1f3f }, + { 0x1f48, 0x1f4d }, + { 0x1f59, 0x1f59 }, + { 0x1f5b, 0x1f5b }, + { 0x1f5d, 0x1f5d }, + { 0x1f5f, 0x1f5f }, + { 0x1f68, 0x1f6f }, + { 0x1fb8, 0x1fbb }, + { 0x1fc8, 0x1fcb }, + { 0x1fd8, 0x1fdb }, + { 0x1fe8, 0x1fec }, + { 0x1ff8, 0x1ffb }, + { 0x2102, 0x2102 }, + { 0x2107, 0x2107 }, + { 0x210b, 0x210d }, + { 0x2110, 0x2112 }, + { 0x2115, 0x2115 }, + { 0x2119, 0x211d }, + { 0x2124, 0x2124 }, + { 0x2126, 0x2126 }, + { 0x2128, 0x2128 }, + { 0x212a, 0x212d }, + { 0x2130, 0x2133 }, + { 0x213e, 0x213f }, + { 0x2145, 0x2145 }, + { 0x2183, 0x2183 }, + { 0x2c00, 0x2c2f }, + { 0x2c60, 0x2c60 }, + { 0x2c62, 0x2c64 }, + { 0x2c67, 0x2c67 }, + { 0x2c69, 0x2c69 }, + { 0x2c6b, 0x2c6b }, + { 0x2c6d, 0x2c70 }, + { 0x2c72, 0x2c72 }, + { 0x2c75, 0x2c75 }, + { 0x2c7e, 0x2c80 }, + { 0x2c82, 0x2c82 }, + { 0x2c84, 0x2c84 }, + { 0x2c86, 0x2c86 }, + { 0x2c88, 0x2c88 }, + { 0x2c8a, 0x2c8a }, + { 0x2c8c, 0x2c8c }, + { 0x2c8e, 0x2c8e }, + { 0x2c90, 0x2c90 }, + { 0x2c92, 0x2c92 }, + { 0x2c94, 0x2c94 }, + { 0x2c96, 0x2c96 }, + { 0x2c98, 0x2c98 }, + { 0x2c9a, 0x2c9a }, + { 0x2c9c, 0x2c9c }, + { 0x2c9e, 0x2c9e }, + { 0x2ca0, 0x2ca0 }, + { 0x2ca2, 0x2ca2 }, + { 0x2ca4, 0x2ca4 }, + { 0x2ca6, 0x2ca6 }, + { 0x2ca8, 0x2ca8 }, + { 0x2caa, 0x2caa }, + { 0x2cac, 0x2cac }, + { 0x2cae, 0x2cae }, + { 0x2cb0, 0x2cb0 }, + { 0x2cb2, 0x2cb2 }, + { 0x2cb4, 0x2cb4 }, + { 0x2cb6, 0x2cb6 }, + { 0x2cb8, 0x2cb8 }, + { 0x2cba, 0x2cba }, + { 0x2cbc, 0x2cbc }, + { 0x2cbe, 0x2cbe }, + { 0x2cc0, 0x2cc0 }, + { 0x2cc2, 0x2cc2 }, + { 0x2cc4, 0x2cc4 }, + { 0x2cc6, 0x2cc6 }, + { 0x2cc8, 0x2cc8 }, + { 0x2cca, 0x2cca }, + { 0x2ccc, 0x2ccc }, + { 0x2cce, 0x2cce }, + { 0x2cd0, 0x2cd0 }, + { 0x2cd2, 0x2cd2 }, + { 0x2cd4, 0x2cd4 }, + { 0x2cd6, 0x2cd6 }, + { 0x2cd8, 0x2cd8 }, + { 0x2cda, 0x2cda }, + { 0x2cdc, 0x2cdc }, + { 0x2cde, 0x2cde }, + { 0x2ce0, 0x2ce0 }, + { 0x2ce2, 0x2ce2 }, + { 0x2ceb, 0x2ceb }, + { 0x2ced, 0x2ced }, + { 0x2cf2, 0x2cf2 }, + { 0xa640, 0xa640 }, + { 0xa642, 0xa642 }, + { 0xa644, 0xa644 }, + { 0xa646, 0xa646 }, + { 0xa648, 0xa648 }, + { 0xa64a, 0xa64a }, + { 0xa64c, 0xa64c }, + { 0xa64e, 0xa64e }, + { 0xa650, 0xa650 }, + { 0xa652, 0xa652 }, + { 0xa654, 0xa654 }, + { 0xa656, 0xa656 }, + { 0xa658, 0xa658 }, + { 0xa65a, 0xa65a }, + { 0xa65c, 0xa65c }, + { 0xa65e, 0xa65e }, + { 0xa660, 0xa660 }, + { 0xa662, 0xa662 }, + { 0xa664, 0xa664 }, + { 0xa666, 0xa666 }, + { 0xa668, 0xa668 }, + { 0xa66a, 0xa66a }, + { 0xa66c, 0xa66c }, + { 0xa680, 0xa680 }, + { 0xa682, 0xa682 }, + { 0xa684, 0xa684 }, + { 0xa686, 0xa686 }, + { 0xa688, 0xa688 }, + { 0xa68a, 0xa68a }, + { 0xa68c, 0xa68c }, + { 0xa68e, 0xa68e }, + { 0xa690, 0xa690 }, + { 0xa692, 0xa692 }, + { 0xa694, 0xa694 }, + { 0xa696, 0xa696 }, + { 0xa698, 0xa698 }, + { 0xa69a, 0xa69a }, + { 0xa722, 0xa722 }, + { 0xa724, 0xa724 }, + { 0xa726, 0xa726 }, + { 0xa728, 0xa728 }, + { 0xa72a, 0xa72a }, + { 0xa72c, 0xa72c }, + { 0xa72e, 0xa72e }, + { 0xa732, 0xa732 }, + { 0xa734, 0xa734 }, + { 0xa736, 0xa736 }, + { 0xa738, 0xa738 }, + { 0xa73a, 0xa73a }, + { 0xa73c, 0xa73c }, + { 0xa73e, 0xa73e }, + { 0xa740, 0xa740 }, + { 0xa742, 0xa742 }, + { 0xa744, 0xa744 }, + { 0xa746, 0xa746 }, + { 0xa748, 0xa748 }, + { 0xa74a, 0xa74a }, + { 0xa74c, 0xa74c }, + { 0xa74e, 0xa74e }, + { 0xa750, 0xa750 }, + { 0xa752, 0xa752 }, + { 0xa754, 0xa754 }, + { 0xa756, 0xa756 }, + { 0xa758, 0xa758 }, + { 0xa75a, 0xa75a }, + { 0xa75c, 0xa75c }, + { 0xa75e, 0xa75e }, + { 0xa760, 0xa760 }, + { 0xa762, 0xa762 }, + { 0xa764, 0xa764 }, + { 0xa766, 0xa766 }, + { 0xa768, 0xa768 }, + { 0xa76a, 0xa76a }, + { 0xa76c, 0xa76c }, + { 0xa76e, 0xa76e }, + { 0xa779, 0xa779 }, + { 0xa77b, 0xa77b }, + { 0xa77d, 0xa77e }, + { 0xa780, 0xa780 }, + { 0xa782, 0xa782 }, + { 0xa784, 0xa784 }, + { 0xa786, 0xa786 }, + { 0xa78b, 0xa78b }, + { 0xa78d, 0xa78d }, + { 0xa790, 0xa790 }, + { 0xa792, 0xa792 }, + { 0xa796, 0xa796 }, + { 0xa798, 0xa798 }, + { 0xa79a, 0xa79a }, + { 0xa79c, 0xa79c }, + { 0xa79e, 0xa79e }, + { 0xa7a0, 0xa7a0 }, + { 0xa7a2, 0xa7a2 }, + { 0xa7a4, 0xa7a4 }, + { 0xa7a6, 0xa7a6 }, + { 0xa7a8, 0xa7a8 }, + { 0xa7aa, 0xa7ae }, + { 0xa7b0, 0xa7b4 }, + { 0xa7b6, 0xa7b6 }, + { 0xa7b8, 0xa7b8 }, + { 0xa7ba, 0xa7ba }, + { 0xa7bc, 0xa7bc }, + { 0xa7be, 0xa7be }, + { 0xa7c0, 0xa7c0 }, + { 0xa7c2, 0xa7c2 }, + { 0xa7c4, 0xa7c7 }, + { 0xa7c9, 0xa7c9 }, + { 0xa7d0, 0xa7d0 }, + { 0xa7d6, 0xa7d6 }, + { 0xa7d8, 0xa7d8 }, + { 0xa7f5, 0xa7f5 }, + { 0xff21, 0xff3a }, + { 0x10400, 0x10427 }, + { 0x104b0, 0x104d3 }, + { 0x10570, 0x1057a }, + { 0x1057c, 0x1058a }, + { 0x1058c, 0x10592 }, + { 0x10594, 0x10595 }, + { 0x10c80, 0x10cb2 }, + { 0x118a0, 0x118bf }, + { 0x16e40, 0x16e5f }, + { 0x1d400, 0x1d419 }, + { 0x1d434, 0x1d44d }, + { 0x1d468, 0x1d481 }, + { 0x1d49c, 0x1d49c }, + { 0x1d49e, 0x1d49f }, + { 0x1d4a2, 0x1d4a2 }, + { 0x1d4a5, 0x1d4a6 }, + { 0x1d4a9, 0x1d4ac }, + { 0x1d4ae, 0x1d4b5 }, + { 0x1d4d0, 0x1d4e9 }, + { 0x1d504, 0x1d505 }, + { 0x1d507, 0x1d50a }, + { 0x1d50d, 0x1d514 }, + { 0x1d516, 0x1d51c }, + { 0x1d538, 0x1d539 }, + { 0x1d53b, 0x1d53e }, + { 0x1d540, 0x1d544 }, + { 0x1d546, 0x1d546 }, + { 0x1d54a, 0x1d550 }, + { 0x1d56c, 0x1d585 }, + { 0x1d5a0, 0x1d5b9 }, + { 0x1d5d4, 0x1d5ed }, + { 0x1d608, 0x1d621 }, + { 0x1d63c, 0x1d655 }, + { 0x1d670, 0x1d689 }, + { 0x1d6a8, 0x1d6c0 }, + { 0x1d6e2, 0x1d6fa }, + { 0x1d71c, 0x1d734 }, + { 0x1d756, 0x1d76e }, + { 0x1d790, 0x1d7a8 }, + { 0x1d7ca, 0x1d7ca }, + { 0x1e900, 0x1e921 }, +}; + +inline constexpr CharRange lowercase_letter[] = { + { 0x61, 0x7a }, + { 0xb5, 0xb5 }, + { 0xdf, 0xf6 }, + { 0xf8, 0xff }, + { 0x101, 0x101 }, + { 0x103, 0x103 }, + { 0x105, 0x105 }, + { 0x107, 0x107 }, + { 0x109, 0x109 }, + { 0x10b, 0x10b }, + { 0x10d, 0x10d }, + { 0x10f, 0x10f }, + { 0x111, 0x111 }, + { 0x113, 0x113 }, + { 0x115, 0x115 }, + { 0x117, 0x117 }, + { 0x119, 0x119 }, + { 0x11b, 0x11b }, + { 0x11d, 0x11d }, + { 0x11f, 0x11f }, + { 0x121, 0x121 }, + { 0x123, 0x123 }, + { 0x125, 0x125 }, + { 0x127, 0x127 }, + { 0x129, 0x129 }, + { 0x12b, 0x12b }, + { 0x12d, 0x12d }, + { 0x12f, 0x12f }, + { 0x131, 0x131 }, + { 0x133, 0x133 }, + { 0x135, 0x135 }, + { 0x137, 0x138 }, + { 0x13a, 0x13a }, + { 0x13c, 0x13c }, + { 0x13e, 0x13e }, + { 0x140, 0x140 }, + { 0x142, 0x142 }, + { 0x144, 0x144 }, + { 0x146, 0x146 }, + { 0x148, 0x149 }, + { 0x14b, 0x14b }, + { 0x14d, 0x14d }, + { 0x14f, 0x14f }, + { 0x151, 0x151 }, + { 0x153, 0x153 }, + { 0x155, 0x155 }, + { 0x157, 0x157 }, + { 0x159, 0x159 }, + { 0x15b, 0x15b }, + { 0x15d, 0x15d }, + { 0x15f, 0x15f }, + { 0x161, 0x161 }, + { 0x163, 0x163 }, + { 0x165, 0x165 }, + { 0x167, 0x167 }, + { 0x169, 0x169 }, + { 0x16b, 0x16b }, + { 0x16d, 0x16d }, + { 0x16f, 0x16f }, + { 0x171, 0x171 }, + { 0x173, 0x173 }, + { 0x175, 0x175 }, + { 0x177, 0x177 }, + { 0x17a, 0x17a }, + { 0x17c, 0x17c }, + { 0x17e, 0x180 }, + { 0x183, 0x183 }, + { 0x185, 0x185 }, + { 0x188, 0x188 }, + { 0x18c, 0x18d }, + { 0x192, 0x192 }, + { 0x195, 0x195 }, + { 0x199, 0x19b }, + { 0x19e, 0x19e }, + { 0x1a1, 0x1a1 }, + { 0x1a3, 0x1a3 }, + { 0x1a5, 0x1a5 }, + { 0x1a8, 0x1a8 }, + { 0x1aa, 0x1ab }, + { 0x1ad, 0x1ad }, + { 0x1b0, 0x1b0 }, + { 0x1b4, 0x1b4 }, + { 0x1b6, 0x1b6 }, + { 0x1b9, 0x1ba }, + { 0x1bd, 0x1bf }, + { 0x1c6, 0x1c6 }, + { 0x1c9, 0x1c9 }, + { 0x1cc, 0x1cc }, + { 0x1ce, 0x1ce }, + { 0x1d0, 0x1d0 }, + { 0x1d2, 0x1d2 }, + { 0x1d4, 0x1d4 }, + { 0x1d6, 0x1d6 }, + { 0x1d8, 0x1d8 }, + { 0x1da, 0x1da }, + { 0x1dc, 0x1dd }, + { 0x1df, 0x1df }, + { 0x1e1, 0x1e1 }, + { 0x1e3, 0x1e3 }, + { 0x1e5, 0x1e5 }, + { 0x1e7, 0x1e7 }, + { 0x1e9, 0x1e9 }, + { 0x1eb, 0x1eb }, + { 0x1ed, 0x1ed }, + { 0x1ef, 0x1f0 }, + { 0x1f3, 0x1f3 }, + { 0x1f5, 0x1f5 }, + { 0x1f9, 0x1f9 }, + { 0x1fb, 0x1fb }, + { 0x1fd, 0x1fd }, + { 0x1ff, 0x1ff }, + { 0x201, 0x201 }, + { 0x203, 0x203 }, + { 0x205, 0x205 }, + { 0x207, 0x207 }, + { 0x209, 0x209 }, + { 0x20b, 0x20b }, + { 0x20d, 0x20d }, + { 0x20f, 0x20f }, + { 0x211, 0x211 }, + { 0x213, 0x213 }, + { 0x215, 0x215 }, + { 0x217, 0x217 }, + { 0x219, 0x219 }, + { 0x21b, 0x21b }, + { 0x21d, 0x21d }, + { 0x21f, 0x21f }, + { 0x221, 0x221 }, + { 0x223, 0x223 }, + { 0x225, 0x225 }, + { 0x227, 0x227 }, + { 0x229, 0x229 }, + { 0x22b, 0x22b }, + { 0x22d, 0x22d }, + { 0x22f, 0x22f }, + { 0x231, 0x231 }, + { 0x233, 0x239 }, + { 0x23c, 0x23c }, + { 0x23f, 0x240 }, + { 0x242, 0x242 }, + { 0x247, 0x247 }, + { 0x249, 0x249 }, + { 0x24b, 0x24b }, + { 0x24d, 0x24d }, + { 0x24f, 0x293 }, + { 0x295, 0x2af }, + { 0x371, 0x371 }, + { 0x373, 0x373 }, + { 0x377, 0x377 }, + { 0x37b, 0x37d }, + { 0x390, 0x390 }, + { 0x3ac, 0x3ce }, + { 0x3d0, 0x3d1 }, + { 0x3d5, 0x3d7 }, + { 0x3d9, 0x3d9 }, + { 0x3db, 0x3db }, + { 0x3dd, 0x3dd }, + { 0x3df, 0x3df }, + { 0x3e1, 0x3e1 }, + { 0x3e3, 0x3e3 }, + { 0x3e5, 0x3e5 }, + { 0x3e7, 0x3e7 }, + { 0x3e9, 0x3e9 }, + { 0x3eb, 0x3eb }, + { 0x3ed, 0x3ed }, + { 0x3ef, 0x3f3 }, + { 0x3f5, 0x3f5 }, + { 0x3f8, 0x3f8 }, + { 0x3fb, 0x3fc }, + { 0x430, 0x45f }, + { 0x461, 0x461 }, + { 0x463, 0x463 }, + { 0x465, 0x465 }, + { 0x467, 0x467 }, + { 0x469, 0x469 }, + { 0x46b, 0x46b }, + { 0x46d, 0x46d }, + { 0x46f, 0x46f }, + { 0x471, 0x471 }, + { 0x473, 0x473 }, + { 0x475, 0x475 }, + { 0x477, 0x477 }, + { 0x479, 0x479 }, + { 0x47b, 0x47b }, + { 0x47d, 0x47d }, + { 0x47f, 0x47f }, + { 0x481, 0x481 }, + { 0x48b, 0x48b }, + { 0x48d, 0x48d }, + { 0x48f, 0x48f }, + { 0x491, 0x491 }, + { 0x493, 0x493 }, + { 0x495, 0x495 }, + { 0x497, 0x497 }, + { 0x499, 0x499 }, + { 0x49b, 0x49b }, + { 0x49d, 0x49d }, + { 0x49f, 0x49f }, + { 0x4a1, 0x4a1 }, + { 0x4a3, 0x4a3 }, + { 0x4a5, 0x4a5 }, + { 0x4a7, 0x4a7 }, + { 0x4a9, 0x4a9 }, + { 0x4ab, 0x4ab }, + { 0x4ad, 0x4ad }, + { 0x4af, 0x4af }, + { 0x4b1, 0x4b1 }, + { 0x4b3, 0x4b3 }, + { 0x4b5, 0x4b5 }, + { 0x4b7, 0x4b7 }, + { 0x4b9, 0x4b9 }, + { 0x4bb, 0x4bb }, + { 0x4bd, 0x4bd }, + { 0x4bf, 0x4bf }, + { 0x4c2, 0x4c2 }, + { 0x4c4, 0x4c4 }, + { 0x4c6, 0x4c6 }, + { 0x4c8, 0x4c8 }, + { 0x4ca, 0x4ca }, + { 0x4cc, 0x4cc }, + { 0x4ce, 0x4cf }, + { 0x4d1, 0x4d1 }, + { 0x4d3, 0x4d3 }, + { 0x4d5, 0x4d5 }, + { 0x4d7, 0x4d7 }, + { 0x4d9, 0x4d9 }, + { 0x4db, 0x4db }, + { 0x4dd, 0x4dd }, + { 0x4df, 0x4df }, + { 0x4e1, 0x4e1 }, + { 0x4e3, 0x4e3 }, + { 0x4e5, 0x4e5 }, + { 0x4e7, 0x4e7 }, + { 0x4e9, 0x4e9 }, + { 0x4eb, 0x4eb }, + { 0x4ed, 0x4ed }, + { 0x4ef, 0x4ef }, + { 0x4f1, 0x4f1 }, + { 0x4f3, 0x4f3 }, + { 0x4f5, 0x4f5 }, + { 0x4f7, 0x4f7 }, + { 0x4f9, 0x4f9 }, + { 0x4fb, 0x4fb }, + { 0x4fd, 0x4fd }, + { 0x4ff, 0x4ff }, + { 0x501, 0x501 }, + { 0x503, 0x503 }, + { 0x505, 0x505 }, + { 0x507, 0x507 }, + { 0x509, 0x509 }, + { 0x50b, 0x50b }, + { 0x50d, 0x50d }, + { 0x50f, 0x50f }, + { 0x511, 0x511 }, + { 0x513, 0x513 }, + { 0x515, 0x515 }, + { 0x517, 0x517 }, + { 0x519, 0x519 }, + { 0x51b, 0x51b }, + { 0x51d, 0x51d }, + { 0x51f, 0x51f }, + { 0x521, 0x521 }, + { 0x523, 0x523 }, + { 0x525, 0x525 }, + { 0x527, 0x527 }, + { 0x529, 0x529 }, + { 0x52b, 0x52b }, + { 0x52d, 0x52d }, + { 0x52f, 0x52f }, + { 0x560, 0x588 }, + { 0x10d0, 0x10fa }, + { 0x10fd, 0x10ff }, + { 0x13f8, 0x13fd }, + { 0x1c80, 0x1c88 }, + { 0x1d00, 0x1d2b }, + { 0x1d6b, 0x1d77 }, + { 0x1d79, 0x1d9a }, + { 0x1e01, 0x1e01 }, + { 0x1e03, 0x1e03 }, + { 0x1e05, 0x1e05 }, + { 0x1e07, 0x1e07 }, + { 0x1e09, 0x1e09 }, + { 0x1e0b, 0x1e0b }, + { 0x1e0d, 0x1e0d }, + { 0x1e0f, 0x1e0f }, + { 0x1e11, 0x1e11 }, + { 0x1e13, 0x1e13 }, + { 0x1e15, 0x1e15 }, + { 0x1e17, 0x1e17 }, + { 0x1e19, 0x1e19 }, + { 0x1e1b, 0x1e1b }, + { 0x1e1d, 0x1e1d }, + { 0x1e1f, 0x1e1f }, + { 0x1e21, 0x1e21 }, + { 0x1e23, 0x1e23 }, + { 0x1e25, 0x1e25 }, + { 0x1e27, 0x1e27 }, + { 0x1e29, 0x1e29 }, + { 0x1e2b, 0x1e2b }, + { 0x1e2d, 0x1e2d }, + { 0x1e2f, 0x1e2f }, + { 0x1e31, 0x1e31 }, + { 0x1e33, 0x1e33 }, + { 0x1e35, 0x1e35 }, + { 0x1e37, 0x1e37 }, + { 0x1e39, 0x1e39 }, + { 0x1e3b, 0x1e3b }, + { 0x1e3d, 0x1e3d }, + { 0x1e3f, 0x1e3f }, + { 0x1e41, 0x1e41 }, + { 0x1e43, 0x1e43 }, + { 0x1e45, 0x1e45 }, + { 0x1e47, 0x1e47 }, + { 0x1e49, 0x1e49 }, + { 0x1e4b, 0x1e4b }, + { 0x1e4d, 0x1e4d }, + { 0x1e4f, 0x1e4f }, + { 0x1e51, 0x1e51 }, + { 0x1e53, 0x1e53 }, + { 0x1e55, 0x1e55 }, + { 0x1e57, 0x1e57 }, + { 0x1e59, 0x1e59 }, + { 0x1e5b, 0x1e5b }, + { 0x1e5d, 0x1e5d }, + { 0x1e5f, 0x1e5f }, + { 0x1e61, 0x1e61 }, + { 0x1e63, 0x1e63 }, + { 0x1e65, 0x1e65 }, + { 0x1e67, 0x1e67 }, + { 0x1e69, 0x1e69 }, + { 0x1e6b, 0x1e6b }, + { 0x1e6d, 0x1e6d }, + { 0x1e6f, 0x1e6f }, + { 0x1e71, 0x1e71 }, + { 0x1e73, 0x1e73 }, + { 0x1e75, 0x1e75 }, + { 0x1e77, 0x1e77 }, + { 0x1e79, 0x1e79 }, + { 0x1e7b, 0x1e7b }, + { 0x1e7d, 0x1e7d }, + { 0x1e7f, 0x1e7f }, + { 0x1e81, 0x1e81 }, + { 0x1e83, 0x1e83 }, + { 0x1e85, 0x1e85 }, + { 0x1e87, 0x1e87 }, + { 0x1e89, 0x1e89 }, + { 0x1e8b, 0x1e8b }, + { 0x1e8d, 0x1e8d }, + { 0x1e8f, 0x1e8f }, + { 0x1e91, 0x1e91 }, + { 0x1e93, 0x1e93 }, + { 0x1e95, 0x1e9d }, + { 0x1e9f, 0x1e9f }, + { 0x1ea1, 0x1ea1 }, + { 0x1ea3, 0x1ea3 }, + { 0x1ea5, 0x1ea5 }, + { 0x1ea7, 0x1ea7 }, + { 0x1ea9, 0x1ea9 }, + { 0x1eab, 0x1eab }, + { 0x1ead, 0x1ead }, + { 0x1eaf, 0x1eaf }, + { 0x1eb1, 0x1eb1 }, + { 0x1eb3, 0x1eb3 }, + { 0x1eb5, 0x1eb5 }, + { 0x1eb7, 0x1eb7 }, + { 0x1eb9, 0x1eb9 }, + { 0x1ebb, 0x1ebb }, + { 0x1ebd, 0x1ebd }, + { 0x1ebf, 0x1ebf }, + { 0x1ec1, 0x1ec1 }, + { 0x1ec3, 0x1ec3 }, + { 0x1ec5, 0x1ec5 }, + { 0x1ec7, 0x1ec7 }, + { 0x1ec9, 0x1ec9 }, + { 0x1ecb, 0x1ecb }, + { 0x1ecd, 0x1ecd }, + { 0x1ecf, 0x1ecf }, + { 0x1ed1, 0x1ed1 }, + { 0x1ed3, 0x1ed3 }, + { 0x1ed5, 0x1ed5 }, + { 0x1ed7, 0x1ed7 }, + { 0x1ed9, 0x1ed9 }, + { 0x1edb, 0x1edb }, + { 0x1edd, 0x1edd }, + { 0x1edf, 0x1edf }, + { 0x1ee1, 0x1ee1 }, + { 0x1ee3, 0x1ee3 }, + { 0x1ee5, 0x1ee5 }, + { 0x1ee7, 0x1ee7 }, + { 0x1ee9, 0x1ee9 }, + { 0x1eeb, 0x1eeb }, + { 0x1eed, 0x1eed }, + { 0x1eef, 0x1eef }, + { 0x1ef1, 0x1ef1 }, + { 0x1ef3, 0x1ef3 }, + { 0x1ef5, 0x1ef5 }, + { 0x1ef7, 0x1ef7 }, + { 0x1ef9, 0x1ef9 }, + { 0x1efb, 0x1efb }, + { 0x1efd, 0x1efd }, + { 0x1eff, 0x1f07 }, + { 0x1f10, 0x1f15 }, + { 0x1f20, 0x1f27 }, + { 0x1f30, 0x1f37 }, + { 0x1f40, 0x1f45 }, + { 0x1f50, 0x1f57 }, + { 0x1f60, 0x1f67 }, + { 0x1f70, 0x1f7d }, + { 0x1f80, 0x1f87 }, + { 0x1f90, 0x1f97 }, + { 0x1fa0, 0x1fa7 }, + { 0x1fb0, 0x1fb4 }, + { 0x1fb6, 0x1fb7 }, + { 0x1fbe, 0x1fbe }, + { 0x1fc2, 0x1fc4 }, + { 0x1fc6, 0x1fc7 }, + { 0x1fd0, 0x1fd3 }, + { 0x1fd6, 0x1fd7 }, + { 0x1fe0, 0x1fe7 }, + { 0x1ff2, 0x1ff4 }, + { 0x1ff6, 0x1ff7 }, + { 0x210a, 0x210a }, + { 0x210e, 0x210f }, + { 0x2113, 0x2113 }, + { 0x212f, 0x212f }, + { 0x2134, 0x2134 }, + { 0x2139, 0x2139 }, + { 0x213c, 0x213d }, + { 0x2146, 0x2149 }, + { 0x214e, 0x214e }, + { 0x2184, 0x2184 }, + { 0x2c30, 0x2c5f }, + { 0x2c61, 0x2c61 }, + { 0x2c65, 0x2c66 }, + { 0x2c68, 0x2c68 }, + { 0x2c6a, 0x2c6a }, + { 0x2c6c, 0x2c6c }, + { 0x2c71, 0x2c71 }, + { 0x2c73, 0x2c74 }, + { 0x2c76, 0x2c7b }, + { 0x2c81, 0x2c81 }, + { 0x2c83, 0x2c83 }, + { 0x2c85, 0x2c85 }, + { 0x2c87, 0x2c87 }, + { 0x2c89, 0x2c89 }, + { 0x2c8b, 0x2c8b }, + { 0x2c8d, 0x2c8d }, + { 0x2c8f, 0x2c8f }, + { 0x2c91, 0x2c91 }, + { 0x2c93, 0x2c93 }, + { 0x2c95, 0x2c95 }, + { 0x2c97, 0x2c97 }, + { 0x2c99, 0x2c99 }, + { 0x2c9b, 0x2c9b }, + { 0x2c9d, 0x2c9d }, + { 0x2c9f, 0x2c9f }, + { 0x2ca1, 0x2ca1 }, + { 0x2ca3, 0x2ca3 }, + { 0x2ca5, 0x2ca5 }, + { 0x2ca7, 0x2ca7 }, + { 0x2ca9, 0x2ca9 }, + { 0x2cab, 0x2cab }, + { 0x2cad, 0x2cad }, + { 0x2caf, 0x2caf }, + { 0x2cb1, 0x2cb1 }, + { 0x2cb3, 0x2cb3 }, + { 0x2cb5, 0x2cb5 }, + { 0x2cb7, 0x2cb7 }, + { 0x2cb9, 0x2cb9 }, + { 0x2cbb, 0x2cbb }, + { 0x2cbd, 0x2cbd }, + { 0x2cbf, 0x2cbf }, + { 0x2cc1, 0x2cc1 }, + { 0x2cc3, 0x2cc3 }, + { 0x2cc5, 0x2cc5 }, + { 0x2cc7, 0x2cc7 }, + { 0x2cc9, 0x2cc9 }, + { 0x2ccb, 0x2ccb }, + { 0x2ccd, 0x2ccd }, + { 0x2ccf, 0x2ccf }, + { 0x2cd1, 0x2cd1 }, + { 0x2cd3, 0x2cd3 }, + { 0x2cd5, 0x2cd5 }, + { 0x2cd7, 0x2cd7 }, + { 0x2cd9, 0x2cd9 }, + { 0x2cdb, 0x2cdb }, + { 0x2cdd, 0x2cdd }, + { 0x2cdf, 0x2cdf }, + { 0x2ce1, 0x2ce1 }, + { 0x2ce3, 0x2ce4 }, + { 0x2cec, 0x2cec }, + { 0x2cee, 0x2cee }, + { 0x2cf3, 0x2cf3 }, + { 0x2d00, 0x2d25 }, + { 0x2d27, 0x2d27 }, + { 0x2d2d, 0x2d2d }, + { 0xa641, 0xa641 }, + { 0xa643, 0xa643 }, + { 0xa645, 0xa645 }, + { 0xa647, 0xa647 }, + { 0xa649, 0xa649 }, + { 0xa64b, 0xa64b }, + { 0xa64d, 0xa64d }, + { 0xa64f, 0xa64f }, + { 0xa651, 0xa651 }, + { 0xa653, 0xa653 }, + { 0xa655, 0xa655 }, + { 0xa657, 0xa657 }, + { 0xa659, 0xa659 }, + { 0xa65b, 0xa65b }, + { 0xa65d, 0xa65d }, + { 0xa65f, 0xa65f }, + { 0xa661, 0xa661 }, + { 0xa663, 0xa663 }, + { 0xa665, 0xa665 }, + { 0xa667, 0xa667 }, + { 0xa669, 0xa669 }, + { 0xa66b, 0xa66b }, + { 0xa66d, 0xa66d }, + { 0xa681, 0xa681 }, + { 0xa683, 0xa683 }, + { 0xa685, 0xa685 }, + { 0xa687, 0xa687 }, + { 0xa689, 0xa689 }, + { 0xa68b, 0xa68b }, + { 0xa68d, 0xa68d }, + { 0xa68f, 0xa68f }, + { 0xa691, 0xa691 }, + { 0xa693, 0xa693 }, + { 0xa695, 0xa695 }, + { 0xa697, 0xa697 }, + { 0xa699, 0xa699 }, + { 0xa69b, 0xa69b }, + { 0xa723, 0xa723 }, + { 0xa725, 0xa725 }, + { 0xa727, 0xa727 }, + { 0xa729, 0xa729 }, + { 0xa72b, 0xa72b }, + { 0xa72d, 0xa72d }, + { 0xa72f, 0xa731 }, + { 0xa733, 0xa733 }, + { 0xa735, 0xa735 }, + { 0xa737, 0xa737 }, + { 0xa739, 0xa739 }, + { 0xa73b, 0xa73b }, + { 0xa73d, 0xa73d }, + { 0xa73f, 0xa73f }, + { 0xa741, 0xa741 }, + { 0xa743, 0xa743 }, + { 0xa745, 0xa745 }, + { 0xa747, 0xa747 }, + { 0xa749, 0xa749 }, + { 0xa74b, 0xa74b }, + { 0xa74d, 0xa74d }, + { 0xa74f, 0xa74f }, + { 0xa751, 0xa751 }, + { 0xa753, 0xa753 }, + { 0xa755, 0xa755 }, + { 0xa757, 0xa757 }, + { 0xa759, 0xa759 }, + { 0xa75b, 0xa75b }, + { 0xa75d, 0xa75d }, + { 0xa75f, 0xa75f }, + { 0xa761, 0xa761 }, + { 0xa763, 0xa763 }, + { 0xa765, 0xa765 }, + { 0xa767, 0xa767 }, + { 0xa769, 0xa769 }, + { 0xa76b, 0xa76b }, + { 0xa76d, 0xa76d }, + { 0xa76f, 0xa76f }, + { 0xa771, 0xa778 }, + { 0xa77a, 0xa77a }, + { 0xa77c, 0xa77c }, + { 0xa77f, 0xa77f }, + { 0xa781, 0xa781 }, + { 0xa783, 0xa783 }, + { 0xa785, 0xa785 }, + { 0xa787, 0xa787 }, + { 0xa78c, 0xa78c }, + { 0xa78e, 0xa78e }, + { 0xa791, 0xa791 }, + { 0xa793, 0xa795 }, + { 0xa797, 0xa797 }, + { 0xa799, 0xa799 }, + { 0xa79b, 0xa79b }, + { 0xa79d, 0xa79d }, + { 0xa79f, 0xa79f }, + { 0xa7a1, 0xa7a1 }, + { 0xa7a3, 0xa7a3 }, + { 0xa7a5, 0xa7a5 }, + { 0xa7a7, 0xa7a7 }, + { 0xa7a9, 0xa7a9 }, + { 0xa7af, 0xa7af }, + { 0xa7b5, 0xa7b5 }, + { 0xa7b7, 0xa7b7 }, + { 0xa7b9, 0xa7b9 }, + { 0xa7bb, 0xa7bb }, + { 0xa7bd, 0xa7bd }, + { 0xa7bf, 0xa7bf }, + { 0xa7c1, 0xa7c1 }, + { 0xa7c3, 0xa7c3 }, + { 0xa7c8, 0xa7c8 }, + { 0xa7ca, 0xa7ca }, + { 0xa7d1, 0xa7d1 }, + { 0xa7d3, 0xa7d3 }, + { 0xa7d5, 0xa7d5 }, + { 0xa7d7, 0xa7d7 }, + { 0xa7d9, 0xa7d9 }, + { 0xa7f6, 0xa7f6 }, + { 0xa7fa, 0xa7fa }, + { 0xab30, 0xab5a }, + { 0xab60, 0xab68 }, + { 0xab70, 0xabbf }, + { 0xfb00, 0xfb06 }, + { 0xfb13, 0xfb17 }, + { 0xff41, 0xff5a }, + { 0x10428, 0x1044f }, + { 0x104d8, 0x104fb }, + { 0x10597, 0x105a1 }, + { 0x105a3, 0x105b1 }, + { 0x105b3, 0x105b9 }, + { 0x105bb, 0x105bc }, + { 0x10cc0, 0x10cf2 }, + { 0x118c0, 0x118df }, + { 0x16e60, 0x16e7f }, + { 0x1d41a, 0x1d433 }, + { 0x1d44e, 0x1d454 }, + { 0x1d456, 0x1d467 }, + { 0x1d482, 0x1d49b }, + { 0x1d4b6, 0x1d4b9 }, + { 0x1d4bb, 0x1d4bb }, + { 0x1d4bd, 0x1d4c3 }, + { 0x1d4c5, 0x1d4cf }, + { 0x1d4ea, 0x1d503 }, + { 0x1d51e, 0x1d537 }, + { 0x1d552, 0x1d56b }, + { 0x1d586, 0x1d59f }, + { 0x1d5ba, 0x1d5d3 }, + { 0x1d5ee, 0x1d607 }, + { 0x1d622, 0x1d63b }, + { 0x1d656, 0x1d66f }, + { 0x1d68a, 0x1d6a5 }, + { 0x1d6c2, 0x1d6da }, + { 0x1d6dc, 0x1d6e1 }, + { 0x1d6fc, 0x1d714 }, + { 0x1d716, 0x1d71b }, + { 0x1d736, 0x1d74e }, + { 0x1d750, 0x1d755 }, + { 0x1d770, 0x1d788 }, + { 0x1d78a, 0x1d78f }, + { 0x1d7aa, 0x1d7c2 }, + { 0x1d7c4, 0x1d7c9 }, + { 0x1d7cb, 0x1d7cb }, + { 0x1df00, 0x1df09 }, + { 0x1df0b, 0x1df1e }, + { 0x1df25, 0x1df2a }, + { 0x1e922, 0x1e943 }, }; #endif // CHAR_RANGE_INC diff --git a/core/string/char_utils.h b/core/string/char_utils.h index 4ff8fb7320..fc2fbb95a1 100644 --- a/core/string/char_utils.h +++ b/core/string/char_utils.h @@ -35,24 +35,43 @@ #include "char_range.inc" +#define BSEARCH_CHAR_RANGE(m_array) \ + int low = 0; \ + int high = sizeof(m_array) / sizeof(m_array[0]) - 1; \ + int middle; \ + \ + while (low <= high) { \ + middle = (low + high) / 2; \ + \ + if (c < m_array[middle].start) { \ + high = middle - 1; \ + } else if (c > m_array[middle].end) { \ + low = middle + 1; \ + } else { \ + return true; \ + } \ + } \ + \ + return false + static _FORCE_INLINE_ bool is_unicode_identifier_start(char32_t c) { - for (int i = 0; xid_start[i].start != 0; i++) { - if (c >= xid_start[i].start && c <= xid_start[i].end) { - return true; - } - } - return false; + BSEARCH_CHAR_RANGE(xid_start); } static _FORCE_INLINE_ bool is_unicode_identifier_continue(char32_t c) { - for (int i = 0; xid_continue[i].start != 0; i++) { - if (c >= xid_continue[i].start && c <= xid_continue[i].end) { - return true; - } - } - return false; + BSEARCH_CHAR_RANGE(xid_continue); +} + +static _FORCE_INLINE_ bool is_unicode_upper_case(char32_t c) { + BSEARCH_CHAR_RANGE(uppercase_letter); } +static _FORCE_INLINE_ bool is_unicode_lower_case(char32_t c) { + BSEARCH_CHAR_RANGE(lowercase_letter); +} + +#undef BSEARCH_CHAR_RANGE + static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) { return (c >= 'A' && c <= 'Z'); } @@ -73,7 +92,7 @@ static _FORCE_INLINE_ bool is_binary_digit(char32_t c) { return (c == '0' || c == '1'); } -static _FORCE_INLINE_ bool is_ascii_char(char32_t c) { +static _FORCE_INLINE_ bool is_ascii_alphabet_char(char32_t c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index 32e4564c5e..8ae2efb787 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -92,6 +92,14 @@ StringName NodePath::get_subname(int p_idx) const { return data->subpath[p_idx]; } +int NodePath::get_total_name_count() const { + if (!data) { + return 0; + } + + return data->path.size() + data->subpath.size(); +} + void NodePath::unref() { if (data && data->refcount.unref()) { memdelete(data); @@ -229,6 +237,27 @@ StringName NodePath::get_concatenated_subnames() const { return data->concatenated_subpath; } +NodePath NodePath::slice(int p_begin, int p_end) const { + const int name_count = get_name_count(); + const int total_count = get_total_name_count(); + + int begin = CLAMP(p_begin, -total_count, total_count); + if (begin < 0) { + begin += total_count; + } + int end = CLAMP(p_end, -total_count, total_count); + if (end < 0) { + end += total_count; + } + const int sub_begin = MAX(begin - name_count - 1, 0); + const int sub_end = MAX(end - name_count, 0); + + const Vector<StringName> names = get_names().slice(begin, end); + const Vector<StringName> sub_names = get_subnames().slice(sub_begin, sub_end); + const bool absolute = is_absolute() && (begin == 0); + return NodePath(names, sub_names, absolute); +} + NodePath NodePath::rel_path_to(const NodePath &p_np) const { ERR_FAIL_COND_V(!is_absolute(), NodePath()); ERR_FAIL_COND_V(!p_np.is_absolute(), NodePath()); @@ -331,7 +360,7 @@ NodePath NodePath::simplified() const { } NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { - if (p_path.size() == 0) { + if (p_path.size() == 0 && !p_absolute) { return; } @@ -343,7 +372,7 @@ NodePath::NodePath(const Vector<StringName> &p_path, bool p_absolute) { } NodePath::NodePath(const Vector<StringName> &p_path, const Vector<StringName> &p_subpath, bool p_absolute) { - if (p_path.size() == 0 && p_subpath.size() == 0) { + if (p_path.size() == 0 && p_subpath.size() == 0 && !p_absolute) { return; } diff --git a/core/string/node_path.h b/core/string/node_path.h index 876d69924e..56799839d7 100644 --- a/core/string/node_path.h +++ b/core/string/node_path.h @@ -57,10 +57,12 @@ public: StringName get_name(int p_idx) const; int get_subname_count() const; StringName get_subname(int p_idx) const; + int get_total_name_count() const; Vector<StringName> get_names() const; Vector<StringName> get_subnames() const; StringName get_concatenated_names() const; StringName get_concatenated_subnames() const; + NodePath slice(int p_begin, int p_end = INT_MAX) const; NodePath rel_path_to(const NodePath &p_np) const; NodePath get_as_property_path() const; diff --git a/core/string/print_string.cpp b/core/string/print_string.cpp index e3614be359..0f019c405f 100644 --- a/core/string/print_string.cpp +++ b/core/string/print_string.cpp @@ -68,7 +68,7 @@ void remove_print_handler(const PrintHandlerList *p_handler) { ERR_FAIL_NULL(l); } -void __print_line(String p_string) { +void __print_line(const String &p_string) { if (!CoreGlobals::print_line_enabled) { return; } @@ -85,7 +85,7 @@ void __print_line(String p_string) { _global_unlock(); } -void __print_line_rich(String p_string) { +void __print_line_rich(const String &p_string) { if (!CoreGlobals::print_line_enabled) { return; } @@ -178,7 +178,7 @@ void __print_line_rich(String p_string) { _global_unlock(); } -void print_error(String p_string) { +void print_error(const String &p_string) { if (!CoreGlobals::print_error_enabled) { return; } @@ -199,6 +199,6 @@ bool is_print_verbose_enabled() { return OS::get_singleton()->is_stdout_verbose(); } -String stringify_variants(Variant p_var) { +String stringify_variants(const Variant &p_var) { return p_var.operator String(); } diff --git a/core/string/print_string.h b/core/string/print_string.h index 7656e9bfa1..570e08c5fb 100644 --- a/core/string/print_string.h +++ b/core/string/print_string.h @@ -46,19 +46,19 @@ struct PrintHandlerList { PrintHandlerList() {} }; -String stringify_variants(Variant p_var); +String stringify_variants(const Variant &p_var); template <typename... Args> -String stringify_variants(Variant p_var, Args... p_args) { +String stringify_variants(const Variant &p_var, Args... p_args) { return p_var.operator String() + " " + stringify_variants(p_args...); } void add_print_handler(PrintHandlerList *p_handler); void remove_print_handler(const PrintHandlerList *p_handler); -extern void __print_line(String p_string); -extern void __print_line_rich(String p_string); -extern void print_error(String p_string); +extern void __print_line(const String &p_string); +extern void __print_line_rich(const String &p_string); +extern void print_error(const String &p_string); extern bool is_print_verbose_enabled(); // This version avoids processing the text to be printed until it actually has to be printed, saving some CPU usage. @@ -69,21 +69,21 @@ extern bool is_print_verbose_enabled(); } \ } -inline void print_line(Variant v) { +inline void print_line(const Variant &v) { __print_line(stringify_variants(v)); } -inline void print_line_rich(Variant v) { +inline void print_line_rich(const Variant &v) { __print_line_rich(stringify_variants(v)); } template <typename... Args> -void print_line(Variant p_var, Args... p_args) { +void print_line(const Variant &p_var, Args... p_args) { __print_line(stringify_variants(p_var, p_args...)); } template <typename... Args> -void print_line_rich(Variant p_var, Args... p_args) { +void print_line_rich(const Variant &p_var, Args... p_args) { __print_line_rich(stringify_variants(p_var, p_args...)); } diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 5a8df07410..658297d805 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -122,7 +122,7 @@ void StringName::unref() { if (_data && _data->refcount.unref()) { MutexLock lock(mutex); - if (_data->static_count.get() > 0) { + if (CoreGlobals::leak_reporting_enabled && _data->static_count.get() > 0) { if (_data->cname) { ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname)); } else { diff --git a/core/string/string_name.h b/core/string/string_name.h index 4ed58d8286..89b4c07e0e 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -71,11 +71,6 @@ class StringName { _Data *_data = nullptr; - union _HashUnion { - _Data *ptr = nullptr; - uint32_t hash; - }; - void unref(); friend void register_core_types(); friend void unregister_core_types(); diff --git a/core/string/translation.compat.inc b/core/string/translation.compat.inc new file mode 100644 index 0000000000..d792d4a6fc --- /dev/null +++ b/core/string/translation.compat.inc @@ -0,0 +1,46 @@ +/**************************************************************************/ +/* translation.compat.inc */ +/**************************************************************************/ +/* 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 DISABLE_DEPRECATED + +void Translation::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); +} + +void TranslationServer::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); + ClassDB::bind_compatibility_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); +} + +#endif diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 02380c92bb..344fe42fa0 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "translation.h" +#include "translation.compat.inc" #include "core/config/project_settings.h" #include "core/io/resource_loader.h" @@ -155,11 +156,11 @@ int Translation::get_message_count() const { void Translation::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale); - ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL("")); - ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL("")); - ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL("")); - ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL("")); + ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list); ClassDB::bind_method(D_METHOD("get_translated_message_list"), &Translation::get_translated_message_list); ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count); @@ -518,13 +519,17 @@ String TranslationServer::get_country_name(const String &p_country) const { } void TranslationServer::set_locale(const String &p_locale) { - locale = standardize_locale(p_locale); + String new_locale = standardize_locale(p_locale); + if (locale == new_locale) { + return; + } + + locale = new_locale; + ResourceLoader::reload_translation_remaps(); if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - - ResourceLoader::reload_translation_remaps(); } String TranslationServer::get_locale() const { @@ -767,6 +772,20 @@ StringName TranslationServer::tool_translate_plural(const StringName &p_message, return p_message_plural; } +void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) { + property_translation = p_translation; +} + +StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const { + if (property_translation.is_valid()) { + StringName r = property_translation->get_message(p_message, p_context); + if (r) { + return r; + } + } + return p_message; +} + void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) { doc_translation = p_translation; } @@ -795,13 +814,13 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message, return p_message_plural; } -void TranslationServer::set_property_translation(const Ref<Translation> &p_translation) { - property_translation = p_translation; +void TranslationServer::set_extractable_translation(const Ref<Translation> &p_translation) { + extractable_translation = p_translation; } -StringName TranslationServer::property_translate(const StringName &p_message) const { - if (property_translation.is_valid()) { - StringName r = property_translation->get_message(p_message); +StringName TranslationServer::extractable_translate(const StringName &p_message, const StringName &p_context) const { + if (extractable_translation.is_valid()) { + StringName r = extractable_translation->get_message(p_message, p_context); if (r) { return r; } @@ -809,6 +828,20 @@ StringName TranslationServer::property_translate(const StringName &p_message) co return p_message; } +StringName TranslationServer::extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { + if (extractable_translation.is_valid()) { + StringName r = extractable_translation->get_plural_message(p_message, p_message_plural, p_n, p_context); + if (r) { + return r; + } + } + + if (p_n == 1) { + return p_message; + } + return p_message_plural; +} + bool TranslationServer::is_pseudolocalization_enabled() const { return pseudolocalization_enabled; } @@ -816,10 +849,11 @@ bool TranslationServer::is_pseudolocalization_enabled() const { void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { pseudolocalization_enabled = p_enabled; + ResourceLoader::reload_translation_remaps(); + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - ResourceLoader::reload_translation_remaps(); } void TranslationServer::set_editor_pseudolocalization(bool p_enabled) { @@ -836,10 +870,11 @@ void TranslationServer::reload_pseudolocalization() { pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + ResourceLoader::reload_translation_remaps(); + if (OS::get_singleton()->get_main_loop()) { OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); } - ResourceLoader::reload_translation_remaps(); } StringName TranslationServer::pseudolocalize(const StringName &p_message) const { @@ -875,7 +910,7 @@ StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) c String TranslationServer::get_override_string(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -889,7 +924,7 @@ String TranslationServer::get_override_string(String &p_message) const { String TranslationServer::double_vowels(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -907,7 +942,7 @@ String TranslationServer::double_vowels(String &p_message) const { String TranslationServer::replace_with_accented_string(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -930,7 +965,7 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const char32_t fakebidisuffix = U'\u202c'; res += fakebidiprefix; // The fake bidi unicode gets popped at every newline so pushing it back at every newline. - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (p_message[i] == '\n') { res += fakebidisuffix; res += p_message[i]; @@ -958,7 +993,7 @@ String TranslationServer::add_padding(const String &p_message, int p_length) con } const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { - if (!is_ascii_char(p_character)) { + if (!is_ascii_alphabet_char(p_character)) { return nullptr; } @@ -972,11 +1007,34 @@ const char32_t *TranslationServer::get_accented_version(char32_t p_character) co } bool TranslationServer::is_placeholder(String &p_message, int p_index) const { - return p_index < p_message.size() - 1 && p_message[p_index] == '%' && + return p_index < p_message.length() - 1 && p_message[p_index] == '%' && (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); } +#ifdef TOOLS_ENABLED +void TranslationServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + const String pf = p_function; + if (p_idx == 0) { + HashMap<String, String> *target_hash_map = nullptr; + if (pf == "get_language_name") { + target_hash_map = &language_map; + } else if (pf == "get_script_name") { + target_hash_map = &script_map; + } else if (pf == "get_country_name") { + target_hash_map = &country_name_map; + } + + if (target_hash_map) { + for (const KeyValue<String, String> &E : *target_hash_map) { + r_options->push_back(E.key.quote()); + } + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif // TOOLS_ENABLED + void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); @@ -996,8 +1054,8 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); - ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); - ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); + ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(StringName())); + ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(StringName())); ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); diff --git a/core/string/translation.h b/core/string/translation.h index 3f9dbcc476..78d6721347 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -51,6 +51,10 @@ class Translation : public Resource { protected: static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + static void _bind_compatibility_methods(); +#endif + GDVIRTUAL2RC(StringName, _get_message, StringName, StringName); GDVIRTUAL4RC(StringName, _get_plural_message, StringName, StringName, int, StringName); @@ -78,8 +82,9 @@ class TranslationServer : public Object { HashSet<Ref<Translation>> translations; Ref<Translation> tool_translation; - Ref<Translation> doc_translation; Ref<Translation> property_translation; + Ref<Translation> doc_translation; + Ref<Translation> extractable_translation; bool enabled = true; @@ -111,6 +116,10 @@ class TranslationServer : public Object { static void _bind_methods(); +#ifndef DISABLE_DEPRECATED + static void _bind_compatibility_methods(); +#endif + struct LocaleScriptInfo { String name; String script; @@ -173,11 +182,14 @@ public: Ref<Translation> get_tool_translation() const; StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const; StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + void set_property_translation(const Ref<Translation> &p_translation); + StringName property_translate(const StringName &p_message, const StringName &p_context = "") const; void set_doc_translation(const Ref<Translation> &p_translation); StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const; StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; - void set_property_translation(const Ref<Translation> &p_translation); - StringName property_translate(const StringName &p_message) const; + void set_extractable_translation(const Ref<Translation> &p_translation); + StringName extractable_translate(const StringName &p_message, const StringName &p_context = "") const; + StringName extractable_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; void setup(); @@ -185,6 +197,10 @@ public: void load_translations(); +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif // TOOLS_ENABLED + TranslationServer(); }; diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp index 6b1595174a..06fd4717d7 100644 --- a/core/string/translation_po.cpp +++ b/core/string/translation_po.cpp @@ -41,8 +41,8 @@ void TranslationPO::print_translation_map() { return; } - file->store_line("NPlural : " + String::num_int64(this->get_plural_forms())); - file->store_line("Plural rule : " + this->get_plural_rule()); + file->store_line("NPlural : " + String::num_int64(get_plural_forms())); + file->store_line("Plural rule : " + get_plural_rule()); file->store_line(""); List<StringName> context_l; diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 9be7c04158..2b62b72a51 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -302,10 +302,14 @@ void String::copy_from(const char *p_cstr) { resize(len + 1); // include 0 - char32_t *dst = this->ptrw(); + char32_t *dst = ptrw(); for (size_t i = 0; i <= len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_cstr[i]; +#else uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); +#endif if (c == 0 && i < len) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -335,10 +339,14 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) { resize(len + 1); // include 0 - char32_t *dst = this->ptrw(); + char32_t *dst = ptrw(); for (int i = 0; i < len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_cstr[i]; +#else uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]); +#endif if (c == 0) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -544,7 +552,11 @@ String &String::operator+=(const char *p_str) { char32_t *dst = ptrw() + lhs_len; for (size_t i = 0; i <= rhs_len; i++) { +#if CHAR_MIN == 0 + uint8_t c = p_str[i]; +#else uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]); +#endif if (c == 0 && i < rhs_len) { print_unicode_error("NUL character", true); dst[i] = _replacement_char; @@ -915,52 +927,49 @@ static _FORCE_INLINE_ signed char natural_cmp_common(const char32_t *&r_this_str return 0; } -signed char String::naturalcasecmp_to(const String &p_str) const { - const char32_t *this_str = get_data(); - const char32_t *that_str = p_str.get_data(); - - if (this_str && that_str) { - while (*this_str == '.' || *that_str == '.') { - if (*this_str++ != '.') { +static _FORCE_INLINE_ signed char naturalcasecmp_to_base(const char32_t *p_this_str, const char32_t *p_that_str) { + if (p_this_str && p_that_str) { + while (*p_this_str == '.' || *p_that_str == '.') { + if (*p_this_str++ != '.') { return 1; } - if (*that_str++ != '.') { + if (*p_that_str++ != '.') { return -1; } - if (!*that_str) { + if (!*p_that_str) { return 1; } - if (!*this_str) { + if (!*p_this_str) { return -1; } } - while (*this_str) { - if (!*that_str) { + while (*p_this_str) { + if (!*p_that_str) { return 1; - } else if (is_digit(*this_str)) { - if (!is_digit(*that_str)) { + } else if (is_digit(*p_this_str)) { + if (!is_digit(*p_that_str)) { return -1; } - signed char ret = natural_cmp_common(this_str, that_str); + signed char ret = natural_cmp_common(p_this_str, p_that_str); if (ret) { return ret; } - } else if (is_digit(*that_str)) { + } else if (is_digit(*p_that_str)) { return 1; } else { - if (*this_str < *that_str) { // If current character in this is less, we are less. + if (*p_this_str < *p_that_str) { // If current character in this is less, we are less. return -1; - } else if (*this_str > *that_str) { // If current character in this is greater, we are greater. + } else if (*p_this_str > *p_that_str) { // If current character in this is greater, we are greater. return 1; } - this_str++; - that_str++; + p_this_str++; + p_that_str++; } } - if (*that_str) { + if (*p_that_str) { return -1; } } @@ -968,52 +977,56 @@ signed char String::naturalcasecmp_to(const String &p_str) const { return 0; } -signed char String::naturalnocasecmp_to(const String &p_str) const { +signed char String::naturalcasecmp_to(const String &p_str) const { const char32_t *this_str = get_data(); const char32_t *that_str = p_str.get_data(); - if (this_str && that_str) { - while (*this_str == '.' || *that_str == '.') { - if (*this_str++ != '.') { + return naturalcasecmp_to_base(this_str, that_str); +} + +static _FORCE_INLINE_ signed char naturalnocasecmp_to_base(const char32_t *p_this_str, const char32_t *p_that_str) { + if (p_this_str && p_that_str) { + while (*p_this_str == '.' || *p_that_str == '.') { + if (*p_this_str++ != '.') { return 1; } - if (*that_str++ != '.') { + if (*p_that_str++ != '.') { return -1; } - if (!*that_str) { + if (!*p_that_str) { return 1; } - if (!*this_str) { + if (!*p_this_str) { return -1; } } - while (*this_str) { - if (!*that_str) { + while (*p_this_str) { + if (!*p_that_str) { return 1; - } else if (is_digit(*this_str)) { - if (!is_digit(*that_str)) { + } else if (is_digit(*p_this_str)) { + if (!is_digit(*p_that_str)) { return -1; } - signed char ret = natural_cmp_common(this_str, that_str); + signed char ret = natural_cmp_common(p_this_str, p_that_str); if (ret) { return ret; } - } else if (is_digit(*that_str)) { + } else if (is_digit(*p_that_str)) { return 1; } else { - if (_find_upper(*this_str) < _find_upper(*that_str)) { // If current character in this is less, we are less. + if (_find_upper(*p_this_str) < _find_upper(*p_that_str)) { // If current character in this is less, we are less. return -1; - } else if (_find_upper(*this_str) > _find_upper(*that_str)) { // If current character in this is greater, we are greater. + } else if (_find_upper(*p_this_str) > _find_upper(*p_that_str)) { // If current character in this is greater, we are greater. return 1; } - this_str++; - that_str++; + p_this_str++; + p_that_str++; } } - if (*that_str) { + if (*p_that_str) { return -1; } } @@ -1021,6 +1034,53 @@ signed char String::naturalnocasecmp_to(const String &p_str) const { return 0; } +signed char String::naturalnocasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + return naturalnocasecmp_to_base(this_str, that_str); +} + +static _FORCE_INLINE_ signed char file_cmp_common(const char32_t *&r_this_str, const char32_t *&r_that_str) { + // Compare leading `_` sequences. + while ((*r_this_str == '_' && *r_that_str) || (*r_this_str && *r_that_str == '_')) { + // Sort `_` lower than everything except `.` + if (*r_this_str != '_') { + return *r_this_str == '.' ? -1 : 1; + } else if (*r_that_str != '_') { + return *r_that_str == '.' ? 1 : -1; + } + r_this_str++; + r_that_str++; + } + + return 0; +} + +signed char String::filecasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + signed char ret = file_cmp_common(this_str, that_str); + if (ret) { + return ret; + } + + return naturalcasecmp_to_base(this_str, that_str); +} + +signed char String::filenocasecmp_to(const String &p_str) const { + const char32_t *this_str = get_data(); + const char32_t *that_str = p_str.get_data(); + + signed char ret = file_cmp_common(this_str, that_str); + if (ret) { + return ret; + } + + return naturalnocasecmp_to_base(this_str, that_str); +} + const char32_t *String::get_data() const { static const char32_t zero = 0; return size() ? &operator[](0) : &zero; @@ -1031,18 +1091,18 @@ String String::_camelcase_to_underscore() const { String new_string; int start_index = 0; - for (int i = 1; i < this->size(); i++) { - bool is_prev_upper = is_ascii_upper_case(cstr[i - 1]); - bool is_prev_lower = is_ascii_lower_case(cstr[i - 1]); + for (int i = 1; i < size(); i++) { + bool is_prev_upper = is_unicode_upper_case(cstr[i - 1]); + bool is_prev_lower = is_unicode_lower_case(cstr[i - 1]); bool is_prev_digit = is_digit(cstr[i - 1]); - bool is_curr_upper = is_ascii_upper_case(cstr[i]); - bool is_curr_lower = is_ascii_lower_case(cstr[i]); + bool is_curr_upper = is_unicode_upper_case(cstr[i]); + bool is_curr_lower = is_unicode_lower_case(cstr[i]); bool is_curr_digit = is_digit(cstr[i]); bool is_next_lower = false; - if (i + 1 < this->size()) { - is_next_lower = is_ascii_lower_case(cstr[i + 1]); + if (i + 1 < size()) { + is_next_lower = is_unicode_lower_case(cstr[i + 1]); } const bool cond_a = is_prev_lower && is_curr_upper; // aA @@ -1051,17 +1111,17 @@ String String::_camelcase_to_underscore() const { const bool cond_d = (is_prev_upper || is_prev_lower) && is_curr_digit; // A2, a2 if (cond_a || cond_b || cond_c || cond_d) { - new_string += this->substr(start_index, i - start_index) + "_"; + new_string += substr(start_index, i - start_index) + "_"; start_index = i; } } - new_string += this->substr(start_index, this->size() - start_index); + new_string += substr(start_index, size() - start_index); return new_string.to_lower(); } String String::capitalize() const { - String aux = this->_camelcase_to_underscore().replace("_", " ").strip_edges(); + String aux = _camelcase_to_underscore().replace("_", " ").strip_edges(); String cap; for (int i = 0; i < aux.get_slice_count(" "); i++) { String slice = aux.get_slicec(' ', i); @@ -1078,7 +1138,7 @@ String String::capitalize() const { } String String::to_camel_case() const { - String s = this->to_pascal_case(); + String s = to_pascal_case(); if (!s.is_empty()) { s[0] = _find_lower(s[0]); } @@ -1086,11 +1146,11 @@ String String::to_camel_case() const { } String String::to_pascal_case() const { - return this->capitalize().replace(" ", ""); + return capitalize().replace(" ", ""); } String String::to_snake_case() const { - return this->_camelcase_to_underscore().replace(" ", "_").strip_edges(); + return _camelcase_to_underscore().replace(" ", "_").strip_edges(); } String String::get_with_code_lines() const { @@ -1105,7 +1165,7 @@ String String::get_with_code_lines() const { return ret; } -int String::get_slice_count(String p_splitter) const { +int String::get_slice_count(const String &p_splitter) const { if (is_empty()) { return 0; } @@ -1124,7 +1184,7 @@ int String::get_slice_count(String p_splitter) const { return slices; } -String String::get_slice(String p_splitter, int p_slice) const { +String String::get_slice(const String &p_splitter, int p_slice) const { if (is_empty() || p_splitter.is_empty()) { return ""; } @@ -1173,7 +1233,7 @@ String String::get_slicec(char32_t p_splitter, int p_slice) const { return String(); } - const char32_t *c = this->ptr(); + const char32_t *c = ptr(); int i = 0; int prev = 0; int count = 0; @@ -1426,7 +1486,7 @@ Vector<int> String::split_ints_mk(const Vector<String> &p_splitters, bool p_allo return ret; } -String String::join(Vector<String> parts) const { +String String::join(const Vector<String> &parts) const { String ret; for (int i = 0; i < parts.size(); ++i) { if (i > 0) { @@ -1524,7 +1584,7 @@ String String::num(double p_num, int p_decimals) { fmt[5] = 'f'; fmt[6] = 0; } - // if we want to convert a double with as much decimal places as as + // if we want to convert a double with as much decimal places as // DBL_MAX or DBL_MIN then we would theoretically need a buffer of at least // DBL_MAX_10_EXP + 2 for DBL_MAX and DBL_MAX_10_EXP + 4 for DBL_MIN. // BUT those values where still giving me exceptions, so I tested from @@ -1810,11 +1870,15 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { bool decode_failed = false; { const char *ptrtmp = p_utf8; - const char *ptrtmp_limit = &p_utf8[p_len]; + const char *ptrtmp_limit = p_len >= 0 ? &p_utf8[p_len] : nullptr; int skip = 0; uint8_t c_start = 0; while (ptrtmp != ptrtmp_limit && *ptrtmp) { +#if CHAR_MIN == 0 + uint8_t c = *ptrtmp; +#else uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp); +#endif if (skip == 0) { if (p_skip_cr && c == '\r') { @@ -1882,7 +1946,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) { int skip = 0; uint32_t unichar = 0; while (cstr_size) { +#if CHAR_MIN == 0 + uint8_t c = *p_utf8; +#else uint8_t c = *p_utf8 >= 0 ? *p_utf8 : uint8_t(256 + *p_utf8); +#endif if (skip == 0) { if (p_skip_cr && c == '\r') { @@ -2042,12 +2110,12 @@ CharString String::utf8() const { String String::utf16(const char16_t *p_utf16, int p_len) { String ret; - ret.parse_utf16(p_utf16, p_len); + ret.parse_utf16(p_utf16, p_len, true); return ret; } -Error String::parse_utf16(const char16_t *p_utf16, int p_len) { +Error String::parse_utf16(const char16_t *p_utf16, int p_len, bool p_default_little_endian) { if (!p_utf16) { return ERR_INVALID_DATA; } @@ -2057,8 +2125,12 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) { int cstr_size = 0; int str_size = 0; +#ifdef BIG_ENDIAN_ENABLED + bool byteswap = p_default_little_endian; +#else + bool byteswap = !p_default_little_endian; +#endif /* HANDLE BOM (Byte Order Mark) */ - bool byteswap = false; // assume correct endianness if no BOM found if (p_len < 0 || p_len >= 1) { bool has_bom = false; if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is @@ -2079,7 +2151,7 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) { bool decode_error = false; { const char16_t *ptrtmp = p_utf16; - const char16_t *ptrtmp_limit = &p_utf16[p_len]; + const char16_t *ptrtmp_limit = p_len >= 0 ? &p_utf16[p_len] : nullptr; uint32_t c_prev = 0; bool skip = false; while (ptrtmp != ptrtmp_limit && *ptrtmp) { @@ -2439,7 +2511,7 @@ bool String::is_numeric() const { return true; // TODO: Use the parser below for this instead } -template <class C> +template <typename C> static double built_in_strtod( /* A decimal ASCII floating-point number, * optionally preceded by white space. Must @@ -3309,10 +3381,14 @@ bool String::begins_with(const String &p_string) const { bool String::begins_with(const char *p_string) const { int l = length(); - if (l == 0 || !p_string) { + if (!p_string) { return false; } + if (l == 0) { + return *p_string == 0; + } + const char32_t *str = &operator[](0); int i = 0; @@ -3495,8 +3571,8 @@ bool String::matchn(const String &p_wildcard) const { return _wildcard_match(p_wildcard.get_data(), get_data(), false); } -String String::format(const Variant &values, String placeholder) const { - String new_string = String(this->ptr()); +String String::format(const Variant &values, const String &placeholder) const { + String new_string = String(ptr()); if (values.get_type() == Variant::ARRAY) { Array values_arr = values; @@ -3941,27 +4017,42 @@ static int _humanize_digits(int p_num) { } String String::humanize_size(uint64_t p_size) { + int magnitude = 0; uint64_t _div = 1; - Vector<String> prefixes; - prefixes.push_back(RTR("B")); - prefixes.push_back(RTR("KiB")); - prefixes.push_back(RTR("MiB")); - prefixes.push_back(RTR("GiB")); - prefixes.push_back(RTR("TiB")); - prefixes.push_back(RTR("PiB")); - prefixes.push_back(RTR("EiB")); - - int prefix_idx = 0; - - while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) { + while (p_size > _div * 1024 && magnitude < 6) { _div *= 1024; - prefix_idx++; + magnitude++; } - const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0; - const double divisor = prefix_idx > 0 ? _div : 1; + if (magnitude == 0) { + return String::num(p_size) + " " + RTR("B"); + } else { + String suffix; + switch (magnitude) { + case 1: + suffix = RTR("KiB"); + break; + case 2: + suffix = RTR("MiB"); + break; + case 3: + suffix = RTR("GiB"); + break; + case 4: + suffix = RTR("TiB"); + break; + case 5: + suffix = RTR("PiB"); + break; + case 6: + suffix = RTR("EiB"); + break; + } - return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx]; + const double divisor = _div; + const int digits = _humanize_digits(p_size / _div); + return String::num(p_size / divisor).pad_decimals(digits) + " " + suffix; + } } bool String::is_absolute_path() const { @@ -4432,7 +4523,7 @@ bool String::is_valid_float() const { String String::path_to_file(const String &p_path) const { // Don't get base dir for src, this is expected to be a dir already. - String src = this->replace("\\", "/"); + String src = replace("\\", "/"); String dst = p_path.replace("\\", "/").get_base_dir(); String rel = src.path_to(dst); if (rel == dst) { // failed @@ -4443,7 +4534,7 @@ String String::path_to_file(const String &p_path) const { } String String::path_to(const String &p_path) const { - String src = this->replace("\\", "/"); + String src = replace("\\", "/"); String dst = p_path.replace("\\", "/"); if (!src.ends_with("/")) { src += "/"; @@ -4549,7 +4640,7 @@ bool String::is_valid_ip_address() const { if (find(":") >= 0) { Vector<String> ip = split(":"); for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; + const String &n = ip[i]; if (n.is_empty()) { continue; } @@ -4571,7 +4662,7 @@ bool String::is_valid_ip_address() const { return false; } for (int i = 0; i < ip.size(); i++) { - String n = ip[i]; + const String &n = ip[i]; if (!n.is_valid_int()) { return false; } @@ -4699,11 +4790,16 @@ String String::property_name_encode() const { static const char32_t invalid_node_name_characters[] = { '.', ':', '@', '/', '\"', UNIQUE_NODE_PREFIX[0], 0 }; -String String::get_invalid_node_name_characters() { +String String::get_invalid_node_name_characters(bool p_allow_internal) { // Do not use this function for critical validation. String r; const char32_t *c = invalid_node_name_characters; while (*c) { + if (p_allow_internal && *c == '@') { + c++; + continue; + } + if (c != invalid_node_name_characters) { r += " "; } @@ -4817,6 +4913,7 @@ String String::sprintf(const Array &values, bool *error) const { bool pad_with_zeros = false; bool left_justified = false; bool show_sign = false; + bool as_unsigned = false; if (error) { *error = true; @@ -4857,16 +4954,27 @@ String String::sprintf(const Array &values, bool *error) const { case 'x': break; case 'X': - base = 16; capitalize = true; break; } // Get basic number. - String str = String::num_int64(ABS(value), base, capitalize); + String str; + if (!as_unsigned) { + str = String::num_int64(ABS(value), base, capitalize); + } else { + uint64_t uvalue = *((uint64_t *)&value); + // In unsigned hex, if the value fits in 32 bits, trim it down to that. + if (base == 16 && value < 0 && value >= INT32_MIN) { + uvalue &= 0xffffffff; + } + str = String::num_uint64(uvalue, base, capitalize); + } int number_len = str.length(); + bool negative = value < 0 && !as_unsigned; + // Padding. - int pad_chars_count = (value < 0 || show_sign) ? min_chars - 1 : min_chars; + int pad_chars_count = (negative || show_sign) ? min_chars - 1 : min_chars; String pad_char = pad_with_zeros ? String("0") : String(" "); if (left_justified) { str = str.rpad(pad_chars_count, pad_char); @@ -4875,8 +4983,8 @@ String String::sprintf(const Array &values, bool *error) const { } // Sign. - if (show_sign || value < 0) { - String sign_char = value < 0 ? "-" : "+"; + if (show_sign || negative) { + String sign_char = negative ? "-" : "+"; if (left_justified) { str = str.insert(0, sign_char); } else { @@ -5069,6 +5177,10 @@ String String::sprintf(const Array &values, bool *error) const { show_sign = true; break; } + case 'u': { // Treat as unsigned (for int/hex). + as_unsigned = true; + break; + } case '0': case '1': case '2': @@ -5167,7 +5279,7 @@ String String::sprintf(const Array &values, bool *error) const { return formatted; } -String String::quote(String quotechar) const { +String String::quote(const String "echar) const { return quotechar + *this + quotechar; } @@ -5335,9 +5447,7 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St /** * "Run-time TRanslate". Performs string replacement for internationalization - * within a running project. The translation string must be supplied by the - * project, as Godot does not provide built-in translations for `RTR()` strings - * to keep binary size low. A translation context can optionally be specified to + * without the editor. A translation context can optionally be specified to * disambiguate between identical source strings in translations. When * placeholders are desired, use `vformat(RTR("Example: %s"), some_string)`. * If a string mentions a quantity (and may therefore need a dynamic plural form), @@ -5351,9 +5461,8 @@ String RTR(const String &p_text, const String &p_context) { String rtr = TranslationServer::get_singleton()->tool_translate(p_text, p_context); if (rtr.is_empty() || rtr == p_text) { return TranslationServer::get_singleton()->translate(p_text, p_context); - } else { - return rtr; } + return rtr; } return p_text; @@ -5361,13 +5470,10 @@ String RTR(const String &p_text, const String &p_context) { /** * "Run-time TRanslate for N items". Performs string replacement for - * internationalization within a running project. The translation string must be - * supplied by the project, as Godot does not provide built-in translations for - * `RTRN()` strings to keep binary size low. A translation context can - * optionally be specified to disambiguate between identical source strings in - * translations. Use `RTR()` if the string doesn't need dynamic plural form. - * When placeholders are desired, use - * `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`. + * internationalization without the editor. A translation context can optionally + * be specified to disambiguate between identical source strings in translations. + * Use `RTR()` if the string doesn't need dynamic plural form. When placeholders + * are desired, use `vformat(RTRN("%d item", "%d items", some_integer), some_integer)`. * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`. * * NOTE: Do not use `RTRN()` in editor-only code (typically within the `editor/` @@ -5378,9 +5484,8 @@ String RTRN(const String &p_text, const String &p_text_plural, int p_n, const St String rtr = TranslationServer::get_singleton()->tool_translate_plural(p_text, p_text_plural, p_n, p_context); if (rtr.is_empty() || rtr == p_text || rtr == p_text_plural) { return TranslationServer::get_singleton()->translate_plural(p_text, p_text_plural, p_n, p_context); - } else { - return rtr; } + return rtr; } // Return message based on English plural rule if translation is not possible. diff --git a/core/string/ustring.h b/core/string/ustring.h index f45392eee1..693df6dcba 100644 --- a/core/string/ustring.h +++ b/core/string/ustring.h @@ -43,7 +43,7 @@ /* CharProxy */ /*************************************************************************/ -template <class T> +template <typename T> class CharProxy { friend class Char16String; friend class CharString; @@ -265,6 +265,9 @@ public: signed char nocasecmp_to(const String &p_str) const; signed char naturalcasecmp_to(const String &p_str) const; signed char naturalnocasecmp_to(const String &p_str) const; + // Special sorting for file names. Names starting with `_` are put before all others except those starting with `.`, otherwise natural comparison is used. + signed char filecasecmp_to(const String &p_str) const; + signed char filenocasecmp_to(const String &p_str) const; const char32_t *get_data() const; /* standard size stuff */ @@ -299,7 +302,7 @@ public: bool is_quoted() const; Vector<String> bigrams() const; float similarity(const String &p_string) const; - String format(const Variant &values, String placeholder = "{_}") const; + String format(const Variant &values, const String &placeholder = "{_}") const; String replace_first(const String &p_key, const String &p_with) const; String replace(const String &p_key, const String &p_with) const; String replace(const char *p_key, const char *p_with) const; @@ -315,7 +318,7 @@ public: String lpad(int min_length, const String &character = " ") const; String rpad(int min_length, const String &character = " ") const; String sprintf(const Array &values, bool *error) const; - String quote(String quotechar = "\"") const; + String quote(const String "echar = "\"") const; String unquote() const; static String num(double p_num, int p_decimals = -1); static String num_scientific(double p_num); @@ -349,8 +352,8 @@ public: String to_snake_case() const; String get_with_code_lines() const; - int get_slice_count(String p_splitter) const; - String get_slice(String p_splitter, int p_slice) const; + int get_slice_count(const String &p_splitter) const; + String get_slice(const String &p_splitter, int p_slice) const; String get_slicec(char32_t p_splitter, int p_slice) const; Vector<String> split(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const; @@ -361,7 +364,7 @@ public: Vector<int> split_ints(const String &p_splitter, bool p_allow_empty = true) const; Vector<int> split_ints_mk(const Vector<String> &p_splitters, bool p_allow_empty = true) const; - String join(Vector<String> parts) const; + String join(const Vector<String> &parts) const; static char32_t char_uppercase(char32_t p_char); static char32_t char_lowercase(char32_t p_char); @@ -390,7 +393,7 @@ public: static String utf8(const char *p_utf8, int p_len = -1); Char16String utf16() const; - Error parse_utf16(const char16_t *p_utf16, int p_len = -1); + Error parse_utf16(const char16_t *p_utf16, int p_len = -1, bool p_default_little_endian = true); static String utf16(const char16_t *p_utf16, int p_len = -1); static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */ @@ -437,7 +440,7 @@ public: String property_name_encode() const; // node functions - static String get_invalid_node_name_characters(); + static String get_invalid_node_name_characters(bool p_allow_internal = false); String validate_node_name() const; String validate_identifier() const; String validate_filename() const; @@ -499,6 +502,12 @@ struct NaturalNoCaseComparator { } }; +struct FileNoCaseComparator { + bool operator()(const String &p_a, const String &p_b) const { + return p_a.filenocasecmp_to(p_b) < 0; + } +}; + template <typename L, typename R> _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) { while (true) { @@ -556,6 +565,43 @@ String DTRN(const String &p_text, const String &p_text_plural, int p_n, const St String RTR(const String &p_text, const String &p_context = ""); String RTRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = ""); +/** + * "Extractable TRanslate". Used for strings that can appear inside an exported + * project (such as the ones in nodes like `FileDialog`), which are made possible + * to add in the POT generator. A translation context can optionally be specified + * to disambiguate between identical source strings in translations. + * When placeholders are desired, use vformat(ETR("Example: %s"), some_string)`. + * If a string mentions a quantity (and may therefore need a dynamic plural form), + * use `ETRN()` instead of `ETR()`. + * + * NOTE: This function is for string extraction only, and will just return the + * string it was given. The translation itself should be done internally by nodes + * with `atr()` instead. + */ +_FORCE_INLINE_ String ETR(const String &p_text, const String &p_context = "") { + return p_text; +} + +/** + * "Extractable TRanslate for N items". Used for strings that can appear inside an + * exported project (such as the ones in nodes like `FileDialog`), which are made + * possible to add in the POT generator. A translation context can optionally be + * specified to disambiguate between identical source strings in translations. + * Use `ETR()` if the string doesn't need dynamic plural form. When placeholders + * are desired, use `vformat(ETRN("%d item", "%d items", some_integer), some_integer)`. + * The placeholder must be present in both strings to avoid run-time warnings in `vformat()`. + * + * NOTE: This function is for string extraction only, and will just return the + * string it was given. The translation itself should be done internally by nodes + * with `atr()` instead. + */ +_FORCE_INLINE_ String ETRN(const String &p_text, const String &p_text_plural, int p_n, const String &p_context = "") { + if (p_n == 1) { + return p_text; + } + return p_text_plural; +} + bool select_word(const String &p_s, int p_col, int &r_beg, int &r_end); _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr) { @@ -565,13 +611,13 @@ _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str) { arr.push_back(p_str); } -template <class... P> +template <typename... P> _FORCE_INLINE_ void sarray_add_str(Vector<String> &arr, const String &p_str, P... p_args) { arr.push_back(p_str); sarray_add_str(arr, p_args...); } -template <class... P> +template <typename... P> _FORCE_INLINE_ Vector<String> sarray(P... p_args) { Vector<String> arr; sarray_add_str(arr, p_args...); diff --git a/core/templates/bin_sorted_array.h b/core/templates/bin_sorted_array.h index 500d1f1377..30c1fefbbd 100644 --- a/core/templates/bin_sorted_array.h +++ b/core/templates/bin_sorted_array.h @@ -34,7 +34,7 @@ #include "core/templates/local_vector.h" #include "core/templates/paged_array.h" -template <class T> +template <typename T> class BinSortedArray { PagedArray<T> array; LocalVector<uint64_t> bin_limits; diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp index 6ecd75ebc1..0c5c6394a1 100644 --- a/core/templates/command_queue_mt.cpp +++ b/core/templates/command_queue_mt.cpp @@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() { return &sync_sems[idx]; } -CommandQueueMT::CommandQueueMT(bool p_sync) { - if (p_sync) { - sync = memnew(Semaphore); - } +CommandQueueMT::CommandQueueMT() { } CommandQueueMT::~CommandQueueMT() { - if (sync) { - memdelete(sync); - } } diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index 7e480653ac..a4ac338bed 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -31,6 +31,7 @@ #ifndef COMMAND_QUEUE_MT_H #define COMMAND_QUEUE_MT_H +#include "core/object/worker_thread_pool.h" #include "core/os/memory.h" #include "core/os/mutex.h" #include "core/os/semaphore.h" @@ -206,63 +207,64 @@ #define ARG(N) p##N #define PARAM(N) P##N p##N -#define TYPE_PARAM(N) class P##N +#define TYPE_PARAM(N) typename P##N #define PARAM_DECL(N) typename GetSimpleTypeT<P##N>::type_t p##N -#define DECL_CMD(N) \ - template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ - struct Command##N : public CommandBase { \ - T *instance; \ - M method; \ - SEMIC_SEP_LIST(PARAM_DECL, N); \ - virtual void call() override { \ - (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ - } \ +#define DECL_CMD(N) \ + template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ + struct Command##N : public CommandBase { \ + T *instance; \ + M method; \ + SEMIC_SEP_LIST(PARAM_DECL, N); \ + virtual void call() override { \ + (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ + } \ }; -#define DECL_CMD_RET(N) \ - template <class T, class M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) class R> \ - struct CommandRet##N : public SyncCommand { \ - R *ret; \ - T *instance; \ - M method; \ - SEMIC_SEP_LIST(PARAM_DECL, N); \ - virtual void call() override { \ - *ret = (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ - } \ +#define DECL_CMD_RET(N) \ + template <typename T, typename M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) typename R> \ + struct CommandRet##N : public SyncCommand { \ + R *ret; \ + T *instance; \ + M method; \ + SEMIC_SEP_LIST(PARAM_DECL, N); \ + virtual void call() override { \ + *ret = (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ + } \ }; -#define DECL_CMD_SYNC(N) \ - template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ - struct CommandSync##N : public SyncCommand { \ - T *instance; \ - M method; \ - SEMIC_SEP_LIST(PARAM_DECL, N); \ - virtual void call() override { \ - (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ - } \ +#define DECL_CMD_SYNC(N) \ + template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ + struct CommandSync##N : public SyncCommand { \ + T *instance; \ + M method; \ + SEMIC_SEP_LIST(PARAM_DECL, N); \ + virtual void call() override { \ + (instance->*method)(COMMA_SEP_LIST(ARG, N)); \ + } \ }; #define TYPE_ARG(N) P##N #define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)> #define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N -#define DECL_PUSH(N) \ - template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ - void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ - CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \ - cmd->instance = p_instance; \ - cmd->method = p_method; \ - SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ - unlock(); \ - if (sync) \ - sync->post(); \ +#define DECL_PUSH(N) \ + template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ + void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ + CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \ + cmd->instance = p_instance; \ + cmd->method = p_method; \ + SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ + unlock(); \ } #define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R> #define DECL_PUSH_AND_RET(N) \ - template <class T, class M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) class R> \ + template <typename T, typename M, COMMA_SEP_LIST(TYPE_PARAM, N) COMMA(N) typename R> \ void push_and_ret(T *p_instance, M p_method, COMMA_SEP_LIST(PARAM, N) COMMA(N) R *r_ret) { \ SyncSemaphore *ss = _alloc_sync_sem(); \ CMD_RET_TYPE(N) *cmd = allocate_and_lock<CMD_RET_TYPE(N)>(); \ @@ -271,9 +273,10 @@ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ cmd->ret = r_ret; \ cmd->sync_sem = ss; \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ unlock(); \ - if (sync) \ - sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -281,7 +284,7 @@ #define CMD_SYNC_TYPE(N) CommandSync##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)> #define DECL_PUSH_AND_SYNC(N) \ - template <class T, class M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ + template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ void push_and_sync(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \ SyncSemaphore *ss = _alloc_sync_sem(); \ CMD_SYNC_TYPE(N) *cmd = allocate_and_lock<CMD_SYNC_TYPE(N)>(); \ @@ -289,9 +292,10 @@ cmd->method = p_method; \ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \ cmd->sync_sem = ss; \ + if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \ + WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \ + } \ unlock(); \ - if (sync) \ - sync->post(); \ ss->sem.wait(); \ ss->in_use = false; \ } @@ -306,15 +310,15 @@ class CommandQueueMT { struct CommandBase { virtual void call() = 0; - virtual void post() {} - virtual ~CommandBase() {} + virtual SyncSemaphore *get_sync_semaphore() { return nullptr; } + virtual ~CommandBase() = default; // Won't be called. }; struct SyncCommand : public CommandBase { SyncSemaphore *sync_sem = nullptr; - virtual void post() override { - sync_sem->sem.post(); + virtual SyncSemaphore *get_sync_semaphore() override { + return sync_sem; } }; @@ -339,9 +343,10 @@ class CommandQueueMT { LocalVector<uint8_t> command_mem; SyncSemaphore sync_sems[SYNC_SEMAPHORES]; Mutex mutex; - Semaphore *sync = nullptr; + WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID; + uint64_t flush_read_ptr = 0; - template <class T> + template <typename T> T *allocate() { // alloc size is size+T+safeguard uint32_t alloc_size = ((sizeof(T) + 8 - 1) & ~(8 - 1)); @@ -352,7 +357,7 @@ class CommandQueueMT { return cmd; } - template <class T> + template <typename T> T *allocate_and_lock() { lock(); T *ret = allocate<T>(); @@ -362,31 +367,40 @@ class CommandQueueMT { void _flush() { lock(); - uint64_t read_ptr = 0; - uint64_t limit = command_mem.size(); + if (unlikely(flush_read_ptr)) { + // Re-entrant call. + unlock(); + return; + } - while (read_ptr < limit) { - uint64_t size = *(uint64_t *)&command_mem[read_ptr]; - read_ptr += 8; - CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]); + WorkerThreadPool::thread_enter_command_queue_mt_flush(this); + while (flush_read_ptr < command_mem.size()) { + uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr]; + flush_read_ptr += 8; + CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]); - cmd->call(); //execute the function - cmd->post(); //release in case it needs sync/ret - cmd->~CommandBase(); //should be done, so erase the command + SyncSemaphore *sync_sem = cmd->get_sync_semaphore(); + cmd->call(); + if (sync_sem) { + sync_sem->sem.post(); // Release in case it needs sync/ret. + } - read_ptr += size; + flush_read_ptr += size; } + WorkerThreadPool::thread_exit_command_queue_mt_flush(); command_mem.clear(); + flush_read_ptr = 0; unlock(); } - void lock(); - void unlock(); void wait_for_flush(); SyncSemaphore *_alloc_sync_sem(); public: + void lock(); + void unlock(); + /* NORMAL PUSH COMMANDS */ DECL_PUSH(0) SPACE_SEP_LIST(DECL_PUSH, 15) @@ -409,12 +423,16 @@ public: } void wait_and_flush() { - ERR_FAIL_NULL(sync); - sync->wait(); + ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID); + WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id); _flush(); } - CommandQueueMT(bool p_sync); + void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) { + pump_task_id = p_task_id; + } + + CommandQueueMT(); ~CommandQueueMT(); }; diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h index 46d9797d6c..f22ae1f1d3 100644 --- a/core/templates/cowdata.h +++ b/core/templates/cowdata.h @@ -38,15 +38,15 @@ #include <string.h> #include <type_traits> -template <class T> +template <typename T> class Vector; class String; class Char16String; class CharString; -template <class T, class V> +template <typename T, typename V> class VMap; -SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) +static_assert(std::is_trivially_destructible_v<std::atomic<uint64_t>>); // Silence a false positive warning (see GH-52119). #if defined(__GNUC__) && !defined(__clang__) @@ -54,55 +54,102 @@ SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t) #pragma GCC diagnostic ignored "-Wplacement-new" #endif -template <class T> +template <typename T> class CowData { - template <class TV> + template <typename TV> friend class Vector; friend class String; friend class Char16String; friend class CharString; - template <class TV, class VV> + template <typename TV, typename VV> friend class VMap; +public: + typedef int64_t Size; + typedef uint64_t USize; + static constexpr USize MAX_INT = INT64_MAX; + private: + // Function to find the next power of 2 to an integer. + static _FORCE_INLINE_ USize next_po2(USize x) { + if (x == 0) { + return 0; + } + + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + if (sizeof(USize) == 8) { + x |= x >> 32; + } + + return ++x; + } + + // Alignment: ↓ max_align_t ↓ USize ↓ max_align_t + // ┌────────────────────┬──┬─────────────┬──┬───────────... + // │ SafeNumeric<USize> │░░│ USize │░░│ T[] + // │ ref. count │░░│ data size │░░│ data + // └────────────────────┴──┴─────────────┴──┴───────────... + // Offset: ↑ REF_COUNT_OFFSET ↑ SIZE_OFFSET ↑ DATA_OFFSET + + static constexpr size_t REF_COUNT_OFFSET = 0; + static constexpr size_t SIZE_OFFSET = ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize) == 0) ? (REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) : ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) + alignof(USize) - ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize))); + static constexpr size_t DATA_OFFSET = ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t) == 0) ? (SIZE_OFFSET + sizeof(USize)) : ((SIZE_OFFSET + sizeof(USize)) + alignof(max_align_t) - ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t))); + mutable T *_ptr = nullptr; // internal helpers - _FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const { + static _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount_ptr(uint8_t *p_ptr) { + return (SafeNumeric<USize> *)(p_ptr + REF_COUNT_OFFSET); + } + + static _FORCE_INLINE_ USize *_get_size_ptr(uint8_t *p_ptr) { + return (USize *)(p_ptr + SIZE_OFFSET); + } + + static _FORCE_INLINE_ T *_get_data_ptr(uint8_t *p_ptr) { + return (T *)(p_ptr + DATA_OFFSET); + } + + _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount() const { if (!_ptr) { return nullptr; } - return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2; + return (SafeNumeric<USize> *)((uint8_t *)_ptr - DATA_OFFSET + REF_COUNT_OFFSET); } - _FORCE_INLINE_ uint32_t *_get_size() const { + _FORCE_INLINE_ USize *_get_size() const { if (!_ptr) { return nullptr; } - return reinterpret_cast<uint32_t *>(_ptr) - 1; + return (USize *)((uint8_t *)_ptr - DATA_OFFSET + SIZE_OFFSET); } - _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const { - return next_power_of_2(p_elements * sizeof(T)); + _FORCE_INLINE_ USize _get_alloc_size(USize p_elements) const { + return next_po2(p_elements * sizeof(T)); } - _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const { + _FORCE_INLINE_ bool _get_alloc_size_checked(USize p_elements, USize *out) const { if (unlikely(p_elements == 0)) { *out = 0; return true; } -#if defined(__GNUC__) - size_t o; - size_t p; +#if defined(__GNUC__) && defined(IS_32_BIT) + USize o; + USize p; if (__builtin_mul_overflow(p_elements, sizeof(T), &o)) { *out = 0; return false; } - *out = next_power_of_2(o); - if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) { + *out = next_po2(o); + if (__builtin_add_overflow(o, static_cast<USize>(32), &p)) { return false; // No longer allocated here. } #else @@ -116,7 +163,7 @@ private: void _unref(void *p_data); void _ref(const CowData *p_from); void _ref(const CowData &p_from); - uint32_t _copy_on_write(); + USize _copy_on_write(); public: void operator=(const CowData<T> &p_from) { _ref(p_from); } @@ -130,8 +177,8 @@ public: return _ptr; } - _FORCE_INLINE_ int size() const { - uint32_t *size = (uint32_t *)_get_size(); + _FORCE_INLINE_ Size size() const { + USize *size = (USize *)_get_size(); if (size) { return *size; } else { @@ -142,42 +189,42 @@ public: _FORCE_INLINE_ void clear() { resize(0); } _FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; } - _FORCE_INLINE_ void set(int p_index, const T &p_elem) { + _FORCE_INLINE_ void set(Size p_index, const T &p_elem) { ERR_FAIL_INDEX(p_index, size()); _copy_on_write(); _ptr[p_index] = p_elem; } - _FORCE_INLINE_ T &get_m(int p_index) { + _FORCE_INLINE_ T &get_m(Size p_index) { CRASH_BAD_INDEX(p_index, size()); _copy_on_write(); return _ptr[p_index]; } - _FORCE_INLINE_ const T &get(int p_index) const { + _FORCE_INLINE_ const T &get(Size p_index) const { CRASH_BAD_INDEX(p_index, size()); return _ptr[p_index]; } template <bool p_ensure_zero = false> - Error resize(int p_size); + Error resize(Size p_size); - _FORCE_INLINE_ void remove_at(int p_index) { + _FORCE_INLINE_ void remove_at(Size p_index) { ERR_FAIL_INDEX(p_index, size()); T *p = ptrw(); - int len = size(); - for (int i = p_index; i < len - 1; i++) { + Size len = size(); + for (Size i = p_index; i < len - 1; i++) { p[i] = p[i + 1]; } resize(len - 1); } - Error insert(int p_pos, const T &p_val) { + Error insert(Size p_pos, const T &p_val) { ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER); resize(size() + 1); - for (int i = (size() - 1); i > p_pos; i--) { + for (Size i = (size() - 1); i > p_pos; i--) { set(i, get(i - 1)); } set(p_pos, p_val); @@ -185,86 +232,88 @@ public: return OK; } - int find(const T &p_val, int p_from = 0) const; - int rfind(const T &p_val, int p_from = -1) const; - int count(const T &p_val) const; + Size find(const T &p_val, Size p_from = 0) const; + Size rfind(const T &p_val, Size p_from = -1) const; + Size count(const T &p_val) const; _FORCE_INLINE_ CowData() {} _FORCE_INLINE_ ~CowData(); _FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); }; }; -template <class T> +template <typename T> void CowData<T>::_unref(void *p_data) { if (!p_data) { return; } - SafeNumeric<uint32_t> *refc = _get_refcount(); + SafeNumeric<USize> *refc = _get_refcount(); if (refc->decrement() > 0) { return; // still in use } // clean up - if (!std::is_trivially_destructible<T>::value) { - uint32_t *count = _get_size(); + if constexpr (!std::is_trivially_destructible_v<T>) { + USize *count = _get_size(); T *data = (T *)(count + 1); - for (uint32_t i = 0; i < *count; ++i) { + for (USize i = 0; i < *count; ++i) { // call destructors data[i].~T(); } } // free mem - Memory::free_static((uint8_t *)p_data, true); + Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false); } -template <class T> -uint32_t CowData<T>::_copy_on_write() { +template <typename T> +typename CowData<T>::USize CowData<T>::_copy_on_write() { if (!_ptr) { return 0; } - SafeNumeric<uint32_t> *refc = _get_refcount(); + SafeNumeric<USize> *refc = _get_refcount(); - uint32_t rc = refc->get(); + USize rc = refc->get(); if (unlikely(rc > 1)) { /* in use by more than me */ - uint32_t current_size = *_get_size(); + USize current_size = *_get_size(); - uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true); + uint8_t *mem_new = (uint8_t *)Memory::alloc_static(_get_alloc_size(current_size) + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, 0); - new (mem_new - 2) SafeNumeric<uint32_t>(1); //refcount - *(mem_new - 1) = current_size; //size + SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new); + USize *_size_ptr = _get_size_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); - T *_data = (T *)(mem_new); + new (_refc_ptr) SafeNumeric<USize>(1); //refcount + *(_size_ptr) = current_size; //size // initialize new elements - if (std::is_trivially_copyable<T>::value) { - memcpy(mem_new, _ptr, current_size * sizeof(T)); - + if constexpr (std::is_trivially_copyable_v<T>) { + memcpy((uint8_t *)_data_ptr, _ptr, current_size * sizeof(T)); } else { - for (uint32_t i = 0; i < current_size; i++) { - memnew_placement(&_data[i], T(_ptr[i])); + for (USize i = 0; i < current_size; i++) { + memnew_placement(&_data_ptr[i], T(_ptr[i])); } } _unref(_ptr); - _ptr = _data; + _ptr = _data_ptr; rc = 1; } return rc; } -template <class T> +template <typename T> template <bool p_ensure_zero> -Error CowData<T>::resize(int p_size) { +Error CowData<T>::resize(Size p_size) { ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER); - int current_size = size(); + Size current_size = size(); if (p_size == current_size) { return OK; @@ -278,36 +327,45 @@ Error CowData<T>::resize(int p_size) { } // possibly changing size, copy on write - uint32_t rc = _copy_on_write(); + USize rc = _copy_on_write(); - size_t current_alloc_size = _get_alloc_size(current_size); - size_t alloc_size; + USize current_alloc_size = _get_alloc_size(current_size); + USize alloc_size; ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY); if (p_size > current_size) { if (alloc_size != current_alloc_size) { if (current_size == 0) { // alloc from scratch - uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true); - ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY); - *(ptr - 1) = 0; //size, currently none - new (ptr - 2) SafeNumeric<uint32_t>(1); //refcount + uint8_t *mem_new = (uint8_t *)Memory::alloc_static(alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); - _ptr = (T *)ptr; + SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new); + USize *_size_ptr = _get_size_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); + + new (_refc_ptr) SafeNumeric<USize>(1); //refcount + *(_size_ptr) = 0; //size, currently none + + _ptr = _data_ptr; } else { - uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); - new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount + uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); + + SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); - _ptr = (T *)(_ptrnew); + new (_refc_ptr) SafeNumeric<USize>(rc); //refcount + + _ptr = _data_ptr; } } // construct the newly created elements - if (!std::is_trivially_constructible<T>::value) { - for (int i = *_get_size(); i < p_size; i++) { + if constexpr (!std::is_trivially_constructible_v<T>) { + for (Size i = *_get_size(); i < p_size; i++) { memnew_placement(&_ptr[i], T); } } else if (p_ensure_zero) { @@ -317,20 +375,24 @@ Error CowData<T>::resize(int p_size) { *_get_size() = p_size; } else if (p_size < current_size) { - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { // deinitialize no longer needed elements - for (uint32_t i = p_size; i < *_get_size(); i++) { + for (USize i = p_size; i < *_get_size(); i++) { T *t = &_ptr[i]; t->~T(); } } if (alloc_size != current_alloc_size) { - uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true); - ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY); - new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount + uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false); + ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY); + + SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new); + T *_data_ptr = _get_data_ptr(mem_new); + + new (_refc_ptr) SafeNumeric<USize>(rc); //refcount - _ptr = (T *)(_ptrnew); + _ptr = _data_ptr; } *_get_size() = p_size; @@ -339,15 +401,15 @@ Error CowData<T>::resize(int p_size) { return OK; } -template <class T> -int CowData<T>::find(const T &p_val, int p_from) const { - int ret = -1; +template <typename T> +typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const { + Size ret = -1; if (p_from < 0 || size() == 0) { return ret; } - for (int i = p_from; i < size(); i++) { + for (Size i = p_from; i < size(); i++) { if (get(i) == p_val) { ret = i; break; @@ -357,9 +419,9 @@ int CowData<T>::find(const T &p_val, int p_from) const { return ret; } -template <class T> -int CowData<T>::rfind(const T &p_val, int p_from) const { - const int s = size(); +template <typename T> +typename CowData<T>::Size CowData<T>::rfind(const T &p_val, Size p_from) const { + const Size s = size(); if (p_from < 0) { p_from = s + p_from; @@ -368,7 +430,7 @@ int CowData<T>::rfind(const T &p_val, int p_from) const { p_from = s - 1; } - for (int i = p_from; i >= 0; i--) { + for (Size i = p_from; i >= 0; i--) { if (get(i) == p_val) { return i; } @@ -376,10 +438,10 @@ int CowData<T>::rfind(const T &p_val, int p_from) const { return -1; } -template <class T> -int CowData<T>::count(const T &p_val) const { - int amount = 0; - for (int i = 0; i < size(); i++) { +template <typename T> +typename CowData<T>::Size CowData<T>::count(const T &p_val) const { + Size amount = 0; + for (Size i = 0; i < size(); i++) { if (get(i) == p_val) { amount++; } @@ -387,12 +449,12 @@ int CowData<T>::count(const T &p_val) const { return amount; } -template <class T> +template <typename T> void CowData<T>::_ref(const CowData *p_from) { _ref(*p_from); } -template <class T> +template <typename T> void CowData<T>::_ref(const CowData &p_from) { if (_ptr == p_from._ptr) { return; // self assign, do nothing. @@ -410,7 +472,7 @@ void CowData<T>::_ref(const CowData &p_from) { } } -template <class T> +template <typename T> CowData<T>::~CowData() { _unref(_ptr); } diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index e1745110d7..a3e8c2c788 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -51,7 +51,7 @@ * The assignment operator copy the pairs from one map to the other. */ -template <class TKey, class TValue> +template <typename TKey, typename TValue> struct HashMapElement { HashMapElement *next = nullptr; HashMapElement *prev = nullptr; @@ -61,10 +61,10 @@ struct HashMapElement { data(p_key, p_value) {} }; -template <class TKey, class TValue, - class Hasher = HashMapHasherDefault, - class Comparator = HashMapComparatorDefault<TKey>, - class Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>> +template <typename TKey, typename TValue, + typename Hasher = HashMapHasherDefault, + typename Comparator = HashMapComparatorDefault<TKey>, + typename Allocator = DefaultTypedAllocator<HashMapElement<TKey, TValue>>> class HashMap { public: static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. diff --git a/core/templates/hash_set.h b/core/templates/hash_set.h index 00f4acbc9c..295b07e5e7 100644 --- a/core/templates/hash_set.h +++ b/core/templates/hash_set.h @@ -46,9 +46,9 @@ * */ -template <class TKey, - class Hasher = HashMapHasherDefault, - class Comparator = HashMapComparatorDefault<TKey>> +template <typename TKey, + typename Hasher = HashMapHasherDefault, + typename Comparator = HashMapComparatorDefault<TKey>> class HashSet { public: static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 2a212f3dcb..fc7a78bcf5 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -248,7 +248,7 @@ static _FORCE_INLINE_ uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i); } -template <class T> +template <typename T> static _FORCE_INLINE_ uint32_t hash_make_uint32_t(T p_in) { union { T t; @@ -281,7 +281,7 @@ static _FORCE_INLINE_ uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = return ((p_prev << 5) + p_prev) ^ p_in; } -template <class T> +template <typename T> static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) { union { T t; @@ -293,15 +293,15 @@ static _FORCE_INLINE_ uint64_t hash_make_uint64_t(T p_in) { return _u._u64; } -template <class T> +template <typename T> class Ref; struct HashMapHasherDefault { // Generic hash function for any type. - template <class T> + template <typename T> static _FORCE_INLINE_ uint32_t hash(const T *p_pointer) { return hash_one_uint64((uint64_t)p_pointer); } - template <class T> + template <typename T> static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); } static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); } @@ -310,7 +310,7 @@ struct HashMapHasherDefault { static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); } static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); } static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); } - static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.ptr()); } + static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); } static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); } static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); } static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); } @@ -387,7 +387,7 @@ struct HashMapHasherDefault { }; // TODO: Fold this into HashMapHasherDefault once C++20 concepts are allowed -template <class T> +template <typename T> struct HashableHasher { static _FORCE_INLINE_ uint32_t hash(const T &hashable) { return hashable.hash(); } }; @@ -429,7 +429,7 @@ struct HashMapComparatorDefault<Vector3> { constexpr uint32_t HASH_TABLE_SIZE_MAX = 29; -const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { +inline constexpr uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { 5, 13, 23, @@ -462,7 +462,7 @@ const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = { }; // Computed with elem_i = UINT64_C (0 x FFFFFFFF FFFFFFFF ) / d_i + 1, where d_i is the i-th element of the above array. -const uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = { +inline constexpr uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = { 3689348814741910324, 1418980313362273202, 802032351030850071, diff --git a/core/templates/list.h b/core/templates/list.h index 6393b942ff..b4d4beb930 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -43,7 +43,7 @@ * from the iterator. */ -template <class T, class A = DefaultAllocator> +template <typename T, typename A = DefaultAllocator> class List { struct _Data; @@ -132,6 +132,8 @@ public: data->erase(this); } + void transfer_to_back(List<T, A> *p_dst_list); + _FORCE_INLINE_ Element() {} }; @@ -408,7 +410,7 @@ public: /** * find an element in the list, */ - template <class T_v> + template <typename T_v> Element *find(const T_v &p_val) { Element *it = front(); while (it) { @@ -644,7 +646,7 @@ public: sort_custom<Comparator<T>>(); } - template <class C> + template <typename C> void sort_custom_inplace() { if (size() < 2) { return; @@ -691,7 +693,7 @@ public: _data->last = to; } - template <class C> + template <typename C> struct AuxiliaryComparator { C compare; _FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const { @@ -699,7 +701,7 @@ public: } }; - template <class C> + template <typename C> void sort_custom() { //this version uses auxiliary memory for speed. //if you don't want to use auxiliary memory, use the in_place version @@ -762,4 +764,41 @@ public: } }; +template <typename T, typename A> +void List<T, A>::Element::transfer_to_back(List<T, A> *p_dst_list) { + // Detach from current. + + if (data->first == this) { + data->first = data->first->next_ptr; + } + if (data->last == this) { + data->last = data->last->prev_ptr; + } + if (prev_ptr) { + prev_ptr->next_ptr = next_ptr; + } + if (next_ptr) { + next_ptr->prev_ptr = prev_ptr; + } + data->size_cache--; + + // Attach to the back of the new one. + + if (!p_dst_list->_data) { + p_dst_list->_data = memnew_allocator(_Data, A); + p_dst_list->_data->first = this; + p_dst_list->_data->last = nullptr; + p_dst_list->_data->size_cache = 0; + prev_ptr = nullptr; + } else { + p_dst_list->_data->last->next_ptr = this; + prev_ptr = p_dst_list->_data->last; + } + p_dst_list->_data->last = this; + next_ptr = nullptr; + + data = p_dst_list->_data; + p_dst_list->_data->size_cache++; +} + #endif // LIST_H diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h index b454821a8f..e0047e0782 100644 --- a/core/templates/local_vector.h +++ b/core/templates/local_vector.h @@ -41,7 +41,7 @@ // If tight, it grows strictly as much as needed. // Otherwise, it grows exponentially (the default and what you want in most cases). -template <class T, class U = uint32_t, bool force_trivial = false, bool tight = false> +template <typename T, typename U = uint32_t, bool force_trivial = false, bool tight = false> class LocalVector { private: U count = 0; @@ -64,7 +64,7 @@ public: CRASH_COND_MSG(!data, "Out of memory"); } - if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) { + if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) { memnew_placement(&data[count++], T(p_elem)); } else { data[count++] = p_elem; @@ -77,7 +77,7 @@ public: for (U i = p_index; i < count; i++) { data[i] = data[i + 1]; } - if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) { data[count].~T(); } } @@ -90,7 +90,7 @@ public: if (count > p_index) { data[p_index] = data[count]; } - if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) { data[count].~T(); } } @@ -104,6 +104,22 @@ public: return false; } + U erase_multiple_unordered(const T &p_val) { + U from = 0; + U occurrences = 0; + while (true) { + int64_t idx = find(p_val, from); + + if (idx == -1) { + break; + } + remove_at_unordered(idx); + from = idx; + occurrences++; + } + return occurrences; + } + void invert() { for (U i = 0; i < count / 2; i++) { SWAP(data[i], data[count - i - 1]); @@ -133,7 +149,7 @@ public: _FORCE_INLINE_ U size() const { return count; } void resize(U p_size) { if (p_size < count) { - if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) { + if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) { for (U i = p_size; i < count; i++) { data[i].~T(); } @@ -145,7 +161,7 @@ public: data = (T *)memrealloc(data, capacity * sizeof(T)); CRASH_COND_MSG(!data, "Out of memory"); } - if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) { + if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) { for (U i = count; i < p_size; i++) { memnew_placement(&data[i], T); } @@ -248,7 +264,7 @@ public: return -1; } - template <class C> + template <typename C> void sort_custom() { U len = count; if (len == 0) { @@ -322,7 +338,7 @@ public: } }; -template <class T, class U = uint32_t, bool force_trivial = false> +template <typename T, typename U = uint32_t, bool force_trivial = false> using TightLocalVector = LocalVector<T, U, force_trivial, true>; #endif // LOCAL_VECTOR_H diff --git a/core/templates/lru.h b/core/templates/lru.h index aecb735c48..919c5605aa 100644 --- a/core/templates/lru.h +++ b/core/templates/lru.h @@ -35,7 +35,7 @@ #include "hash_map.h" #include "list.h" -template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>> +template <typename TKey, typename TData, typename Hasher = HashMapHasherDefault, typename Comparator = HashMapComparatorDefault<TKey>> class LRUCache { private: struct Pair { diff --git a/core/templates/oa_hash_map.h b/core/templates/oa_hash_map.h index 752e264a49..7afb58b7c2 100644 --- a/core/templates/oa_hash_map.h +++ b/core/templates/oa_hash_map.h @@ -50,9 +50,9 @@ * * The assignment operator copy the pairs from one map to the other. */ -template <class TKey, class TValue, - class Hasher = HashMapHasherDefault, - class Comparator = HashMapComparatorDefault<TKey>> +template <typename TKey, typename TValue, + typename Hasher = HashMapHasherDefault, + typename Comparator = HashMapComparatorDefault<TKey>> class OAHashMap { private: TValue *values = nullptr; diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h index 72425a8c3d..4854e1b866 100644 --- a/core/templates/paged_allocator.h +++ b/core/templates/paged_allocator.h @@ -40,7 +40,7 @@ #include <type_traits> #include <typeinfo> -template <class T, bool thread_safe = false> +template <typename T, bool thread_safe = false, uint32_t DEFAULT_PAGE_SIZE = 4096> class PagedAllocator { T **page_pool = nullptr; T ***available_pool = nullptr; @@ -53,12 +53,8 @@ class PagedAllocator { SpinLock spin_lock; public: - enum { - DEFAULT_PAGE_SIZE = 4096 - }; - - template <class... Args> - T *alloc(const Args &&...p_args) { + template <typename... Args> + T *alloc(Args &&...p_args) { if (thread_safe) { spin_lock.lock(); } @@ -99,9 +95,13 @@ public: } } + template <typename... Args> + T *new_allocation(Args &&...p_args) { return alloc(p_args...); } + void delete_allocation(T *p_mem) { free(p_mem); } + private: void _reset(bool p_allow_unfreed) { - if (!p_allow_unfreed || !std::is_trivially_destructible<T>::value) { + if (!p_allow_unfreed || !std::is_trivially_destructible_v<T>) { ERR_FAIL_COND(allocs_available < pages_allocated * page_size); } if (pages_allocated) { diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h index 863e3eef11..1aa8fa2485 100644 --- a/core/templates/paged_array.h +++ b/core/templates/paged_array.h @@ -41,7 +41,7 @@ // PageArrayPool manages central page allocation in a thread safe matter -template <class T> +template <typename T> class PagedArrayPool { T **page_pool = nullptr; uint32_t pages_allocated = 0; @@ -53,7 +53,12 @@ class PagedArrayPool { SpinLock spin_lock; public: - uint32_t alloc_page() { + struct PageInfo { + T *page = nullptr; + uint32_t page_id = 0; + }; + + PageInfo alloc_page() { spin_lock.lock(); if (unlikely(pages_available == 0)) { uint32_t pages_used = pages_allocated; @@ -69,13 +74,11 @@ public: } pages_available--; - uint32_t page = available_page_pool[pages_available]; + uint32_t page_id = available_page_pool[pages_available]; + T *page = page_pool[page_id]; spin_lock.unlock(); - return page; - } - T *get_page(uint32_t p_page_id) { - return page_pool[p_page_id]; + return PageInfo{ page, page_id }; } void free_page(uint32_t p_page_id) { @@ -131,7 +134,7 @@ public: // It does so by allocating pages from a PagedArrayPool. // It is safe to use multiple PagedArrays from different threads, sharing a single PagedArrayPool -template <class T> +template <typename T> class PagedArray { PagedArrayPool<T> *page_pool = nullptr; @@ -190,16 +193,16 @@ public: _grow_page_array(); //keep out of inline } - uint32_t page_id = page_pool->alloc_page(); - page_data[page_count] = page_pool->get_page(page_id); - page_ids[page_count] = page_id; + typename PagedArrayPool<T>::PageInfo page_info = page_pool->alloc_page(); + page_data[page_count] = page_info.page; + page_ids[page_count] = page_info.page_id; } // place the new value uint32_t page = count >> page_size_shift; uint32_t offset = count & page_size_mask; - if (!std::is_trivially_constructible<T>::value) { + if constexpr (!std::is_trivially_constructible_v<T>) { memnew_placement(&page_data[page][offset], T(p_value)); } else { page_data[page][offset] = p_value; @@ -211,7 +214,7 @@ public: _FORCE_INLINE_ void pop_back() { ERR_FAIL_COND(count == 0); - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { uint32_t page = (count - 1) >> page_size_shift; uint32_t offset = (count - 1) & page_size_mask; page_data[page][offset].~T(); @@ -226,9 +229,15 @@ public: count--; } + void remove_at_unordered(uint64_t p_index) { + ERR_FAIL_UNSIGNED_INDEX(p_index, count); + (*this)[p_index] = (*this)[count - 1]; + pop_back(); + } + void clear() { //destruct if needed - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { for (uint64_t i = 0; i < count; i++) { uint32_t page = i >> page_size_shift; uint32_t offset = i & page_size_mask; @@ -309,13 +318,13 @@ public: uint32_t to_copy = MIN(page_size - new_remainder, remainder); for (uint32_t i = 0; i < to_copy; i++) { - if (!std::is_trivially_constructible<T>::value) { + if constexpr (!std::is_trivially_constructible_v<T>) { memnew_placement(&dst_page[i + new_remainder], T(remainder_page[i + remainder - to_copy])); } else { dst_page[i + new_remainder] = remainder_page[i + remainder - to_copy]; } - if (!std::is_trivially_destructible<T>::value) { + if constexpr (!std::is_trivially_destructible_v<T>) { remainder_page[i + remainder - to_copy].~T(); } } diff --git a/core/templates/pair.h b/core/templates/pair.h index ff093a58e6..fd774641c2 100644 --- a/core/templates/pair.h +++ b/core/templates/pair.h @@ -33,7 +33,7 @@ #include "core/templates/hashfuncs.h" #include "core/typedefs.h" -template <class F, class S> +template <typename F, typename S> struct Pair { F first; S second; @@ -49,17 +49,17 @@ struct Pair { } }; -template <class F, class S> +template <typename F, typename S> bool operator==(const Pair<F, S> &pair, const Pair<F, S> &other) { return (pair.first == other.first) && (pair.second == other.second); } -template <class F, class S> +template <typename F, typename S> bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) { return (pair.first != other.first) || (pair.second != other.second); } -template <class F, class S> +template <typename F, typename S> struct PairSort { bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const { if (A.first != B.first) { @@ -69,7 +69,7 @@ struct PairSort { } }; -template <class F, class S> +template <typename F, typename S> struct PairHash { static uint32_t hash(const Pair<F, S> &P) { uint64_t h1 = HashMapHasherDefault::hash(P.first); @@ -78,7 +78,7 @@ struct PairHash { } }; -template <class K, class V> +template <typename K, typename V> struct KeyValue { const K key; V value; @@ -94,17 +94,17 @@ struct KeyValue { } }; -template <class K, class V> +template <typename K, typename V> bool operator==(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) { return (pair.key == other.key) && (pair.value == other.value); } -template <class K, class V> +template <typename K, typename V> bool operator!=(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) { return (pair.key != other.key) || (pair.value != other.value); } -template <class K, class V> +template <typename K, typename V> struct KeyValueSort { bool operator()(const KeyValue<K, V> &A, const KeyValue<K, V> &B) const { return A.key < B.key; diff --git a/core/templates/pooled_list.h b/core/templates/pooled_list.h index 0d96bbb239..0e7048732e 100644 --- a/core/templates/pooled_list.h +++ b/core/templates/pooled_list.h @@ -55,7 +55,7 @@ #include "core/templates/local_vector.h" -template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> +template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> class PooledList { LocalVector<T, U, force_trivial> list; LocalVector<U, U, true> freelist; @@ -128,7 +128,7 @@ public: }; // a pooled list which automatically keeps a list of the active members -template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> +template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> class TrackedPooledList { public: U pool_used_size() const { return _pool.used_size(); } diff --git a/core/templates/rb_map.h b/core/templates/rb_map.h index d373713669..ef555e4a16 100644 --- a/core/templates/rb_map.h +++ b/core/templates/rb_map.h @@ -38,7 +38,7 @@ // based on the very nice implementation of rb-trees by: // https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html -template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator> +template <typename K, typename V, typename C = Comparator<K>, typename A = DefaultAllocator> class RBMap { enum Color { RED, @@ -111,11 +111,16 @@ public: return *this; } - _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; } - _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; } + _FORCE_INLINE_ bool operator==(const Iterator &p_it) const { return E == p_it.E; } + _FORCE_INLINE_ bool operator!=(const Iterator &p_it) const { return E != p_it.E; } explicit operator bool() const { return E != nullptr; } + + Iterator &operator=(const Iterator &p_it) { + E = p_it.E; + return *this; + } Iterator(Element *p_E) { E = p_E; } Iterator() {} Iterator(const Iterator &p_it) { E = p_it.E; } @@ -138,11 +143,16 @@ public: return *this; } - _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; } - _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; } + _FORCE_INLINE_ bool operator==(const ConstIterator &p_it) const { return E == p_it.E; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &p_it) const { return E != p_it.E; } explicit operator bool() const { return E != nullptr; } + + ConstIterator &operator=(const ConstIterator &p_it) { + E = p_it.E; + return *this; + } ConstIterator(const Element *p_E) { E = p_E; } ConstIterator() {} ConstIterator(const ConstIterator &p_it) { E = p_it.E; } diff --git a/core/templates/rb_set.h b/core/templates/rb_set.h index 0fc88709e4..ac7a8df36a 100644 --- a/core/templates/rb_set.h +++ b/core/templates/rb_set.h @@ -37,7 +37,7 @@ // based on the very nice implementation of rb-trees by: // https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html -template <class T, class C = Comparator<T>, class A = DefaultAllocator> +template <typename T, typename C = Comparator<T>, typename A = DefaultAllocator> class RBSet { enum Color { RED, diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index e6c62ebf43..86304d3c73 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -67,7 +67,7 @@ public: virtual ~RID_AllocBase() {} }; -template <class T, bool THREAD_SAFE = false> +template <typename T, bool THREAD_SAFE = false> class RID_Alloc : public RID_AllocBase { T **chunks = nullptr; uint32_t **free_list_chunks = nullptr; @@ -270,7 +270,7 @@ public: if (THREAD_SAFE) { spin_lock.unlock(); } - ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID"); + ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID."); } else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) { if (THREAD_SAFE) { spin_lock.unlock(); @@ -364,7 +364,7 @@ public: } }; -template <class T, bool THREAD_SAFE = false> +template <typename T, bool THREAD_SAFE = false> class RID_PtrOwner { RID_Alloc<T *, THREAD_SAFE> alloc; @@ -423,7 +423,7 @@ public: alloc(p_target_chunk_byte_size) {} }; -template <class T, bool THREAD_SAFE = false> +template <typename T, bool THREAD_SAFE = false> class RID_Owner { RID_Alloc<T, THREAD_SAFE> alloc; diff --git a/core/templates/ring_buffer.h b/core/templates/ring_buffer.h index d878894946..54148a59bf 100644 --- a/core/templates/ring_buffer.h +++ b/core/templates/ring_buffer.h @@ -197,7 +197,7 @@ public: int old_size = size(); int new_size = 1 << p_power; int mask = new_size - 1; - data.resize(1 << p_power); + data.resize(int64_t(1) << int64_t(p_power)); if (old_size < new_size && read_pos > write_pos) { for (int i = 0; i < write_pos; i++) { data.write[(old_size + i) & mask] = data[i]; diff --git a/core/templates/safe_list.h b/core/templates/safe_list.h index 79457db24c..60ccdd9423 100644 --- a/core/templates/safe_list.h +++ b/core/templates/safe_list.h @@ -48,7 +48,7 @@ // This is used in very specific areas of the engine where it's critical that these guarantees are held. -template <class T, class A = DefaultAllocator> +template <typename T, typename A = DefaultAllocator> class SafeList { struct SafeListNode { std::atomic<SafeListNode *> next = nullptr; diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h index 20fb0c6501..637b068da9 100644 --- a/core/templates/safe_refcount.h +++ b/core/templates/safe_refcount.h @@ -54,12 +54,12 @@ #define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \ static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \ static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \ - static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value); + static_assert(std::is_trivially_destructible_v<std::atomic<m_type>>); #define SAFE_FLAG_TYPE_PUN_GUARANTEES \ static_assert(sizeof(SafeFlag) == sizeof(bool)); \ static_assert(alignof(SafeFlag) == alignof(bool)); -template <class T> +template <typename T> class SafeNumeric { std::atomic<T> value; diff --git a/core/templates/search_array.h b/core/templates/search_array.h index f537ef67fd..835bcd63a6 100644 --- a/core/templates/search_array.h +++ b/core/templates/search_array.h @@ -33,17 +33,17 @@ #include <core/templates/sort_array.h> -template <class T, class Comparator = _DefaultComparator<T>> +template <typename T, typename Comparator = _DefaultComparator<T>> class SearchArray { public: Comparator compare; - inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const { - int lo = 0; - int hi = p_len; + inline int64_t bisect(const T *p_array, int64_t p_len, const T &p_value, bool p_before) const { + int64_t lo = 0; + int64_t hi = p_len; if (p_before) { while (lo < hi) { - const int mid = (lo + hi) / 2; + const int64_t mid = (lo + hi) / 2; if (compare(p_array[mid], p_value)) { lo = mid + 1; } else { @@ -52,7 +52,7 @@ public: } } else { while (lo < hi) { - const int mid = (lo + hi) / 2; + const int64_t mid = (lo + hi) / 2; if (compare(p_value, p_array[mid])) { hi = mid; } else { diff --git a/core/templates/self_list.h b/core/templates/self_list.h index fdf91beacc..a04972594e 100644 --- a/core/templates/self_list.h +++ b/core/templates/self_list.h @@ -34,7 +34,7 @@ #include "core/error/error_macros.h" #include "core/typedefs.h" -template <class T> +template <typename T> class SelfList { public: class List { @@ -109,7 +109,7 @@ public: sort_custom<Comparator<T>>(); } - template <class C> + template <typename C> void sort_custom() { if (_first == _last) { return; @@ -159,6 +159,9 @@ public: _FORCE_INLINE_ SelfList<T> *first() { return _first; } _FORCE_INLINE_ const SelfList<T> *first() const { return _first; } + // Forbid copying, which has broken behavior. + void operator=(const List &) = delete; + _FORCE_INLINE_ List() {} _FORCE_INLINE_ ~List() { // A self list must be empty on destruction. @@ -185,6 +188,9 @@ public: _FORCE_INLINE_ const SelfList<T> *prev() const { return _prev; } _FORCE_INLINE_ T *self() const { return _self; } + // Forbid copying, which has broken behavior. + void operator=(const SelfList<T> &) = delete; + _FORCE_INLINE_ SelfList(T *p_self) { _self = p_self; } diff --git a/core/templates/simple_type.h b/core/templates/simple_type.h index 3950158c5a..b2ae0110e2 100644 --- a/core/templates/simple_type.h +++ b/core/templates/simple_type.h @@ -33,22 +33,22 @@ /* Batch of specializations to obtain the actual simple type */ -template <class T> +template <typename T> struct GetSimpleTypeT { typedef T type_t; }; -template <class T> +template <typename T> struct GetSimpleTypeT<T &> { typedef T type_t; }; -template <class T> +template <typename T> struct GetSimpleTypeT<T const> { typedef T type_t; }; -template <class T> +template <typename T> struct GetSimpleTypeT<T const &> { typedef T type_t; }; diff --git a/core/templates/sort_array.h b/core/templates/sort_array.h index fbe2a89a84..e7eaf8ee81 100644 --- a/core/templates/sort_array.h +++ b/core/templates/sort_array.h @@ -40,7 +40,7 @@ break; \ } -template <class T> +template <typename T> struct _DefaultComparator { _FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); } }; @@ -51,7 +51,7 @@ struct _DefaultComparator { #define SORT_ARRAY_VALIDATE_ENABLED false #endif -template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> +template <typename T, typename Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED> class SortArray { enum { INTROSORT_THRESHOLD = 16 @@ -78,8 +78,8 @@ public: } } - inline int bitlog(int n) const { - int k; + inline int64_t bitlog(int64_t n) const { + int64_t k; for (k = 0; n != 1; n >>= 1) { ++k; } @@ -88,8 +88,8 @@ public: /* Heap / Heapsort functions */ - inline void push_heap(int p_first, int p_hole_idx, int p_top_index, T p_value, T *p_array) const { - int parent = (p_hole_idx - 1) / 2; + inline void push_heap(int64_t p_first, int64_t p_hole_idx, int64_t p_top_index, T p_value, T *p_array) const { + int64_t parent = (p_hole_idx - 1) / 2; while (p_hole_idx > p_top_index && compare(p_array[p_first + parent], p_value)) { p_array[p_first + p_hole_idx] = p_array[p_first + parent]; p_hole_idx = parent; @@ -98,17 +98,17 @@ public: p_array[p_first + p_hole_idx] = p_value; } - inline void pop_heap(int p_first, int p_last, int p_result, T p_value, T *p_array) const { + inline void pop_heap(int64_t p_first, int64_t p_last, int64_t p_result, T p_value, T *p_array) const { p_array[p_result] = p_array[p_first]; adjust_heap(p_first, 0, p_last - p_first, p_value, p_array); } - inline void pop_heap(int p_first, int p_last, T *p_array) const { + inline void pop_heap(int64_t p_first, int64_t p_last, T *p_array) const { pop_heap(p_first, p_last - 1, p_last - 1, p_array[p_last - 1], p_array); } - inline void adjust_heap(int p_first, int p_hole_idx, int p_len, T p_value, T *p_array) const { - int top_index = p_hole_idx; - int second_child = 2 * p_hole_idx + 2; + inline void adjust_heap(int64_t p_first, int64_t p_hole_idx, int64_t p_len, T p_value, T *p_array) const { + int64_t top_index = p_hole_idx; + int64_t second_child = 2 * p_hole_idx + 2; while (second_child < p_len) { if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) { @@ -127,18 +127,18 @@ public: push_heap(p_first, p_hole_idx, top_index, p_value, p_array); } - inline void sort_heap(int p_first, int p_last, T *p_array) const { + inline void sort_heap(int64_t p_first, int64_t p_last, T *p_array) const { while (p_last - p_first > 1) { pop_heap(p_first, p_last--, p_array); } } - inline void make_heap(int p_first, int p_last, T *p_array) const { + inline void make_heap(int64_t p_first, int64_t p_last, T *p_array) const { if (p_last - p_first < 2) { return; } - int len = p_last - p_first; - int parent = (len - 2) / 2; + int64_t len = p_last - p_first; + int64_t parent = (len - 2) / 2; while (true) { adjust_heap(p_first, parent, len, p_array[p_first + parent], p_array); @@ -149,9 +149,9 @@ public: } } - inline void partial_sort(int p_first, int p_last, int p_middle, T *p_array) const { + inline void partial_sort(int64_t p_first, int64_t p_last, int64_t p_middle, T *p_array) const { make_heap(p_first, p_middle, p_array); - for (int i = p_middle; i < p_last; i++) { + for (int64_t i = p_middle; i < p_last; i++) { if (compare(p_array[i], p_array[p_first])) { pop_heap(p_first, p_middle, i, p_array[i], p_array); } @@ -159,18 +159,18 @@ public: sort_heap(p_first, p_middle, p_array); } - inline void partial_select(int p_first, int p_last, int p_middle, T *p_array) const { + inline void partial_select(int64_t p_first, int64_t p_last, int64_t p_middle, T *p_array) const { make_heap(p_first, p_middle, p_array); - for (int i = p_middle; i < p_last; i++) { + for (int64_t i = p_middle; i < p_last; i++) { if (compare(p_array[i], p_array[p_first])) { pop_heap(p_first, p_middle, i, p_array[i], p_array); } } } - inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const { - const int unmodified_first = p_first; - const int unmodified_last = p_last; + inline int64_t partitioner(int64_t p_first, int64_t p_last, T p_pivot, T *p_array) const { + const int64_t unmodified_first = p_first; + const int64_t unmodified_last = p_last; while (true) { while (compare(p_array[p_first], p_pivot)) { @@ -196,7 +196,7 @@ public: } } - inline void introsort(int p_first, int p_last, T *p_array, int p_max_depth) const { + inline void introsort(int64_t p_first, int64_t p_last, T *p_array, int64_t p_max_depth) const { while (p_last - p_first > INTROSORT_THRESHOLD) { if (p_max_depth == 0) { partial_sort(p_first, p_last, p_last, p_array); @@ -205,7 +205,7 @@ public: p_max_depth--; - int cut = partitioner( + int64_t cut = partitioner( p_first, p_last, median_of_3( @@ -219,7 +219,7 @@ public: } } - inline void introselect(int p_first, int p_nth, int p_last, T *p_array, int p_max_depth) const { + inline void introselect(int64_t p_first, int64_t p_nth, int64_t p_last, T *p_array, int64_t p_max_depth) const { while (p_last - p_first > 3) { if (p_max_depth == 0) { partial_select(p_first, p_nth + 1, p_last, p_array); @@ -229,7 +229,7 @@ public: p_max_depth--; - int cut = partitioner( + int64_t cut = partitioner( p_first, p_last, median_of_3( @@ -248,8 +248,8 @@ public: insertion_sort(p_first, p_last, p_array); } - inline void unguarded_linear_insert(int p_last, T p_value, T *p_array) const { - int next = p_last - 1; + inline void unguarded_linear_insert(int64_t p_last, T p_value, T *p_array) const { + int64_t next = p_last - 1; while (compare(p_value, p_array[next])) { if (Validate) { ERR_BAD_COMPARE(next == 0); @@ -261,10 +261,10 @@ public: p_array[p_last] = p_value; } - inline void linear_insert(int p_first, int p_last, T *p_array) const { + inline void linear_insert(int64_t p_first, int64_t p_last, T *p_array) const { T val = p_array[p_last]; if (compare(val, p_array[p_first])) { - for (int i = p_last; i > p_first; i--) { + for (int64_t i = p_last; i > p_first; i--) { p_array[i] = p_array[i - 1]; } @@ -274,22 +274,22 @@ public: } } - inline void insertion_sort(int p_first, int p_last, T *p_array) const { + inline void insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const { if (p_first == p_last) { return; } - for (int i = p_first + 1; i != p_last; i++) { + for (int64_t i = p_first + 1; i != p_last; i++) { linear_insert(p_first, i, p_array); } } - inline void unguarded_insertion_sort(int p_first, int p_last, T *p_array) const { - for (int i = p_first; i != p_last; i++) { + inline void unguarded_insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const { + for (int64_t i = p_first; i != p_last; i++) { unguarded_linear_insert(i, p_array[i], p_array); } } - inline void final_insertion_sort(int p_first, int p_last, T *p_array) const { + inline void final_insertion_sort(int64_t p_first, int64_t p_last, T *p_array) const { if (p_last - p_first > INTROSORT_THRESHOLD) { insertion_sort(p_first, p_first + INTROSORT_THRESHOLD, p_array); unguarded_insertion_sort(p_first + INTROSORT_THRESHOLD, p_last, p_array); @@ -298,18 +298,18 @@ public: } } - inline void sort_range(int p_first, int p_last, T *p_array) const { + inline void sort_range(int64_t p_first, int64_t p_last, T *p_array) const { if (p_first != p_last) { introsort(p_first, p_last, p_array, bitlog(p_last - p_first) * 2); final_insertion_sort(p_first, p_last, p_array); } } - inline void sort(T *p_array, int p_len) const { + inline void sort(T *p_array, int64_t p_len) const { sort_range(0, p_len, p_array); } - inline void nth_element(int p_first, int p_last, int p_nth, T *p_array) const { + inline void nth_element(int64_t p_first, int64_t p_last, int64_t p_nth, T *p_array) const { if (p_first == p_last || p_nth == p_last) { return; } diff --git a/core/templates/vector.h b/core/templates/vector.h index d8bac0870f..52c10eea68 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -45,22 +45,23 @@ #include <climits> #include <initializer_list> -template <class T> +template <typename T> class VectorWriteProxy { public: - _FORCE_INLINE_ T &operator[](int p_index) { + _FORCE_INLINE_ T &operator[](typename CowData<T>::Size p_index) { CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size()); return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index]; } }; -template <class T> +template <typename T> class Vector { friend class VectorWriteProxy<T>; public: VectorWriteProxy<T> write; + typedef typename CowData<T>::Size Size; private: CowData<T> _cowdata; @@ -70,9 +71,9 @@ public: _FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } //alias void fill(T p_elem); - void remove_at(int p_index) { _cowdata.remove_at(p_index); } + void remove_at(Size p_index) { _cowdata.remove_at(p_index); } _FORCE_INLINE_ bool erase(const T &p_val) { - int idx = find(p_val); + Size idx = find(p_val); if (idx >= 0) { remove_at(idx); return true; @@ -87,19 +88,19 @@ public: _FORCE_INLINE_ void clear() { resize(0); } _FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); } - _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); } - _FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); } - _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } - _FORCE_INLINE_ int size() const { return _cowdata.size(); } - Error resize(int p_size) { return _cowdata.resize(p_size); } - Error resize_zeroed(int p_size) { return _cowdata.template resize<true>(p_size); } - _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); } - Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); } - int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); } - int rfind(const T &p_val, int p_from = -1) const { return _cowdata.rfind(p_val, p_from); } - int count(const T &p_val) const { return _cowdata.count(p_val); } + _FORCE_INLINE_ T get(Size p_index) { return _cowdata.get(p_index); } + _FORCE_INLINE_ const T &get(Size p_index) const { return _cowdata.get(p_index); } + _FORCE_INLINE_ void set(Size p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); } + _FORCE_INLINE_ Size size() const { return _cowdata.size(); } + Error resize(Size p_size) { return _cowdata.resize(p_size); } + Error resize_zeroed(Size p_size) { return _cowdata.template resize<true>(p_size); } + _FORCE_INLINE_ const T &operator[](Size p_index) const { return _cowdata.get(p_index); } + Error insert(Size p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); } + Size find(const T &p_val, Size p_from = 0) const { return _cowdata.find(p_val, p_from); } + Size rfind(const T &p_val, Size p_from = -1) const { return _cowdata.rfind(p_val, p_from); } + Size count(const T &p_val) const { return _cowdata.count(p_val); } - void append_array(Vector<T> p_other); + void append_array(const Vector<T> &p_other); _FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; } @@ -107,9 +108,9 @@ public: sort_custom<_DefaultComparator<T>>(); } - template <class Comparator, bool Validate = SORT_ARRAY_VALIDATE_ENABLED, class... Args> + template <typename Comparator, bool Validate = SORT_ARRAY_VALIDATE_ENABLED, typename... Args> void sort_custom(Args &&...args) { - int len = _cowdata.size(); + Size len = _cowdata.size(); if (len == 0) { return; } @@ -119,12 +120,12 @@ public: sorter.sort(data, len); } - int bsearch(const T &p_value, bool p_before) { + Size bsearch(const T &p_value, bool p_before) { return bsearch_custom<_DefaultComparator<T>>(p_value, p_before); } - template <class Comparator, class Value, class... Args> - int bsearch_custom(const Value &p_value, bool p_before, Args &&...args) { + template <typename Comparator, typename Value, typename... Args> + Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) { SearchArray<T, Comparator> search{ args... }; return search.bisect(ptrw(), size(), p_value, p_before); } @@ -134,7 +135,7 @@ public: } void ordered_insert(const T &p_val) { - int i; + Size i; for (i = 0; i < _cowdata.size(); i++) { if (p_val < operator[](i)) { break; @@ -157,28 +158,28 @@ public: return ret; } - Vector<T> slice(int p_begin, int p_end = INT_MAX) const { + Vector<T> slice(Size p_begin, Size p_end = CowData<T>::MAX_INT) const { Vector<T> result; - const int s = size(); + const Size s = size(); - int begin = CLAMP(p_begin, -s, s); + Size begin = CLAMP(p_begin, -s, s); if (begin < 0) { begin += s; } - int end = CLAMP(p_end, -s, s); + Size end = CLAMP(p_end, -s, s); if (end < 0) { end += s; } ERR_FAIL_COND_V(begin > end, result); - int result_size = end - begin; + Size result_size = end - begin; result.resize(result_size); const T *const r = ptr(); T *const w = result.ptrw(); - for (int i = 0; i < result_size; ++i) { + for (Size i = 0; i < result_size; ++i) { w[i] = r[begin + i]; } @@ -186,11 +187,11 @@ public: } bool operator==(const Vector<T> &p_arr) const { - int s = size(); + Size s = size(); if (s != p_arr.size()) { return false; } - for (int i = 0; i < s; i++) { + for (Size i = 0; i < s; i++) { if (operator[](i) != p_arr[i]) { return false; } @@ -199,11 +200,11 @@ public: } bool operator!=(const Vector<T> &p_arr) const { - int s = size(); + Size s = size(); if (s != p_arr.size()) { return true; } - for (int i = 0; i < s; i++) { + for (Size i = 0; i < s; i++) { if (operator[](i) != p_arr[i]) { return true; } @@ -280,7 +281,7 @@ public: Error err = _cowdata.resize(p_init.size()); ERR_FAIL_COND(err); - int i = 0; + Size i = 0; for (const T &element : p_init) { _cowdata.set(i++, element); } @@ -290,28 +291,28 @@ public: _FORCE_INLINE_ ~Vector() {} }; -template <class T> +template <typename T> void Vector<T>::reverse() { - for (int i = 0; i < size() / 2; i++) { + for (Size i = 0; i < size() / 2; i++) { T *p = ptrw(); SWAP(p[i], p[size() - i - 1]); } } -template <class T> -void Vector<T>::append_array(Vector<T> p_other) { - const int ds = p_other.size(); +template <typename T> +void Vector<T>::append_array(const Vector<T> &p_other) { + const Size ds = p_other.size(); if (ds == 0) { return; } - const int bs = size(); + const Size bs = size(); resize(bs + ds); - for (int i = 0; i < ds; ++i) { + for (Size i = 0; i < ds; ++i) { ptrw()[bs + i] = p_other[i]; } } -template <class T> +template <typename T> bool Vector<T>::push_back(T p_elem) { Error err = resize(size() + 1); ERR_FAIL_COND_V(err, true); @@ -320,10 +321,10 @@ bool Vector<T>::push_back(T p_elem) { return false; } -template <class T> +template <typename T> void Vector<T>::fill(T p_elem) { T *p = ptrw(); - for (int i = 0; i < size(); i++) { + for (Size i = 0; i < size(); i++) { p[i] = p_elem; } } diff --git a/core/templates/vmap.h b/core/templates/vmap.h index 5620bd3772..2ad074942b 100644 --- a/core/templates/vmap.h +++ b/core/templates/vmap.h @@ -34,7 +34,7 @@ #include "core/templates/cowdata.h" #include "core/typedefs.h" -template <class T, class V> +template <typename T, typename V> class VMap { public: struct Pair { diff --git a/core/templates/vset.h b/core/templates/vset.h index a4886ca965..614d0e1e5f 100644 --- a/core/templates/vset.h +++ b/core/templates/vset.h @@ -34,7 +34,7 @@ #include "core/templates/vector.h" #include "core/typedefs.h" -template <class T> +template <typename T> class VSet { Vector<T> _data; diff --git a/core/typedefs.h b/core/typedefs.h index 24c247fd38..2b90a911cd 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -92,6 +92,8 @@ #undef Error #undef OK #undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum +#undef MemoryBarrier +#undef MONO_FONT #endif // Make room for our constexpr's below by overriding potential system-specific macros. @@ -130,7 +132,7 @@ constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) { // Generic swap template. #ifndef SWAP #define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y)) -template <class T> +template <typename T> inline void __swap_tmpl(T &x, T &y) { T aux = x; x = y; @@ -184,7 +186,7 @@ static inline int get_shift_from_power_of_2(unsigned int p_bits) { return -1; } -template <class T> +template <typename T> static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) { --x; @@ -232,6 +234,10 @@ constexpr T get_num_bits(T x) { #define BSWAP16(x) __builtin_bswap16(x) #define BSWAP32(x) __builtin_bswap32(x) #define BSWAP64(x) __builtin_bswap64(x) +#elif defined(_MSC_VER) +#define BSWAP16(x) _byteswap_ushort(x) +#define BSWAP32(x) _byteswap_ulong(x) +#define BSWAP64(x) _byteswap_uint64(x) #else static inline uint16_t BSWAP16(uint16_t x) { return (x >> 8) | (x << 8); @@ -250,7 +256,7 @@ static inline uint64_t BSWAP64(uint64_t x) { #endif // Generic comparator used in Map, List, etc. -template <class T> +template <typename T> struct Comparator { _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } }; diff --git a/core/variant/array.cpp b/core/variant/array.cpp index ab0315ae34..3685515db5 100644 --- a/core/variant/array.cpp +++ b/core/variant/array.cpp @@ -81,6 +81,22 @@ void Array::_unref() const { _p = nullptr; } +Array::Iterator Array::begin() { + return Iterator(_p->array.ptrw(), _p->read_only); +} + +Array::Iterator Array::end() { + return Iterator(_p->array.ptrw() + _p->array.size(), _p->read_only); +} + +Array::ConstIterator Array::begin() const { + return ConstIterator(_p->array.ptr(), _p->read_only); +} + +Array::ConstIterator Array::end() const { + return ConstIterator(_p->array.ptr() + _p->array.size(), _p->read_only); +} + Variant &Array::operator[](int p_idx) { if (unlikely(_p->read_only)) { *_p->read_only = _p->array[p_idx]; @@ -316,17 +332,17 @@ void Array::erase(const Variant &p_value) { } Variant Array::front() const { - ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array."); return operator[](0); } Variant Array::back() const { - ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array."); return operator[](_p->array.size() - 1); } Variant Array::pick_random() const { - ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array."); + ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array."); return operator[](Math::rand() % _p->array.size()); } diff --git a/core/variant/array.h b/core/variant/array.h index 8b1f8c0678..3aa957b312 100644 --- a/core/variant/array.h +++ b/core/variant/array.h @@ -46,6 +46,70 @@ class Array { void _unref() const; public: + struct ConstIterator { + _FORCE_INLINE_ const Variant &operator*() const; + _FORCE_INLINE_ const Variant *operator->() const; + + _FORCE_INLINE_ ConstIterator &operator++(); + _FORCE_INLINE_ ConstIterator &operator--(); + + _FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr != p_other.element_ptr; } + + _FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) : + element_ptr(p_element_ptr), read_only(p_read_only) {} + _FORCE_INLINE_ ConstIterator() {} + _FORCE_INLINE_ ConstIterator(const ConstIterator &p_other) : + element_ptr(p_other.element_ptr), read_only(p_other.read_only) {} + + _FORCE_INLINE_ ConstIterator &operator=(const ConstIterator &p_other) { + element_ptr = p_other.element_ptr; + read_only = p_other.read_only; + return *this; + } + + private: + const Variant *element_ptr = nullptr; + Variant *read_only = nullptr; + }; + + struct Iterator { + _FORCE_INLINE_ Variant &operator*() const; + _FORCE_INLINE_ Variant *operator->() const; + + _FORCE_INLINE_ Iterator &operator++(); + _FORCE_INLINE_ Iterator &operator--(); + + _FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return element_ptr == p_other.element_ptr; } + _FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return element_ptr != p_other.element_ptr; } + + _FORCE_INLINE_ Iterator(Variant *p_element_ptr, Variant *p_read_only = nullptr) : + element_ptr(p_element_ptr), read_only(p_read_only) {} + _FORCE_INLINE_ Iterator() {} + _FORCE_INLINE_ Iterator(const Iterator &p_other) : + element_ptr(p_other.element_ptr), read_only(p_other.read_only) {} + + _FORCE_INLINE_ Iterator &operator=(const Iterator &p_other) { + element_ptr = p_other.element_ptr; + read_only = p_other.read_only; + return *this; + } + + operator ConstIterator() const { + return ConstIterator(element_ptr, read_only); + } + + private: + Variant *element_ptr = nullptr; + Variant *read_only = nullptr; + }; + + Iterator begin(); + Iterator end(); + + ConstIterator begin() const; + ConstIterator end() const; + void _ref(const Array &p_from) const; Variant &operator[](int p_idx); diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 34b54f1d00..0fe4518b0f 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -47,11 +47,11 @@ // Variant cannot define an implicit cast operator for every Object subclass, so the // casting is done here, to allow binding methods with parameters more specific than Object * -template <class T> +template <typename T> struct VariantCaster { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t<T>; - if constexpr (std::is_base_of<Object, TStripped>::value) { + if constexpr (std::is_base_of_v<Object, TStripped>) { return Object::cast_to<TStripped>(p_variant); } else { return p_variant; @@ -59,11 +59,11 @@ struct VariantCaster { } }; -template <class T> +template <typename T> struct VariantCaster<T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t<T>; - if constexpr (std::is_base_of<Object, TStripped>::value) { + if constexpr (std::is_base_of_v<Object, TStripped>) { return Object::cast_to<TStripped>(p_variant); } else { return p_variant; @@ -71,11 +71,11 @@ struct VariantCaster<T &> { } }; -template <class T> +template <typename T> struct VariantCaster<const T &> { static _FORCE_INLINE_ T cast(const Variant &p_variant) { using TStripped = std::remove_pointer_t<T>; - if constexpr (std::is_base_of<Object, TStripped>::value) { + if constexpr (std::is_base_of_v<Object, TStripped>) { return Object::cast_to<TStripped>(p_variant); } else { return p_variant; @@ -176,6 +176,7 @@ VARIANT_ENUM_CAST(Variant::Operator); VARIANT_ENUM_CAST(Key); VARIANT_BITFIELD_CAST(KeyModifierMask); +VARIANT_ENUM_CAST(KeyLocation); static inline Key &operator|=(Key &a, BitField<KeyModifierMask> b) { a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b.operator int64_t())); @@ -225,7 +226,7 @@ template <typename T> struct VariantObjectClassChecker { static _FORCE_INLINE_ bool check(const Variant &p_variant) { using TStripped = std::remove_pointer_t<T>; - if constexpr (std::is_base_of<Object, TStripped>::value) { + if constexpr (std::is_base_of_v<Object, TStripped>) { Object *obj = p_variant; return Object::cast_to<TStripped>(p_variant) || !obj; } else { @@ -248,7 +249,7 @@ struct VariantObjectClassChecker<const Ref<T> &> { #ifdef DEBUG_METHODS_ENABLED -template <class T> +template <typename T> struct VariantCasterAndValidate { static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; @@ -263,7 +264,7 @@ struct VariantCasterAndValidate { } }; -template <class T> +template <typename T> struct VariantCasterAndValidate<T &> { static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; @@ -278,7 +279,7 @@ struct VariantCasterAndValidate<T &> { } }; -template <class T> +template <typename T> struct VariantCasterAndValidate<const T &> { static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) { Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE; @@ -295,7 +296,7 @@ struct VariantCasterAndValidate<const T &> { #endif // DEBUG_METHODS_ENABLED -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -307,7 +308,7 @@ void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), con (void)(p_args); //avoid warning } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -319,87 +320,87 @@ void call_with_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) con (void)(p_args); //avoid warning } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_ptr_args_helper(T *p_instance, void (T::*p_method)(P...), const void **p_args, IndexSequence<Is...>) { (p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_ptr_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const void **p_args, IndexSequence<Is...>) { (p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_ptr_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const void **p_args, void *r_ret, IndexSequence<Is...>) { PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_ptr_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const void **p_args, void *r_ret, IndexSequence<Is...>) { PtrToArg<R>::encode((p_instance->*p_method)(PtrToArg<P>::convert(p_args[Is])...), r_ret); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_ptr_args_static_helper(T *p_instance, void (*p_method)(T *, P...), const void **p_args, IndexSequence<Is...>) { p_method(p_instance, PtrToArg<P>::convert(p_args[Is])...); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_ptr_args_static_retc_helper(T *p_instance, R (*p_method)(T *, P...), const void **p_args, void *r_ret, IndexSequence<Is...>) { PtrToArg<R>::encode(p_method(p_instance, PtrToArg<P>::convert(p_args[Is])...), r_ret); } -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> void call_with_ptr_args_static_method_ret_helper(R (*p_method)(P...), const void **p_args, void *r_ret, IndexSequence<Is...>) { PtrToArg<R>::encode(p_method(PtrToArg<P>::convert(p_args[Is])...), r_ret); } -template <class... P, size_t... Is> +template <typename... P, size_t... Is> void call_with_ptr_args_static_method_helper(void (*p_method)(P...), const void **p_args, IndexSequence<Is...>) { p_method(PtrToArg<P>::convert(p_args[Is])...); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_validated_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, IndexSequence<Is...>) { (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_validated_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, IndexSequence<Is...>) { (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_static_retc_helper(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, p_method(p_instance, (VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_validated_variant_args_static_helper(T *p_instance, void (*p_method)(T *, P...), const Variant **p_args, IndexSequence<Is...>) { p_method(p_instance, (VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); } -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> void call_with_validated_variant_args_static_method_ret_helper(R (*p_method)(P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, p_method((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); } -template <class... P, size_t... Is> +template <typename... P, size_t... Is> void call_with_validated_variant_args_static_method_helper(void (*p_method)(P...), const Variant **p_args, IndexSequence<Is...>) { p_method((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); } -template <class T, class... P> +template <typename T, typename... P> void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -417,7 +418,7 @@ void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Vari call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -450,7 +451,7 @@ void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const V call_with_variant_args_helper(p_instance, p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -468,7 +469,7 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -501,7 +502,7 @@ void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, call_with_variant_argsc_helper(p_instance, p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -534,7 +535,7 @@ void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const call_with_variant_args_ret_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -567,111 +568,111 @@ void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, call_with_variant_args_retc_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_ptr_args(T *p_instance, void (T::*p_method)(P...), const void **p_args) { call_with_ptr_args_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_ptr_argsc(T *p_instance, void (T::*p_method)(P...) const, const void **p_args) { call_with_ptr_argsc_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_ptr_args_ret(T *p_instance, R (T::*p_method)(P...), const void **p_args, void *r_ret) { call_with_ptr_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_ptr_args_retc(T *p_instance, R (T::*p_method)(P...) const, const void **p_args, void *r_ret) { call_with_ptr_args_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_ptr_args_static(T *p_instance, void (*p_method)(T *, P...), const void **p_args) { call_with_ptr_args_static_helper<T, P...>(p_instance, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_ptr_args_static_retc(T *p_instance, R (*p_method)(T *, P...), const void **p_args, void *r_ret) { call_with_ptr_args_static_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> void call_with_ptr_args_static_method_ret(R (*p_method)(P...), const void **p_args, void *r_ret) { call_with_ptr_args_static_method_ret_helper<R, P...>(p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> void call_with_ptr_args_static_method(void (*p_method)(P...), const void **p_args) { call_with_ptr_args_static_method_helper<P...>(p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } // Validated -template <class T, class... P> +template <typename T, typename... P> void call_with_validated_variant_args(Variant *base, void (T::*p_method)(P...), const Variant **p_args) { call_with_validated_variant_args_helper<T, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_variant_args_ret(Variant *base, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_ret_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_variant_args_retc(Variant *base, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_retc_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_validated_variant_args_static(Variant *base, void (*p_method)(T *, P...), const Variant **p_args) { call_with_validated_variant_args_static_helper<T, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_variant_args_static_retc(Variant *base, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_retc_helper<T, R, P...>(VariantGetInternalPtr<T>::get_ptr(base), p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> void call_with_validated_variant_args_static_method(void (*p_method)(P...), const Variant **p_args) { call_with_validated_variant_args_static_method_helper<P...>(p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> void call_with_validated_variant_args_static_method_ret(R (*p_method)(P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_method_ret_helper<R, P...>(p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } // Validated Object -template <class T, class... P> +template <typename T, typename... P> void call_with_validated_object_instance_args(T *base, void (T::*p_method)(P...), const Variant **p_args) { call_with_validated_variant_args_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_validated_object_instance_argsc(T *base, void (T::*p_method)(P...) const, const Variant **p_args) { call_with_validated_variant_argsc_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_object_instance_args_ret(T *base, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_ret_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_object_instance_args_retc(T *base, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_retc_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P> +template <typename T, typename... P> void call_with_validated_object_instance_args_static(T *base, void (*p_method)(T *, P...), const Variant **p_args) { call_with_validated_variant_args_static_helper<T, P...>(base, p_method, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_validated_object_instance_args_static_retc(T *base, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_retc_helper<T, R, P...>(base, p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } @@ -683,7 +684,7 @@ void call_with_validated_object_instance_args_static_retc(T *base, R (*p_method) #pragma GCC diagnostic ignored "-Wunused-but-set-parameter" #endif -template <class Q> +template <typename Q> void call_get_argument_type_helper(int p_arg, int &index, Variant::Type &type) { if (p_arg == index) { type = GetTypeInfo<Q>::VARIANT_TYPE; @@ -691,7 +692,7 @@ void call_get_argument_type_helper(int p_arg, int &index, Variant::Type &type) { index++; } -template <class... P> +template <typename... P> Variant::Type call_get_argument_type(int p_arg) { Variant::Type type = Variant::NIL; int index = 0; @@ -703,7 +704,7 @@ Variant::Type call_get_argument_type(int p_arg) { return type; } -template <class Q> +template <typename Q> void call_get_argument_type_info_helper(int p_arg, int &index, PropertyInfo &info) { if (p_arg == index) { info = GetTypeInfo<Q>::get_class_info(); @@ -711,7 +712,7 @@ void call_get_argument_type_info_helper(int p_arg, int &index, PropertyInfo &inf index++; } -template <class... P> +template <typename... P> void call_get_argument_type_info(int p_arg, PropertyInfo &info) { int index = 0; // I think rocket science is simpler than modern C++. @@ -722,7 +723,7 @@ void call_get_argument_type_info(int p_arg, PropertyInfo &info) { } #ifdef DEBUG_METHODS_ENABLED -template <class Q> +template <typename Q> void call_get_argument_metadata_helper(int p_arg, int &index, GodotTypeInfo::Metadata &md) { if (p_arg == index) { md = GetTypeInfo<Q>::METADATA; @@ -730,7 +731,7 @@ void call_get_argument_metadata_helper(int p_arg, int &index, GodotTypeInfo::Met index++; } -template <class... P> +template <typename... P> GodotTypeInfo::Metadata call_get_argument_metadata(int p_arg) { GodotTypeInfo::Metadata md = GodotTypeInfo::METADATA_NONE; @@ -747,7 +748,7 @@ GodotTypeInfo::Metadata call_get_argument_metadata(int p_arg) { ////////////////////// -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -758,7 +759,7 @@ void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), co #endif } -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -769,7 +770,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar #endif } -template <class... P, size_t... Is> +template <typename... P, size_t... Is> void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -780,7 +781,7 @@ void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_arg #endif } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -798,7 +799,7 @@ void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Var call_with_variant_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -810,7 +811,7 @@ void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) co (void)p_args; } -template <class R, class... P> +template <typename R, typename... P> void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -828,7 +829,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar call_with_variant_args_static_ret<R, P...>(p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -846,7 +847,7 @@ void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p call_with_variant_args_static<P...>(p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -864,7 +865,7 @@ void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, co call_with_variant_args_retc_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class R, class... P, size_t... Is> +template <typename T, typename R, typename... P, size_t... Is> void call_with_variant_args_retc_static_helper(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -877,7 +878,7 @@ void call_with_variant_args_retc_static_helper(T *p_instance, R (*p_method)(T *, (void)p_args; } -template <class T, class R, class... P> +template <typename T, typename R, typename... P> void call_with_variant_args_retc_static_helper_dv(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &default_values, Callable::CallError &r_error) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -910,7 +911,7 @@ void call_with_variant_args_retc_static_helper_dv(T *p_instance, R (*p_method)(T call_with_variant_args_retc_static_helper(p_instance, p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class T, class... P, size_t... Is> +template <typename T, typename... P, size_t... Is> void call_with_variant_args_static_helper(T *p_instance, void (*p_method)(T *, P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; @@ -923,7 +924,7 @@ void call_with_variant_args_static_helper(T *p_instance, void (*p_method)(T *, P (void)p_args; } -template <class T, class... P> +template <typename T, typename... P> void call_with_variant_args_static_helper_dv(T *p_instance, void (*p_method)(T *, P...), const Variant **p_args, int p_argcount, const Vector<Variant> &default_values, Callable::CallError &r_error) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -956,7 +957,7 @@ void call_with_variant_args_static_helper_dv(T *p_instance, void (*p_method)(T * call_with_variant_args_static_helper(p_instance, p_method, args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { @@ -989,7 +990,7 @@ void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const Variant **p call_with_variant_args_static_ret(p_method, args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> void call_with_variant_args_static_dv(void (*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error, const Vector<Variant> &default_values) { #ifdef DEBUG_ENABLED if ((size_t)p_argcount > sizeof...(P)) { diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index 0b1174c873..c6fbfd93a1 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -30,14 +30,14 @@ #include "callable.h" -#include "callable_bind.h" -#include "core/object/message_queue.h" #include "core/object/object.h" #include "core/object/ref_counted.h" #include "core/object/script_language.h" +#include "core/variant/callable_bind.h" +#include "core/variant/variant_callable.h" void Callable::call_deferredp(const Variant **p_arguments, int p_argcount) const { - MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount); + MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount, true); } void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const { @@ -92,10 +92,31 @@ Error Callable::rpcp(int p_id, const Variant **p_arguments, int p_argcount, Call r_call_error.expected = 0; return ERR_UNCONFIGURED; } else if (!is_custom()) { - r_call_error.error = CallError::CALL_ERROR_INVALID_METHOD; - r_call_error.argument = 0; - r_call_error.expected = 0; - return ERR_UNCONFIGURED; + Object *obj = ObjectDB::get_instance(ObjectID(object)); +#ifdef DEBUG_ENABLED + if (!obj || !obj->is_class("Node")) { + r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL; + r_call_error.argument = 0; + r_call_error.expected = 0; + return ERR_UNCONFIGURED; + } +#endif + + int argcount = p_argcount + 2; + const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * argcount); + const Variant args[2] = { p_id, method }; + + argptrs[0] = &args[0]; + argptrs[1] = &args[1]; + for (int i = 0; i < p_argcount; ++i) { + argptrs[i + 2] = p_arguments[i]; + } + + CallError tmp; + Error err = (Error)obj->callp(SNAME("rpc_id"), argptrs, argcount, tmp).operator int64_t(); + + r_call_error.error = Callable::CallError::CALL_OK; + return err; } else { return custom->rpc(p_id, p_arguments, p_argcount, r_call_error); } @@ -163,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(); @@ -328,14 +363,27 @@ Callable::operator String() const { } } +Callable Callable::create(const Variant &p_variant, const StringName &p_method) { + ERR_FAIL_COND_V_MSG(p_method == StringName(), Callable(), "Method argument to Callable::create method must be a non-empty string."); + + switch (p_variant.get_type()) { + case Variant::NIL: + return Callable(ObjectID(), p_method); + case Variant::OBJECT: + return Callable(p_variant.operator ObjectID(), p_method); + default: + return Callable(memnew(VariantCallable(p_variant, p_method))); + } +} + Callable::Callable(const Object *p_object, const StringName &p_method) { - if (p_method == StringName()) { + if (unlikely(p_method == StringName())) { object = 0; - ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string"); + ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string."); } - if (p_object == nullptr) { + if (unlikely(p_object == nullptr)) { object = 0; - ERR_FAIL_MSG("Object argument to Callable constructor must be non-null"); + ERR_FAIL_MSG("Object argument to Callable constructor must be non-null."); } object = p_object->get_instance_id(); @@ -343,9 +391,9 @@ Callable::Callable(const Object *p_object, const StringName &p_method) { } Callable::Callable(ObjectID p_object, const StringName &p_method) { - if (p_method == StringName()) { + if (unlikely(p_method == StringName())) { object = 0; - ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string"); + ERR_FAIL_MSG("Method argument to Callable constructor must be a non-empty string."); } object = p_object; @@ -353,9 +401,9 @@ Callable::Callable(ObjectID p_object, const StringName &p_method) { } Callable::Callable(CallableCustom *p_custom) { - if (p_custom->referenced) { + if (unlikely(p_custom->referenced)) { object = 0; - ERR_FAIL_MSG("Callable custom is already referenced"); + ERR_FAIL_MSG("Callable custom is already referenced."); } p_custom->referenced = true; object = 0; //ensure object is all zero, since pointer may be 32 bits @@ -404,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 3ae424e9bf..63757d9d6e 100644 --- a/core/variant/callable.h +++ b/core/variant/callable.h @@ -99,7 +99,7 @@ public: bool is_valid() const; template <typename... VarArgs> - Callable bind(VarArgs... p_args); + Callable bind(VarArgs... p_args) const; Callable bindv(const Array &p_arguments); Callable bindp(const Variant **p_arguments, int p_argcount) const; @@ -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; @@ -125,6 +126,8 @@ public: operator String() const; + static Callable create(const Variant &p_variant, const StringName &p_method); + Callable(const Object *p_object, const StringName &p_method); Callable(ObjectID p_object, const StringName &p_method); Callable(CallableCustom *p_custom); @@ -153,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 9a6380a55f..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(); } @@ -133,7 +141,7 @@ void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int & } void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount)); + const Variant **args = (const Variant **)alloca(sizeof(Variant *) * (binds.size() + p_argcount)); for (int i = 0; i < p_argcount; i++) { args[i] = (const Variant *)p_arguments[i]; } @@ -145,7 +153,7 @@ void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Varia } Error CallableCustomBind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const { - const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount)); + const Variant **args = (const Variant **)alloca(sizeof(Variant *) * (binds.size() + p_argcount)); for (int i = 0; i < p_argcount; i++) { args[i] = (const Variant *)p_arguments[i]; } @@ -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/dictionary.cpp b/core/variant/dictionary.cpp index 141ce25fa6..7416101d51 100644 --- a/core/variant/dictionary.cpp +++ b/core/variant/dictionary.cpp @@ -150,6 +150,15 @@ Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const { return *result; } +Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) { + const Variant *result = getptr(p_key); + if (!result) { + operator[](p_key) = p_default; + return p_default; + } + return *result; +} + int Dictionary::size() const { return _p->variant_map.size(); } @@ -240,13 +249,20 @@ void Dictionary::clear() { } void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) { + ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state."); for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) { if (p_overwrite || !has(E.key)) { - this->operator[](E.key) = E.value; + operator[](E.key) = E.value; } } } +Dictionary Dictionary::merged(const Dictionary &p_dictionary, bool p_overwrite) const { + Dictionary ret = duplicate(); + ret.merge(p_dictionary, p_overwrite); + return ret; +} + void Dictionary::_unref() const { ERR_FAIL_NULL(_p); if (_p->refcount.unref()) { diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h index 8935d35ed9..67178ee7b7 100644 --- a/core/variant/dictionary.h +++ b/core/variant/dictionary.h @@ -58,11 +58,13 @@ public: Variant get_valid(const Variant &p_key) const; Variant get(const Variant &p_key, const Variant &p_default) const; + Variant get_or_add(const Variant &p_key, const Variant &p_default); int size() const; bool is_empty() const; void clear(); void merge(const Dictionary &p_dictionary, bool p_overwrite = false); + Dictionary merged(const Dictionary &p_dictionary, bool p_overwrite = false) const; bool has(const Variant &p_key) const; bool has_all(const Array &p_keys) const; diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index 79be85cae6..123f2067e2 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -35,7 +35,7 @@ #include "core/typedefs.h" #include "core/variant/variant.h" -template <class T> +template <typename T> struct PtrToArg {}; #define MAKE_PTRARG(m_type) \ @@ -156,7 +156,7 @@ MAKE_PTRARG_BY_REFERENCE(Variant); // This is for Object. -template <class T> +template <typename T> struct PtrToArg<T *> { _FORCE_INLINE_ static T *convert(const void *p_ptr) { if (p_ptr == nullptr) { @@ -170,7 +170,7 @@ struct PtrToArg<T *> { } }; -template <class T> +template <typename T> struct PtrToArg<const T *> { _FORCE_INLINE_ static const T *convert(const void *p_ptr) { if (p_ptr == nullptr) { @@ -216,7 +216,7 @@ struct PtrToArg<ObjectID> { } \ return ret; \ } \ - _FORCE_INLINE_ static void encode(Vector<m_type> p_vec, void *p_ptr) { \ + _FORCE_INLINE_ static void encode(const Vector<m_type> &p_vec, void *p_ptr) { \ Vector<m_type> *dv = reinterpret_cast<Vector<m_type> *>(p_ptr); \ int len = p_vec.size(); \ dv->resize(len); \ @@ -246,49 +246,49 @@ struct PtrToArg<ObjectID> { } // No EncodeT because direct pointer conversion not possible. -#define MAKE_VECARG_ALT(m_type, m_type_alt) \ - template <> \ - struct PtrToArg<Vector<m_type_alt>> { \ - _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ - const Vector<m_type> *dvs = reinterpret_cast<const Vector<m_type> *>(p_ptr); \ - Vector<m_type_alt> ret; \ - int len = dvs->size(); \ - ret.resize(len); \ - { \ - const m_type *r = dvs->ptr(); \ - for (int i = 0; i < len; i++) { \ - ret.write[i] = r[i]; \ - } \ - } \ - return ret; \ - } \ - _FORCE_INLINE_ static void encode(Vector<m_type_alt> p_vec, void *p_ptr) { \ - Vector<m_type> *dv = reinterpret_cast<Vector<m_type> *>(p_ptr); \ - int len = p_vec.size(); \ - dv->resize(len); \ - { \ - m_type *w = dv->ptrw(); \ - for (int i = 0; i < len; i++) { \ - w[i] = p_vec[i]; \ - } \ - } \ - } \ - }; \ - template <> \ - struct PtrToArg<const Vector<m_type_alt> &> { \ - _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ - const Vector<m_type> *dvs = reinterpret_cast<const Vector<m_type> *>(p_ptr); \ - Vector<m_type_alt> ret; \ - int len = dvs->size(); \ - ret.resize(len); \ - { \ - const m_type *r = dvs->ptr(); \ - for (int i = 0; i < len; i++) { \ - ret.write[i] = r[i]; \ - } \ - } \ - return ret; \ - } \ +#define MAKE_VECARG_ALT(m_type, m_type_alt) \ + template <> \ + struct PtrToArg<Vector<m_type_alt>> { \ + _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ + const Vector<m_type> *dvs = reinterpret_cast<const Vector<m_type> *>(p_ptr); \ + Vector<m_type_alt> ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + const m_type *r = dvs->ptr(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ + _FORCE_INLINE_ static void encode(const Vector<m_type_alt> &p_vec, void *p_ptr) { \ + Vector<m_type> *dv = reinterpret_cast<Vector<m_type> *>(p_ptr); \ + int len = p_vec.size(); \ + dv->resize(len); \ + { \ + m_type *w = dv->ptrw(); \ + for (int i = 0; i < len; i++) { \ + w[i] = p_vec[i]; \ + } \ + } \ + } \ + }; \ + template <> \ + struct PtrToArg<const Vector<m_type_alt> &> { \ + _FORCE_INLINE_ static Vector<m_type_alt> convert(const void *p_ptr) { \ + const Vector<m_type> *dvs = reinterpret_cast<const Vector<m_type> *>(p_ptr); \ + Vector<m_type_alt> ret; \ + int len = dvs->size(); \ + ret.resize(len); \ + { \ + const m_type *r = dvs->ptr(); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = r[i]; \ + } \ + } \ + return ret; \ + } \ } MAKE_VECARG_ALT(String, StringName); @@ -296,40 +296,40 @@ MAKE_VECARG_ALT(String, StringName); // For stuff that gets converted to Array vectors. // No EncodeT because direct pointer conversion not possible. -#define MAKE_VECARR(m_type) \ - template <> \ - struct PtrToArg<Vector<m_type>> { \ - _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ - const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ - Vector<m_type> ret; \ - int len = arr->size(); \ - ret.resize(len); \ - for (int i = 0; i < len; i++) { \ - ret.write[i] = (*arr)[i]; \ - } \ - return ret; \ - } \ - _FORCE_INLINE_ static void encode(Vector<m_type> p_vec, void *p_ptr) { \ - Array *arr = reinterpret_cast<Array *>(p_ptr); \ - int len = p_vec.size(); \ - arr->resize(len); \ - for (int i = 0; i < len; i++) { \ - (*arr)[i] = p_vec[i]; \ - } \ - } \ - }; \ - template <> \ - struct PtrToArg<const Vector<m_type> &> { \ - _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ - const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ - Vector<m_type> ret; \ - int len = arr->size(); \ - ret.resize(len); \ - for (int i = 0; i < len; i++) { \ - ret.write[i] = (*arr)[i]; \ - } \ - return ret; \ - } \ +#define MAKE_VECARR(m_type) \ + template <> \ + struct PtrToArg<Vector<m_type>> { \ + _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ + const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ + Vector<m_type> ret; \ + int len = arr->size(); \ + ret.resize(len); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = (*arr)[i]; \ + } \ + return ret; \ + } \ + _FORCE_INLINE_ static void encode(const Vector<m_type> &p_vec, void *p_ptr) { \ + Array *arr = reinterpret_cast<Array *>(p_ptr); \ + int len = p_vec.size(); \ + arr->resize(len); \ + for (int i = 0; i < len; i++) { \ + (*arr)[i] = p_vec[i]; \ + } \ + } \ + }; \ + template <> \ + struct PtrToArg<const Vector<m_type> &> { \ + _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ + const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ + Vector<m_type> ret; \ + int len = arr->size(); \ + ret.resize(len); \ + for (int i = 0; i < len; i++) { \ + ret.write[i] = (*arr)[i]; \ + } \ + return ret; \ + } \ } MAKE_VECARR(Variant); @@ -337,49 +337,49 @@ MAKE_VECARR(RID); MAKE_VECARR(Plane); // No EncodeT because direct pointer conversion not possible. -#define MAKE_DVECARR(m_type) \ - template <> \ - struct PtrToArg<Vector<m_type>> { \ - _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ - const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ - Vector<m_type> ret; \ - int len = arr->size(); \ - ret.resize(len); \ - { \ - m_type *w = ret.ptrw(); \ - for (int i = 0; i < len; i++) { \ - w[i] = (*arr)[i]; \ - } \ - } \ - return ret; \ - } \ - _FORCE_INLINE_ static void encode(Vector<m_type> p_vec, void *p_ptr) { \ - Array *arr = reinterpret_cast<Array *>(p_ptr); \ - int len = p_vec.size(); \ - arr->resize(len); \ - { \ - const m_type *r = p_vec.ptr(); \ - for (int i = 0; i < len; i++) { \ - (*arr)[i] = r[i]; \ - } \ - } \ - } \ - }; \ - template <> \ - struct PtrToArg<const Vector<m_type> &> { \ - _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ - const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ - Vector<m_type> ret; \ - int len = arr->size(); \ - ret.resize(len); \ - { \ - m_type *w = ret.ptrw(); \ - for (int i = 0; i < len; i++) { \ - w[i] = (*arr)[i]; \ - } \ - } \ - return ret; \ - } \ +#define MAKE_DVECARR(m_type) \ + template <> \ + struct PtrToArg<Vector<m_type>> { \ + _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ + const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ + Vector<m_type> ret; \ + int len = arr->size(); \ + ret.resize(len); \ + { \ + m_type *w = ret.ptrw(); \ + for (int i = 0; i < len; i++) { \ + w[i] = (*arr)[i]; \ + } \ + } \ + return ret; \ + } \ + _FORCE_INLINE_ static void encode(const Vector<m_type> &p_vec, void *p_ptr) { \ + Array *arr = reinterpret_cast<Array *>(p_ptr); \ + int len = p_vec.size(); \ + arr->resize(len); \ + { \ + const m_type *r = p_vec.ptr(); \ + for (int i = 0; i < len; i++) { \ + (*arr)[i] = r[i]; \ + } \ + } \ + } \ + }; \ + template <> \ + struct PtrToArg<const Vector<m_type> &> { \ + _FORCE_INLINE_ static Vector<m_type> convert(const void *p_ptr) { \ + const Array *arr = reinterpret_cast<const Array *>(p_ptr); \ + Vector<m_type> ret; \ + int len = arr->size(); \ + ret.resize(len); \ + { \ + m_type *w = ret.ptrw(); \ + for (int i = 0; i < len; i++) { \ + w[i] = (*arr)[i]; \ + } \ + } \ + return ret; \ + } \ } // Special case for IPAddress. @@ -427,7 +427,7 @@ struct PtrToArg<Vector<Face3>> { } return ret; } - _FORCE_INLINE_ static void encode(Vector<Face3> p_vec, void *p_ptr) { + _FORCE_INLINE_ static void encode(const Vector<Face3> &p_vec, void *p_ptr) { Vector<Vector3> *arr = reinterpret_cast<Vector<Vector3> *>(p_ptr); int len = p_vec.size(); arr->resize(len * 3); diff --git a/core/variant/native_ptr.h b/core/variant/native_ptr.h index 9199b12845..33ba038132 100644 --- a/core/variant/native_ptr.h +++ b/core/variant/native_ptr.h @@ -35,7 +35,7 @@ #include "core/variant/method_ptrcall.h" #include "core/variant/type_info.h" -template <class T> +template <typename T> struct GDExtensionConstPtr { const T *data = nullptr; GDExtensionConstPtr(const T *p_assign) { data = p_assign; } @@ -44,7 +44,7 @@ struct GDExtensionConstPtr { operator Variant() const { return uint64_t(data); } }; -template <class T> +template <typename T> struct GDExtensionPtr { T *data = nullptr; GDExtensionPtr(T *p_assign) { data = p_assign; } @@ -95,7 +95,7 @@ struct GDExtensionPtr { static _FORCE_INLINE_ void set(Variant *v, const GDExtensionPtr<m_type> &p_value) { *VariantInternal::get_int(v) = uint64_t(p_value.data); } \ }; -template <class T> +template <typename T> struct GetTypeInfo<GDExtensionConstPtr<T>> { static const Variant::Type VARIANT_TYPE = Variant::NIL; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -104,7 +104,7 @@ struct GetTypeInfo<GDExtensionConstPtr<T>> { } }; -template <class T> +template <typename T> struct GetTypeInfo<GDExtensionPtr<T>> { static const Variant::Type VARIANT_TYPE = Variant::NIL; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -113,7 +113,7 @@ struct GetTypeInfo<GDExtensionPtr<T>> { } }; -template <class T> +template <typename T> struct PtrToArg<GDExtensionConstPtr<T>> { _FORCE_INLINE_ static GDExtensionConstPtr<T> convert(const void *p_ptr) { return GDExtensionConstPtr<T>(reinterpret_cast<const T *>(p_ptr)); @@ -123,7 +123,7 @@ struct PtrToArg<GDExtensionConstPtr<T>> { *((const T **)p_ptr) = p_val.data; } }; -template <class T> +template <typename T> struct PtrToArg<GDExtensionPtr<T>> { _FORCE_INLINE_ static GDExtensionPtr<T> convert(const void *p_ptr) { return GDExtensionPtr<T>(reinterpret_cast<const T *>(p_ptr)); diff --git a/core/variant/type_info.h b/core/variant/type_info.h index e89658d25b..15cb6c9c1a 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -33,35 +33,7 @@ #include "core/typedefs.h" -template <bool C, typename T = void> -struct EnableIf { - typedef T type; -}; - -template <typename T> -struct EnableIf<false, T> { -}; - -template <typename, typename> -struct TypesAreSame { - static bool const value = false; -}; - -template <typename A> -struct TypesAreSame<A, A> { - static bool const value = true; -}; - -template <typename B, typename D> -struct TypeInherits { - static D *get_d(); - - static char (&test(B *))[1]; - static char (&test(...))[2]; - - static bool const value = sizeof(test(get_d())) == sizeof(char) && - !TypesAreSame<B volatile const, void volatile const>::value; -}; +#include <type_traits> namespace GodotTypeInfo { enum Metadata { @@ -84,7 +56,7 @@ enum Metadata { // If 'T' is a class that inherits 'Object', make sure it can see the actual class declaration // instead of a forward declaration. You can always forward declare 'T' in a header file, and then // include the actual declaration of 'T' in the source file where 'GetTypeInfo<T>' is instantiated. -template <class T, typename = void> +template <typename T, typename = void> struct GetTypeInfo; #define MAKE_TYPE_INFO(m_type, m_var_type) \ @@ -227,16 +199,7 @@ MAKE_TEMPLATE_TYPE_INFO(Vector, Face3, Variant::PACKED_VECTOR3_ARRAY) MAKE_TEMPLATE_TYPE_INFO(Vector, StringName, Variant::PACKED_STRING_ARRAY) template <typename T> -struct GetTypeInfo<T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { - static const Variant::Type VARIANT_TYPE = Variant::OBJECT; - static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; - static inline PropertyInfo get_class_info() { - return PropertyInfo(StringName(T::get_class_static())); - } -}; - -template <typename T> -struct GetTypeInfo<const T *, typename EnableIf<TypeInherits<Object, T>::value>::type> { +struct GetTypeInfo<T *, std::enable_if_t<std::is_base_of_v<Object, T>>> { static const Variant::Type VARIANT_TYPE = Variant::OBJECT; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; static inline PropertyInfo get_class_info() { @@ -282,21 +245,25 @@ inline StringName __constant_get_enum_name(T param, const String &p_constant) { return GetTypeInfo<T>::get_class_info().class_name; } -template <class T> +template <typename T> class BitField { int64_t value = 0; public: - _FORCE_INLINE_ void set_flag(T p_flag) { value |= (int64_t)p_flag; } + _FORCE_INLINE_ BitField<T> &set_flag(T p_flag) { + value |= (int64_t)p_flag; + return *this; + } _FORCE_INLINE_ bool has_flag(T p_flag) const { return value & (int64_t)p_flag; } _FORCE_INLINE_ bool is_empty() const { return value == 0; } _FORCE_INLINE_ void clear_flag(T p_flag) { value &= ~(int64_t)p_flag; } _FORCE_INLINE_ void clear() { value = 0; } - _FORCE_INLINE_ BitField() = default; - _FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; } - _FORCE_INLINE_ BitField(T p_value) { value = (int64_t)p_value; } + _FORCE_INLINE_ constexpr BitField() = default; + _FORCE_INLINE_ constexpr BitField(int64_t p_value) { value = p_value; } + _FORCE_INLINE_ constexpr BitField(T p_value) { value = (int64_t)p_value; } _FORCE_INLINE_ operator int64_t() const { return value; } _FORCE_INLINE_ operator Variant() const { return value; } + _FORCE_INLINE_ BitField<T> operator^(const BitField<T> &p_b) const { return BitField<T>(value ^ p_b.value); } }; #define TEMPL_MAKE_BITFIELD_TYPE_INFO(m_enum, m_impl) \ diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h index 037c5c7c2e..0befd19864 100644 --- a/core/variant/typed_array.h +++ b/core/variant/typed_array.h @@ -38,7 +38,7 @@ #include "core/variant/type_info.h" #include "core/variant/variant.h" -template <class T> +template <typename T> class TypedArray : public Array { public: _FORCE_INLINE_ void operator=(const Array &p_array) { @@ -56,12 +56,12 @@ public: } }; -template <class T> +template <typename T> struct VariantInternalAccessor<TypedArray<T>> { static _FORCE_INLINE_ TypedArray<T> get(const Variant *v) { return *VariantInternal::get_array(v); } static _FORCE_INLINE_ void set(Variant *v, const TypedArray<T> &p_array) { *VariantInternal::get_array(v) = p_array; } }; -template <class T> +template <typename T> struct VariantInternalAccessor<const TypedArray<T> &> { static _FORCE_INLINE_ TypedArray<T> get(const Variant *v) { return *VariantInternal::get_array(v); } static _FORCE_INLINE_ void set(Variant *v, const TypedArray<T> &p_array) { *VariantInternal::get_array(v) = p_array; } @@ -88,6 +88,8 @@ struct VariantInternalAccessor<const TypedArray<T> &> { } \ }; +// All Variant::OBJECT types are intentionally omitted from this list because they are handled by +// the unspecialized TypedArray definition. MAKE_TYPED_ARRAY(bool, Variant::BOOL) MAKE_TYPED_ARRAY(uint8_t, Variant::INT) MAKE_TYPED_ARRAY(int8_t, Variant::INT) @@ -107,11 +109,14 @@ MAKE_TYPED_ARRAY(Rect2i, Variant::RECT2I) MAKE_TYPED_ARRAY(Vector3, Variant::VECTOR3) MAKE_TYPED_ARRAY(Vector3i, Variant::VECTOR3I) MAKE_TYPED_ARRAY(Transform2D, Variant::TRANSFORM2D) +MAKE_TYPED_ARRAY(Vector4, Variant::VECTOR4) +MAKE_TYPED_ARRAY(Vector4i, Variant::VECTOR4I) MAKE_TYPED_ARRAY(Plane, Variant::PLANE) MAKE_TYPED_ARRAY(Quaternion, Variant::QUATERNION) MAKE_TYPED_ARRAY(AABB, Variant::AABB) MAKE_TYPED_ARRAY(Basis, Variant::BASIS) MAKE_TYPED_ARRAY(Transform3D, Variant::TRANSFORM3D) +MAKE_TYPED_ARRAY(Projection, Variant::PROJECTION) MAKE_TYPED_ARRAY(Color, Variant::COLOR) MAKE_TYPED_ARRAY(StringName, Variant::STRING_NAME) MAKE_TYPED_ARRAY(NodePath, Variant::NODE_PATH) @@ -120,18 +125,18 @@ MAKE_TYPED_ARRAY(Callable, Variant::CALLABLE) MAKE_TYPED_ARRAY(Signal, Variant::SIGNAL) MAKE_TYPED_ARRAY(Dictionary, Variant::DICTIONARY) MAKE_TYPED_ARRAY(Array, Variant::ARRAY) -MAKE_TYPED_ARRAY(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY) -MAKE_TYPED_ARRAY(Vector<int32_t>, Variant::PACKED_INT32_ARRAY) -MAKE_TYPED_ARRAY(Vector<int64_t>, Variant::PACKED_INT64_ARRAY) -MAKE_TYPED_ARRAY(Vector<float>, Variant::PACKED_FLOAT32_ARRAY) -MAKE_TYPED_ARRAY(Vector<double>, Variant::PACKED_FLOAT64_ARRAY) -MAKE_TYPED_ARRAY(Vector<String>, Variant::PACKED_STRING_ARRAY) -MAKE_TYPED_ARRAY(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY) -MAKE_TYPED_ARRAY(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) -MAKE_TYPED_ARRAY(Vector<Color>, Variant::PACKED_COLOR_ARRAY) +MAKE_TYPED_ARRAY(PackedByteArray, Variant::PACKED_BYTE_ARRAY) +MAKE_TYPED_ARRAY(PackedInt32Array, Variant::PACKED_INT32_ARRAY) +MAKE_TYPED_ARRAY(PackedInt64Array, Variant::PACKED_INT64_ARRAY) +MAKE_TYPED_ARRAY(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY) +MAKE_TYPED_ARRAY(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY) +MAKE_TYPED_ARRAY(PackedStringArray, Variant::PACKED_STRING_ARRAY) +MAKE_TYPED_ARRAY(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY) +MAKE_TYPED_ARRAY(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY) +MAKE_TYPED_ARRAY(PackedColorArray, Variant::PACKED_COLOR_ARRAY) MAKE_TYPED_ARRAY(IPAddress, Variant::STRING) -template <class T> +template <typename T> struct PtrToArg<TypedArray<T>> { _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) { return TypedArray<T>(*reinterpret_cast<const Array *>(p_ptr)); @@ -142,7 +147,7 @@ struct PtrToArg<TypedArray<T>> { } }; -template <class T> +template <typename T> struct PtrToArg<const TypedArray<T> &> { typedef Array EncodeT; _FORCE_INLINE_ static TypedArray<T> convert(const void *p_ptr) { @@ -150,7 +155,7 @@ struct PtrToArg<const TypedArray<T> &> { } }; -template <class T> +template <typename T> struct GetTypeInfo<TypedArray<T>> { static const Variant::Type VARIANT_TYPE = Variant::ARRAY; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -159,7 +164,7 @@ struct GetTypeInfo<TypedArray<T>> { } }; -template <class T> +template <typename T> struct GetTypeInfo<const TypedArray<T> &> { static const Variant::Type VARIANT_TYPE = Variant::ARRAY; static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_NONE; @@ -205,11 +210,14 @@ MAKE_TYPED_ARRAY_INFO(Rect2i, Variant::RECT2I) MAKE_TYPED_ARRAY_INFO(Vector3, Variant::VECTOR3) MAKE_TYPED_ARRAY_INFO(Vector3i, Variant::VECTOR3I) MAKE_TYPED_ARRAY_INFO(Transform2D, Variant::TRANSFORM2D) +MAKE_TYPED_ARRAY_INFO(Vector4, Variant::VECTOR4) +MAKE_TYPED_ARRAY_INFO(Vector4i, Variant::VECTOR4I) MAKE_TYPED_ARRAY_INFO(Plane, Variant::PLANE) MAKE_TYPED_ARRAY_INFO(Quaternion, Variant::QUATERNION) MAKE_TYPED_ARRAY_INFO(AABB, Variant::AABB) MAKE_TYPED_ARRAY_INFO(Basis, Variant::BASIS) MAKE_TYPED_ARRAY_INFO(Transform3D, Variant::TRANSFORM3D) +MAKE_TYPED_ARRAY_INFO(Projection, Variant::PROJECTION) MAKE_TYPED_ARRAY_INFO(Color, Variant::COLOR) MAKE_TYPED_ARRAY_INFO(StringName, Variant::STRING_NAME) MAKE_TYPED_ARRAY_INFO(NodePath, Variant::NODE_PATH) @@ -218,15 +226,15 @@ MAKE_TYPED_ARRAY_INFO(Callable, Variant::CALLABLE) MAKE_TYPED_ARRAY_INFO(Signal, Variant::SIGNAL) MAKE_TYPED_ARRAY_INFO(Dictionary, Variant::DICTIONARY) MAKE_TYPED_ARRAY_INFO(Array, Variant::ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<int32_t>, Variant::PACKED_INT32_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<int64_t>, Variant::PACKED_INT64_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<float>, Variant::PACKED_FLOAT32_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<double>, Variant::PACKED_FLOAT64_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<String>, Variant::PACKED_STRING_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY) -MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedByteArray, Variant::PACKED_BYTE_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedInt32Array, Variant::PACKED_INT32_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedInt64Array, Variant::PACKED_INT64_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedStringArray, Variant::PACKED_STRING_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY) +MAKE_TYPED_ARRAY_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY) MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING) #undef MAKE_TYPED_ARRAY diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index 09fb34e7c1..8e702ce8bb 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -931,7 +931,7 @@ bool Variant::is_zero() const { return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID(); } case OBJECT: { - return _get_obj().obj == nullptr; + return get_validated_object() == nullptr; } case CALLABLE: { return reinterpret_cast<const Callable *>(_data._mem)->is_null(); @@ -1246,52 +1246,58 @@ void Variant::zero() { case NIL: break; case BOOL: - this->_data._bool = false; + _data._bool = false; break; case INT: - this->_data._int = 0; + _data._int = 0; break; case FLOAT: - this->_data._float = 0; + _data._float = 0; break; case VECTOR2: - *reinterpret_cast<Vector2 *>(this->_data._mem) = Vector2(); + *reinterpret_cast<Vector2 *>(_data._mem) = Vector2(); break; case VECTOR2I: - *reinterpret_cast<Vector2i *>(this->_data._mem) = Vector2i(); + *reinterpret_cast<Vector2i *>(_data._mem) = Vector2i(); break; case RECT2: - *reinterpret_cast<Rect2 *>(this->_data._mem) = Rect2(); + *reinterpret_cast<Rect2 *>(_data._mem) = Rect2(); break; case RECT2I: - *reinterpret_cast<Rect2i *>(this->_data._mem) = Rect2i(); + *reinterpret_cast<Rect2i *>(_data._mem) = Rect2i(); break; case VECTOR3: - *reinterpret_cast<Vector3 *>(this->_data._mem) = Vector3(); + *reinterpret_cast<Vector3 *>(_data._mem) = Vector3(); break; case VECTOR3I: - *reinterpret_cast<Vector3i *>(this->_data._mem) = Vector3i(); + *reinterpret_cast<Vector3i *>(_data._mem) = Vector3i(); break; case VECTOR4: - *reinterpret_cast<Vector4 *>(this->_data._mem) = Vector4(); + *reinterpret_cast<Vector4 *>(_data._mem) = Vector4(); break; case VECTOR4I: - *reinterpret_cast<Vector4i *>(this->_data._mem) = Vector4i(); + *reinterpret_cast<Vector4i *>(_data._mem) = Vector4i(); break; case PLANE: - *reinterpret_cast<Plane *>(this->_data._mem) = Plane(); + *reinterpret_cast<Plane *>(_data._mem) = Plane(); break; case QUATERNION: - *reinterpret_cast<Quaternion *>(this->_data._mem) = Quaternion(); + *reinterpret_cast<Quaternion *>(_data._mem) = Quaternion(); break; case COLOR: - *reinterpret_cast<Color *>(this->_data._mem) = Color(); + *reinterpret_cast<Color *>(_data._mem) = Color(); break; default: - this->clear(); + Type prev_type = type; + clear(); + if (type != prev_type) { + // clear() changes type to NIL, so it needs to be restored. + Callable::CallError ce; + Variant::construct(prev_type, *this, nullptr, 0, ce); + } break; } } @@ -1411,25 +1417,7 @@ void Variant::_clear_internal() { } } -Variant::operator signed int() const { - switch (type) { - case NIL: - return 0; - case BOOL: - return _data._bool ? 1 : 0; - case INT: - return _data._int; - case FLOAT: - return _data._float; - case STRING: - return operator String().to_int(); - default: { - return 0; - } - } -} - -Variant::operator unsigned int() const { +Variant::operator int64_t() const { switch (type) { case NIL: return 0; @@ -1447,7 +1435,7 @@ Variant::operator unsigned int() const { } } -Variant::operator int64_t() const { +Variant::operator int32_t() const { switch (type) { case NIL: return 0; @@ -1465,7 +1453,7 @@ Variant::operator int64_t() const { } } -Variant::operator uint64_t() const { +Variant::operator int16_t() const { switch (type) { case NIL: return 0; @@ -1483,18 +1471,7 @@ Variant::operator uint64_t() const { } } -Variant::operator ObjectID() const { - if (type == INT) { - return ObjectID(_data._int); - } else if (type == OBJECT) { - return _get_obj().id; - } else { - return ObjectID(); - } -} - -#ifdef NEED_LONG_INT -Variant::operator signed long() const { +Variant::operator int8_t() const { switch (type) { case NIL: return 0; @@ -1510,11 +1487,9 @@ Variant::operator signed long() const { return 0; } } - - return 0; } -Variant::operator unsigned long() const { +Variant::operator uint64_t() const { switch (type) { case NIL: return 0; @@ -1530,12 +1505,9 @@ Variant::operator unsigned long() const { return 0; } } - - return 0; } -#endif -Variant::operator signed short() const { +Variant::operator uint32_t() const { switch (type) { case NIL: return 0; @@ -1553,7 +1525,7 @@ Variant::operator signed short() const { } } -Variant::operator unsigned short() const { +Variant::operator uint16_t() const { switch (type) { case NIL: return 0; @@ -1571,7 +1543,7 @@ Variant::operator unsigned short() const { } } -Variant::operator signed char() const { +Variant::operator uint8_t() const { switch (type) { case NIL: return 0; @@ -1589,26 +1561,18 @@ Variant::operator signed char() const { } } -Variant::operator unsigned char() const { - switch (type) { - case NIL: - return 0; - case BOOL: - return _data._bool ? 1 : 0; - case INT: - return _data._int; - case FLOAT: - return _data._float; - case STRING: - return operator String().to_int(); - default: { - return 0; - } +Variant::operator ObjectID() const { + if (type == INT) { + return ObjectID(_data._int); + } else if (type == OBJECT) { + return _get_obj().id; + } else { + return ObjectID(); } } Variant::operator char32_t() const { - return operator unsigned int(); + return operator uint32_t(); } Variant::operator float() const { @@ -1670,7 +1634,7 @@ Variant::operator String() const { return stringify(0); } -String stringify_variant_clean(const Variant p_variant, int recursion_count) { +String stringify_variant_clean(const Variant &p_variant, int recursion_count) { String s = p_variant.stringify(recursion_count); // Wrap strings in quotes to avoid ambiguity. @@ -1691,7 +1655,7 @@ String stringify_variant_clean(const Variant p_variant, int recursion_count) { return s; } -template <class T> +template <typename T> String stringify_vector(const T &vec, int recursion_count) { String str("["); for (int i = 0; i < vec.size(); i++) { @@ -1787,31 +1751,31 @@ String Variant::stringify(int recursion_count) const { } // Packed arrays cannot contain recursive structures, the recursion_count increment is not needed. case PACKED_VECTOR2_ARRAY: { - return stringify_vector(operator Vector<Vector2>(), recursion_count); + return stringify_vector(operator PackedVector2Array(), recursion_count); } case PACKED_VECTOR3_ARRAY: { - return stringify_vector(operator Vector<Vector3>(), recursion_count); + return stringify_vector(operator PackedVector3Array(), recursion_count); } case PACKED_COLOR_ARRAY: { - return stringify_vector(operator Vector<Color>(), recursion_count); + return stringify_vector(operator PackedColorArray(), recursion_count); } case PACKED_STRING_ARRAY: { - return stringify_vector(operator Vector<String>(), recursion_count); + return stringify_vector(operator PackedStringArray(), recursion_count); } case PACKED_BYTE_ARRAY: { - return stringify_vector(operator Vector<uint8_t>(), recursion_count); + return stringify_vector(operator PackedByteArray(), recursion_count); } case PACKED_INT32_ARRAY: { - return stringify_vector(operator Vector<int32_t>(), recursion_count); + return stringify_vector(operator PackedInt32Array(), recursion_count); } case PACKED_INT64_ARRAY: { - return stringify_vector(operator Vector<int64_t>(), recursion_count); + return stringify_vector(operator PackedInt64Array(), recursion_count); } case PACKED_FLOAT32_ARRAY: { - return stringify_vector(operator Vector<float>(), recursion_count); + return stringify_vector(operator PackedFloat32Array(), recursion_count); } case PACKED_FLOAT64_ARRAY: { - return stringify_vector(operator Vector<double>(), recursion_count); + return stringify_vector(operator PackedFloat64Array(), recursion_count); } case ARRAY: { ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!"); @@ -2182,7 +2146,7 @@ Variant::operator Signal() const { } } -template <class DA, class SA> +template <typename DA, typename SA> inline DA _convert_array(const SA &p_array) { DA da; da.resize(p_array.size()); @@ -2194,38 +2158,38 @@ inline DA _convert_array(const SA &p_array) { return da; } -template <class DA> +template <typename DA> inline DA _convert_array_from_variant(const Variant &p_variant) { switch (p_variant.get_type()) { case Variant::ARRAY: { return _convert_array<DA, Array>(p_variant.operator Array()); } case Variant::PACKED_BYTE_ARRAY: { - return _convert_array<DA, Vector<uint8_t>>(p_variant.operator Vector<uint8_t>()); + return _convert_array<DA, PackedByteArray>(p_variant.operator PackedByteArray()); } case Variant::PACKED_INT32_ARRAY: { - return _convert_array<DA, Vector<int32_t>>(p_variant.operator Vector<int32_t>()); + return _convert_array<DA, PackedInt32Array>(p_variant.operator PackedInt32Array()); } case Variant::PACKED_INT64_ARRAY: { - return _convert_array<DA, Vector<int64_t>>(p_variant.operator Vector<int64_t>()); + return _convert_array<DA, PackedInt64Array>(p_variant.operator PackedInt64Array()); } case Variant::PACKED_FLOAT32_ARRAY: { - return _convert_array<DA, Vector<float>>(p_variant.operator Vector<float>()); + return _convert_array<DA, PackedFloat32Array>(p_variant.operator PackedFloat32Array()); } case Variant::PACKED_FLOAT64_ARRAY: { - return _convert_array<DA, Vector<double>>(p_variant.operator Vector<double>()); + return _convert_array<DA, PackedFloat64Array>(p_variant.operator PackedFloat64Array()); } case Variant::PACKED_STRING_ARRAY: { - return _convert_array<DA, Vector<String>>(p_variant.operator Vector<String>()); + return _convert_array<DA, PackedStringArray>(p_variant.operator PackedStringArray()); } case Variant::PACKED_VECTOR2_ARRAY: { - return _convert_array<DA, Vector<Vector2>>(p_variant.operator Vector<Vector2>()); + return _convert_array<DA, PackedVector2Array>(p_variant.operator PackedVector2Array()); } case Variant::PACKED_VECTOR3_ARRAY: { - return _convert_array<DA, Vector<Vector3>>(p_variant.operator Vector<Vector3>()); + return _convert_array<DA, PackedVector3Array>(p_variant.operator PackedVector3Array()); } case Variant::PACKED_COLOR_ARRAY: { - return _convert_array<DA, Vector<Color>>(p_variant.operator Vector<Color>()); + return _convert_array<DA, PackedColorArray>(p_variant.operator PackedColorArray()); } default: { return DA(); @@ -2241,75 +2205,75 @@ Variant::operator Array() const { } } -Variant::operator Vector<uint8_t>() const { +Variant::operator PackedByteArray() const { if (type == PACKED_BYTE_ARRAY) { return static_cast<PackedArrayRef<uint8_t> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<uint8_t>>(*this); + return _convert_array_from_variant<PackedByteArray>(*this); } } -Variant::operator Vector<int32_t>() const { +Variant::operator PackedInt32Array() const { if (type == PACKED_INT32_ARRAY) { return static_cast<PackedArrayRef<int32_t> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<int>>(*this); + return _convert_array_from_variant<PackedInt32Array>(*this); } } -Variant::operator Vector<int64_t>() const { +Variant::operator PackedInt64Array() const { if (type == PACKED_INT64_ARRAY) { return static_cast<PackedArrayRef<int64_t> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<int64_t>>(*this); + return _convert_array_from_variant<PackedInt64Array>(*this); } } -Variant::operator Vector<float>() const { +Variant::operator PackedFloat32Array() const { if (type == PACKED_FLOAT32_ARRAY) { return static_cast<PackedArrayRef<float> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<float>>(*this); + return _convert_array_from_variant<PackedFloat32Array>(*this); } } -Variant::operator Vector<double>() const { +Variant::operator PackedFloat64Array() const { if (type == PACKED_FLOAT64_ARRAY) { return static_cast<PackedArrayRef<double> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<double>>(*this); + return _convert_array_from_variant<PackedFloat64Array>(*this); } } -Variant::operator Vector<String>() const { +Variant::operator PackedStringArray() const { if (type == PACKED_STRING_ARRAY) { return static_cast<PackedArrayRef<String> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<String>>(*this); + return _convert_array_from_variant<PackedStringArray>(*this); } } -Variant::operator Vector<Vector3>() const { - if (type == PACKED_VECTOR3_ARRAY) { - return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array; +Variant::operator PackedVector2Array() const { + if (type == PACKED_VECTOR2_ARRAY) { + return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<Vector3>>(*this); + return _convert_array_from_variant<PackedVector2Array>(*this); } } -Variant::operator Vector<Vector2>() const { - if (type == PACKED_VECTOR2_ARRAY) { - return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array; +Variant::operator PackedVector3Array() const { + if (type == PACKED_VECTOR3_ARRAY) { + return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<Vector2>>(*this); + return _convert_array_from_variant<PackedVector3Array>(*this); } } -Variant::operator Vector<Color>() const { +Variant::operator PackedColorArray() const { if (type == PACKED_COLOR_ARRAY) { return static_cast<PackedArrayRef<Color> *>(_data.packed_array)->array; } else { - return _convert_array_from_variant<Vector<Color>>(*this); + return _convert_array_from_variant<PackedColorArray>(*this); } } @@ -2344,7 +2308,7 @@ Variant::operator Vector<Plane>() const { } Variant::operator Vector<Face3>() const { - Vector<Vector3> va = operator Vector<Vector3>(); + PackedVector3Array va = operator PackedVector3Array(); Vector<Face3> faces; int va_size = va.size(); if (va_size == 0) { @@ -2380,7 +2344,7 @@ Variant::operator Vector<Variant>() const { } Variant::operator Vector<StringName>() const { - Vector<String> from = operator Vector<String>(); + PackedStringArray from = operator PackedStringArray(); Vector<StringName> to; int len = from.size(); to.resize(len); @@ -2409,197 +2373,183 @@ Variant::operator IPAddress() const { return IPAddress(operator String()); } -Variant::Variant(bool p_bool) { - type = BOOL; +Variant::Variant(bool p_bool) : + type(BOOL) { _data._bool = p_bool; } -Variant::Variant(signed int p_int) { - type = INT; - _data._int = p_int; +Variant::Variant(int64_t p_int64) : + type(INT) { + _data._int = p_int64; } -Variant::Variant(unsigned int p_int) { - type = INT; - _data._int = p_int; +Variant::Variant(int32_t p_int32) : + type(INT) { + _data._int = p_int32; } -#ifdef NEED_LONG_INT - -Variant::Variant(signed long p_int) { - type = INT; - _data._int = p_int; +Variant::Variant(int16_t p_int16) : + type(INT) { + _data._int = p_int16; } -Variant::Variant(unsigned long p_int) { - type = INT; - _data._int = p_int; -} -#endif - -Variant::Variant(int64_t p_int) { - type = INT; - _data._int = p_int; +Variant::Variant(int8_t p_int8) : + type(INT) { + _data._int = p_int8; } -Variant::Variant(uint64_t p_int) { - type = INT; - _data._int = p_int; +Variant::Variant(uint64_t p_uint64) : + type(INT) { + _data._int = p_uint64; } -Variant::Variant(signed short p_short) { - type = INT; - _data._int = p_short; +Variant::Variant(uint32_t p_uint32) : + type(INT) { + _data._int = p_uint32; } -Variant::Variant(unsigned short p_short) { - type = INT; - _data._int = p_short; +Variant::Variant(uint16_t p_uint16) : + type(INT) { + _data._int = p_uint16; } -Variant::Variant(signed char p_char) { - type = INT; - _data._int = p_char; +Variant::Variant(uint8_t p_uint8) : + type(INT) { + _data._int = p_uint8; } -Variant::Variant(unsigned char p_char) { - type = INT; - _data._int = p_char; -} - -Variant::Variant(float p_float) { - type = FLOAT; +Variant::Variant(float p_float) : + type(FLOAT) { _data._float = p_float; } -Variant::Variant(double p_double) { - type = FLOAT; +Variant::Variant(double p_double) : + type(FLOAT) { _data._float = p_double; } -Variant::Variant(const ObjectID &p_id) { - type = INT; +Variant::Variant(const ObjectID &p_id) : + type(INT) { _data._int = p_id; } -Variant::Variant(const StringName &p_string) { - type = STRING_NAME; +Variant::Variant(const StringName &p_string) : + type(STRING_NAME) { memnew_placement(_data._mem, StringName(p_string)); } -Variant::Variant(const String &p_string) { - type = STRING; +Variant::Variant(const String &p_string) : + type(STRING) { memnew_placement(_data._mem, String(p_string)); } -Variant::Variant(const char *const p_cstring) { - type = STRING; +Variant::Variant(const char *const p_cstring) : + type(STRING) { memnew_placement(_data._mem, String((const char *)p_cstring)); } -Variant::Variant(const char32_t *p_wstring) { - type = STRING; +Variant::Variant(const char32_t *p_wstring) : + type(STRING) { memnew_placement(_data._mem, String(p_wstring)); } -Variant::Variant(const Vector3 &p_vector3) { - type = VECTOR3; +Variant::Variant(const Vector3 &p_vector3) : + type(VECTOR3) { memnew_placement(_data._mem, Vector3(p_vector3)); } -Variant::Variant(const Vector3i &p_vector3i) { - type = VECTOR3I; +Variant::Variant(const Vector3i &p_vector3i) : + type(VECTOR3I) { memnew_placement(_data._mem, Vector3i(p_vector3i)); } -Variant::Variant(const Vector4 &p_vector4) { - type = VECTOR4; +Variant::Variant(const Vector4 &p_vector4) : + type(VECTOR4) { memnew_placement(_data._mem, Vector4(p_vector4)); } -Variant::Variant(const Vector4i &p_vector4i) { - type = VECTOR4I; +Variant::Variant(const Vector4i &p_vector4i) : + type(VECTOR4I) { memnew_placement(_data._mem, Vector4i(p_vector4i)); } -Variant::Variant(const Vector2 &p_vector2) { - type = VECTOR2; +Variant::Variant(const Vector2 &p_vector2) : + type(VECTOR2) { memnew_placement(_data._mem, Vector2(p_vector2)); } -Variant::Variant(const Vector2i &p_vector2i) { - type = VECTOR2I; +Variant::Variant(const Vector2i &p_vector2i) : + type(VECTOR2I) { memnew_placement(_data._mem, Vector2i(p_vector2i)); } -Variant::Variant(const Rect2 &p_rect2) { - type = RECT2; +Variant::Variant(const Rect2 &p_rect2) : + type(RECT2) { memnew_placement(_data._mem, Rect2(p_rect2)); } -Variant::Variant(const Rect2i &p_rect2i) { - type = RECT2I; +Variant::Variant(const Rect2i &p_rect2i) : + type(RECT2I) { memnew_placement(_data._mem, Rect2i(p_rect2i)); } -Variant::Variant(const Plane &p_plane) { - type = PLANE; +Variant::Variant(const Plane &p_plane) : + type(PLANE) { memnew_placement(_data._mem, Plane(p_plane)); } -Variant::Variant(const ::AABB &p_aabb) { - type = AABB; +Variant::Variant(const ::AABB &p_aabb) : + type(AABB) { _data._aabb = (::AABB *)Pools::_bucket_small.alloc(); memnew_placement(_data._aabb, ::AABB(p_aabb)); } -Variant::Variant(const Basis &p_matrix) { - type = BASIS; +Variant::Variant(const Basis &p_matrix) : + type(BASIS) { _data._basis = (Basis *)Pools::_bucket_medium.alloc(); memnew_placement(_data._basis, Basis(p_matrix)); } -Variant::Variant(const Quaternion &p_quaternion) { - type = QUATERNION; +Variant::Variant(const Quaternion &p_quaternion) : + type(QUATERNION) { memnew_placement(_data._mem, Quaternion(p_quaternion)); } -Variant::Variant(const Transform3D &p_transform) { - type = TRANSFORM3D; +Variant::Variant(const Transform3D &p_transform) : + type(TRANSFORM3D) { _data._transform3d = (Transform3D *)Pools::_bucket_medium.alloc(); memnew_placement(_data._transform3d, Transform3D(p_transform)); } -Variant::Variant(const Projection &pp_projection) { - type = PROJECTION; +Variant::Variant(const Projection &pp_projection) : + type(PROJECTION) { _data._projection = (Projection *)Pools::_bucket_large.alloc(); memnew_placement(_data._projection, Projection(pp_projection)); } -Variant::Variant(const Transform2D &p_transform) { - type = TRANSFORM2D; +Variant::Variant(const Transform2D &p_transform) : + type(TRANSFORM2D) { _data._transform2d = (Transform2D *)Pools::_bucket_small.alloc(); memnew_placement(_data._transform2d, Transform2D(p_transform)); } -Variant::Variant(const Color &p_color) { - type = COLOR; +Variant::Variant(const Color &p_color) : + type(COLOR) { memnew_placement(_data._mem, Color(p_color)); } -Variant::Variant(const NodePath &p_node_path) { - type = NODE_PATH; +Variant::Variant(const NodePath &p_node_path) : + type(NODE_PATH) { memnew_placement(_data._mem, NodePath(p_node_path)); } -Variant::Variant(const ::RID &p_rid) { - type = RID; +Variant::Variant(const ::RID &p_rid) : + type(RID) { memnew_placement(_data._mem, ::RID(p_rid)); } -Variant::Variant(const Object *p_object) { - type = OBJECT; - +Variant::Variant(const Object *p_object) : + type(OBJECT) { memnew_placement(_data._mem, ObjData); if (p_object) { @@ -2620,98 +2570,97 @@ Variant::Variant(const Object *p_object) { } } -Variant::Variant(const Callable &p_callable) { - type = CALLABLE; +Variant::Variant(const Callable &p_callable) : + type(CALLABLE) { memnew_placement(_data._mem, Callable(p_callable)); } -Variant::Variant(const Signal &p_callable) { - type = SIGNAL; +Variant::Variant(const Signal &p_callable) : + type(SIGNAL) { memnew_placement(_data._mem, Signal(p_callable)); } -Variant::Variant(const Dictionary &p_dictionary) { - type = DICTIONARY; +Variant::Variant(const Dictionary &p_dictionary) : + type(DICTIONARY) { memnew_placement(_data._mem, Dictionary(p_dictionary)); } -Variant::Variant(const Array &p_array) { - type = ARRAY; +Variant::Variant(const Array &p_array) : + type(ARRAY) { memnew_placement(_data._mem, Array(p_array)); } -Variant::Variant(const Vector<Plane> &p_array) { - type = ARRAY; - - Array *plane_array = memnew_placement(_data._mem, Array); - - plane_array->resize(p_array.size()); - - for (int i = 0; i < p_array.size(); i++) { - plane_array->operator[](i) = Variant(p_array[i]); - } -} - -Variant::Variant(const Vector<::RID> &p_array) { - type = ARRAY; - - Array *rid_array = memnew_placement(_data._mem, Array); - - rid_array->resize(p_array.size()); - - for (int i = 0; i < p_array.size(); i++) { - rid_array->set(i, Variant(p_array[i])); - } -} - -Variant::Variant(const Vector<uint8_t> &p_byte_array) { - type = PACKED_BYTE_ARRAY; - +Variant::Variant(const PackedByteArray &p_byte_array) : + type(PACKED_BYTE_ARRAY) { _data.packed_array = PackedArrayRef<uint8_t>::create(p_byte_array); } -Variant::Variant(const Vector<int32_t> &p_int32_array) { - type = PACKED_INT32_ARRAY; +Variant::Variant(const PackedInt32Array &p_int32_array) : + type(PACKED_INT32_ARRAY) { _data.packed_array = PackedArrayRef<int32_t>::create(p_int32_array); } -Variant::Variant(const Vector<int64_t> &p_int64_array) { - type = PACKED_INT64_ARRAY; +Variant::Variant(const PackedInt64Array &p_int64_array) : + type(PACKED_INT64_ARRAY) { _data.packed_array = PackedArrayRef<int64_t>::create(p_int64_array); } -Variant::Variant(const Vector<float> &p_float32_array) { - type = PACKED_FLOAT32_ARRAY; +Variant::Variant(const PackedFloat32Array &p_float32_array) : + type(PACKED_FLOAT32_ARRAY) { _data.packed_array = PackedArrayRef<float>::create(p_float32_array); } -Variant::Variant(const Vector<double> &p_float64_array) { - type = PACKED_FLOAT64_ARRAY; +Variant::Variant(const PackedFloat64Array &p_float64_array) : + type(PACKED_FLOAT64_ARRAY) { _data.packed_array = PackedArrayRef<double>::create(p_float64_array); } -Variant::Variant(const Vector<String> &p_string_array) { - type = PACKED_STRING_ARRAY; +Variant::Variant(const PackedStringArray &p_string_array) : + type(PACKED_STRING_ARRAY) { _data.packed_array = PackedArrayRef<String>::create(p_string_array); } -Variant::Variant(const Vector<Vector3> &p_vector3_array) { - type = PACKED_VECTOR3_ARRAY; - _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array); +Variant::Variant(const PackedVector2Array &p_vector2_array) : + type(PACKED_VECTOR2_ARRAY) { + _data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array); } -Variant::Variant(const Vector<Vector2> &p_vector2_array) { - type = PACKED_VECTOR2_ARRAY; - _data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array); +Variant::Variant(const PackedVector3Array &p_vector3_array) : + type(PACKED_VECTOR3_ARRAY) { + _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array); } -Variant::Variant(const Vector<Color> &p_color_array) { - type = PACKED_COLOR_ARRAY; +Variant::Variant(const PackedColorArray &p_color_array) : + type(PACKED_COLOR_ARRAY) { _data.packed_array = PackedArrayRef<Color>::create(p_color_array); } -Variant::Variant(const Vector<Face3> &p_face_array) { - Vector<Vector3> vertices; +/* helpers */ +Variant::Variant(const Vector<::RID> &p_array) : + type(ARRAY) { + Array *rid_array = memnew_placement(_data._mem, Array); + + rid_array->resize(p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + rid_array->set(i, Variant(p_array[i])); + } +} + +Variant::Variant(const Vector<Plane> &p_array) : + type(ARRAY) { + Array *plane_array = memnew_placement(_data._mem, Array); + + plane_array->resize(p_array.size()); + + for (int i = 0; i < p_array.size(); i++) { + plane_array->operator[](i) = Variant(p_array[i]); + } +} + +Variant::Variant(const Vector<Face3> &p_face_array) : + type(NIL) { + PackedVector3Array vertices; int face_count = p_face_array.size(); vertices.resize(face_count * 3); @@ -2726,14 +2675,11 @@ Variant::Variant(const Vector<Face3> &p_face_array) { } } - type = NIL; - *this = vertices; } -/* helpers */ -Variant::Variant(const Vector<Variant> &p_array) { - type = NIL; +Variant::Variant(const Vector<Variant> &p_array) : + type(NIL) { Array arr; arr.resize(p_array.size()); for (int i = 0; i < p_array.size(); i++) { @@ -2742,9 +2688,9 @@ Variant::Variant(const Vector<Variant> &p_array) { *this = arr; } -Variant::Variant(const Vector<StringName> &p_array) { - type = NIL; - Vector<String> v; +Variant::Variant(const Vector<StringName> &p_array) : + type(NIL) { + PackedStringArray v; int len = p_array.size(); v.resize(len); for (int i = 0; i < len; i++) { @@ -2912,12 +2858,13 @@ void Variant::operator=(const Variant &p_variant) { } } -Variant::Variant(const IPAddress &p_address) { - type = STRING; +Variant::Variant(const IPAddress &p_address) : + type(STRING) { memnew_placement(_data._mem, String(p_address)); } -Variant::Variant(const Variant &p_variant) { +Variant::Variant(const Variant &p_variant) : + type(NIL) { reference(p_variant); } @@ -3094,7 +3041,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_BYTE_ARRAY: { - const Vector<uint8_t> &arr = PackedArrayRef<uint8_t>::get_array(_data.packed_array); + const PackedByteArray &arr = PackedArrayRef<uint8_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { const uint8_t *r = arr.ptr(); @@ -3105,7 +3052,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_INT32_ARRAY: { - const Vector<int32_t> &arr = PackedArrayRef<int32_t>::get_array(_data.packed_array); + const PackedInt32Array &arr = PackedArrayRef<int32_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { const int32_t *r = arr.ptr(); @@ -3116,7 +3063,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_INT64_ARRAY: { - const Vector<int64_t> &arr = PackedArrayRef<int64_t>::get_array(_data.packed_array); + const PackedInt64Array &arr = PackedArrayRef<int64_t>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { const int64_t *r = arr.ptr(); @@ -3127,7 +3074,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_FLOAT32_ARRAY: { - const Vector<float> &arr = PackedArrayRef<float>::get_array(_data.packed_array); + const PackedFloat32Array &arr = PackedArrayRef<float>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3143,7 +3090,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_FLOAT64_ARRAY: { - const Vector<double> &arr = PackedArrayRef<double>::get_array(_data.packed_array); + const PackedFloat64Array &arr = PackedArrayRef<double>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3160,7 +3107,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_STRING_ARRAY: { uint32_t hash = HASH_MURMUR3_SEED; - const Vector<String> &arr = PackedArrayRef<String>::get_array(_data.packed_array); + const PackedStringArray &arr = PackedArrayRef<String>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3176,7 +3123,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_VECTOR2_ARRAY: { uint32_t hash = HASH_MURMUR3_SEED; - const Vector<Vector2> &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array); + const PackedVector2Array &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3193,7 +3140,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_VECTOR3_ARRAY: { uint32_t hash = HASH_MURMUR3_SEED; - const Vector<Vector3> &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array); + const PackedVector3Array &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { @@ -3211,7 +3158,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const { } break; case PACKED_COLOR_ARRAY: { uint32_t hash = HASH_MURMUR3_SEED; - const Vector<Color> &arr = PackedArrayRef<Color>::get_array(_data.packed_array); + const PackedColorArray &arr = PackedArrayRef<Color>::get_array(_data.packed_array); int len = arr.size(); if (likely(len)) { diff --git a/core/variant/variant.h b/core/variant/variant.h index 21342ae6ef..e40df3171f 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -70,6 +70,7 @@ typedef Vector<int32_t> PackedInt32Array; typedef Vector<int64_t> PackedInt64Array; typedef Vector<float> PackedFloat32Array; typedef Vector<double> PackedFloat64Array; +typedef Vector<real_t> PackedRealArray; typedef Vector<String> PackedStringArray; typedef Vector<Vector2> PackedVector2Array; typedef Vector<Vector3> PackedVector3Array; @@ -164,7 +165,7 @@ private: // Variant takes 20 bytes when real_t is float, and 36 if double // it only allocates extra memory for aabb/matrix. - Type type = NIL; + Type type; struct ObjData { ObjectID id; @@ -175,7 +176,7 @@ private: struct PackedArrayRefBase { SafeRefCount refcount; _FORCE_INLINE_ PackedArrayRefBase *reference() { - if (this->refcount.ref()) { + if (refcount.ref()) { return this; } else { return nullptr; @@ -203,7 +204,7 @@ private: _FORCE_INLINE_ virtual ~PackedArrayRefBase() {} //needs virtual destructor, but make inline }; - template <class T> + template <typename T> struct PackedArrayRef : public PackedArrayRefBase { Vector<T> array; static _FORCE_INLINE_ PackedArrayRef<T> *create() { @@ -338,6 +339,9 @@ public: _FORCE_INLINE_ bool is_num() const { return type == INT || type == FLOAT; } + _FORCE_INLINE_ bool is_string() const { + return type == STRING || type == STRING_NAME; + } _FORCE_INLINE_ bool is_array() const { return type >= ARRAY; } @@ -351,19 +355,14 @@ public: const Variant &operator[](const Variant &p_key) const = delete; operator bool() const; - operator signed int() const; - operator unsigned int() const; // this is the real one - operator signed short() const; - operator unsigned short() const; - operator signed char() const; - operator unsigned char() const; - //operator long unsigned int() const; operator int64_t() const; + operator int32_t() const; + operator int16_t() const; + operator int8_t() const; operator uint64_t() const; -#ifdef NEED_LONG_INT - operator signed long() const; - operator unsigned long() const; -#endif + operator uint32_t() const; + operator uint16_t() const; + operator uint8_t() const; operator ObjectID() const; @@ -400,21 +399,21 @@ public: operator Dictionary() const; operator Array() const; - operator Vector<uint8_t>() const; - operator Vector<int32_t>() const; - operator Vector<int64_t>() const; - operator Vector<float>() const; - operator Vector<double>() const; - operator Vector<String>() const; - operator Vector<Vector3>() const; - operator Vector<Color>() const; + operator PackedByteArray() const; + operator PackedInt32Array() const; + operator PackedInt64Array() const; + operator PackedFloat32Array() const; + operator PackedFloat64Array() const; + operator PackedStringArray() const; + operator PackedVector3Array() const; + operator PackedVector2Array() const; + operator PackedColorArray() const; + + operator Vector<::RID>() const; operator Vector<Plane>() const; operator Vector<Face3>() const; - operator Vector<Variant>() const; operator Vector<StringName>() const; - operator Vector<::RID>() const; - operator Vector<Vector2>() const; // some core type enums to convert to operator Side() const; @@ -426,18 +425,14 @@ public: Object *get_validated_object_with_check(bool &r_previously_freed) const; Variant(bool p_bool); - Variant(signed int p_int); // real one - Variant(unsigned int p_int); -#ifdef NEED_LONG_INT - Variant(signed long p_long); // real one - Variant(unsigned long p_long); -#endif - Variant(signed short p_short); // real one - Variant(unsigned short p_short); - Variant(signed char p_char); // real one - Variant(unsigned char p_char); - Variant(int64_t p_int); // real one - Variant(uint64_t p_int); + Variant(int64_t p_int64); + Variant(int32_t p_int32); + Variant(int16_t p_int16); + Variant(int8_t p_int8); + Variant(uint64_t p_uint64); + Variant(uint32_t p_uint32); + Variant(uint16_t p_uint16); + Variant(uint8_t p_uint8); Variant(float p_float); Variant(double p_double); Variant(const ObjectID &p_id); @@ -469,27 +464,27 @@ public: Variant(const Dictionary &p_dictionary); Variant(const Array &p_array); + Variant(const PackedByteArray &p_byte_array); + Variant(const PackedInt32Array &p_int32_array); + Variant(const PackedInt64Array &p_int64_array); + Variant(const PackedFloat32Array &p_float32_array); + Variant(const PackedFloat64Array &p_float64_array); + Variant(const PackedStringArray &p_string_array); + Variant(const PackedVector2Array &p_vector2_array); + Variant(const PackedVector3Array &p_vector3_array); + Variant(const PackedColorArray &p_color_array); + + Variant(const Vector<::RID> &p_array); // helper Variant(const Vector<Plane> &p_array); // helper - Variant(const Vector<uint8_t> &p_byte_array); - Variant(const Vector<int32_t> &p_int32_array); - Variant(const Vector<int64_t> &p_int64_array); - Variant(const Vector<float> &p_float32_array); - Variant(const Vector<double> &p_float64_array); - Variant(const Vector<String> &p_string_array); - Variant(const Vector<Vector3> &p_vector3_array); - Variant(const Vector<Color> &p_color_array); Variant(const Vector<Face3> &p_face_array); - Variant(const Vector<Variant> &p_array); Variant(const Vector<StringName> &p_array); - Variant(const Vector<::RID> &p_array); // helper - Variant(const Vector<Vector2> &p_array); // helper Variant(const IPAddress &p_address); #define VARIANT_ENUM_CLASS_CONSTRUCTOR(m_enum) \ - Variant(m_enum p_value) { \ - type = INT; \ + Variant(m_enum p_value) : \ + type(INT) { \ _data._int = (int64_t)p_value; \ } @@ -498,6 +493,7 @@ public: VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyAxis) VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyButton) VARIANT_ENUM_CLASS_CONSTRUCTOR(Key) + VARIANT_ENUM_CLASS_CONSTRUCTOR(KeyLocation) VARIANT_ENUM_CLASS_CONSTRUCTOR(MIDIMessage) VARIANT_ENUM_CLASS_CONSTRUCTOR(MouseButton) @@ -568,6 +564,7 @@ public: static ValidatedBuiltInMethod get_validated_builtin_method(Variant::Type p_type, const StringName &p_method); static PTRBuiltInMethod get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method); + static MethodInfo get_builtin_method_info(Variant::Type p_type, const StringName &p_method); static int get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method); static Variant::Type get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument); static String get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument); @@ -703,9 +700,20 @@ public: bool has_key(const Variant &p_key, bool &r_valid) const; /* Generic */ - - void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr); - Variant get(const Variant &p_index, bool *r_valid = nullptr) const; + enum VariantSetError { + SET_OK, + SET_KEYED_ERR, + SET_NAMED_ERR, + SET_INDEXED_ERR + }; + enum VariantGetError { + GET_OK, + GET_KEYED_ERR, + GET_NAMED_ERR, + GET_INDEXED_ERR + }; + void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr, VariantSetError *err_code = nullptr); + Variant get(const Variant &p_index, bool *r_valid = nullptr, VariantGetError *err_code = nullptr) const; bool in(const Variant &p_index, bool *r_valid = nullptr) const; bool iter_init(Variant &r_iter, bool &r_valid) const; @@ -765,8 +773,8 @@ public: static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = nullptr); static void get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums); - static void get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations); - static int get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid = nullptr); + static void get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations); + static int get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid = nullptr); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); @@ -780,7 +788,8 @@ public: static void unregister_types(); Variant(const Variant &p_variant); - _FORCE_INLINE_ Variant() {} + _FORCE_INLINE_ Variant() : + type(NIL) {} _FORCE_INLINE_ ~Variant() { clear(); } @@ -848,7 +857,7 @@ Variant Callable::call(VarArgs... p_args) const { } template <typename... VarArgs> -Callable Callable::bind(VarArgs... p_args) { +Callable Callable::bind(VarArgs... p_args) const { Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported. const Variant *argptrs[sizeof...(p_args) + 1]; for (uint32_t i = 0; i < sizeof...(p_args); i++) { @@ -857,4 +866,56 @@ Callable Callable::bind(VarArgs... p_args) { return bindp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args)); } +Variant &Array::Iterator::operator*() const { + if (unlikely(read_only)) { + *read_only = *element_ptr; + return *read_only; + } + return *element_ptr; +} + +Variant *Array::Iterator::operator->() const { + if (unlikely(read_only)) { + *read_only = *element_ptr; + return read_only; + } + return element_ptr; +} + +Array::Iterator &Array::Iterator::operator++() { + element_ptr++; + return *this; +} + +Array::Iterator &Array::Iterator::operator--() { + element_ptr--; + return *this; +} + +const Variant &Array::ConstIterator::operator*() const { + if (unlikely(read_only)) { + *read_only = *element_ptr; + return *read_only; + } + return *element_ptr; +} + +const Variant *Array::ConstIterator::operator->() const { + if (unlikely(read_only)) { + *read_only = *element_ptr; + return read_only; + } + return element_ptr; +} + +Array::ConstIterator &Array::ConstIterator::operator++() { + element_ptr++; + return *this; +} + +Array::ConstIterator &Array::ConstIterator::operator--() { + element_ptr--; + return *this; +} + #endif // VARIANT_H diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index f041d2c95e..d0d940c47d 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -43,329 +43,329 @@ typedef void (*VariantFunc)(Variant &r_ret, Variant &p_self, const Variant **p_args); typedef void (*VariantConstructFunc)(Variant &r_ret, const Variant **p_args); -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void vc_static_method_call(R (*method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_static_ret_dv(method, p_args, p_argcount, r_ret, r_error, p_defvals); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void vc_static_method_call(void (*method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_static_dv(method, p_args, p_argcount, r_error, p_defvals); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_ret_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_ret, r_error, p_defvals); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_method_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_retc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_ret, r_error, p_defvals); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { VariantInternal::clear(&r_ret); call_with_variant_args_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { VariantInternal::clear(&r_ret); call_with_variant_argsc_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_error, p_defvals); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_method_call(R (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_variant_args_ret_dv(&converted, method, p_args, p_argcount, r_ret, r_error, p_defvals); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_method_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_variant_args_retc_dv(&converted, method, p_args, p_argcount, r_ret, r_error, p_defvals); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_method_call(void (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_variant_args_dv(&converted, method, p_args, p_argcount, r_error, p_defvals); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_method_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_variant_argsc_dv(&converted, method, p_args, p_argcount, r_error, p_defvals); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_method_call_static(R (*method)(T *, P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_retc_static_helper_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, r_ret, p_defvals, r_error); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_method_call_static(void (*method)(T *, P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { call_with_variant_args_static_helper_dv(VariantGetInternalPtr<T>::get_ptr(base), method, p_args, p_argcount, p_defvals, r_error); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call(R (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_ret(base, method, p_args, r_ret); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_retc(base, method, p_args, r_ret); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call(void (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args(base, method, p_args); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_argsc(base, method, p_args); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_validated_call(R (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_validated_variant_args_ret_helper<T, R, P...>(&converted, method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_validated_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_validated_variant_args_retc_helper<T, R, P...>(&converted, method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_validated_call(void (T::*method)(P...), Variant *base, const Variant **p_args, Variant *r_ret) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_validated_variant_args_helper<T, P...>(&converted, method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_validated_call(void (T::*method)(P...) const, Variant *base, const Variant **p_args, Variant *r_ret) { T converted(static_cast<T>(*VariantGetInternalPtr<From>::get_ptr(base))); call_with_validated_variant_argsc_helper<T, P...>(&converted, method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call_static(R (*method)(T *, P...), Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_retc(base, method, p_args, r_ret); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_validated_call_static(void (*method)(T *, P...), Variant *base, const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static(base, method, p_args); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void vc_validated_static_call(R (*method)(P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_method_ret(method, p_args, r_ret); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void vc_validated_static_call(void (*method)(P...), const Variant **p_args, Variant *r_ret) { call_with_validated_variant_args_static_method(method, p_args); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(R (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { call_with_ptr_args_ret(reinterpret_cast<T *>(p_base), method, p_args, r_ret); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(R (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { call_with_ptr_args_retc(reinterpret_cast<T *>(p_base), method, p_args, r_ret); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(void (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { call_with_ptr_args(reinterpret_cast<T *>(p_base), method, p_args); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(void (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { call_with_ptr_argsc(reinterpret_cast<T *>(p_base), method, p_args); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_ptrcall(R (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { T converted(*reinterpret_cast<From *>(p_base)); call_with_ptr_args_ret(&converted, method, p_args, r_ret); } -template <class From, class R, class T, class... P> +template <typename From, typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_ptrcall(R (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { T converted(*reinterpret_cast<From *>(p_base)); call_with_ptr_args_retc(&converted, method, p_args, r_ret); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_ptrcall(void (T::*method)(P...), void *p_base, const void **p_args, void *r_ret) { T converted(*reinterpret_cast<From *>(p_base)); call_with_ptr_args(&converted, method, p_args); } -template <class From, class T, class... P> +template <typename From, typename T, typename... P> static _FORCE_INLINE_ void vc_convert_ptrcall(void (T::*method)(P...) const, void *p_base, const void **p_args, void *r_ret) { T converted(*reinterpret_cast<From *>(p_base)); call_with_ptr_argsc(&converted, method, p_args); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ int vc_get_argument_count(R (T::*method)(P...)) { return sizeof...(P); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ int vc_get_argument_count(R (T::*method)(P...) const) { return sizeof...(P); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ int vc_get_argument_count(void (T::*method)(P...)) { return sizeof...(P); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ int vc_get_argument_count(void (T::*method)(P...) const) { return sizeof...(P); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ int vc_get_argument_count(R (*method)(T *, P...)) { return sizeof...(P); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ int vc_get_argument_count_static(R (*method)(P...)) { return sizeof...(P); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (T::*method)(P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (T::*method)(P...) const, int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type(void (T::*method)(P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type(void (T::*method)(P...) const, int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type(R (*method)(T *, P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_argument_type_static(R (*method)(P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (T::*method)(P...)) { return GetTypeInfo<R>::VARIANT_TYPE; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (T::*method)(P...) const) { return GetTypeInfo<R>::VARIANT_TYPE; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(void (T::*method)(P...)) { return Variant::NIL; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(void (T::*method)(P...) const) { return Variant::NIL; } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(R (*method)(P...)) { return GetTypeInfo<R>::VARIANT_TYPE; } -template <class... P> +template <typename... P> static _FORCE_INLINE_ Variant::Type vc_get_return_type(void (*method)(P...)) { return Variant::NIL; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ bool vc_has_return_type(R (T::*method)(P...)) { return true; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ bool vc_has_return_type(R (T::*method)(P...) const) { return true; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ bool vc_has_return_type(void (T::*method)(P...)) { return false; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ bool vc_has_return_type(void (T::*method)(P...) const) { return false; } -template <class... P> +template <typename... P> static _FORCE_INLINE_ bool vc_has_return_type_static(void (*method)(P...)) { return false; } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ bool vc_has_return_type_static(R (*method)(P...)) { return true; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ bool vc_is_const(R (T::*method)(P...)) { return false; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ bool vc_is_const(R (T::*method)(P...) const) { return true; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ bool vc_is_const(void (T::*method)(P...)) { return false; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ bool vc_is_const(void (T::*method)(P...) const) { return true; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_base_type(R (T::*method)(P...)) { return GetTypeInfo<T>::VARIANT_TYPE; } -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_base_type(R (T::*method)(P...) const) { return GetTypeInfo<T>::VARIANT_TYPE; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_base_type(void (T::*method)(P...)) { return GetTypeInfo<T>::VARIANT_TYPE; } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ Variant::Type vc_get_base_type(void (T::*method)(P...) const) { return GetTypeInfo<T>::VARIANT_TYPE; } @@ -450,12 +450,12 @@ static _FORCE_INLINE_ Variant::Type vc_get_base_type(void (T::*method)(P...) con } \ }; -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void vc_static_ptrcall(R (*method)(P...), const void **p_args, void *r_ret) { call_with_ptr_args_static_method_ret<R, P...>(method, p_args, r_ret); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void vc_static_ptrcall(void (*method)(P...), const void **p_args, void *r_ret) { call_with_ptr_args_static_method<P...>(method, p_args); } @@ -500,12 +500,12 @@ static _FORCE_INLINE_ void vc_static_ptrcall(void (*method)(P...), const void ** } \ }; -template <class R, class T, class... P> +template <typename R, typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(R (*method)(T *, P...), void *p_base, const void **p_args, void *r_ret) { call_with_ptr_args_static_retc<T, R, P...>(reinterpret_cast<T *>(p_base), method, p_args, r_ret); } -template <class T, class... P> +template <typename T, typename... P> static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, const void **p_args, void *r_ret) { call_with_ptr_args_static<T, P...>(reinterpret_cast<T *>(p_base), method, p_args); } @@ -666,7 +666,7 @@ struct _VariantCall { CharString cs; cs.resize(p_instance->size() + 1); memcpy(cs.ptrw(), r, p_instance->size()); - cs[p_instance->size()] = 0; + cs[(int)p_instance->size()] = 0; s = cs.get_data(); } @@ -885,7 +885,7 @@ struct _VariantCall { ERR_FAIL_COND_V_MSG(size % sizeof(int32_t), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit integer) to convert to PackedInt32Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(int32_t)); - ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. memcpy(dest.ptrw(), r, dest.size() * sizeof(int32_t)); return dest; } @@ -899,7 +899,7 @@ struct _VariantCall { ERR_FAIL_COND_V_MSG(size % sizeof(int64_t), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit integer) to convert to PackedInt64Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(int64_t)); - ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. memcpy(dest.ptrw(), r, dest.size() * sizeof(int64_t)); return dest; } @@ -913,7 +913,7 @@ struct _VariantCall { ERR_FAIL_COND_V_MSG(size % sizeof(float), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit float) to convert to PackedFloat32Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(float)); - ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. memcpy(dest.ptrw(), r, dest.size() * sizeof(float)); return dest; } @@ -927,7 +927,7 @@ struct _VariantCall { ERR_FAIL_COND_V_MSG(size % sizeof(double), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit double) to convert to PackedFloat64Array."); const uint8_t *r = p_instance->ptr(); dest.resize(size / sizeof(double)); - ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed. + ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed. memcpy(dest.ptrw(), r, dest.size() * sizeof(double)); return dest; } @@ -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); @@ -1071,14 +1075,14 @@ struct _VariantCall { static ConstantData *constant_data; - static void add_constant(int p_type, StringName p_constant_name, int64_t p_constant_value) { + static void add_constant(int p_type, const StringName &p_constant_name, int64_t p_constant_value) { constant_data[p_type].value[p_constant_name] = p_constant_value; #ifdef DEBUG_ENABLED constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } - static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + static void add_variant_constant(int p_type, const StringName &p_constant_name, const Variant &p_constant_value) { constant_data[p_type].variant_value[p_constant_name] = p_constant_value; #ifdef DEBUG_ENABLED constant_data[p_type].variant_value_ordered.push_back(p_constant_name); @@ -1091,7 +1095,7 @@ struct _VariantCall { static EnumData *enum_data; - static void add_enum_constant(int p_type, StringName p_enum_type_name, StringName p_enumeration_name, int p_enum_value) { + static void add_enum_constant(int p_type, const StringName &p_enum_type_name, const StringName &p_enumeration_name, int p_enum_value) { enum_data[p_type].value[p_enum_type_name][p_enumeration_name] = p_enum_value; } }; @@ -1114,13 +1118,53 @@ struct VariantBuiltInMethodInfo { Variant::Type return_type; int argument_count = 0; Variant::Type (*get_argument_type)(int p_arg) = nullptr; + + MethodInfo get_method_info(const StringName &p_name) const { + MethodInfo mi; + mi.name = p_name; + + if (has_return_type) { + mi.return_val.type = return_type; + if (mi.return_val.type == Variant::NIL) { + mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + } + + if (is_const) { + mi.flags |= METHOD_FLAG_CONST; + } + if (is_vararg) { + mi.flags |= METHOD_FLAG_VARARG; + } + if (is_static) { + mi.flags |= METHOD_FLAG_STATIC; + } + + for (int i = 0; i < argument_count; i++) { + PropertyInfo pi; +#ifdef DEBUG_METHODS_ENABLED + pi.name = argument_names[i]; +#else + pi.name = "arg" + itos(i + 1); +#endif + pi.type = (*get_argument_type)(i); + if (pi.type == Variant::NIL) { + pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; + } + mi.arguments.push_back(pi); + } + + mi.default_arguments = default_arguments; + + return mi; + } }; typedef OAHashMap<StringName, VariantBuiltInMethodInfo> BuiltinMethodMap; static BuiltinMethodMap *builtin_method_info; static List<StringName> *builtin_method_names; -template <class T> +template <typename T> static void register_builtin_method(const Vector<String> &p_argnames, const Vector<Variant> &p_def_args) { StringName name = T::get_name(); @@ -1268,6 +1312,13 @@ Variant::PTRBuiltInMethod Variant::get_ptr_builtin_method(Variant::Type p_type, return method->ptrcall; } +MethodInfo Variant::get_builtin_method_info(Variant::Type p_type, const StringName &p_method) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, MethodInfo()); + const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); + ERR_FAIL_NULL_V(method, MethodInfo()); + return method->get_method_info(p_method); +} + int Variant::get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method) { ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0); const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method); @@ -1378,43 +1429,7 @@ void Variant::get_method_list(List<MethodInfo> *p_list) const { for (const StringName &E : builtin_method_names[type]) { const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E); ERR_CONTINUE(!method); - - MethodInfo mi; - mi.name = E; - - //return type - if (method->has_return_type) { - mi.return_val.type = method->return_type; - if (mi.return_val.type == Variant::NIL) { - mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - } - } - - if (method->is_const) { - mi.flags |= METHOD_FLAG_CONST; - } - if (method->is_vararg) { - mi.flags |= METHOD_FLAG_VARARG; - } - if (method->is_static) { - mi.flags |= METHOD_FLAG_STATIC; - } - for (int i = 0; i < method->argument_count; i++) { - PropertyInfo pi; -#ifdef DEBUG_METHODS_ENABLED - pi.name = method->argument_names[i]; -#else - pi.name = "arg" + itos(i + 1); -#endif - pi.type = method->get_argument_type(i); - if (pi.type == Variant::NIL) { - pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT; - } - mi.arguments.push_back(pi); - } - - mi.default_arguments = method->default_arguments; - p_list->push_back(mi); + p_list->push_back(method->get_method_info(E)); } } } @@ -1493,7 +1508,7 @@ void Variant::get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums } } -void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations) { +void Variant::get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); _VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type]; @@ -1505,7 +1520,7 @@ void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_ } } -int Variant::get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid) { +int Variant::get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid) { if (r_valid) { *r_valid = false; } @@ -1629,6 +1644,8 @@ static void _register_variant_builtin_methods() { bind_string_method(nocasecmp_to, sarray("to"), varray()); bind_string_method(naturalcasecmp_to, sarray("to"), varray()); bind_string_method(naturalnocasecmp_to, sarray("to"), varray()); + bind_string_method(filecasecmp_to, sarray("to"), varray()); + bind_string_method(filenocasecmp_to, sarray("to"), varray()); bind_string_method(length, sarray(), varray()); bind_string_method(substr, sarray("from", "len"), varray(-1)); bind_string_method(get_slice, sarray("delimiter", "slice"), varray()); @@ -1782,7 +1799,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, dot, sarray("with"), varray()); bind_method(Vector2, slide, sarray("n"), varray()); bind_method(Vector2, bounce, sarray("n"), varray()); - bind_method(Vector2, reflect, sarray("n"), varray()); + bind_method(Vector2, reflect, sarray("line"), varray()); bind_method(Vector2, cross, sarray("with"), varray()); bind_method(Vector2, abs, sarray(), varray()); bind_method(Vector2, sign, sarray(), varray()); @@ -1796,6 +1813,8 @@ static void _register_variant_builtin_methods() { bind_method(Vector2i, aspect, sarray(), varray()); bind_method(Vector2i, max_axis_index, sarray(), varray()); bind_method(Vector2i, min_axis_index, sarray(), varray()); + bind_method(Vector2i, distance_to, sarray("to"), varray()); + bind_method(Vector2i, distance_squared_to, sarray("to"), varray()); bind_method(Vector2i, length, sarray(), varray()); bind_method(Vector2i, length_squared, sarray(), varray()); bind_method(Vector2i, sign, sarray(), varray()); @@ -1877,7 +1896,7 @@ static void _register_variant_builtin_methods() { bind_method(Vector3, project, sarray("b"), varray()); bind_method(Vector3, slide, sarray("n"), varray()); bind_method(Vector3, bounce, sarray("n"), varray()); - bind_method(Vector3, reflect, sarray("n"), varray()); + bind_method(Vector3, reflect, sarray("direction"), varray()); bind_method(Vector3, sign, sarray(), varray()); bind_method(Vector3, octahedron_encode, sarray(), varray()); bind_static_method(Vector3, octahedron_decode, sarray("uv"), varray()); @@ -1886,6 +1905,8 @@ static void _register_variant_builtin_methods() { bind_method(Vector3i, min_axis_index, sarray(), varray()); bind_method(Vector3i, max_axis_index, sarray(), varray()); + bind_method(Vector3i, distance_to, sarray("to"), varray()); + bind_method(Vector3i, distance_squared_to, sarray("to"), varray()); bind_method(Vector3i, length, sarray(), varray()); bind_method(Vector3i, length_squared, sarray(), varray()); bind_method(Vector3i, sign, sarray(), varray()); @@ -1932,6 +1953,8 @@ static void _register_variant_builtin_methods() { bind_method(Vector4i, abs, sarray(), varray()); bind_method(Vector4i, clamp, sarray("min", "max"), varray()); bind_method(Vector4i, snapped, sarray("step"), varray()); + bind_method(Vector4i, distance_to, sarray("to"), varray()); + bind_method(Vector4i, distance_squared_to, sarray("to"), varray()); /* Plane */ @@ -2016,11 +2039,13 @@ static void _register_variant_builtin_methods() { bind_method(NodePath, get_subname, sarray("idx"), varray()); bind_method(NodePath, get_concatenated_names, sarray(), varray()); bind_method(NodePath, get_concatenated_subnames, sarray(), varray()); + bind_method(NodePath, slice, sarray("begin", "end"), varray(INT_MAX)); bind_method(NodePath, get_as_property_path, sarray(), varray()); bind_method(NodePath, is_empty, sarray(), varray()); /* Callable */ + bind_static_method(Callable, create, sarray("variant", "method"), varray()); bind_method(Callable, callv, sarray("arguments"), varray()); bind_method(Callable, is_null, sarray(), varray()); bind_method(Callable, is_custom, sarray(), varray()); @@ -2029,6 +2054,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()); @@ -2186,6 +2212,7 @@ static void _register_variant_builtin_methods() { bind_method(Dictionary, is_empty, sarray(), varray()); bind_method(Dictionary, clear, sarray(), varray()); bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false)); + bind_method(Dictionary, merged, sarray("dictionary", "overwrite"), varray(false)); bind_method(Dictionary, has, sarray("key"), varray()); bind_method(Dictionary, has_all, sarray("keys"), varray()); bind_method(Dictionary, find_key, sarray("value"), varray()); @@ -2195,6 +2222,7 @@ static void _register_variant_builtin_methods() { bind_method(Dictionary, values, sarray(), varray()); bind_method(Dictionary, duplicate, sarray("deep"), varray(false)); bind_method(Dictionary, get, sarray("key", "default"), varray(Variant())); + bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant())); bind_method(Dictionary, make_read_only, sarray(), varray()); bind_method(Dictionary, is_read_only, sarray(), varray()); diff --git a/core/variant/variant_callable.cpp b/core/variant/variant_callable.cpp new file mode 100644 index 0000000000..21f9a4f52b --- /dev/null +++ b/core/variant/variant_callable.cpp @@ -0,0 +1,90 @@ +/**************************************************************************/ +/* variant_callable.cpp */ +/**************************************************************************/ +/* 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. */ +/**************************************************************************/ + +#include "variant_callable.h" + +#include "core/templates/hashfuncs.h" + +bool VariantCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { + return p_a->hash() == p_b->hash(); +} + +bool VariantCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) { + return p_a->hash() < p_b->hash(); +} + +uint32_t VariantCallable::hash() const { + return h; +} + +String VariantCallable::get_as_text() const { + return vformat("%s::%s (Callable)", Variant::get_type_name(variant.get_type()), method); +} + +CallableCustom::CompareEqualFunc VariantCallable::get_compare_equal_func() const { + return compare_equal; +} + +CallableCustom::CompareLessFunc VariantCallable::get_compare_less_func() const { + return compare_less; +} + +bool VariantCallable::is_valid() const { + return Variant::has_builtin_method(variant.get_type(), method); +} + +StringName VariantCallable::get_method() const { + return method; +} + +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); +} + +VariantCallable::VariantCallable(const Variant &p_variant, const StringName &p_method) { + variant = p_variant; + method = p_method; + h = variant.hash(); + h = hash_murmur3_one_64(Variant::get_builtin_method_hash(variant.get_type(), method), h); +} diff --git a/core/variant/variant_callable.h b/core/variant/variant_callable.h new file mode 100644 index 0000000000..1811f3bb9a --- /dev/null +++ b/core/variant/variant_callable.h @@ -0,0 +1,59 @@ +/**************************************************************************/ +/* variant_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 VARIANT_CALLABLE_H +#define VARIANT_CALLABLE_H + +#include "core/variant/callable.h" +#include "core/variant/variant.h" + +class VariantCallable : public CallableCustom { + Variant variant; + StringName method; + uint32_t h = 0; + + static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b); + static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); + +public: + uint32_t hash() const override; + String get_as_text() const override; + CompareEqualFunc get_compare_equal_func() const override; + CompareLessFunc get_compare_less_func() const override; + 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); +}; + +#endif // VARIANT_CALLABLE_H diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp index 3427950224..b0ed49be5d 100644 --- a/core/variant/variant_construct.cpp +++ b/core/variant/variant_construct.cpp @@ -41,7 +41,7 @@ struct VariantConstructData { static LocalVector<VariantConstructData> construct_data[Variant::VARIANT_MAX]; -template <class T> +template <typename T> static void add_constructor(const Vector<String> &arg_names) { ERR_FAIL_COND_MSG(arg_names.size() != T::get_argument_count(), "Argument names size mismatch for " + Variant::get_type_name(T::get_base_type()) + "."); diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h index ef7bf2dfc2..a93723a910 100644 --- a/core/variant/variant_construct.h +++ b/core/variant/variant_construct.h @@ -42,7 +42,7 @@ #include "core/templates/local_vector.h" #include "core/templates/oa_hash_map.h" -template <class T> +template <typename T> struct PtrConstruct {}; #define MAKE_PTRCONSTRUCT(m_type) \ @@ -99,7 +99,7 @@ MAKE_PTRCONSTRUCT(PackedVector3Array); MAKE_PTRCONSTRUCT(PackedColorArray); MAKE_PTRCONSTRUCT(Variant); -template <class T, class... P> +template <typename T, typename... P> class VariantConstructor { template <size_t... Is> static _FORCE_INLINE_ void construct_helper(T &base, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { @@ -222,7 +222,7 @@ public: } }; -template <class T> +template <typename T> class VariantConstructorFromString { public: static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { @@ -470,7 +470,7 @@ public: } }; -template <class T> +template <typename T> class VariantConstructorToArray { public: static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { @@ -529,7 +529,7 @@ public: } }; -template <class T> +template <typename T> class VariantConstructorFromArray { public: static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { @@ -622,7 +622,7 @@ public: } }; -template <class T> +template <typename T> class VariantConstructNoArgs { public: static void construct(Variant &r_ret, const Variant **p_args, Callable::CallError &r_error) { @@ -661,7 +661,7 @@ public: VariantInternal::clear(r_ret); } static void ptr_construct(void *base, const void **p_args) { - ERR_FAIL_MSG("can't ptrcall nil constructor"); + ERR_FAIL_MSG("Cannot ptrcall nil constructor"); } static int get_argument_count() { diff --git a/core/variant/variant_destruct.cpp b/core/variant/variant_destruct.cpp index 5308eba97d..c7455d5117 100644 --- a/core/variant/variant_destruct.cpp +++ b/core/variant/variant_destruct.cpp @@ -34,7 +34,7 @@ static Variant::PTRDestructor destruct_pointers[Variant::VARIANT_MAX] = { nullptr }; -template <class T> +template <typename T> static void add_destructor() { destruct_pointers[T::get_base_type()] = T::ptr_destruct; } diff --git a/core/variant/variant_destruct.h b/core/variant/variant_destruct.h index c5f9c260c0..c496189c6d 100644 --- a/core/variant/variant_destruct.h +++ b/core/variant/variant_destruct.h @@ -35,7 +35,7 @@ #include "core/object/class_db.h" -template <class T> +template <typename T> struct VariantDestruct {}; #define MAKE_PTRDESTRUCT(m_type) \ diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 116210e8de..dbd4a6a7ad 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -211,7 +211,7 @@ public: _FORCE_INLINE_ static const ObjectID get_object_id(const Variant *v) { return v->_get_obj().id; } - template <class T> + template <typename T> _FORCE_INLINE_ static void init_generic(Variant *v) { v->type = GetTypeInfo<T>::VARIANT_TYPE; } @@ -510,7 +510,7 @@ public: } }; -template <class T> +template <typename T> struct VariantGetInternalPtr { }; @@ -797,7 +797,7 @@ struct VariantGetInternalPtr<PackedColorArray> { static const PackedColorArray *get_ptr(const Variant *v) { return VariantInternal::get_color_array(v); } }; -template <class T> +template <typename T> struct VariantInternalAccessor { }; @@ -810,7 +810,7 @@ struct VariantInternalAccessor<bool> { #define VARIANT_ACCESSOR_NUMBER(m_type) \ template <> \ struct VariantInternalAccessor<m_type> { \ - static _FORCE_INLINE_ m_type get(const Variant *v) { return (m_type)*VariantInternal::get_int(v); } \ + static _FORCE_INLINE_ m_type get(const Variant *v) { return (m_type) * VariantInternal::get_int(v); } \ static _FORCE_INLINE_ void set(Variant *v, m_type p_value) { *VariantInternal::get_int(v) = p_value; } \ }; @@ -830,13 +830,13 @@ struct VariantInternalAccessor<ObjectID> { static _FORCE_INLINE_ void set(Variant *v, ObjectID p_value) { *VariantInternal::get_int(v) = p_value; } }; -template <class T> +template <typename T> struct VariantInternalAccessor<T *> { static _FORCE_INLINE_ T *get(const Variant *v) { return const_cast<T *>(static_cast<const T *>(*VariantInternal::get_object(v))); } static _FORCE_INLINE_ void set(Variant *v, const T *p_value) { VariantInternal::object_assign(v, p_value); } }; -template <class T> +template <typename T> struct VariantInternalAccessor<const T *> { static _FORCE_INLINE_ const T *get(const Variant *v) { return static_cast<const T *>(*VariantInternal::get_object(v)); } static _FORCE_INLINE_ void set(Variant *v, const T *p_value) { VariantInternal::object_assign(v, p_value); } @@ -1091,7 +1091,7 @@ struct VariantInternalAccessor<Vector<Variant>> { } }; -template <class T> +template <typename T> struct VariantInitializer { }; @@ -1301,196 +1301,196 @@ struct VariantInitializer<Object *> { static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_object(v); } }; -template <class T> -struct VariantZeroAssigner { +template <typename T> +struct VariantDefaultInitializer { }; template <> -struct VariantZeroAssigner<bool> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_bool(v) = false; } +struct VariantDefaultInitializer<bool> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_bool(v) = false; } }; template <> -struct VariantZeroAssigner<int64_t> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int(v) = 0; } +struct VariantDefaultInitializer<int64_t> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int(v) = 0; } }; template <> -struct VariantZeroAssigner<double> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<double> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<float> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; } +struct VariantDefaultInitializer<float> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; } }; template <> -struct VariantZeroAssigner<String> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string(v) = String(); } +struct VariantDefaultInitializer<String> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string(v) = String(); } }; template <> -struct VariantZeroAssigner<Vector2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } +struct VariantDefaultInitializer<Vector2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); } }; template <> -struct VariantZeroAssigner<Vector2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } +struct VariantDefaultInitializer<Vector2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); } }; template <> -struct VariantZeroAssigner<Rect2> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } +struct VariantDefaultInitializer<Rect2> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); } }; template <> -struct VariantZeroAssigner<Rect2i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } +struct VariantDefaultInitializer<Rect2i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); } }; template <> -struct VariantZeroAssigner<Vector3> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } +struct VariantDefaultInitializer<Vector3> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); } }; template <> -struct VariantZeroAssigner<Vector3i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } +struct VariantDefaultInitializer<Vector3i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); } }; template <> -struct VariantZeroAssigner<Vector4> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } +struct VariantDefaultInitializer<Vector4> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); } }; template <> -struct VariantZeroAssigner<Vector4i> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } +struct VariantDefaultInitializer<Vector4i> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); } }; template <> -struct VariantZeroAssigner<Transform2D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } +struct VariantDefaultInitializer<Transform2D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); } }; template <> -struct VariantZeroAssigner<Plane> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } +struct VariantDefaultInitializer<Plane> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_plane(v) = Plane(); } }; template <> -struct VariantZeroAssigner<Quaternion> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } +struct VariantDefaultInitializer<Quaternion> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); } }; template <> -struct VariantZeroAssigner<AABB> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } +struct VariantDefaultInitializer<AABB> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); } }; template <> -struct VariantZeroAssigner<Basis> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } +struct VariantDefaultInitializer<Basis> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_basis(v) = Basis(); } }; template <> -struct VariantZeroAssigner<Transform3D> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } +struct VariantDefaultInitializer<Transform3D> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); } }; template <> -struct VariantZeroAssigner<Projection> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } +struct VariantDefaultInitializer<Projection> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_projection(v) = Projection(); } }; template <> -struct VariantZeroAssigner<Color> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); } +struct VariantDefaultInitializer<Color> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color(v) = Color(); } }; template <> -struct VariantZeroAssigner<StringName> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } +struct VariantDefaultInitializer<StringName> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); } }; template <> -struct VariantZeroAssigner<NodePath> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } +struct VariantDefaultInitializer<NodePath> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); } }; template <> -struct VariantZeroAssigner<::RID> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rid(v) = RID(); } +struct VariantDefaultInitializer<::RID> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rid(v) = RID(); } }; template <> -struct VariantZeroAssigner<Callable> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } +struct VariantDefaultInitializer<Callable> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_callable(v) = Callable(); } }; template <> -struct VariantZeroAssigner<Signal> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } +struct VariantDefaultInitializer<Signal> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_signal(v) = Signal(); } }; template <> -struct VariantZeroAssigner<Dictionary> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } +struct VariantDefaultInitializer<Dictionary> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); } }; template <> -struct VariantZeroAssigner<Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); } +struct VariantDefaultInitializer<Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_array(v) = Array(); } }; template <> -struct VariantZeroAssigner<PackedByteArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } +struct VariantDefaultInitializer<PackedByteArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); } }; template <> -struct VariantZeroAssigner<PackedInt32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } +struct VariantDefaultInitializer<PackedInt32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); } }; template <> -struct VariantZeroAssigner<PackedInt64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } +struct VariantDefaultInitializer<PackedInt64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat32Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } +struct VariantDefaultInitializer<PackedFloat32Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); } }; template <> -struct VariantZeroAssigner<PackedFloat64Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } +struct VariantDefaultInitializer<PackedFloat64Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); } }; template <> -struct VariantZeroAssigner<PackedStringArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } +struct VariantDefaultInitializer<PackedStringArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); } }; template <> -struct VariantZeroAssigner<PackedVector2Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } +struct VariantDefaultInitializer<PackedVector2Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); } }; template <> -struct VariantZeroAssigner<PackedVector3Array> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } +struct VariantDefaultInitializer<PackedVector3Array> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); } }; template <> -struct VariantZeroAssigner<PackedColorArray> { - static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } +struct VariantDefaultInitializer<PackedColorArray> { + static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); } }; -template <class T> +template <typename T> struct VariantTypeChanger { static _FORCE_INLINE_ void change(Variant *v) { if (v->get_type() != GetTypeInfo<T>::VARIANT_TYPE || GetTypeInfo<T>::VARIANT_TYPE >= Variant::PACKED_BYTE_ARRAY) { //second condition removed by optimizer @@ -1504,11 +1504,11 @@ struct VariantTypeChanger { VariantInitializer<T>::init(v); } - VariantZeroAssigner<T>::zero(v); + VariantDefaultInitializer<T>::init(v); } }; -template <class T> +template <typename T> struct VariantTypeAdjust { _FORCE_INLINE_ static void adjust(Variant *r_ret) { VariantTypeChanger<typename GetSimpleTypeT<T>::type_t>::change(r_ret); @@ -1532,7 +1532,7 @@ struct VariantTypeAdjust<Object *> { // GDExtension helpers. -template <class T> +template <typename T> struct VariantTypeConstructor { _FORCE_INLINE_ static void variant_from_type(void *r_variant, void *p_value) { // r_variant is provided by caller as uninitialized memory diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp index aed83ac010..dcf4b287d1 100644 --- a/core/variant/variant_op.cpp +++ b/core/variant/variant_op.cpp @@ -37,7 +37,7 @@ static VariantEvaluatorFunction operator_evaluator_table[Variant::OP_MAX][Varian static Variant::ValidatedOperatorEvaluator validated_operator_evaluator_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; static Variant::PTROperatorEvaluator ptr_operator_evaluator_table[Variant::OP_MAX][Variant::VARIANT_MAX][Variant::VARIANT_MAX]; -template <class T> +template <typename T> void register_op(Variant::Operator p_op, Variant::Type p_type_a, Variant::Type p_type_b) { operator_return_type_table[p_op][p_type_a][p_type_b] = T::get_return_type(); operator_evaluator_table[p_op][p_type_a][p_type_b] = T::evaluate; @@ -230,18 +230,20 @@ public: }; #define register_string_op(m_op_type, m_op_code) \ - do { \ + if constexpr (true) { \ register_op<m_op_type<String, String>>(m_op_code, Variant::STRING, Variant::STRING); \ register_op<m_op_type<String, StringName>>(m_op_code, Variant::STRING, Variant::STRING_NAME); \ register_op<m_op_type<StringName, String>>(m_op_code, Variant::STRING_NAME, Variant::STRING); \ register_op<m_op_type<StringName, StringName>>(m_op_code, Variant::STRING_NAME, Variant::STRING_NAME); \ - } while (false) + } else \ + ((void)0) #define register_string_modulo_op(m_class, m_type) \ - do { \ + if constexpr (true) { \ register_op<OperatorEvaluatorStringFormat<String, m_class>>(Variant::OP_MODULE, Variant::STRING, m_type); \ register_op<OperatorEvaluatorStringFormat<StringName, m_class>>(Variant::OP_MODULE, Variant::STRING_NAME, m_type); \ - } while (false) + } else \ + ((void)0) void Variant::_register_variant_operators() { memset(operator_return_type_table, 0, sizeof(operator_return_type_table)); @@ -412,6 +414,15 @@ void Variant::_register_variant_operators() { register_op<OperatorEvaluatorDivNZ<Vector4, Vector4i, double>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::FLOAT); register_op<OperatorEvaluatorDivNZ<Vector4i, Vector4i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::INT); + register_op<OperatorEvaluatorDiv<Transform2D, Transform2D, int64_t>>(Variant::OP_DIVIDE, Variant::TRANSFORM2D, Variant::INT); + register_op<OperatorEvaluatorDiv<Transform2D, Transform2D, double>>(Variant::OP_DIVIDE, Variant::TRANSFORM2D, Variant::FLOAT); + + register_op<OperatorEvaluatorDiv<Transform3D, Transform3D, int64_t>>(Variant::OP_DIVIDE, Variant::TRANSFORM3D, Variant::INT); + register_op<OperatorEvaluatorDiv<Transform3D, Transform3D, double>>(Variant::OP_DIVIDE, Variant::TRANSFORM3D, Variant::FLOAT); + + register_op<OperatorEvaluatorDiv<Basis, Basis, int64_t>>(Variant::OP_DIVIDE, Variant::BASIS, Variant::INT); + register_op<OperatorEvaluatorDiv<Basis, Basis, double>>(Variant::OP_DIVIDE, Variant::BASIS, Variant::FLOAT); + register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT); register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, int64_t>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::INT); diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index 9e6367ab6d..3142f49fc6 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -37,7 +37,7 @@ #include "core/debugger/engine_debugger.h" #include "core/object/class_db.h" -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorAdd { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -55,7 +55,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorSub { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -73,7 +73,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorMul { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -91,7 +91,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorPow { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -109,7 +109,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorXForm { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -127,7 +127,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorXFormInv { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -145,7 +145,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorDiv { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -163,7 +163,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorDivNZ { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -258,7 +258,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<Vector4i>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorMod { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -276,7 +276,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorModNZ { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -371,7 +371,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<Vector4i>::VARIANT_TYPE; } }; -template <class R, class A> +template <typename R, typename A> class OperatorEvaluatorNeg { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -388,7 +388,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A> +template <typename R, typename A> class OperatorEvaluatorPos { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -405,7 +405,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorShiftLeft { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -431,7 +431,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorShiftRight { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -457,7 +457,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorBitOr { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -475,7 +475,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorBitAnd { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -493,7 +493,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A, class B> +template <typename R, typename A, typename B> class OperatorEvaluatorBitXor { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -511,7 +511,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class R, class A> +template <typename R, typename A> class OperatorEvaluatorBitNeg { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -528,7 +528,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<R>::VARIANT_TYPE; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorEqual { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -549,14 +549,14 @@ public: class OperatorEvaluatorEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *a = p_left.get_validated_object(); - const Object *b = p_right.get_validated_object(); + const ObjectID &a = VariantInternal::get_object_id(&p_left); + const ObjectID &b = VariantInternal::get_object_id(&p_right); *r_ret = a == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *a = left->get_validated_object(); - const Object *b = right->get_validated_object(); + const ObjectID &a = VariantInternal::get_object_id(left); + const ObjectID &b = VariantInternal::get_object_id(right); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -568,12 +568,12 @@ public: class OperatorEvaluatorEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *a = p_left.get_validated_object(); + const Object *a = p_left.operator Object *(); *r_ret = a == nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *a = left->get_validated_object(); + const Object *a = left->operator Object *(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -585,12 +585,12 @@ public: class OperatorEvaluatorEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - const Object *b = p_right.get_validated_object(); + const Object *b = p_right.operator Object *(); *r_ret = nullptr == b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - const Object *b = right->get_validated_object(); + const Object *b = right->operator Object *(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -599,7 +599,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorNotEqual { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -620,14 +620,14 @@ public: class OperatorEvaluatorNotEqualObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *a = p_left.get_validated_object(); - Object *b = p_right.get_validated_object(); + const ObjectID &a = VariantInternal::get_object_id(&p_left); + const ObjectID &b = VariantInternal::get_object_id(&p_right); *r_ret = a != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *a = left->get_validated_object(); - Object *b = right->get_validated_object(); + const ObjectID &a = VariantInternal::get_object_id(left); + const ObjectID &b = VariantInternal::get_object_id(right); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -639,12 +639,12 @@ public: class OperatorEvaluatorNotEqualObjectNil { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *a = p_left.get_validated_object(); + Object *a = p_left.operator Object *(); *r_ret = a != nullptr; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *a = left->get_validated_object(); + Object *a = left->operator Object *(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -656,12 +656,12 @@ public: class OperatorEvaluatorNotEqualNilObject { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { - Object *b = p_right.get_validated_object(); + Object *b = p_right.operator Object *(); *r_ret = nullptr != b; r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { - Object *b = right->get_validated_object(); + Object *b = right->operator Object *(); *VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b; } static void ptr_evaluate(const void *left, const void *right, void *r_ret) { @@ -670,7 +670,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorLess { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -688,7 +688,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorLessEqual { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -706,7 +706,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorGreater { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -724,7 +724,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorGreaterEqual { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -742,7 +742,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorAnd { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -760,7 +760,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorOr { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -779,7 +779,7 @@ public: }; #define XOR_OP(m_a, m_b) (((m_a) || (m_b)) && !((m_a) && (m_b))) -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorXor { public: _FORCE_INLINE_ static bool xor_op(const A &a, const B &b) { @@ -800,7 +800,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A> +template <typename A> class OperatorEvaluatorNot { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -857,7 +857,7 @@ public: static Variant::Type get_return_type() { return Variant::ARRAY; } }; -template <class T> +template <typename T> class OperatorEvaluatorAppendArray { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -880,7 +880,7 @@ public: static Variant::Type get_return_type() { return GetTypeInfo<Vector<T>>::VARIANT_TYPE; } }; -template <class Left, class Right> +template <typename Left, typename Right> class OperatorEvaluatorStringConcat { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -902,10 +902,10 @@ public: static Variant::Type get_return_type() { return Variant::STRING; } }; -template <class S, class T> +template <typename S, typename T> class OperatorEvaluatorStringFormat; -template <class S> +template <typename S> class OperatorEvaluatorStringFormat<S, void> { public: _FORCE_INLINE_ static String do_mod(const String &s, bool *r_valid) { @@ -933,7 +933,7 @@ public: static Variant::Type get_return_type() { return Variant::STRING; } }; -template <class S> +template <typename S> class OperatorEvaluatorStringFormat<S, Array> { public: _FORCE_INLINE_ static String do_mod(const String &s, const Array &p_values, bool *r_valid) { @@ -958,7 +958,7 @@ public: static Variant::Type get_return_type() { return Variant::STRING; } }; -template <class S> +template <typename S> class OperatorEvaluatorStringFormat<S, Object> { public: _FORCE_INLINE_ static String do_mod(const String &s, const Object *p_object, bool *r_valid) { @@ -986,7 +986,7 @@ public: static Variant::Type get_return_type() { return Variant::STRING; } }; -template <class S, class T> +template <typename S, typename T> class OperatorEvaluatorStringFormat { public: _FORCE_INLINE_ static String do_mod(const String &s, const T &p_value, bool *r_valid) { @@ -1317,10 +1317,10 @@ public: //// -template <class Left, class Right> +template <typename Left, typename Right> class OperatorEvaluatorInStringFind; -template <class Left> +template <typename Left> class OperatorEvaluatorInStringFind<Left, String> { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -1341,7 +1341,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class Left> +template <typename Left> class OperatorEvaluatorInStringFind<Left, StringName> { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -1362,7 +1362,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A, class B> +template <typename A, typename B> class OperatorEvaluatorInArrayFind { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { @@ -1417,7 +1417,7 @@ public: static Variant::Type get_return_type() { return Variant::BOOL; } }; -template <class A> +template <typename A> class OperatorEvaluatorInDictionaryHas { public: static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) { diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 86e7654090..50f8007efa 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -30,6 +30,7 @@ #include "variant_parser.h" +#include "core/crypto/crypto_core.h" #include "core/input/input_event.h" #include "core/io/resource_loader.h" #include "core/object/script_language.h" @@ -488,11 +489,11 @@ Error VariantParser::get_token(Stream *p_stream, Token &r_token, int &line, Stri r_token.value = num.as_int(); } return OK; - } else if (is_ascii_char(cchar) || is_underscore(cchar)) { + } else if (is_ascii_alphabet_char(cchar) || is_underscore(cchar)) { StringBuffer<> id; bool first = true; - while (is_ascii_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { + while (is_ascii_alphabet_char(cchar) || is_underscore(cchar) || (!first && is_digit(cchar))) { id += cchar; cchar = p_stream->get_char(); first = false; @@ -546,7 +547,7 @@ Error VariantParser::_parse_enginecfg(Stream *p_stream, Vector<String> &strings, } } -template <class T> +template <typename T> Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, int &line, String &r_err_str) { Token token; get_token(p_stream, token, line, r_err_str); @@ -595,6 +596,82 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct, return OK; } +Error VariantParser::_parse_byte_array(Stream *p_stream, Vector<uint8_t> &r_construct, int &line, String &r_err_str) { + Token token; + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_PARENTHESIS_OPEN) { + r_err_str = "Expected '(' in constructor"; + return ERR_PARSE_ERROR; + } + + get_token(p_stream, token, line, r_err_str); + if (token.type == TK_STRING) { + // Base64 encoded array. + String base64_encoded_string = token.value; + int strlen = base64_encoded_string.length(); + CharString cstr = base64_encoded_string.ascii(); + + size_t arr_len = 0; + r_construct.resize(strlen / 4 * 3 + 1); + uint8_t *w = r_construct.ptrw(); + Error err = CryptoCore::b64_decode(&w[0], r_construct.size(), &arr_len, (unsigned char *)cstr.get_data(), strlen); + if (err) { + r_err_str = "Invalid base64-encoded string"; + return ERR_PARSE_ERROR; + } + r_construct.resize(arr_len); + + get_token(p_stream, token, line, r_err_str); + if (token.type != TK_PARENTHESIS_CLOSE) { + r_err_str = "Expected ')' in constructor"; + return ERR_PARSE_ERROR; + } + + } else if (token.type == TK_NUMBER || token.type == TK_IDENTIFIER) { + // Individual elements. + while (true) { + if (token.type != TK_NUMBER) { + bool valid = false; + if (token.type == TK_IDENTIFIER) { + double real = stor_fix(token.value); + if (real != -1) { + token.type = TK_NUMBER; + token.value = real; + valid = true; + } + } + if (!valid) { + r_err_str = "Expected number in constructor"; + return ERR_PARSE_ERROR; + } + } + + r_construct.push_back(token.value); + + get_token(p_stream, token, line, r_err_str); + + if (token.type == TK_COMMA) { + //do none + } else if (token.type == TK_PARENTHESIS_CLOSE) { + break; + } else { + r_err_str = "Expected ',' or ')' in constructor"; + return ERR_PARSE_ERROR; + } + + get_token(p_stream, token, line, r_err_str); + } + } else if (token.type == TK_PARENTHESIS_CLOSE) { + // Empty array. + return OK; + } else { + r_err_str = "Expected base64 string, or list of numbers in constructor"; + return ERR_PARSE_ERROR; + } + + return OK; +} + Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) { if (token.type == TK_CURLY_BRACKET_OPEN) { Dictionary d; @@ -1026,7 +1103,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, Ref<Resource> res; Error err = p_res_parser->ext_func(p_res_parser->userdata, p_stream, res, line, r_err_str); if (err) { - return err; + // If the file is missing, the error can be ignored. + if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN && err != ERR_FILE_CANT_OPEN) { + return err; + } } value = res; @@ -1145,7 +1225,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, value = array; } else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") { Vector<uint8_t> args; - Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str); + Error err = _parse_byte_array(p_stream, args, line, r_err_str); if (err) { return err; } @@ -1709,7 +1789,7 @@ static String rtos_fix(double p_value) { } } -Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count) { +Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count, bool p_compat) { switch (p_variant.get_type()) { case Variant::NIL: { p_store_string_func(p_store_string_ud, "null"); @@ -1929,7 +2009,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str } p_store_string_func(p_store_string_ud, "\"" + E.name + "\":"); - write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(obj->get(E.name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } } @@ -1955,9 +2035,9 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, "{\n"); for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); p_store_string_func(p_store_string_ud, ": "); - write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(dict[E->get()], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); if (E->next()) { p_store_string_func(p_store_string_ud, ",\n"); } else { @@ -2009,12 +2089,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_recursion_count++; p_store_string_func(p_store_string_ud, "["); - int len = array.size(); - for (int i = 0; i < len; i++) { - if (i > 0) { + bool first = true; + for (const Variant &var : array) { + if (first) { + first = false; + } else { p_store_string_func(p_store_string_ud, ", "); } - write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count); + write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count, p_compat); } p_store_string_func(p_store_string_ud, "]"); @@ -2028,17 +2110,20 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str case Variant::PACKED_BYTE_ARRAY: { p_store_string_func(p_store_string_ud, "PackedByteArray("); Vector<uint8_t> data = p_variant; - int len = data.size(); - const uint8_t *ptr = data.ptr(); - - for (int i = 0; i < len; i++) { - if (i > 0) { - p_store_string_func(p_store_string_ud, ", "); + if (p_compat) { + int len = data.size(); + const uint8_t *ptr = data.ptr(); + for (int i = 0; i < len; i++) { + if (i > 0) { + p_store_string_func(p_store_string_ud, ", "); + } + p_store_string_func(p_store_string_ud, itos(ptr[i])); } - - p_store_string_func(p_store_string_ud, itos(ptr[i])); + } else if (data.size() > 0) { + p_store_string_func(p_store_string_ud, "\""); + p_store_string_func(p_store_string_ud, CryptoCore::b64_encode_str(data.ptr(), data.size())); + p_store_string_func(p_store_string_ud, "\""); } - p_store_string_func(p_store_string_ud, ")"); } break; case Variant::PACKED_INT32_ARRAY: { @@ -2179,8 +2264,8 @@ static Error _write_to_str(void *ud, const String &p_string) { return OK; } -Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud) { +Error VariantWriter::write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, bool p_compat) { r_string = String(); - return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud); + return write(p_variant, _write_to_str, &r_string, p_encode_res_func, p_encode_res_ud, 0, p_compat); } diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 8505fff739..b0ac07170d 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -139,8 +139,9 @@ public: private: static const char *tk_name[TK_MAX]; - template <class T> + template <typename T> static Error _parse_construct(Stream *p_stream, Vector<T> &r_construct, int &line, String &r_err_str); + static Error _parse_byte_array(Stream *p_stream, Vector<uint8_t> &r_construct, int &line, String &r_err_str); static Error _parse_enginecfg(Stream *p_stream, Vector<String> &strings, int &line, String &r_err_str); static Error _parse_dictionary(Dictionary &object, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser = nullptr); static Error _parse_array(Array &array, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser = nullptr); @@ -160,8 +161,8 @@ public: typedef Error (*StoreStringFunc)(void *ud, const String &p_string); typedef String (*EncodeResourceFunc)(void *ud, const Ref<Resource> &p_resource); - static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0); - static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr); + static Error write(const Variant &p_variant, StoreStringFunc p_store_string_func, void *p_store_string_ud, EncodeResourceFunc p_encode_res_func, void *p_encode_res_ud, int p_recursion_count = 0, bool p_compat = true); + static Error write_to_string(const Variant &p_variant, String &r_string, EncodeResourceFunc p_encode_res_func = nullptr, void *p_encode_res_ud = nullptr, bool p_compat = true); }; #endif // VARIANT_PARSER_H diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index 05f7abf32c..9d5ed22b1a 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -30,6 +30,8 @@ #include "variant_setget.h" +#include "variant_callable.h" + struct VariantSetterGetterInfo { void (*setter)(Variant *base, const Variant *value, bool &valid); void (*getter)(const Variant *base, Variant *value); @@ -43,7 +45,7 @@ struct VariantSetterGetterInfo { static LocalVector<VariantSetterGetterInfo> variant_setters_getters[Variant::VARIANT_MAX]; static LocalVector<StringName> variant_setters_getters_names[Variant::VARIANT_MAX]; //one next to another to make it cache friendly -template <class T> +template <typename T> static void register_member(Variant::Type p_type, const StringName &p_member) { VariantSetterGetterInfo sgi; sgi.setter = T::set; @@ -264,42 +266,45 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool } Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { - Variant ret; uint32_t s = variant_setters_getters[type].size(); if (s) { for (uint32_t i = 0; i < s; i++) { if (variant_setters_getters_names[type][i] == p_member) { + Variant ret; variant_setters_getters[type][i].getter(this, &ret); r_valid = true; return ret; } } + } - r_valid = false; - - } else if (type == Variant::OBJECT) { - Object *obj = get_validated_object(); - if (!obj) { - r_valid = false; - return "Instance base is null."; - } else { - return obj->get(p_member, &r_valid); - } - } else if (type == Variant::DICTIONARY) { - const Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member); - if (v) { - r_valid = true; - - return *v; - } else { - r_valid = false; - } - - } else { - r_valid = false; + switch (type) { + case Variant::OBJECT: { + Object *obj = get_validated_object(); + if (!obj) { + r_valid = false; + return "Instance base is null."; + } else { + return obj->get(p_member, &r_valid); + } + } break; + case Variant::DICTIONARY: { + const Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member); + if (v) { + r_valid = true; + return *v; + } + } break; + default: { + if (Variant::has_builtin_method(type, p_member)) { + r_valid = true; + return Callable(memnew(VariantCallable(*this, p_member))); + } + } break; } - return ret; + r_valid = false; + return Variant(); } /**** INDEXED SETTERS AND GETTERS ****/ @@ -428,9 +433,9 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { } \ m_assign_type num; \ if (value->get_type() == Variant::INT) { \ - num = (m_assign_type)*VariantGetInternalPtr<int64_t>::get_ptr(value); \ + num = (m_assign_type) * VariantGetInternalPtr<int64_t>::get_ptr(value); \ } else if (value->get_type() == Variant::FLOAT) { \ - num = (m_assign_type)*VariantGetInternalPtr<double>::get_ptr(value); \ + num = (m_assign_type) * VariantGetInternalPtr<double>::get_ptr(value); \ } else { \ *oob = false; \ *valid = false; \ @@ -490,9 +495,9 @@ Variant Variant::get_named(const StringName &p_member, bool &r_valid) const { } \ m_assign_type num; \ if (value->get_type() == Variant::INT) { \ - num = (m_assign_type)*VariantGetInternalPtr<int64_t>::get_ptr(value); \ + num = (m_assign_type) * VariantGetInternalPtr<int64_t>::get_ptr(value); \ } else if (value->get_type() == Variant::FLOAT) { \ - num = (m_assign_type)*VariantGetInternalPtr<double>::get_ptr(value); \ + num = (m_assign_type) * VariantGetInternalPtr<double>::get_ptr(value); \ } else { \ *oob = false; \ *valid = false; \ @@ -868,7 +873,7 @@ struct VariantIndexedSetterGetterInfo { static VariantIndexedSetterGetterInfo variant_indexed_setters_getters[Variant::VARIANT_MAX]; -template <class T> +template <typename T> static void register_indexed_member(Variant::Type p_type) { VariantIndexedSetterGetterInfo &sgi = variant_indexed_setters_getters[p_type]; @@ -1089,7 +1094,7 @@ struct VariantKeyedSetterGetterInfo { static VariantKeyedSetterGetterInfo variant_keyed_setters_getters[Variant::VARIANT_MAX]; -template <class T> +template <typename T> static void register_keyed_member(Variant::Type p_type) { VariantKeyedSetterGetterInfo &sgi = variant_keyed_setters_getters[p_type]; @@ -1166,30 +1171,48 @@ bool Variant::has_key(const Variant &p_key, bool &r_valid) const { } } -void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) { +void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid, VariantSetError *err_code) { + if (err_code) { + *err_code = VariantSetError::SET_OK; + } if (type == DICTIONARY || type == OBJECT) { bool valid; set_keyed(p_index, p_value, valid); if (r_valid) { *r_valid = valid; + if (!valid && err_code) { + *err_code = VariantSetError::SET_KEYED_ERR; + } } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { set_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), p_value, valid); + if (!valid && err_code) { + *err_code = VariantSetError::SET_NAMED_ERR; + } } else if (p_index.get_type() == INT) { bool obb; set_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantSetError::SET_INDEXED_ERR; + } } } else if (p_index.get_type() == STRING) { // less efficient version of named set_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), p_value, valid); + if (!valid && err_code) { + *err_code = VariantSetError::SET_NAMED_ERR; + } } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; set_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), p_value, valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantSetError::SET_INDEXED_ERR; + } } } if (r_valid) { @@ -1198,31 +1221,49 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) } } -Variant Variant::get(const Variant &p_index, bool *r_valid) const { +Variant Variant::get(const Variant &p_index, bool *r_valid, VariantGetError *err_code) const { + if (err_code) { + *err_code = VariantGetError::GET_OK; + } Variant ret; if (type == DICTIONARY || type == OBJECT) { bool valid; ret = get_keyed(p_index, valid); if (r_valid) { *r_valid = valid; + if (!valid && err_code) { + *err_code = VariantGetError::GET_KEYED_ERR; + } } } else { bool valid = false; if (p_index.get_type() == STRING_NAME) { ret = get_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), valid); + if (!valid && err_code) { + *err_code = VariantGetError::GET_NAMED_ERR; + } } else if (p_index.get_type() == INT) { bool obb; ret = get_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantGetError::GET_INDEXED_ERR; + } } } else if (p_index.get_type() == STRING) { // less efficient version of named ret = get_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), valid); + if (!valid && err_code) { + *err_code = VariantGetError::GET_NAMED_ERR; + } } else if (p_index.get_type() == FLOAT) { // less efficient version of indexed bool obb; ret = get_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), valid, obb); if (obb) { valid = false; + if (err_code) { + *err_code = VariantGetError::GET_INDEXED_ERR; + } } } if (r_valid) { diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index cc48394b64..916ba7aa2f 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -109,7 +109,7 @@ int64_t VariantUtilityFunctions::posmod(int64_t b, int64_t r) { return Math::posmod(b, r); } -Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error) { +Variant VariantUtilityFunctions::floor(const Variant &x, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_OK; switch (x.get_type()) { case Variant::INT: { @@ -153,7 +153,7 @@ int64_t VariantUtilityFunctions::floori(double x) { return int64_t(Math::floor(x)); } -Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) { +Variant VariantUtilityFunctions::ceil(const Variant &x, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_OK; switch (x.get_type()) { case Variant::INT: { @@ -197,7 +197,7 @@ int64_t VariantUtilityFunctions::ceili(double x) { return int64_t(Math::ceil(x)); } -Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error) { +Variant VariantUtilityFunctions::round(const Variant &x, Callable::CallError &r_error) { r_error.error = Callable::CallError::CALL_OK; switch (x.get_type()) { case Variant::INT: { @@ -1235,7 +1235,7 @@ bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) { #define VCALL p_func(VariantCaster<P>::cast(*p_args[Is])...) #endif -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> static _FORCE_INLINE_ void call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; VCALLR; @@ -1243,51 +1243,51 @@ static _FORCE_INLINE_ void call_helperpr(R (*p_func)(P...), Variant *ret, const (void)r_error; } -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> static _FORCE_INLINE_ void validated_call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, IndexSequence<Is...>) { *ret = p_func(VariantCaster<P>::cast(*p_args[Is])...); (void)p_args; } -template <class R, class... P, size_t... Is> +template <typename R, typename... P, size_t... Is> static _FORCE_INLINE_ void ptr_call_helperpr(R (*p_func)(P...), void *ret, const void **p_args, IndexSequence<Is...>) { PtrToArg<R>::encode(p_func(PtrToArg<P>::convert(p_args[Is])...), ret); (void)p_args; } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error) { call_helperpr(p_func, ret, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void validated_call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args) { validated_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ void ptr_call_helperr(R (*p_func)(P...), void *ret, const void **p_args) { ptr_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ int get_arg_count_helperr(R (*p_func)(P...)) { return sizeof...(P); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ Variant::Type get_arg_type_helperr(R (*p_func)(P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class R, class... P> +template <typename R, typename... P> static _FORCE_INLINE_ Variant::Type get_ret_type_helperr(R (*p_func)(P...)) { return GetTypeInfo<R>::VARIANT_TYPE; } // WITHOUT RET -template <class... P, size_t... Is> +template <typename... P, size_t... Is> static _FORCE_INLINE_ void call_helperp(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) { r_error.error = Callable::CallError::CALL_OK; VCALL; @@ -1295,44 +1295,44 @@ static _FORCE_INLINE_ void call_helperp(void (*p_func)(P...), const Variant **p_ (void)r_error; } -template <class... P, size_t... Is> +template <typename... P, size_t... Is> static _FORCE_INLINE_ void validated_call_helperp(void (*p_func)(P...), const Variant **p_args, IndexSequence<Is...>) { p_func(VariantCaster<P>::cast(*p_args[Is])...); (void)p_args; } -template <class... P, size_t... Is> +template <typename... P, size_t... Is> static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p_args, IndexSequence<Is...>) { p_func(PtrToArg<P>::convert(p_args[Is])...); (void)p_args; } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error) { call_helperp(p_func, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void validated_call_helper(void (*p_func)(P...), const Variant **p_args) { validated_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ void ptr_call_helper(void (*p_func)(P...), const void **p_args) { ptr_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ int get_arg_count_helper(void (*p_func)(P...)) { return sizeof...(P); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ Variant::Type get_arg_type_helper(void (*p_func)(P...), int p_arg) { return call_get_argument_type<P...>(p_arg); } -template <class... P> +template <typename... P> static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) { return Variant::NIL; } @@ -1645,7 +1645,7 @@ struct VariantUtilityFunctionInfo { static OAHashMap<StringName, VariantUtilityFunctionInfo> utility_function_table; static List<StringName> utility_function_name_table; -template <class T> +template <typename T> static void register_utility_function(const String &p_name, const Vector<String> &argnames) { String name = p_name; if (name.begins_with("_")) { diff --git a/core/variant/variant_utility.h b/core/variant/variant_utility.h index a56c84a8e9..75cde4942b 100644 --- a/core/variant/variant_utility.h +++ b/core/variant/variant_utility.h @@ -52,13 +52,13 @@ struct VariantUtilityFunctions { static double fmod(double b, double r); static double fposmod(double b, double r); static int64_t posmod(int64_t b, int64_t r); - static Variant floor(Variant x, Callable::CallError &r_error); + static Variant floor(const Variant &x, Callable::CallError &r_error); static double floorf(double x); static int64_t floori(double x); - static Variant ceil(Variant x, Callable::CallError &r_error); + static Variant ceil(const Variant &x, Callable::CallError &r_error); static double ceilf(double x); static int64_t ceili(double x); - static Variant round(Variant x, Callable::CallError &r_error); + static Variant round(const Variant &x, Callable::CallError &r_error); static double roundf(double x); static int64_t roundi(double x); static Variant abs(const Variant &x, Callable::CallError &r_error); diff --git a/core/version.h b/core/version.h index 5ddb09284e..18a97cadf0 100644 --- a/core/version.h +++ b/core/version.h @@ -33,6 +33,14 @@ #include "core/version_generated.gen.h" +#include <stdint.h> + +// Copied from typedefs.h to stay lean. +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + // Godot versions are of the form <major>.<minor> for the initial release, // and then <major>.<minor>.<patch> for subsequent bugfix releases where <patch> != 0 // That's arbitrary, but we find it pretty and it's the current policy. @@ -71,4 +79,8 @@ // Git commit hash, generated at build time in `core/version_hash.gen.cpp`. extern const char *const VERSION_HASH; +// Git commit date UNIX timestamp (in seconds), generated at build time in `core/version_hash.gen.cpp`. +// Set to 0 if unknown. +extern const uint64_t VERSION_TIMESTAMP; + #endif // VERSION_H |