diff options
Diffstat (limited to 'core')
115 files changed, 3281 insertions, 1264 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp index ad31966a65..495670bc88 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -37,23 +37,23 @@ #include "core/version.h" #include "core/version_hash.gen.h" -void Engine::set_iterations_per_second(int p_ips) { +void Engine::set_physics_ticks_per_second(int p_ips) { ERR_FAIL_COND_MSG(p_ips <= 0, "Engine iterations per second must be greater than 0."); ips = p_ips; } -int Engine::get_iterations_per_second() const { +int Engine::get_physics_ticks_per_second() const { return ips; } -void Engine::set_physics_jitter_fix(float p_threshold) { +void Engine::set_physics_jitter_fix(double p_threshold) { if (p_threshold < 0) { p_threshold = 0; } physics_jitter_fix = p_threshold; } -float Engine::get_physics_jitter_fix() const { +double Engine::get_physics_jitter_fix() const { return physics_jitter_fix; } @@ -77,11 +77,11 @@ uint32_t Engine::get_frame_delay() const { return _frame_delay; } -void Engine::set_time_scale(float p_scale) { +void Engine::set_time_scale(double p_scale) { _time_scale = p_scale; } -float Engine::get_time_scale() const { +double Engine::get_time_scale() const { return _time_scale; } @@ -214,8 +214,8 @@ bool Engine::has_singleton(const String &p_name) const { } void Engine::get_singletons(List<Singleton> *p_singletons) { - for (List<Singleton>::Element *E = singletons.front(); E; E = E->next()) { - p_singletons->push_back(E->get()); + for (const Singleton &E : singletons) { + p_singletons->push_back(E); } } diff --git a/core/config/engine.h b/core/config/engine.h index 970cfb03e8..e6b5df2d5a 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -51,15 +51,15 @@ private: uint64_t frames_drawn = 0; uint32_t _frame_delay = 0; uint64_t _frame_ticks = 0; - float _process_step = 0; + double _process_step = 0; int ips = 60; - float physics_jitter_fix = 0.5; - float _fps = 1; + double physics_jitter_fix = 0.5; + double _fps = 1; int _target_fps = 0; - float _time_scale = 1.0; + double _time_scale = 1.0; uint64_t _physics_frames = 0; - float _physics_interpolation_fraction = 0.0f; + double _physics_interpolation_fraction = 0.0f; bool abort_on_gpu_errors = false; bool use_validation_layers = false; @@ -78,16 +78,16 @@ private: public: static Engine *get_singleton(); - virtual void set_iterations_per_second(int p_ips); - virtual int get_iterations_per_second() const; + virtual void set_physics_ticks_per_second(int p_ips); + virtual int get_physics_ticks_per_second() const; - void set_physics_jitter_fix(float p_threshold); - float get_physics_jitter_fix() const; + void set_physics_jitter_fix(double p_threshold); + double get_physics_jitter_fix() const; virtual void set_target_fps(int p_fps); virtual int get_target_fps() const; - virtual float get_frames_per_second() const { return _fps; } + virtual double get_frames_per_second() const { return _fps; } uint64_t get_frames_drawn(); @@ -95,11 +95,11 @@ public: uint64_t get_process_frames() const { return _process_frames; } bool is_in_physics_frame() const { return _in_physics; } uint64_t get_frame_ticks() const { return _frame_ticks; } - float get_process_step() const { return _process_step; } - float get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } + double get_process_step() const { return _process_step; } + double get_physics_interpolation_fraction() const { return _physics_interpolation_fraction; } - void set_time_scale(float p_scale); - float get_time_scale() const; + void set_time_scale(double p_scale); + double get_time_scale() const; void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 29f53482fa..c5e6c6d685 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -30,7 +30,7 @@ #include "project_settings.h" -#include "core/core_bind.h" +#include "core/core_bind.h" // For Compression enum. #include "core/core_string_names.h" #include "core/input/input_map.h" #include "core/io/dir_access.h" @@ -42,8 +42,6 @@ #include "core/os/os.h" #include "core/variant/variant_parser.h" -#include <zlib.h> - ProjectSettings *ProjectSettings::singleton = nullptr; ProjectSettings *ProjectSettings::get_singleton() { @@ -702,9 +700,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str int count = 0; for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - count++; - } + count += E->get().size(); } if (p_custom_features != String()) { @@ -736,8 +732,7 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const Map<Str } for (Map<String, List<String>>::Element *E = props.front(); E; E = E->next()) { - for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - String key = F->get(); + for (String &key : E->get()) { if (E->key() != "") { key = E->key() + "/" + key; } @@ -805,8 +800,8 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin if (E->key() != "") { file->store_string("[" + E->key() + "]\n\n"); } - for (List<String>::Element *F = E->get().front(); F; F = F->next()) { - String key = F->get(); + for (const String &F : E->get()) { + String key = F; if (E->key() != "") { key = E->key() + "/" + key; } @@ -819,7 +814,7 @@ Error ProjectSettings::_save_settings_text(const String &p_file, const Map<Strin String vstr; VariantWriter::write_to_string(value, vstr); - file->store_string(F->get().property_name_encode() + "=" + vstr + "\n"); + file->store_string(F.property_name_encode() + "=" + vstr + "\n"); } } @@ -933,11 +928,11 @@ Vector<String> ProjectSettings::get_optimizer_presets() const { ProjectSettings::get_singleton()->get_property_list(&pi); Vector<String> names; - for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - if (!E->get().name.begins_with("optimizer_presets/")) { + for (const PropertyInfo &E : pi) { + if (!E.name.begins_with("optimizer_presets/")) { continue; } - names.push_back(E->get().name.get_slicec('/', 1)); + names.push_back(E.name.get_slicec('/', 1)); } names.sort(); @@ -1114,7 +1109,8 @@ ProjectSettings::ProjectSettings() { // Keep the enum values in sync with the `DisplayServer::ScreenOrientation` enum. custom_prop_info["display/window/handheld/orientation"] = PropertyInfo(Variant::INT, "display/window/handheld/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait,Reverse Landscape,Reverse Portrait,Sensor Landscape,Sensor Portrait,Sensor"); - custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::STRING, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); + // Keep the enum values in sync with the `DisplayServer::VSyncMode` enum. + custom_prop_info["display/window/vsync/vsync_mode"] = PropertyInfo(Variant::INT, "display/window/vsync/vsync_mode", PROPERTY_HINT_ENUM, "Disabled,Enabled,Adaptive,Mailbox"); custom_prop_info["rendering/driver/threads/thread_model"] = PropertyInfo(Variant::INT, "rendering/driver/threads/thread_model", PROPERTY_HINT_ENUM, "Single-Unsafe,Single-Safe,Multi-Threaded"); GLOBAL_DEF("physics/2d/run_on_thread", false); GLOBAL_DEF("physics/3d/run_on_thread", false); diff --git a/core/core_bind.cpp b/core/core_bind.cpp index a3349444c4..f3f51e57ee 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -75,8 +75,8 @@ Vector<String> _ResourceLoader::get_recognized_extensions_for_type(const String List<String> exts; ResourceLoader::get_recognized_extensions_for_type(p_type, &exts); Vector<String> ret; - for (List<String>::Element *E = exts.front(); E; E = E->next()) { - ret.push_back(E->get()); + for (const String &E : exts) { + ret.push_back(E); } return ret; @@ -91,8 +91,8 @@ PackedStringArray _ResourceLoader::get_dependencies(const String &p_path) { ResourceLoader::get_dependencies(p_path, &deps); PackedStringArray ret; - for (List<String>::Element *E = deps.front(); E; E = E->next()) { - ret.push_back(E->get()); + for (const String &E : deps) { + ret.push_back(E); } return ret; @@ -107,6 +107,10 @@ bool _ResourceLoader::exists(const String &p_path, const String &p_type_hint) { return ResourceLoader::exists(p_path, p_type_hint); } +ResourceUID::ID _ResourceLoader::get_resource_uid(const String &p_path) { + return ResourceLoader::get_resource_uid(p_path); +} + void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("load_threaded_request", "path", "type_hint", "use_sub_threads"), &_ResourceLoader::load_threaded_request, DEFVAL(""), DEFVAL(false)); ClassDB::bind_method(D_METHOD("load_threaded_get_status", "path", "progress"), &_ResourceLoader::load_threaded_get_status, DEFVAL(Array())); @@ -118,6 +122,7 @@ void _ResourceLoader::_bind_methods() { ClassDB::bind_method(D_METHOD("get_dependencies", "path"), &_ResourceLoader::get_dependencies); ClassDB::bind_method(D_METHOD("has_cached", "path"), &_ResourceLoader::has_cached); ClassDB::bind_method(D_METHOD("exists", "path", "type_hint"), &_ResourceLoader::exists, DEFVAL("")); + ClassDB::bind_method(D_METHOD("get_resource_uid", "path"), &_ResourceLoader::get_resource_uid); BIND_ENUM_CONSTANT(THREAD_LOAD_INVALID_RESOURCE); BIND_ENUM_CONSTANT(THREAD_LOAD_IN_PROGRESS); @@ -141,8 +146,8 @@ Vector<String> _ResourceSaver::get_recognized_extensions(const RES &p_resource) List<String> exts; ResourceSaver::get_recognized_extensions(p_resource, &exts); Vector<String> ret; - for (List<String>::Element *E = exts.front(); E; E = E->next()) { - ret.push_back(E->get()); + for (const String &E : exts) { + ret.push_back(E); } return ret; } @@ -196,6 +201,10 @@ int _OS::get_low_processor_usage_mode_sleep_usec() const { return OS::get_singleton()->get_low_processor_usage_mode_sleep_usec(); } +void _OS::alert(const String &p_alert, const String &p_title) { + OS::get_singleton()->alert(p_alert, p_title); +} + String _OS::get_executable_path() const { return OS::get_singleton()->get_executable_path(); } @@ -264,8 +273,8 @@ String _OS::get_name() const { Vector<String> _OS::get_cmdline_args() { List<String> cmdline = OS::get_singleton()->get_cmdline_args(); Vector<String> cmdlinev; - for (List<String>::Element *E = cmdline.front(); E; E = E->next()) { - cmdlinev.push_back(E->get()); + for (const String &E : cmdline) { + cmdlinev.push_back(E); } return cmdlinev; @@ -351,20 +360,20 @@ void _OS::print_all_textures_by_size() { List<Ref<Resource>> rsrc; ResourceCache::get_cached_resources(&rsrc); - for (List<Ref<Resource>>::Element *E = rsrc.front(); E; E = E->next()) { - if (!E->get()->is_class("ImageTexture")) { + for (Ref<Resource> &res : rsrc) { + if (!res->is_class("Texture")) { continue; } - Size2 size = E->get()->call("get_size"); - int fmt = E->get()->call("get_format"); + Size2 size = res->call("get_size"); + int fmt = res->call("get_format"); _OSCoreBindImg img; img.size = size; img.fmt = fmt; - img.path = E->get()->get_path(); + img.path = res->get_path(); img.vram = Image::get_image_data_size(img.size.width, img.size.height, Image::Format(img.fmt)); - img.id = E->get()->get_instance_id(); + img.id = res->get_instance_id(); total += img.vram; imgs.push_back(img); } @@ -372,20 +381,34 @@ void _OS::print_all_textures_by_size() { imgs.sort(); - for (List<_OSCoreBindImg>::Element *E = imgs.front(); E; E = E->next()) { - total -= E->get().vram; + if (imgs.size() == 0) { + print_line("No textures seem used in this project."); + } else { + print_line("Textures currently in use, sorted by VRAM usage:\n" + "Path - VRAM usage (Dimensions)"); } + + for (const _OSCoreBindImg &img : imgs) { + print_line(vformat("%s - %s %s", + img.path, + String::humanize_size(img.vram), + img.size)); + } + + print_line(vformat("Total VRAM usage: %s.", String::humanize_size(total))); } void _OS::print_resources_by_type(const Vector<String> &p_types) { - Map<String, int> type_count; + ERR_FAIL_COND_MSG(p_types.size() == 0, + "At least one type should be provided to print resources by type."); + print_line(vformat("Resources currently in use for the following types: %s", p_types)); + + Map<String, int> type_count; List<Ref<Resource>> resources; ResourceCache::get_cached_resources(&resources); - for (List<Ref<Resource>>::Element *E = resources.front(); E; E = E->next()) { - Ref<Resource> r = E->get(); - + for (const Ref<Resource> &r : resources) { bool found = false; for (int i = 0; i < p_types.size(); i++) { @@ -402,6 +425,18 @@ void _OS::print_resources_by_type(const Vector<String> &p_types) { } type_count[r->get_class()]++; + + print_line(vformat("%s: %s", r->get_class(), r->get_path())); + + List<StringName> metas; + r->get_meta_list(&metas); + for (const StringName &meta : metas) { + print_line(vformat(" %s: %s", meta, r->get_meta(meta))); + } + } + + for (const KeyValue<String, int> &E : type_count) { + print_line(vformat("%s count: %d", E.key, E.value)); } } @@ -487,6 +522,8 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("open_midi_inputs"), &_OS::open_midi_inputs); ClassDB::bind_method(D_METHOD("close_midi_inputs"), &_OS::close_midi_inputs); + ClassDB::bind_method(D_METHOD("alert", "text", "title"), &_OS::alert, DEFVAL("Alert!")); + ClassDB::bind_method(D_METHOD("set_low_processor_usage_mode", "enable"), &_OS::set_low_processor_usage_mode); ClassDB::bind_method(D_METHOD("is_in_low_processor_usage_mode"), &_OS::is_in_low_processor_usage_mode); @@ -1729,10 +1766,40 @@ void _Thread::_start_func(void *ud) { memdelete(tud); Callable::CallError ce; const Variant *arg[1] = { &t->userdata }; + int argc = 0; + if (arg[0]->get_type() != Variant::NIL) { + // Just pass to the target function whatever came as user data + argc = 1; + } else { + // There are two cases of null user data: + // a) The target function has zero parameters and the caller is just honoring that. + // b) The target function has at least one parameter with no default and the caller is + // leveraging the fact that user data defaults to null in Thread.start(). + // We care about the case of more than one parameter because, even if a thread + // function can have one at most, out mindset here is to do our best with the + // only/first one and let the call handle any other error conditions, like too + // much arguments. + // We must check if we are in case b). + int target_param_count = 0; + int target_default_arg_count = 0; + Ref<Script> script = t->target_instance->get_script(); + if (script.is_valid()) { + MethodInfo mi = script->get_method_info(t->target_method); + target_param_count = mi.arguments.size(); + target_default_arg_count = mi.default_arguments.size(); + } else { + MethodBind *method = ClassDB::get_method(t->target_instance->get_class_name(), t->target_method); + target_param_count = method->get_argument_count(); + target_default_arg_count = method->get_default_argument_count(); + } + if (target_param_count >= 1 && target_default_arg_count < target_param_count) { + argc = 1; + } + } Thread::set_name(t->target_method); - t->ret = t->target_instance->call(t->target_method, arg, 1, ce); + t->ret = t->target_instance->call(t->target_method, arg, argc, ce); if (ce.error != Callable::CallError::CALL_OK) { String reason; switch (ce.error) { @@ -1817,8 +1884,8 @@ PackedStringArray _ClassDB::get_class_list() const { PackedStringArray ret; ret.resize(classes.size()); int idx = 0; - for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { - ret.set(idx++, E->get()); + for (const StringName &E : classes) { + ret.set(idx++, E); } return ret; @@ -1831,8 +1898,8 @@ PackedStringArray _ClassDB::get_inheriters_from_class(const StringName &p_class) PackedStringArray ret; ret.resize(classes.size()); int idx = 0; - for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { - ret.set(idx++, E->get()); + for (const StringName &E : classes) { + ret.set(idx++, E); } return ret; @@ -1886,8 +1953,8 @@ Array _ClassDB::get_signal_list(StringName p_class, bool p_no_inheritance) const ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); Array ret; - for (List<MethodInfo>::Element *E = signals.front(); E; E = E->next()) { - ret.push_back(E->get().operator Dictionary()); + for (const MethodInfo &E : signals) { + ret.push_back(E.operator Dictionary()); } return ret; @@ -1897,8 +1964,8 @@ Array _ClassDB::get_property_list(StringName p_class, bool p_no_inheritance) con List<PropertyInfo> plist; ClassDB::get_property_list(p_class, &plist, p_no_inheritance); Array ret; - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - ret.push_back(E->get().operator Dictionary()); + for (const PropertyInfo &E : plist) { + ret.push_back(E.operator Dictionary()); } return ret; @@ -1930,12 +1997,12 @@ Array _ClassDB::get_method_list(StringName p_class, bool p_no_inheritance) const ClassDB::get_method_list(p_class, &methods, p_no_inheritance); Array ret; - for (List<MethodInfo>::Element *E = methods.front(); E; E = E->next()) { + for (const MethodInfo &E : methods) { #ifdef DEBUG_METHODS_ENABLED - ret.push_back(E->get().operator Dictionary()); + ret.push_back(E.operator Dictionary()); #else Dictionary dict; - dict["name"] = E->get().name; + dict["name"] = E.name; ret.push_back(dict); #endif } @@ -1950,8 +2017,8 @@ PackedStringArray _ClassDB::get_integer_constant_list(const StringName &p_class, PackedStringArray ret; ret.resize(constants.size()); int idx = 0; - for (List<String>::Element *E = constants.front(); E; E = E->next()) { - ret.set(idx++, E->get()); + for (const String &E : constants) { + ret.set(idx++, E); } return ret; @@ -2010,23 +2077,23 @@ void _ClassDB::_bind_methods() { ////// _Engine ////// -void _Engine::set_iterations_per_second(int p_ips) { - Engine::get_singleton()->set_iterations_per_second(p_ips); +void _Engine::set_physics_ticks_per_second(int p_ips) { + Engine::get_singleton()->set_physics_ticks_per_second(p_ips); } -int _Engine::get_iterations_per_second() const { - return Engine::get_singleton()->get_iterations_per_second(); +int _Engine::get_physics_ticks_per_second() const { + return Engine::get_singleton()->get_physics_ticks_per_second(); } -void _Engine::set_physics_jitter_fix(float p_threshold) { +void _Engine::set_physics_jitter_fix(double p_threshold) { Engine::get_singleton()->set_physics_jitter_fix(p_threshold); } -float _Engine::get_physics_jitter_fix() const { +double _Engine::get_physics_jitter_fix() const { return Engine::get_singleton()->get_physics_jitter_fix(); } -float _Engine::get_physics_interpolation_fraction() const { +double _Engine::get_physics_interpolation_fraction() const { return Engine::get_singleton()->get_physics_interpolation_fraction(); } @@ -2038,7 +2105,7 @@ int _Engine::get_target_fps() const { return Engine::get_singleton()->get_target_fps(); } -float _Engine::get_frames_per_second() const { +double _Engine::get_frames_per_second() const { return Engine::get_singleton()->get_frames_per_second(); } @@ -2050,11 +2117,11 @@ uint64_t _Engine::get_process_frames() const { return Engine::get_singleton()->get_process_frames(); } -void _Engine::set_time_scale(float p_scale) { +void _Engine::set_time_scale(double p_scale) { Engine::get_singleton()->set_time_scale(p_scale); } -float _Engine::get_time_scale() { +double _Engine::get_time_scale() { return Engine::get_singleton()->get_time_scale(); } @@ -2120,8 +2187,8 @@ bool _Engine::is_printing_error_messages() const { } void _Engine::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_iterations_per_second", "iterations_per_second"), &_Engine::set_iterations_per_second); - ClassDB::bind_method(D_METHOD("get_iterations_per_second"), &_Engine::get_iterations_per_second); + 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); ClassDB::bind_method(D_METHOD("set_physics_jitter_fix", "physics_jitter_fix"), &_Engine::set_physics_jitter_fix); ClassDB::bind_method(D_METHOD("get_physics_jitter_fix"), &_Engine::get_physics_jitter_fix); ClassDB::bind_method(D_METHOD("get_physics_interpolation_fraction"), &_Engine::get_physics_interpolation_fraction); @@ -2158,7 +2225,7 @@ void _Engine::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_hint"), "set_editor_hint", "is_editor_hint"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "iterations_per_second"), "set_iterations_per_second", "get_iterations_per_second"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "target_fps"), "set_target_fps", "get_target_fps"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "time_scale"), "set_time_scale", "get_time_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "physics_jitter_fix"), "set_physics_jitter_fix", "get_physics_jitter_fix"); @@ -2275,7 +2342,7 @@ void _EngineDebugger::call_add(void *p_user, const Array &p_data) { ERR_FAIL_COND_MSG(err.error != Callable::CallError::CALL_OK, "Error calling 'add' to callable: " + Variant::get_callable_error_text(add, args, 1, err)); } -void _EngineDebugger::call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { +void _EngineDebugger::call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { Callable &tick = ((ProfilerCallable *)p_user)->callable_tick; if (tick.is_null()) { return; diff --git a/core/core_bind.h b/core/core_bind.h index 673dbe32c4..9060aff340 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -58,9 +58,9 @@ 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 and subresource use path cache, but replace existing loaded resources when available with information from disk + 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. }; static _ResourceLoader *get_singleton() { return singleton; } @@ -75,6 +75,7 @@ public: PackedStringArray get_dependencies(const String &p_path); bool has_cached(const String &p_path); bool exists(const String &p_path, const String &p_type_hint = ""); + ResourceUID::ID get_resource_uid(const String &p_path); _ResourceLoader() { singleton = this; } }; @@ -162,6 +163,8 @@ public: void set_low_processor_usage_mode_sleep_usec(int p_usec); int get_low_processor_usage_mode_sleep_usec() const; + void alert(const String &p_alert, const String &p_title = "ALERT!"); + String get_executable_path() const; int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false); int create_process(const String &p_path, const Vector<String> &p_arguments); @@ -620,24 +623,24 @@ protected: public: static _Engine *get_singleton() { return singleton; } - void set_iterations_per_second(int p_ips); - int get_iterations_per_second() const; + void set_physics_ticks_per_second(int p_ips); + int get_physics_ticks_per_second() const; - void set_physics_jitter_fix(float p_threshold); - float get_physics_jitter_fix() const; - float get_physics_interpolation_fraction() const; + void set_physics_jitter_fix(double p_threshold); + double get_physics_jitter_fix() const; + double get_physics_interpolation_fraction() const; void set_target_fps(int p_fps); int get_target_fps() const; - float get_frames_per_second() const; + double get_frames_per_second() const; uint64_t get_physics_frames() const; uint64_t get_process_frames() const; int get_frames_drawn(); - void set_time_scale(float p_scale); - float get_time_scale(); + void set_time_scale(double p_scale); + double get_time_scale(); MainLoop *get_main_loop() const; @@ -709,7 +712,7 @@ public: static void call_toggle(void *p_user, bool p_enable, const Array &p_opts); static void call_add(void *p_user, const Array &p_data); - static void call_tick(void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time); + static void call_tick(void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time); static Error call_capture(void *p_user, const String &p_cmd, const Array &p_data, bool &r_captured); _EngineDebugger() { singleton = this; } diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 0aad21276a..5791f79053 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -104,9 +104,6 @@ static Vector<_CoreConstant> _global_constants; #endif -VARIANT_ENUM_CAST(Key); -VARIANT_ENUM_CAST(KeyModifierMask); - void register_global_constants() { BIND_CORE_ENUM_CONSTANT(SIDE_LEFT); BIND_CORE_ENUM_CONSTANT(SIDE_TOP); @@ -133,6 +130,19 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(VALIGN_CENTER); BIND_CORE_ENUM_CONSTANT(VALIGN_BOTTOM); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP_TO); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER_TO); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM_TO); + + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_TOP); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_CENTER); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BASELINE); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TO_BOTTOM); + + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_TOP); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_CENTER); + BIND_CORE_ENUM_CONSTANT(INLINE_ALIGN_BOTTOM); + // huge list of keys BIND_CORE_CONSTANT(SPKEY); @@ -509,6 +519,7 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NONE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_RANGE); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ENUM_SUGGESTION); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_EXP_EASING); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_LENGTH); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_KEY_ACCEL); @@ -531,8 +542,26 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_ID); BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TYPE_STRING); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_VARIANT_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_BASE_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_INSTANCE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_METHOD_OF_SCRIPT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_VARIANT_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_BASE_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_INSTANCE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PROPERTY_OF_SCRIPT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_OBJECT_TOO_BIG); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_PATH_VALID_TYPES); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_SAVE_FILE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_INT_IS_OBJECTID); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_ARRAY_TYPE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX); + + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NONE); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORAGE); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NETWORK); @@ -548,6 +577,22 @@ void register_global_constants() { BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESTART_IF_CHANGED); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_VARIABLE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_STORE_IF_NULL); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_ANIMATE_AS_TRIGGER); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_CLASS_IS_ENUM); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NIL_IS_VARIANT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_INTERNAL); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_HIGH_END_GFX); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_KEYING_INCREMENTS); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFERRED_SET_RESOURCE); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_EDITOR_BASIC_SETTING); + BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_DEFAULT_INTL); BIND_CORE_ENUM_CONSTANT(PROPERTY_USAGE_NOEDITOR); diff --git a/core/debugger/debugger_marshalls.cpp b/core/debugger/debugger_marshalls.cpp index 26f82c2658..2353a6ebf8 100644 --- a/core/debugger/debugger_marshalls.cpp +++ b/core/debugger/debugger_marshalls.cpp @@ -40,11 +40,11 @@ Array DebuggerMarshalls::ResourceUsage::serialize() { Array arr; arr.push_back(infos.size() * 4); - for (List<ResourceInfo>::Element *E = infos.front(); E; E = E->next()) { - arr.push_back(E->get().path); - arr.push_back(E->get().format); - arr.push_back(E->get().type); - arr.push_back(E->get().vram); + for (const ResourceInfo &E : infos) { + arr.push_back(E.path); + arr.push_back(E.format); + arr.push_back(E.type); + arr.push_back(E.vram); } return arr; } diff --git a/core/debugger/debugger_marshalls.h b/core/debugger/debugger_marshalls.h index 3e8c34d84b..98ad2b98d1 100644 --- a/core/debugger/debugger_marshalls.h +++ b/core/debugger/debugger_marshalls.h @@ -83,14 +83,14 @@ struct DebuggerMarshalls { StringName name; int sig_id = -1; int call_count = 0; - float self_time = 0; - float total_time = 0; + double self_time = 0; + double total_time = 0; }; // Servers profiler struct ServerFunctionInfo { StringName name; - float time = 0; + double time = 0; }; struct ServerInfo { @@ -100,11 +100,11 @@ struct DebuggerMarshalls { struct ServersProfilerFrame { int frame_number = 0; - float frame_time = 0; - float idle_time = 0; - float physics_time = 0; - float physics_frame_time = 0; - float script_time = 0; + double frame_time = 0; + double idle_time = 0; + double physics_time = 0; + double physics_frame_time = 0; + double script_time = 0; List<ServerInfo> servers; Vector<ScriptFunctionInfo> script_functions; diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp index e5dba029c9..a522b1310f 100644 --- a/core/debugger/engine_debugger.cpp +++ b/core/debugger/engine_debugger.cpp @@ -117,7 +117,7 @@ void EngineDebugger::line_poll() { poll_every++; } -void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time) { +void EngineDebugger::iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time) { frame_time = USEC_TO_SEC(p_frame_ticks); process_time = USEC_TO_SEC(p_process_ticks); physics_time = USEC_TO_SEC(p_physics_ticks); diff --git a/core/debugger/engine_debugger.h b/core/debugger/engine_debugger.h index c6daea6e2f..22c6ef943e 100644 --- a/core/debugger/engine_debugger.h +++ b/core/debugger/engine_debugger.h @@ -44,7 +44,7 @@ class ScriptDebugger; class EngineDebugger { public: typedef void (*ProfilingToggle)(void *p_user, bool p_enable, const Array &p_opts); - typedef void (*ProfilingTick)(void *p_user, float p_frame_time, float p_process_time, float p_physics_time, float p_physics_frame_time); + typedef void (*ProfilingTick)(void *p_user, double p_frame_time, double p_process_time, double p_physics_time, double p_physics_frame_time); typedef void (*ProfilingAdd)(void *p_user, const Array &p_arr); typedef Error (*CaptureFunc)(void *p_user, const String &p_msg, const Array &p_args, bool &r_captured); @@ -85,10 +85,10 @@ public: }; private: - float frame_time = 0.0; - float process_time = 0.0; - float physics_time = 0.0; - float physics_frame_time = 0.0; + double frame_time = 0.0; + double process_time = 0.0; + double physics_time = 0.0; + double physics_frame_time = 0.0; uint32_t poll_every = 0; @@ -120,7 +120,7 @@ public: static void register_uri_handler(const String &p_protocol, CreatePeerFunc p_func); - void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, float p_physics_frame_time); + void iteration(uint64_t p_frame_ticks, uint64_t p_process_ticks, uint64_t p_physics_ticks, double p_physics_frame_time); void profiler_enable(const StringName &p_name, bool p_enabled, const Array &p_opts = Array()); Error capture_parse(const StringName &p_name, const String &p_msg, const Array &p_args, bool &r_captured); diff --git a/core/debugger/local_debugger.cpp b/core/debugger/local_debugger.cpp index ab368471e4..b0b3f11424 100644 --- a/core/debugger/local_debugger.cpp +++ b/core/debugger/local_debugger.cpp @@ -41,7 +41,7 @@ struct LocalDebugger::ScriptsProfiler { } }; - float frame_time = 0; + double frame_time = 0; uint64_t idle_accum = 0; Vector<ScriptLanguage::ProfilingInfo> pinfo; @@ -61,7 +61,7 @@ struct LocalDebugger::ScriptsProfiler { } } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { frame_time = p_frame_time; _print_frame_data(false); } @@ -92,8 +92,8 @@ struct LocalDebugger::ScriptsProfiler { for (int i = 0; i < ofs; i++) { script_time_us += pinfo[i].self_time; } - float script_time = USEC_TO_SEC(script_time_us); - float total_time = p_accumulated ? script_time : frame_time; + double script_time = USEC_TO_SEC(script_time_us); + double total_time = p_accumulated ? script_time : frame_time; if (!p_accumulated) { print_line("FRAME: total: " + rtos(total_time) + " script: " + rtos(script_time) + "/" + itos(script_time * 100 / total_time) + " %"); @@ -103,8 +103,8 @@ struct LocalDebugger::ScriptsProfiler { for (int i = 0; i < ofs; i++) { print_line(itos(i) + ":" + pinfo[i].signature); - float tt = USEC_TO_SEC(pinfo[i].total_time); - float st = USEC_TO_SEC(pinfo[i].self_time); + double tt = USEC_TO_SEC(pinfo[i].total_time); + double st = USEC_TO_SEC(pinfo[i].self_time); print_line("\ttotal: " + rtos(tt) + "/" + itos(tt * 100 / total_time) + " % \tself: " + rtos(st) + "/" + itos(st * 100 / total_time) + " % tcalls: " + itos(pinfo[i].call_count)); } } @@ -320,13 +320,13 @@ void LocalDebugger::print_variables(const List<String> &names, const List<Varian String value; Vector<String> value_lines; const List<Variant>::Element *V = values.front(); - for (const List<String>::Element *E = names.front(); E; E = E->next()) { + for (const String &E : names) { value = String(V->get()); if (variable_prefix.is_empty()) { - print_line(E->get() + ": " + String(V->get())); + print_line(E + ": " + String(V->get())); } else { - print_line(E->get() + ":"); + print_line(E + ":"); value_lines = value.split("\n"); for (int i = 0; i < value_lines.size(); ++i) { print_line(variable_prefix + value_lines[i]); @@ -373,7 +373,7 @@ LocalDebugger::LocalDebugger() { ((ScriptsProfiler *)p_user)->toggle(p_enable, p_opts); }, nullptr, - [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { ((ScriptsProfiler *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); }); register_profiler("scripts", scr_prof); diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index bdbb7766fa..f865dfe102 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -50,7 +50,7 @@ void RemoteDebugger::_bind_profiler(const String &p_name, T *p_prof) { [](void *p_user, const Array &p_data) { ((T *)p_user)->add(p_data); }, - [](void *p_user, float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + [](void *p_user, double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { ((T *)p_user)->tick(p_frame_time, p_idle_time, p_physics_time, p_physics_frame_time); }); EngineDebugger::register_profiler(p_name, prof); @@ -164,7 +164,7 @@ public: } } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { uint64_t pt = OS::get_singleton()->get_ticks_msec(); if (pt - last_bandwidth_time > 200) { last_bandwidth_time = pt; @@ -278,10 +278,10 @@ struct RemoteDebugger::ServersProfiler { Map<StringName, ServerInfo> server_data; ScriptsProfiler scripts_profiler; - float frame_time = 0; - float idle_time = 0; - float physics_time = 0; - float physics_frame_time = 0; + double frame_time = 0; + double idle_time = 0; + double physics_time = 0; + double physics_frame_time = 0; void toggle(bool p_enable, const Array &p_opts) { skip_profile_frame = false; @@ -308,7 +308,7 @@ struct RemoteDebugger::ServersProfiler { srv.functions.push_back(fi); } - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { frame_time = p_frame_time; idle_time = p_idle_time; physics_time = p_physics_time; @@ -358,7 +358,7 @@ struct RemoteDebugger::VisualProfiler { void add(const Array &p_data) {} - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { Vector<RS::FrameProfileArea> profile_areas = RS::get_singleton()->get_frame_profile(); DebuggerMarshalls::VisualProfilerFrame frame; if (!profile_areas.size()) { @@ -378,7 +378,7 @@ struct RemoteDebugger::PerformanceProfiler { void toggle(bool p_enable, const Array &p_opts) {} void add(const Array &p_data) {} - void tick(float p_frame_time, float p_idle_time, float p_physics_time, float p_physics_frame_time) { + void tick(double p_frame_time, double p_idle_time, double p_physics_time, double p_physics_frame_time) { if (!performance) { return; } @@ -427,16 +427,16 @@ void RemoteDebugger::_send_resource_usage() { List<RS::TextureInfo> tinfo; RS::get_singleton()->texture_debug_usage(&tinfo); - for (List<RS::TextureInfo>::Element *E = tinfo.front(); E; E = E->next()) { + for (const RS::TextureInfo &E : tinfo) { DebuggerMarshalls::ResourceInfo info; - info.path = E->get().path; - info.vram = E->get().bytes; - info.id = E->get().texture; + info.path = E.path; + info.vram = E.bytes; + info.id = E.texture; info.type = "Texture"; - if (E->get().depth == 0) { - info.format = itos(E->get().width) + "x" + itos(E->get().height) + " " + Image::get_format_name(E->get().format); + if (E.depth == 0) { + info.format = itos(E.width) + "x" + itos(E.height) + " " + Image::get_format_name(E.format); } else { - info.format = itos(E->get().width) + "x" + itos(E->get().height) + "x" + itos(E->get().depth) + " " + Image::get_format_name(E->get().format); + info.format = itos(E.width) + "x" + itos(E.height) + "x" + itos(E.depth) + " " + Image::get_format_name(E.format); } usage.infos.push_back(info); } @@ -706,6 +706,8 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { Array msg; msg.push_back(p_can_continue); msg.push_back(error_str); + ERR_FAIL_COND(!script_lang); + msg.push_back(script_lang->debug_get_stack_level_count() > 0); send_message("debug_enter", msg); servers_profiler->skip_profile_frame = true; // Avoid frame time spike in debug. @@ -754,7 +756,6 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { break; } else if (command == "get_stack_dump") { - ERR_FAIL_COND(!script_lang); DebuggerMarshalls::ScriptStackDump dump; int slc = script_lang->debug_get_stack_level_count(); for (int i = 0; i < slc; i++) { @@ -790,7 +791,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) { script_lang->debug_get_globals(&globals, &globals_vals); ERR_FAIL_COND(globals.size() != globals_vals.size()); - send_message("stack_frame_vars", Array()); + Array var_size; + var_size.push_back(local_vals.size() + member_vals.size() + globals_vals.size()); + send_message("stack_frame_vars", var_size); _send_stack_vars(locals, local_vals, 0); _send_stack_vars(members, member_vals, 1); _send_stack_vars(globals, globals_vals, 2); diff --git a/core/doc_data.h b/core/doc_data.h index 46ab697768..a3011fe275 100644 --- a/core/doc_data.h +++ b/core/doc_data.h @@ -116,6 +116,17 @@ public: } }; + struct ThemeItemDoc { + String name; + String type; + String data_type; + String description; + String default_value; + bool operator<(const ThemeItemDoc &p_theme_item) const { + return name < p_theme_item.name; + } + }; + struct TutorialDoc { String link; String title; @@ -133,7 +144,7 @@ public: Vector<ConstantDoc> constants; Map<String, String> enums; Vector<PropertyDoc> properties; - Vector<PropertyDoc> theme_properties; + Vector<ThemeItemDoc> theme_properties; bool is_script_doc = false; String script_path; bool operator<(const ClassDoc &p_class) const { diff --git a/core/error/error_list.cpp b/core/error/error_list.cpp new file mode 100644 index 0000000000..a8355065fe --- /dev/null +++ b/core/error/error_list.cpp @@ -0,0 +1,85 @@ +/*************************************************************************/ +/* error_list.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "error_list.h" + +const char *error_names[] = { + "No error", + "Generic error", + "Requested operation is unsupported/unavailable", + "The object hasn't been set up properly", + "Missing credentials for requested resource", + "Parameter out of range", + "Out of memory", + "File not found", + "Bad drive", + "Bad path", + "Permission denied", + "Already in use", + "Can't open file", + "Can't write file", + "Can't read file", + "File unrecognized", + "File corrupt", + "Missing dependencies for file", + "Unexpected eof", + "Can't open resource/socket/file", // File too? What's the difference to ERR_FILE_CANT_OPEN + "Can't create", // What can't be created, + "Query failed", // What query, + "Already in use", + "Resource is locked", + "Timeout", + "Can't connect", + "Can't resolve hostname", // I guessed it's the hostname here. + "Connection error", + "Can't acquire resource", + "Can't fork", + "Invalid data", + "Invalid parameter", + "Item already exists", + "Item does not exist", + "Can't read from database", // Comments say, it's full? Is that correct? + "Can't write to database", // Is the database always full when this is raised? + "Compilation failed", + "Method not found", + "Link failed", + "Script failed", + "Cyclic link detected", + "Invalid declaration", + "Duplicate symbol", + "Parse error", + "Resource is busy", + "Skip error", // ???? What's this? String taken from the docs + "Help error", // More specific? + "Bug", + "Printer on fire", +}; + +static_assert(sizeof(error_names) / sizeof(*error_names) == ERR_MAX); diff --git a/core/error/error_list.h b/core/error/error_list.h index f032f44c1f..e7c7f10265 100644 --- a/core/error/error_list.h +++ b/core/error/error_list.h @@ -88,6 +88,9 @@ enum Error { ERR_HELP, ///< user requested help!! ERR_BUG, ///< a bug in the software certainly happened, due to a double check failing or unexpected behavior. ERR_PRINTER_ON_FIRE, /// the parallel port printer is engulfed in flames + ERR_MAX, // Not being returned, value represents the number of errors }; +extern const char *error_names[]; + #endif // ERROR_LIST_H diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 3c132a619d..49570cd1c1 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -38,6 +38,25 @@ #ifdef TOOLS_ENABLED +static String get_type_name(const PropertyInfo &p_info) { + if (p_info.type == Variant::INT && (p_info.usage & PROPERTY_USAGE_CLASS_IS_ENUM)) { + return String("enum::") + String(p_info.class_name); + } + if (p_info.class_name != StringName()) { + return p_info.class_name; + } + if (p_info.hint == PROPERTY_HINT_RESOURCE_TYPE) { + return p_info.hint_string; + } + if (p_info.type == Variant::NIL && (p_info.usage & PROPERTY_USAGE_NIL_IS_VARIANT)) { + return "Variant"; + } + if (p_info.type == Variant::NIL) { + return "void"; + } + return Variant::get_type_name(p_info.type); +} + Dictionary NativeExtensionAPIDump::generate_extension_api() { Dictionary api_dump; @@ -60,20 +79,38 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { const uint32_t vec3_elems = 3; const uint32_t ptrsize_32 = 4; - const uint32_t ptrsize_64 = 4; + const uint32_t ptrsize_64 = 8; static const char *build_config_name[4] = { "float_32", "float_64", "double_32", "double_64" }; { //type sizes - struct { + constexpr struct { Variant::Type type; uint32_t size_32_bits_real_float; uint32_t size_64_bits_real_float; uint32_t size_32_bits_real_double; uint32_t size_64_bits_real_double; + + // For compile-time size check. + constexpr uint32_t operator[](int index) const { + switch (index) { +#ifndef REAL_T_IS_DOUBLE + case sizeof(uint32_t): + return size_32_bits_real_float; + case sizeof(uint64_t): + return size_64_bits_real_float; +#else // REAL_T_IS_DOUBLE + case sizeof(uint32_t): + return size_32_bits_real_double; + case sizeof(uint64_t): + return size_64_bits_real_double; +#endif + } + return -1; + } } type_size_array[Variant::VARIANT_MAX + 1] = { { Variant::NIL, 0, 0, 0, 0 }, - { Variant::BOOL, sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t), sizeof(uint32_t) }, + { Variant::BOOL, sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t), sizeof(uint8_t) }, { Variant::INT, sizeof(int64_t), sizeof(int64_t), sizeof(int64_t), sizeof(int64_t) }, { Variant::FLOAT, sizeof(double), sizeof(double), sizeof(double), sizeof(double) }, { Variant::STRING, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, @@ -94,29 +131,66 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { { Variant::NODE_PATH, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, { Variant::RID, sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t), sizeof(uint64_t) }, { Variant::OBJECT, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, //harcoded align - { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, //harcoded align + { Variant::CALLABLE, sizeof(Callable), sizeof(Callable), sizeof(Callable), sizeof(Callable) }, // Hardcoded align. + { Variant::SIGNAL, sizeof(Signal), sizeof(Signal), sizeof(Signal), sizeof(Signal) }, // Hardcoded align. { Variant::DICTIONARY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, { Variant::ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_BYTE_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_INT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_INT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_STRING_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, - { Variant::PACKED_COLOR_ARRAY, ptrsize_32, ptrsize_64, ptrsize_32, ptrsize_64 }, + { Variant::PACKED_BYTE_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_INT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_INT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_FLOAT32_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_FLOAT64_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_STRING_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_VECTOR2_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_VECTOR3_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, + { Variant::PACKED_COLOR_ARRAY, ptrsize_32 * 2, ptrsize_64 * 2, ptrsize_32 * 2, ptrsize_64 * 2 }, { Variant::VARIANT_MAX, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(float) * 4, sizeof(uint64_t) + sizeof(double) * 4, sizeof(uint64_t) + sizeof(double) * 4 }, }; + // Validate sizes at compile time for the current build configuration. + static_assert(type_size_array[Variant::BOOL][sizeof(void *)] == sizeof(GDNativeBool), "Size of bool mismatch"); + static_assert(type_size_array[Variant::INT][sizeof(void *)] == sizeof(GDNativeInt), "Size of int mismatch"); + static_assert(type_size_array[Variant::FLOAT][sizeof(void *)] == sizeof(double), "Size of float mismatch"); + static_assert(type_size_array[Variant::STRING][sizeof(void *)] == sizeof(String), "Size of String mismatch"); + static_assert(type_size_array[Variant::VECTOR2][sizeof(void *)] == sizeof(Vector2), "Size of Vector2 mismatch"); + static_assert(type_size_array[Variant::VECTOR2I][sizeof(void *)] == sizeof(Vector2i), "Size of Vector2i mismatch"); + static_assert(type_size_array[Variant::RECT2][sizeof(void *)] == sizeof(Rect2), "Size of Rect2 mismatch"); + static_assert(type_size_array[Variant::RECT2I][sizeof(void *)] == sizeof(Rect2i), "Size of Rect2i mismatch"); + static_assert(type_size_array[Variant::VECTOR3][sizeof(void *)] == sizeof(Vector3), "Size of Vector3 mismatch"); + static_assert(type_size_array[Variant::VECTOR3I][sizeof(void *)] == sizeof(Vector3i), "Size of Vector3i mismatch"); + static_assert(type_size_array[Variant::TRANSFORM2D][sizeof(void *)] == sizeof(Transform2D), "Size of Transform2D mismatch"); + static_assert(type_size_array[Variant::PLANE][sizeof(void *)] == sizeof(Plane), "Size of Plane mismatch"); + static_assert(type_size_array[Variant::QUATERNION][sizeof(void *)] == sizeof(Quaternion), "Size of Quaternion mismatch"); + static_assert(type_size_array[Variant::AABB][sizeof(void *)] == sizeof(AABB), "Size of AABB mismatch"); + static_assert(type_size_array[Variant::BASIS][sizeof(void *)] == sizeof(Basis), "Size of Basis mismatch"); + static_assert(type_size_array[Variant::TRANSFORM3D][sizeof(void *)] == sizeof(Transform3D), "Size of Transform3D mismatch"); + static_assert(type_size_array[Variant::COLOR][sizeof(void *)] == sizeof(Color), "Size of Color mismatch"); + static_assert(type_size_array[Variant::STRING_NAME][sizeof(void *)] == sizeof(StringName), "Size of StringName mismatch"); + static_assert(type_size_array[Variant::NODE_PATH][sizeof(void *)] == sizeof(NodePath), "Size of NodePath mismatch"); + static_assert(type_size_array[Variant::RID][sizeof(void *)] == sizeof(RID), "Size of RID mismatch"); + static_assert(type_size_array[Variant::OBJECT][sizeof(void *)] == sizeof(Object *), "Size of Object mismatch"); + static_assert(type_size_array[Variant::CALLABLE][sizeof(void *)] == sizeof(Callable), "Size of Callable mismatch"); + static_assert(type_size_array[Variant::SIGNAL][sizeof(void *)] == sizeof(Signal), "Size of Signal mismatch"); + static_assert(type_size_array[Variant::DICTIONARY][sizeof(void *)] == sizeof(Dictionary), "Size of Dictionary mismatch"); + static_assert(type_size_array[Variant::ARRAY][sizeof(void *)] == sizeof(Array), "Size of Array mismatch"); + static_assert(type_size_array[Variant::PACKED_BYTE_ARRAY][sizeof(void *)] == sizeof(PackedByteArray), "Size of PackedByteArray mismatch"); + static_assert(type_size_array[Variant::PACKED_INT32_ARRAY][sizeof(void *)] == sizeof(PackedInt32Array), "Size of PackedInt32Array mismatch"); + static_assert(type_size_array[Variant::PACKED_INT64_ARRAY][sizeof(void *)] == sizeof(PackedInt64Array), "Size of PackedInt64Array mismatch"); + static_assert(type_size_array[Variant::PACKED_FLOAT32_ARRAY][sizeof(void *)] == sizeof(PackedFloat32Array), "Size of PackedFloat32Array mismatch"); + static_assert(type_size_array[Variant::PACKED_FLOAT64_ARRAY][sizeof(void *)] == sizeof(PackedFloat64Array), "Size of PackedFloat64Array mismatch"); + static_assert(type_size_array[Variant::PACKED_STRING_ARRAY][sizeof(void *)] == sizeof(PackedStringArray), "Size of PackedStringArray mismatch"); + static_assert(type_size_array[Variant::PACKED_VECTOR2_ARRAY][sizeof(void *)] == sizeof(PackedVector2Array), "Size of PackedVector2Array mismatch"); + static_assert(type_size_array[Variant::PACKED_VECTOR3_ARRAY][sizeof(void *)] == sizeof(PackedVector3Array), "Size of PackedVector3Array mismatch"); + static_assert(type_size_array[Variant::PACKED_COLOR_ARRAY][sizeof(void *)] == sizeof(PackedColorArray), "Size of PackedColorArray mismatch"); + static_assert(type_size_array[Variant::VARIANT_MAX][sizeof(void *)] == sizeof(Variant), "Size of Variant mismatch"); + Array core_type_sizes; for (int i = 0; i < 4; i++) { Dictionary d; d["build_configuration"] = build_config_name[i]; Array sizes; - for (int j = 0; j < Variant::VARIANT_MAX; j++) { + for (int j = 0; j <= Variant::VARIANT_MAX; j++) { Variant::Type t = type_size_array[j].type; String name = t == Variant::VARIANT_MAX ? String("Variant") : Variant::get_type_name(t); Dictionary d2; @@ -146,7 +220,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { } { - //member offsets sizes + // Member offsets sizes. struct { Variant::Type type; const char *member; @@ -180,7 +254,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { { Variant::QUATERNION, "w", 3 * sizeof(float), 3 * sizeof(float), 3 * sizeof(double), 3 * sizeof(double) }, { Variant::AABB, "position", 0, 0, 0, 0 }, { Variant::AABB, "size", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, - //rememer that basis vectors are flipped! + // Remember that basis vectors are flipped! { Variant::BASIS, "x", 0, 0, 0, 0 }, { Variant::BASIS, "y", vec3_elems * sizeof(float), vec3_elems * sizeof(float), vec3_elems * sizeof(double), vec3_elems * sizeof(double) }, { Variant::BASIS, "z", vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(float), vec3_elems * 2 * sizeof(double), vec3_elems * 2 * sizeof(double) }, @@ -251,7 +325,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { } { - // global enums and constants + // Global enums and constants. Array constants; Map<String, List<Pair<String, int>>> enum_list; @@ -276,10 +350,10 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Dictionary d1; d1["name"] = E->key(); Array values; - for (List<Pair<String, int>>::Element *F = E->get().front(); F; F = F->next()) { + for (const Pair<String, int> &F : E->get()) { Dictionary d2; - d2["name"] = F->get().first; - d2["value"] = F->get().second; + d2["name"] = F.first; + d2["value"] = F.second; values.push_back(d2); } d1["values"] = values; @@ -294,8 +368,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { List<StringName> utility_func_names; Variant::get_utility_function_list(&utility_func_names); - for (List<StringName>::Element *E = utility_func_names.front(); E; E = E->next()) { - StringName name = E->get(); + for (const StringName &name : utility_func_names) { Dictionary func; func["name"] = String(name); if (Variant::has_utility_function_return_value(name)) { @@ -357,14 +430,15 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { d["indexing_return_type"] = index_type == Variant::NIL ? String("Variant") : Variant::get_type_name(index_type); } + d["is_keyed"] = Variant::ValidatedKeyedSetter(type); + { //members Array members; List<StringName> member_names; Variant::get_member_list(type, &member_names); - for (List<StringName>::Element *E = member_names.front(); E; E = E->next()) { - StringName member_name = E->get(); + for (const StringName &member_name : member_names) { Dictionary d2; d2["name"] = String(member_name); d2["type"] = Variant::get_type_name(Variant::get_member_type(type, member_name)); @@ -380,8 +454,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { List<StringName> constant_names; Variant::get_constants_for_type(type, &constant_names); - for (List<StringName>::Element *E = constant_names.front(); E; E = E->next()) { - StringName constant_name = E->get(); + for (const StringName &constant_name : constant_names) { Dictionary d2; d2["name"] = String(constant_name); Variant constant = Variant::get_constant_value(type, constant_name); @@ -406,6 +479,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) { d2["right_type"] = Variant::get_type_name(Variant::Type(j)); } + d2["return_type"] = Variant::get_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j))); operators.push_back(d2); } } @@ -420,8 +494,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { List<StringName> method_names; Variant::get_builtin_method_list(type, &method_names); - for (List<StringName>::Element *E = method_names.front(); E; E = E->next()) { - StringName method_name = E->get(); + for (const StringName &method_name : method_names) { Dictionary d2; d2["name"] = String(method_name); if (Variant::has_builtin_method_return_value(type, method_name)) { @@ -486,6 +559,10 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { d["constructors"] = constructors; } } + { + //destructor + d["has_destructor"] = Variant::has_destructor(type); + } builtins.push_back(d); } @@ -503,9 +580,8 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { class_list.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = class_list.front(); E; E = E->next()) { + for (const StringName &class_name : class_list) { Dictionary d; - StringName class_name = E->get(); d["name"] = String(class_name); d["is_refcounted"] = ClassDB::is_parent_class(class_name, "RefCounted"); d["is_instantiable"] = ClassDB::can_instantiate(class_name); @@ -525,15 +601,15 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Array constants; List<String> constant_list; ClassDB::get_integer_constant_list(class_name, &constant_list, true); - for (List<String>::Element *F = constant_list.front(); F; F = F->next()) { - StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F->get()); + for (const String &F : constant_list) { + StringName enum_name = ClassDB::get_integer_constant_enum(class_name, F); if (enum_name != StringName()) { continue; //enums will be handled on their own } Dictionary d2; - d2["name"] = String(F->get()); - d2["value"] = ClassDB::get_integer_constant(class_name, F->get()); + d2["name"] = String(F); + d2["value"] = ClassDB::get_integer_constant(class_name, F); constants.push_back(d2); } @@ -547,13 +623,13 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Array enums; List<StringName> enum_list; ClassDB::get_enum_list(class_name, &enum_list, true); - for (List<StringName>::Element *F = enum_list.front(); F; F = F->next()) { + for (const StringName &F : enum_list) { Dictionary d2; - d2["name"] = String(F->get()); + d2["name"] = String(F); Array values; List<StringName> enum_constant_list; - ClassDB::get_enum_constants(class_name, F->get(), &enum_constant_list, true); + ClassDB::get_enum_constants(class_name, F, &enum_constant_list, true); for (List<StringName>::Element *G = enum_constant_list.front(); G; G = G->next()) { Dictionary d3; d3["name"] = String(G->get()); @@ -575,14 +651,14 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Array methods; List<MethodInfo> method_list; ClassDB::get_method_list(class_name, &method_list, true); - for (List<MethodInfo>::Element *F = method_list.front(); F; F = F->next()) { - StringName method_name = F->get().name; - if (F->get().flags & METHOD_FLAG_VIRTUAL) { + for (const MethodInfo &F : method_list) { + StringName method_name = F.name; + if (F.flags & METHOD_FLAG_VIRTUAL) { //virtual method - const MethodInfo &mi = F->get(); + const MethodInfo &mi = F; Dictionary d2; d2["name"] = String(method_name); - d2["is_const"] = (F->get().flags & METHOD_FLAG_CONST) ? true : false; + d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false; d2["is_vararg"] = false; d2["is_virtual"] = true; // virtual functions have no hash since no MethodBind is involved @@ -595,16 +671,8 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { if (i >= 0) { d3["name"] = pinfo.name; } - if (pinfo.class_name != StringName()) { - d3["type"] = String(pinfo.class_name); - } else { - Variant::Type type = pinfo.type; - if (type == Variant::NIL) { - d3["type"] = "Variant"; - } else { - d3["type"] = Variant::get_type_name(type); - } - } + + d3["type"] = get_type_name(pinfo); if (i == -1) { d2["return_value"] = d3; @@ -619,7 +687,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { methods.push_back(d2); - } else if (F->get().name.begins_with("_")) { + } else if (F.name.begins_with("_")) { //hidden method, ignore } else { @@ -646,16 +714,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { if (i >= 0) { d3["name"] = pinfo.name; } - if (pinfo.class_name != StringName()) { - d3["type"] = String(pinfo.class_name); - } else { - Variant::Type type = pinfo.type; - if (type == Variant::NIL) { - d3["type"] = "Variant"; - } else { - d3["type"] = Variant::get_type_name(type); - } - } + d3["type"] = get_type_name(pinfo); if (method->get_argument_meta(i) > 0) { static const char *argmeta[11] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double" }; @@ -692,24 +751,17 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Array signals; List<MethodInfo> signal_list; ClassDB::get_signal_list(class_name, &signal_list, true); - for (List<MethodInfo>::Element *F = signal_list.front(); F; F = F->next()) { - StringName signal_name = F->get().name; + for (const MethodInfo &F : signal_list) { + StringName signal_name = F.name; Dictionary d2; d2["name"] = String(signal_name); Array arguments; - for (int i = 0; i < F->get().arguments.size(); i++) { + for (int i = 0; i < F.arguments.size(); i++) { Dictionary d3; - d3["name"] = F->get().arguments[i].name; - Variant::Type type = F->get().arguments[i].type; - if (F->get().arguments[i].class_name != StringName()) { - d3["type"] = String(F->get().arguments[i].class_name); - } else if (type == Variant::NIL) { - d3["type"] = "Variant"; - } else { - d3["type"] = Variant::get_type_name(type); - } + d3["name"] = F.arguments[i].name; + d3["type"] = get_type_name(F.arguments[i]); arguments.push_back(d3); } if (arguments.size()) { @@ -728,28 +780,20 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { Array properties; List<PropertyInfo> property_list; ClassDB::get_property_list(class_name, &property_list, true); - for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { - if (F->get().usage & PROPERTY_USAGE_CATEGORY || F->get().usage & PROPERTY_USAGE_GROUP || F->get().usage & PROPERTY_USAGE_SUBGROUP) { + for (const PropertyInfo &F : property_list) { + if (F.usage & PROPERTY_USAGE_CATEGORY || F.usage & PROPERTY_USAGE_GROUP || F.usage & PROPERTY_USAGE_SUBGROUP) { continue; //not real properties } - if (F->get().name.begins_with("_")) { + if (F.name.begins_with("_")) { continue; //hidden property } - StringName property_name = F->get().name; + StringName property_name = F.name; Dictionary d2; + d2["type"] = get_type_name(F); d2["name"] = String(property_name); - - if (F->get().class_name != StringName()) { - d2["type"] = String(F->get().class_name); - } else if (F->get().type == Variant::NIL && F->get().usage & PROPERTY_USAGE_NIL_IS_VARIANT) { - d2["type"] = "Variant"; - } else { - d2["type"] = Variant::get_type_name(F->get().type); - } - - d2["setter"] = ClassDB::get_property_setter(class_name, F->get().name); - d2["getter"] = ClassDB::get_property_getter(class_name, F->get().name); - d2["index"] = ClassDB::get_property_index(class_name, F->get().name); + d2["setter"] = ClassDB::get_property_setter(class_name, F.name); + d2["getter"] = ClassDB::get_property_getter(class_name, F.name); + d2["index"] = ClassDB::get_property_index(class_name, F.name); properties.push_back(d2); } @@ -771,8 +815,7 @@ Dictionary NativeExtensionAPIDump::generate_extension_api() { List<Engine::Singleton> singleton_list; Engine::get_singleton()->get_singletons(&singleton_list); - for (List<Engine::Singleton>::Element *E = singleton_list.front(); E; E = E->next()) { - const Engine::Singleton &s = E->get(); + for (const Engine::Singleton &s : singleton_list) { Dictionary d; d["name"] = s.name; if (s.class_name != StringName()) { diff --git a/core/extension/gdnative_interface.cpp b/core/extension/gdnative_interface.cpp index 8f68b8d848..a444b2e18e 100644 --- a/core/extension/gdnative_interface.cpp +++ b/core/extension/gdnative_interface.cpp @@ -281,8 +281,9 @@ static GDNativeBool gdnative_variant_has_key(const GDNativeVariantPtr p_self, co const Variant *self = (const Variant *)p_self; const Variant *key = (const Variant *)p_key; bool valid; - return self->has_key(*key, valid); + bool ret = self->has_key(*key, valid); *r_valid = valid; + return ret; } static void gdnative_variant_get_type_name(GDNativeVariantType p_type, GDNativeStringPtr r_ret) { @@ -298,6 +299,161 @@ static GDNativeBool gdnative_variant_can_convert_strict(GDNativeVariantType p_fr return Variant::can_convert_strict((Variant::Type)p_from, (Variant::Type)p_to); } +// Variant interaction. +static GDNativeVariantFromTypeConstructorFunc gdnative_get_variant_from_type_constructor(GDNativeVariantType p_type) { + switch (p_type) { + case GDNATIVE_VARIANT_TYPE_BOOL: + return VariantTypeConstructor<bool>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_INT: + return VariantTypeConstructor<int64_t>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_FLOAT: + return VariantTypeConstructor<double>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_STRING: + return VariantTypeConstructor<String>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_VECTOR2: + return VariantTypeConstructor<Vector2>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_VECTOR2I: + return VariantTypeConstructor<Vector2i>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_RECT2: + return VariantTypeConstructor<Rect2>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_RECT2I: + return VariantTypeConstructor<Rect2i>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_VECTOR3: + return VariantTypeConstructor<Vector3>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_VECTOR3I: + return VariantTypeConstructor<Vector3i>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_TRANSFORM2D: + return VariantTypeConstructor<Transform2D>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PLANE: + return VariantTypeConstructor<Plane>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_QUATERNION: + return VariantTypeConstructor<Quaternion>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_AABB: + return VariantTypeConstructor<AABB>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_BASIS: + return VariantTypeConstructor<Basis>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_TRANSFORM3D: + return VariantTypeConstructor<Transform3D>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_COLOR: + return VariantTypeConstructor<Color>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_STRING_NAME: + return VariantTypeConstructor<StringName>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_NODE_PATH: + return VariantTypeConstructor<NodePath>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_RID: + return VariantTypeConstructor<RID>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_OBJECT: + return VariantTypeConstructor<Object *>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_CALLABLE: + return VariantTypeConstructor<Callable>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_SIGNAL: + return VariantTypeConstructor<Signal>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_DICTIONARY: + return VariantTypeConstructor<Dictionary>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_ARRAY: + return VariantTypeConstructor<Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY: + return VariantTypeConstructor<PackedByteArray>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY: + return VariantTypeConstructor<PackedInt32Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY: + return VariantTypeConstructor<PackedInt64Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY: + return VariantTypeConstructor<PackedFloat32Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY: + return VariantTypeConstructor<PackedFloat64Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY: + return VariantTypeConstructor<PackedStringArray>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY: + return VariantTypeConstructor<PackedVector2Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY: + return VariantTypeConstructor<PackedVector3Array>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY: + return VariantTypeConstructor<PackedColorArray>::variant_from_type; + case GDNATIVE_VARIANT_TYPE_NIL: + case GDNATIVE_VARIANT_TYPE_VARIANT_MAX: + ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type"); + } + ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type"); +} + +static GDNativeTypeFromVariantConstructorFunc gdnative_get_type_from_variant_constructor(GDNativeVariantType p_type) { + switch (p_type) { + case GDNATIVE_VARIANT_TYPE_BOOL: + return VariantTypeConstructor<bool>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_INT: + return VariantTypeConstructor<int64_t>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_FLOAT: + return VariantTypeConstructor<double>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_STRING: + return VariantTypeConstructor<String>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_VECTOR2: + return VariantTypeConstructor<Vector2>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_VECTOR2I: + return VariantTypeConstructor<Vector2i>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_RECT2: + return VariantTypeConstructor<Rect2>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_RECT2I: + return VariantTypeConstructor<Rect2i>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_VECTOR3: + return VariantTypeConstructor<Vector3>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_VECTOR3I: + return VariantTypeConstructor<Vector3i>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_TRANSFORM2D: + return VariantTypeConstructor<Transform2D>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PLANE: + return VariantTypeConstructor<Plane>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_QUATERNION: + return VariantTypeConstructor<Quaternion>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_AABB: + return VariantTypeConstructor<AABB>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_BASIS: + return VariantTypeConstructor<Basis>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_TRANSFORM3D: + return VariantTypeConstructor<Transform3D>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_COLOR: + return VariantTypeConstructor<Color>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_STRING_NAME: + return VariantTypeConstructor<StringName>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_NODE_PATH: + return VariantTypeConstructor<NodePath>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_RID: + return VariantTypeConstructor<RID>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_OBJECT: + return VariantTypeConstructor<Object *>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_CALLABLE: + return VariantTypeConstructor<Callable>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_SIGNAL: + return VariantTypeConstructor<Signal>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_DICTIONARY: + return VariantTypeConstructor<Dictionary>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_ARRAY: + return VariantTypeConstructor<Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_BYTE_ARRAY: + return VariantTypeConstructor<PackedByteArray>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_INT32_ARRAY: + return VariantTypeConstructor<PackedInt32Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_INT64_ARRAY: + return VariantTypeConstructor<PackedInt64Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT32_ARRAY: + return VariantTypeConstructor<PackedFloat32Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_FLOAT64_ARRAY: + return VariantTypeConstructor<PackedFloat64Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_STRING_ARRAY: + return VariantTypeConstructor<PackedStringArray>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR2_ARRAY: + return VariantTypeConstructor<PackedVector2Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_VECTOR3_ARRAY: + return VariantTypeConstructor<PackedVector3Array>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_PACKED_COLOR_ARRAY: + return VariantTypeConstructor<PackedColorArray>::type_from_variant; + case GDNATIVE_VARIANT_TYPE_NIL: + case GDNATIVE_VARIANT_TYPE_VARIANT_MAX: + ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type"); + } + ERR_FAIL_V_MSG(nullptr, "Getting Variant conversion function with invalid type"); +} + // ptrcalls static GDNativePtrOperatorEvaluator gdnative_variant_get_ptr_operator_evaluator(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b) { return (GDNativePtrOperatorEvaluator)Variant::get_ptr_operator_evaluator(Variant::Operator(p_operator), Variant::Type(p_type_a), Variant::Type(p_type_b)); @@ -315,6 +471,9 @@ static GDNativePtrBuiltInMethod gdnative_variant_get_ptr_builtin_method(GDNative static GDNativePtrConstructor gdnative_variant_get_ptr_constructor(GDNativeVariantType p_type, int32_t p_constructor) { return (GDNativePtrConstructor)Variant::get_ptr_constructor(Variant::Type(p_type), p_constructor); } +static GDNativePtrDestructor gdnative_variant_get_ptr_destructor(GDNativeVariantType p_type) { + return (GDNativePtrDestructor)Variant::get_ptr_destructor(Variant::Type(p_type)); +} static void gdnative_variant_construct(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error) { memnew_placement(p_base, Variant); @@ -520,11 +679,16 @@ static GDNativeObjectPtr gdnative_global_get_singleton(const char *p_name) { return (GDNativeObjectPtr)Engine::get_singleton()->get_singleton_object(String(p_name)); } -static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_instance, void *p_token, GDNativeInstanceBindingCallbacks *p_callbacks) { +static void *gdnative_object_get_instance_binding(GDNativeObjectPtr p_instance, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { Object *o = (Object *)p_instance; return o->get_instance_binding(p_token, p_callbacks); } +static void gdnative_object_set_instance_binding(GDNativeObjectPtr p_instance, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { + Object *o = (Object *)p_instance; + o->set_instance_binding(p_token, p_binding, p_callbacks); +} + static GDNativeObjectPtr gdnative_object_get_instance_from_id(GDObjectInstanceID p_instance_id) { return (GDNativeObjectPtr)ObjectDB::get_instance(ObjectID(p_instance_id)); } @@ -625,15 +789,15 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.variant_can_convert = gdnative_variant_can_convert; gdni.variant_can_convert_strict = gdnative_variant_can_convert_strict; - //ptrcalls -#if 0 - GDNativeVariantFromTypeConstructorFunc (*get_variant_from_type_constructor)(GDNativeVariantType p_type); - GDNativeTypeFromVariantConstructorFunc (*get_variant_to_type_constructor)(GDNativeVariantType p_type); -#endif + gdni.get_variant_from_type_constructor = gdnative_get_variant_from_type_constructor; + gdni.get_variant_to_type_constructor = gdnative_get_type_from_variant_constructor; + + // ptrcalls. gdni.variant_get_ptr_operator_evaluator = gdnative_variant_get_ptr_operator_evaluator; gdni.variant_get_ptr_builtin_method = gdnative_variant_get_ptr_builtin_method; gdni.variant_get_ptr_constructor = gdnative_variant_get_ptr_constructor; + gdni.variant_get_ptr_destructor = gdnative_variant_get_ptr_destructor; gdni.variant_construct = gdnative_variant_construct; gdni.variant_get_ptr_setter = gdnative_variant_get_ptr_setter; gdni.variant_get_ptr_getter = gdnative_variant_get_ptr_getter; @@ -671,6 +835,7 @@ void gdnative_setup_interface(GDNativeInterface *p_interface) { gdni.object_destroy = gdnative_object_destroy; gdni.global_get_singleton = gdnative_global_get_singleton; gdni.object_get_instance_binding = gdnative_object_get_instance_binding; + gdni.object_set_instance_binding = gdnative_object_set_instance_binding; gdni.object_cast_to = gdnative_object_cast_to; gdni.object_get_instance_from_id = gdnative_object_get_instance_from_id; diff --git a/core/extension/gdnative_interface.h b/core/extension/gdnative_interface.h index c1ebb3e76a..3432d8fabe 100644 --- a/core/extension/gdnative_interface.h +++ b/core/extension/gdnative_interface.h @@ -39,6 +39,11 @@ #include <stdint.h> #include <stdio.h> +#ifndef __cplusplus +typedef uint32_t char32_t; +typedef uint16_t char16_t; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -134,18 +139,18 @@ typedef void *GDNativeObjectPtr; typedef void *GDNativeTypePtr; typedef void *GDNativeMethodBindPtr; typedef int64_t GDNativeInt; -typedef uint32_t GDNativeBool; +typedef uint8_t GDNativeBool; typedef uint64_t GDObjectInstanceID; /* VARIANT DATA I/O */ typedef enum { - NATIVE_CALL_OK, - NATIVE_CALL_ERROR_INVALID_METHOD, - NATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */ - NATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ - NATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ - NATIVE_CALL_ERROR_INSTANCE_IS_NULL, + GDNATIVE_CALL_OK, + GDNATIVE_CALL_ERROR_INVALID_METHOD, + GDNATIVE_CALL_ERROR_INVALID_ARGUMENT, /* expected is variant type */ + GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS, /* expected is number of arguments */ + GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS, /* expected is number of arguments */ + GDNATIVE_CALL_ERROR_INSTANCE_IS_NULL, } GDNativeCallErrorType; @@ -160,6 +165,7 @@ typedef void (*GDNativeTypeFromVariantConstructorFunc)(GDNativeTypePtr, GDNative typedef void (*GDNativePtrOperatorEvaluator)(const GDNativeTypePtr p_left, const GDNativeTypePtr p_right, GDNativeTypePtr r_result); typedef void (*GDNativePtrBuiltInMethod)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args, GDNativeTypePtr r_return, int p_argument_count); typedef void (*GDNativePtrConstructor)(GDNativeTypePtr p_base, const GDNativeTypePtr *p_args); +typedef void (*GDNativePtrDestructor)(GDNativeTypePtr p_base); typedef void (*GDNativePtrSetter)(GDNativeTypePtr p_base, const GDNativeTypePtr p_value); typedef void (*GDNativePtrGetter)(const GDNativeTypePtr p_base, GDNativeTypePtr r_value); typedef void (*GDNativePtrIndexedSetter)(GDNativeTypePtr p_base, GDNativeInt p_index, const GDNativeTypePtr p_value); @@ -173,7 +179,7 @@ typedef GDNativeObjectPtr (*GDNativeClassConstructor)(); typedef void *(*GDNativeInstanceBindingCreateCallback)(void *p_token, void *p_instance); typedef void (*GDNativeInstanceBindingFreeCallback)(void *p_token, void *p_instance, void *p_binding); -typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_instance, GDNativeBool p_reference); +typedef GDNativeBool (*GDNativeInstanceBindingReferenceCallback)(void *p_token, void *p_binding, GDNativeBool p_reference); struct GDNativeInstanceBindingCallbacks { GDNativeInstanceBindingCreateCallback create_callback; @@ -206,6 +212,7 @@ typedef void (*GDNativeExtensionClassUnreference)(GDExtensionClassInstancePtr p_ typedef void (*GDNativeExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); typedef GDExtensionClassInstancePtr (*GDNativeExtensionClassCreateInstance)(void *p_userdata); typedef void (*GDNativeExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance); +typedef void (*GDNativeExtensionClassObjectInstance)(GDExtensionClassInstancePtr p_instance, GDNativeObjectPtr p_object_instance); typedef GDNativeExtensionClassCallVirtual (*GDNativeExtensionClassGetVirtual)(void *p_userdata, const char *p_name); typedef struct { @@ -219,7 +226,8 @@ typedef struct { GDNativeExtensionClassUnreference unreference_func; GDNativeExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ GDNativeExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ - GDNativeExtensionClassGetVirtual get_firtual_func; + GDNativeExtensionClassObjectInstance object_instance_func; /* this one is mandatory */ + GDNativeExtensionClassGetVirtual get_virtual_func; void *class_userdata; } GDNativeExtensionClassCreationInfo; @@ -256,8 +264,8 @@ typedef enum { GDNATIVE_EXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE } GDNativeExtensionClassMethodArgumentMetadata; -typedef void (*GDNativeExtensionClassMethodCall)(GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); -typedef void (*GDNativeExtensionClassMethodPtrCall)(GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); +typedef void (*GDNativeExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeVariantPtr *p_args, const GDNativeInt p_argument_count, GDNativeVariantPtr r_return, GDNativeCallError *r_error); +typedef void (*GDNativeExtensionClassMethodPtrCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); /* passing -1 as argument in the following functions refers to the return type */ typedef GDNativeVariantType (*GDNativeExtensionClassMethodGetArgumentType)(void *p_method_userdata, int32_t p_argument); @@ -339,6 +347,7 @@ typedef struct { GDNativePtrOperatorEvaluator (*variant_get_ptr_operator_evaluator)(GDNativeVariantOperator p_operator, GDNativeVariantType p_type_a, GDNativeVariantType p_type_b); GDNativePtrBuiltInMethod (*variant_get_ptr_builtin_method)(GDNativeVariantType p_type, const char *p_method, GDNativeInt p_hash); GDNativePtrConstructor (*variant_get_ptr_constructor)(GDNativeVariantType p_type, int32_t p_constructor); + GDNativePtrDestructor (*variant_get_ptr_destructor)(GDNativeVariantType p_type); void (*variant_construct)(GDNativeVariantType p_type, GDNativeVariantPtr p_base, const GDNativeVariantPtr *p_args, int32_t p_argument_count, GDNativeCallError *r_error); GDNativePtrSetter (*variant_get_ptr_setter)(GDNativeVariantType p_type, const char *p_member); GDNativePtrGetter (*variant_get_ptr_getter)(GDNativeVariantType p_type, const char *p_member); @@ -383,7 +392,8 @@ typedef struct { void (*object_method_bind_ptrcall)(GDNativeMethodBindPtr p_method_bind, GDNativeObjectPtr p_instance, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret); void (*object_destroy)(GDNativeObjectPtr p_o); GDNativeObjectPtr (*global_get_singleton)(const char *p_name); - void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, GDNativeInstanceBindingCallbacks *p_callbacks); + void *(*object_get_instance_binding)(GDNativeObjectPtr p_o, void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); + void (*object_set_instance_binding)(GDNativeObjectPtr p_o, void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); GDNativeObjectPtr (*object_cast_to)(const GDNativeObjectPtr p_object, void *p_class_tag); GDNativeObjectPtr (*object_get_instance_from_id)(GDObjectInstanceID p_instance_id); diff --git a/core/extension/native_extension.cpp b/core/extension/native_extension.cpp index 65718a7507..91d304627a 100644 --- a/core/extension/native_extension.cpp +++ b/core/extension/native_extension.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "native_extension.h" +#include "core/config/project_settings.h" #include "core/io/config_file.h" #include "core/object/class_db.h" #include "core/object/method_bind.h" @@ -69,7 +70,7 @@ public: Variant ret; GDExtensionClassInstancePtr extension_instance = p_object->_get_extension_instance(); GDNativeCallError ce; - call_func(extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce); + call_func(method_userdata, extension_instance, (const GDNativeVariantPtr *)p_args, p_arg_count, (GDNativeVariantPtr)&ret, &ce); r_error.error = Callable::CallError::Error(ce.error); r_error.argument = ce.argument; r_error.expected = ce.expected; @@ -78,7 +79,7 @@ public: virtual void ptrcall(Object *p_object, const void **p_args, void *r_ret) { 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(); - ptrcall_func(extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret); + ptrcall_func(method_userdata, extension_instance, (const GDNativeTypePtr *)p_args, (GDNativeTypePtr)r_ret); } virtual bool is_vararg() const { @@ -91,6 +92,7 @@ public: get_argument_type_func = p_method_info->get_argument_type_func; get_argument_info_func = p_method_info->get_argument_info_func; get_argument_metadata_func = p_method_info->get_argument_metadata_func; + set_name(p_method_info->name); vararg = p_method_info->method_flags & GDNATIVE_EXTENSION_METHOD_FLAG_VARARG; @@ -109,7 +111,7 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr NativeExtension *self = (NativeExtension *)p_library; StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(String(class_name).is_valid_identifier(), "Attempt to register extension clas '" + class_name + "', which is not a valid class identifier."); + ERR_FAIL_COND_MSG(!String(class_name).is_valid_identifier(), "Attempt to register extension class '" + class_name + "', which is not a valid class identifier."); ERR_FAIL_COND_MSG(ClassDB::class_exists(class_name), "Attempt to register extension class '" + class_name + "', which appears to be already registered."); Extension *parent_extension = nullptr; @@ -150,6 +152,7 @@ void NativeExtension::_register_extension_class(const GDNativeExtensionClassLibr extension->native_extension.unreference = p_extension_funcs->unreference_func; extension->native_extension.class_userdata = p_extension_funcs->class_userdata; extension->native_extension.create_instance = p_extension_funcs->create_instance_func; + extension->native_extension.set_object_instance = p_extension_funcs->object_instance_func; extension->native_extension.free_instance = p_extension_funcs->free_instance_func; ClassDB::register_extension_class(&extension->native_extension); @@ -159,11 +162,12 @@ void NativeExtension::_register_extension_class_method(const GDNativeExtensionCl StringName class_name = p_class_name; StringName method_name = p_method_info->name; - ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension method '" + String(method_name) + "' for unexisting class '" + class_name + "'."); //Extension *extension = &self->extension_classes[class_name]; NativeExtensionMethodBind *method = memnew(NativeExtensionMethodBind(p_method_info)); + method->set_instance_class(class_name); ClassDB::bind_method_custom(class_name, method); } @@ -171,7 +175,7 @@ void NativeExtension::_register_extension_class_integer_constant(const GDNativeE NativeExtension *self = (NativeExtension *)p_library; StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension constant '" + String(p_constant_name) + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension constant '" + String(p_constant_name) + "' for unexisting class '" + class_name + "'."); //Extension *extension = &self->extension_classes[class_name]; @@ -181,7 +185,7 @@ void NativeExtension::_register_extension_class_property(const GDNativeExtension NativeExtension *self = (NativeExtension *)p_library; StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class property '" + String(p_info->name) + "' for unexisting class '" + class_name + "'."); //Extension *extension = &self->extension_classes[class_name]; PropertyInfo pinfo; @@ -199,7 +203,7 @@ void NativeExtension::_register_extension_class_signal(const GDNativeExtensionCl NativeExtension *self = (NativeExtension *)p_library; StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to register extension class signal '" + String(p_signal_name) + "' for unexisting class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to register extension class signal '" + String(p_signal_name) + "' for unexisting class '" + class_name + "'."); MethodInfo s; s.name = p_signal_name; @@ -220,7 +224,7 @@ void NativeExtension::_unregister_extension_class(const GDNativeExtensionClassLi NativeExtension *self = (NativeExtension *)p_library; StringName class_name = p_class_name; - ERR_FAIL_COND_MSG(self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'."); + ERR_FAIL_COND_MSG(!self->extension_classes.has(class_name), "Attempt to unregister unexisting extension class '" + class_name + "'."); Extension *ext = &self->extension_classes[class_name]; ERR_FAIL_COND_MSG(ext->native_extension.children.size(), "Attempt to unregister class '" + class_name + "' while other extension classes inherit from it."); @@ -351,8 +355,8 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or String library_path; - for (List<String>::Element *E = libraries.front(); E; E = E->next()) { - Vector<String> tags = E->get().split("."); + for (const String &E : libraries) { + Vector<String> tags = E.split("."); bool all_tags_met = true; for (int i = 0; i < tags.size(); i++) { String tag = tags[i].strip_edges(); @@ -363,12 +367,12 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or } if (all_tags_met) { - library_path = config->get_value("libraries", E->get()); + library_path = config->get_value("libraries", E); break; } } - if (library_path != String()) { + if (library_path == String()) { if (r_error) { *r_error = ERR_FILE_NOT_FOUND; } @@ -381,7 +385,8 @@ RES NativeExtensionResourceLoader::load(const String &p_path, const String &p_or Ref<NativeExtension> lib; lib.instantiate(); - err = lib->open_library(library_path, entry_symbol); + String abs_path = ProjectSettings::get_singleton()->globalize_path(library_path); + err = lib->open_library(abs_path, entry_symbol); if (r_error) { *r_error = err; diff --git a/core/extension/native_extension.h b/core/extension/native_extension.h index 0a23848eb2..8bd95b2867 100644 --- a/core/extension/native_extension.h +++ b/core/extension/native_extension.h @@ -35,8 +35,8 @@ #include "core/io/resource_loader.h" #include "core/object/ref_counted.h" -class NativeExtension : public RefCounted { - GDCLASS(NativeExtension, RefCounted) +class NativeExtension : public Resource { + GDCLASS(NativeExtension, Resource) void *library = nullptr; // pointer if valid, diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 884fb9550c..f136d83496 100644 --- a/core/input/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt @@ -4,19 +4,24 @@ # Windows 03000000fa2d00000100000000000000,3DRUDDER,leftx:a0,lefty:a1,rightx:a5,righty:a2,platform:Windows, 03000000c82d00002038000000000000,8bitdo,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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000951000000000000,8BitDo Dogbone Modkit,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, 03000000c82d000011ab000000000000,8BitDo F30,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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001038000000000000,8BitDo F30 Pro,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000090000000000000,8BitDo FC30 Pro,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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:a4,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00005106000000000000,8BitDo M30 Gamepad,a:b1,b:b0,back:b10,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000151000000000000,8BitDo M30 ModKit,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,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, 03000000c82d00002028000000000000,8BitDo N30,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00008010000000000000,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, +03000000c82d00000451000000000000,8BitDo N30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,start:b11,platform:Windows, 03000000c82d00000190000000000000,8BitDo N30 Pro,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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001590000000000000,8BitDo N30 Pro 2,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006528000000000000,8BitDo N30 Pro 2,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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000022000000090000000000000,8Bitdo NES30 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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, 03000000203800000900000000000000,8Bitdo NES30 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:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows, +03000000c82d00000360000000000000,8BitDo Pro 2,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, +03000000c82d00002867000000000000,8BitDo S30 Modkit,a:b0,b:b1,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00000130000000000000,8BitDo SF30,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000060000000000000,8Bitdo SF30 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:Windows, 03000000c82d00000061000000000000,8Bitdo SF30 Pro,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:Windows, @@ -24,19 +29,22 @@ 03000000102800000900000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003028000000000000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000030000000000000,8BitDo SN30,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, -03000000c82d00000351000000000000,8BitDo SN30,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00001290000000000000,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:Windows, 03000000c82d000020ab000000000000,8BitDo SN30,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00004028000000000000,8BitDo SN30,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:a5,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00006228000000000000,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:Windows, +03000000c82d00000351000000000000,8BitDo SN30 Modkit,a:b1,b:b0,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00000160000000000000,8BitDo SN30 Pro,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:Windows, 03000000c82d00000161000000000000,8BitDo SN30 Pro,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:Windows, +03000000c82d00000121000000000000,8BitDo SN30 Pro for Android,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, 03000000c82d00000260000000000000,8BitDo SN30 Pro+,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:Windows, 03000000c82d00000261000000000000,8BitDo SN30 Pro+,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:Windows, 03000000c82d00000031000000000000,8BitDo Wireless Adapter,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000c82d00001890000000000000,8BitDo Zero 2,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Windows, 03000000c82d00003032000000000000,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:a3,start:b11,x:b4,y:b3,platform:Windows, 03000000a00500003232000000000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows, +03000000a30c00002700000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +03000000a30c00002800000000000000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, 030000008f0e00001200000000000000,Acme GA-02,a:b0,b:b1,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:b2,y:b3,platform:Windows, 03000000c01100000355000011010000,ACRUX USB GAME PAD,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, 03000000fa190000f0ff000000000000,Acteck AGJ-3200,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, @@ -49,7 +57,9 @@ 030000006f0e00001901000000000000,Afterglow 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, 030000006f0e00001a01000000000000,Afterglow 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, 03000000d62000001d57000000000000,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, -03000000869800002400000000007801,Astro C40 TR,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, +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, +03000000ef0500000300000000000000,AxisPad,a:b2,b:b3,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:a3,righty:a2,start:b11,x:b0,y:b1,platform:Windows, 03000000d6200000e557000000000000,Batarang,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, 030000006f0e00003201000000000000,Battlefield 4 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, @@ -65,7 +75,7 @@ 03000000808300000300000000000000,Betop 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:a4,start:b9,x:b3,y:b0,platform:Windows, 030000006b1400000055000000000000,Bigben PS3 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:Windows, 030000006b1400000103000000000000,Bigben PS3 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:b3,y:b2,platform:Windows, -03000000120c0000210e000000000000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Windows, +03000000120c0000210e000000000000,Brook Mars,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, 0300000066f700000500000000000000,BrutalLegendTest,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, 03000000d81d00000b00000000000000,BUFFALO BSGP1601 Series ,a:b5,b:b3,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b9,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b13,x:b4,y:b2,platform:Windows, 03000000e82000006058000000000000,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:Windows, @@ -106,7 +116,6 @@ 03000000ac0500004d04000000000000,GameSir,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:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows, 03000000ffff00000000000000000000,GameStop 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:Windows, 03000000c01100000140000000000000,GameStop PS4 Fun 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, -030000006f0e00000102000000007801,GameStop Xbox 360 Wired 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, 030000009b2800003200000000000000,GC/N64 to USB v3.4,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,GC/N64 to USB v3.6,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, 030000008305000009a0000000000000,Genius,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, @@ -157,6 +166,7 @@ 030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Windows, 030000007e0500000720000000000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Windows, +03000000bd12000003c0000010010000,Joypad Alpha Shock,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, 03000000bd12000003c0000000000000,JY-P70UR,a:b1,b:b0,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b11,righttrigger:b9,rightx:a3,righty:a2,start:b4,x:b3,y:b2,platform:Windows, 03000000242f00002d00000000000000,JYS Wireless Adapter,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:Windows, 03000000242f00008a00000000000000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b0,y:b3,platform:Windows, @@ -184,6 +194,7 @@ 03000000380700001888000000000000,MadCatz SFIV FightStick PS3,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b6,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000380700008081000000000000,MADCATZ SFV Arcade FightStick Alpha PS4,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:a5,start:b9,x:b0,y:b3,platform:Windows, 030000002a0600001024000000000000,Matricom,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:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows, +030000009f000000adbb000000000000,MaxJoypad Virtual Controller,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows, 03000000250900000128000000000000,Mayflash Arcade Stick,a:b1,b:b2,back:b8,leftshoulder:b0,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b3,righttrigger:b7,start:b9,x:b5,y:b6,platform:Windows, 03000000790000004418000000000000,Mayflash GameCube Controller,a:b1,b:b2,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:b0,y:b3,platform:Windows, 03000000790000004318000000000000,Mayflash GameCube Controller Adapter,a:b1,b:b2,back:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b0,leftshoulder:b4,leftstick:b0,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Windows, @@ -202,6 +213,8 @@ 03000000efbe0000edfe000000000000,Monect Virtual Controller,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:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000250900006688000000000000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,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:b8,x:b3,y:b0,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, +03000000921200004b46000000000000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Windows, +03000000790000004518000000000000,NEXILUX GAMECUBE Controller Adapter,platform:Windows,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4, 030000001008000001e5000000000000,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:Windows, 03000000152000000182000000000000,NGDS,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:a3,righty:a4,start:b9,x:b3,y:b0,platform:Windows, 03000000bd12000015d0000000000000,Nintendo Retrolink USB Super SNES Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows, @@ -217,6 +230,7 @@ 030000006f0e00000901000000000000,PDP Versus Fighting Pad,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:Windows, 030000008f0e00000300000000000000,Piranha xtreme,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:a3,righty:a2,start:b9,x:b3,y:b0,platform:Windows, 030000004c050000da0c000000000000,PlayStation Classic Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, +030000004c0500003713000000000000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows, 03000000d62000006dca000000000000,PowerA Pro Ex,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, 03000000d62000009557000000000000,Pro Elite 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, 03000000d62000009f31000000000000,Pro Ex mini 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, @@ -241,6 +255,7 @@ 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: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, 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:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows, +03000000ff000000cb01000000000000,PSP,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:Windows, 03000000300f00000011000000000000,QanBa Arcade JoyStick 1008,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b10,x:b0,y:b3,platform:Windows, 03000000300f00001611000000000000,QanBa Arcade JoyStick 4018,a:b1,b:b2,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b8,x:b0,y:b3,platform:Windows, 03000000222c00000020000000000000,QANBA DRONE ARCADE JOYSTICK,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,rightshoulder:b5,righttrigger:a4,start:b9,x:b0,y:b3,platform:Windows, @@ -292,7 +307,6 @@ 030000009b2800000500000000000000,Saturn_Adapter_2.0,a:b1,b:b2,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows, 03000000a30c00002500000000000000,Sega Genesis Mini 3B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,righttrigger:b5,start:b9,platform:Windows, 03000000a30c00002400000000000000,Sega Mega Drive Mini 6B controller,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Windows, -030000005e0400008e02000000007801,ShanWan PS3/PC Wired GamePad,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, 03000000341a00000208000000000000,SL-6555-SBK,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:-a4,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a3,righty:a2,start:b7,x:b2,y:b3,platform:Windows, 03000000341a00000908000000000000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 030000008f0e00000800000000000000,SpeedLink Strike FX,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, @@ -337,8 +351,7 @@ 030000006f0e00000702000000000000,Victrix Pro Fight Stick for 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:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows, 0300000034120000adbe000000000000,vJoy Device,a:b0,b:b1,back:b15,dpdown:b6,dpleft:b7,dpright:b8,dpup:b5,guide:b16,leftshoulder:b9,leftstick:b13,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b14,righttrigger:b12,rightx:a3,righty:a4,start:b4,x:b2,y:b3,platform:Windows, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,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, -030000005e040000ff02000000007801,Xbox One Elite Controller,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, -030000005e040000130b000000000000,Xbox Series Controller,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, +030000005e040000130b000000000000,Xbox Series 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, 03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows, 03000000450c00002043000000000000,XEOX Gamepad SL-6556-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:Windows, 03000000ac0500005b05000000000000,Xiaoji Gamesir-G3w,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:Windows, @@ -362,6 +375,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c82d00000190000001000000,8Bitdo NES30 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:Mac OS X, 03000000102800000900000000000000,8Bitdo SFC30 GamePad Joystick,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00001290000001000000,8BitDo SN30 Gamepad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X, +03000000c82d00004028000000010000,8Bitdo SN30 GamePad,a:b1,b:b0,x:b4,y:b3,back:b10,start:b11,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,platform:Mac OS X, 03000000c82d00000160000001000000,8BitDo SN30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000161000000010000,8BitDo SN30 Pro,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:a5,start:b11,x:b4,y:b3,platform:Mac OS X, 03000000c82d00000260000001000000,8BitDo SN30 Pro+,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, @@ -371,21 +385,28 @@ 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, 03000000a00500003232000008010000,8Bitdo Zero GamePad,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, 03000000a00500003232000009010000,8Bitdo Zero GamePad,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, +03000000a30c00002700000003030000,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, +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, +03000000ef0500000300000000020000,AxisPad,a:b2,b:b3,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:a3,righty:a2,start:b11,x:b0,y:b1,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, 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, -03000000120c0000200e000000010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Mac OS X, -03000000120c0000210e000000010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, +03000000120c0000200e000000010000,Brook Mars,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, +03000000120c0000210e000000010000,Brook Mars,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, 030000008305000031b0000000000000,Cideko AK08b,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:Mac OS X, 03000000260900008888000088020000,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:Mac OS X, 03000000a306000022f6000001030000,Cyborg V.3 Rumble Pad,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:Mac OS X, +03000000790000004618000000010000,GameCube Controller Adapter,a:b4,b:b0,dpdown:b56,dpleft:b60,dpright:b52,dpup:b48,lefttrigger:a12,leftx:a0,lefty:a4,rightshoulder:b28,righttrigger:a16,rightx:a20,righty:a8,start:b36,x:b8,y:b12,platform:Mac OS X, 03000000ad1b000001f9000000000000,Gamestop BB-070 X360 Controller,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, 0500000047532047616d657061640000,GameStop 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:Mac OS X, 03000000c01100000140000000010000,GameStop PS4 Fun 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, 030000006f0e00000102000000000000,GameStop Xbox 360 Wired Controller,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, 030000007d0400000540000001010000,Gravis Eliminator GamePad Pro,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, -030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,x:b0,y:b1,back:b8,start:b9,leftshoulder:b4,rightshoulder:b6,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a3,righty:a2,lefttrigger:b5,righttrigger:b7,platform:Mac OS X, +03000000280400000140000000020000,Gravis Gamepad Pro,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Mac OS X, +030000008f0e00000300000007010000,GreenAsia Inc. USB Joystick,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Mac OS X, 030000000d0f00002d00000000100000,Hori Fighting Commander 3 Pro,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:Mac OS X, 030000000d0f00005f00000000010000,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:Mac OS X, 030000000d0f00005e00000000010000,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:Mac OS X, @@ -416,11 +437,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000380700005082000000010000,Mad Catz FightPad PRO (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, 03000000380700008433000000010000,Mad Catz FightStick TE S+ (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, 03000000380700008483000000010000,Mad Catz FightStick TE S+ (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, +03000000790000000600000007010000,Marvo GT-004,a:b2,b:b1,x:b3,y:b0,back:b8,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Mac OS X, 03000000790000004418000000010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000242f00007300000000020000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Mac OS X, 0300000079000000d218000026010000,Mayflash Magic NS,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, 03000000d620000010a7000003010000,Mayflash Magic NS,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, 0300000025090000e803000000000000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:b13,dpleft:b12,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Mac OS X, +03000000790000000018000000010000,Mayflash Wii U Pro Controller Adapter,a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 03000000790000000018000000000000,Mayflash WiiU Pro Game Controller Adapter (DInput),a:b4,b:b8,back:b32,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b16,leftstick:b40,lefttrigger:b24,leftx:a0,lefty:a4,rightshoulder:b20,rightstick:b44,righttrigger:b28,rightx:a8,righty:a12,start:b36,x:b0,y:b12,platform:Mac OS X, 03000000d8140000cecf000000000000,MC Cthulhu,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, 030000005e0400002700000001010000,Microsoft SideWinder Plug & Play Game Pad,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,leftx:a0,lefty:a1,righttrigger:b5,x:b2,y:b3,platform:Mac OS X, @@ -428,15 +451,19 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000c62400002a89000000010000,MOGA XP5-A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, 03000000c62400002b89000000010000,MOGA XP5-A 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, 03000000632500007505000000020000,NEOGEO mini PAD Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b9,x:b2,y:b3,platform:Mac OS X, +03000000921200004b46000003020000,NES 2-port Adapter,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b11,platform:Mac OS X, 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, 03000000d620000011a7000000020000,Nintendo Switch Core (Plus) 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: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, +03000000d620000011a7000010050000,Nintendo Switch PowerA 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: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, 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, 03000000550900001472000025050000,NVIDIA Controller v01.04,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, 030000006f0e00000901000002010000,PDP Versus Fighting Pad,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:Mac OS X, 030000008f0e00000300000000000000,Piranha xtreme,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:a3,righty:a2,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c050000da0c000000010000,Playstation Classic Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X, +030000004c0500003713000000010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Mac OS X, 03000000d62000006dca000000010000,PowerA Pro Ex,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, +03000000100800000300000006010000,PS2 Adapter,a:b2,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a4,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X, 030000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Mac OS X, 030000004c050000a00b000000010000,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, @@ -487,9 +514,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005769696d6f74652028303000,Wii Remote,a:b4,b:b5,back:b7,dpdown:b3,dpleft:b0,dpright:b1,dpup:b2,guide:b8,leftshoulder:b11,lefttrigger:b12,leftx:a0,lefty:a1,start:b6,x:b10,y:b9,platform:Mac OS X, 050000005769696d6f74652028313800,Wii U Pro Controller,a:b16,b:b15,back:b7,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b8,leftshoulder:b19,leftstick:b23,lefttrigger:b21,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b24,righttrigger:b22,rightx:a2,righty:a3,start:b6,x:b18,y:b17,platform:Mac OS X, 030000005e0400008e02000000000000,X360 Controller,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, +030000006f0e00000104000000000000,Xbox 360 Wired Controller,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, 03000000c6240000045d000000000000,Xbox 360 Wired Controller,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, 030000005e0400000a0b000000000000,Xbox Adaptive Controller,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, 030000005e040000050b000003090000,Xbox Elite Wireless Controller Series 2,a:b0,b:b1,back:b31,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b53,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X, +03000000c62400003a54000000000000,Xbox One PowerA Wired Controller,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, 030000005e040000d102000000000000,Xbox One Wired Controller,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, 030000005e040000dd02000000000000,Xbox One Wired Controller,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, 030000005e040000e302000000000000,Xbox One Wired Controller,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, @@ -545,18 +574,28 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux, 030000007c1800000006000010010000,Alienware Dual Compatible Game Pad,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:Linux, 05000000491900000204000021000000,Amazon Fire Game 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, 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,leftx:a0,lefty: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, 05000000050b00000045000031000000,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, 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, +03000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux, +05000000503200000110000000000000,Atari Classic Controller,a:b0,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,x:b1,platform:Linux, +03000000503200000210000000000000,Atari Game 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 Game 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, 03000000120c00000500000010010000,AxisPad,a:b2,b:b3,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:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, +03000000ef0500000300000000010000,AxisPad,a:b2,b:b3,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:a3,righty:a2,start:b11,x:b0,y:b1,platform:Linux, 03000000c62400001b89000011010000,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: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 mode,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 GAMEPAD 1.03 mode,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 Controller mode,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, 03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,platform:Linux, -03000000120c0000200e000011010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a5,lefttrigger:a3,righttrigger:a4,platform:Linux, -03000000120c0000210e000011010000,Brook Mars,a:b1,b:b2,x:b0,y:b3,back:b8,guide:b12,start:b9,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Linux, +03000000120c0000200e000011010000,Brook Mars,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,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, 03000000ffff0000ffff000000010000,Chinese-made Xbox Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,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, 030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux, @@ -621,6 +660,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,platform:Linux, 030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, +03000000bd12000003c0000010010000,Joypad Alpha Shock,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, 03000000242f00002d00000011010000,JYS Wireless Adapter,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, 03000000242f00008a00000011010000,JYS Wireless Adapter,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 030000006f0e00000103000000020000,Logic3 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, @@ -650,8 +690,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000ad1b000016f0000090040000,Mad Catz Xbox 360 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, 03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,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:Linux, 03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux, +03000000242f0000f700000001010000,Magic-S 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, 03000000120c00000500000000010000,Manta Dualshock 2,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:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux, -03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,platform:Linux, +03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux, 03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b2,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:b0,y:b3,platform:Linux, 03000000242f00007300000011010000,Mayflash Magic NS,a:b1,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b3,platform:Linux, 0300000079000000d218000011010000,Mayflash Magic NS,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, @@ -681,15 +722,18 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,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:b8,x:b3,y:b0,platform:Linux, 030000006b140000010c000010010000,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:Linux, 030000000d0f00000900000010010000,Natec Genesis P44,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, +03000000790000004518000010010000,NEXILUX GAMECUBE Controller Adapter,a:b1,b:b0,x:b2,y:b3,start:b9,rightshoulder:b7,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a2,lefttrigger:a3,righttrigger:a4,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, 060000007e0500000820000000000000,Nintendo Combined Joy-Cons (joycond),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,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,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:b2,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:b0,y:b3,platform:Linux, -050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,x:b7,y:b10,back:b5,start:b0,leftstick:b6,leftshoulder:b2,rightshoulder:b4,leftx:a1,lefty:a0~,platform:Linux, -050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,x:b0,y:b3,back:b9,start:b8,leftstick:b10,leftshoulder:b4,rightshoulder:b6,leftx:a1~,lefty:a0~,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, +050000007e0500000620000001800000,Nintendo Switch Left Joy-Con,a:b9,b:b8,back:b5,leftshoulder:b2,leftstick:b6,leftx:a1,lefty:a0~,rightshoulder:b4,start:b0,x:b7,y:b10,platform:Linux, +030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, 050000007e0500000920000001000000,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:Linux, 050000007e0500000920000001800000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, -030000007e0500000920000011810000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux, +050000007e0500000720000001800000,Nintendo Switch Right Joy-Con,a:b1,b:b2,back:b9,leftshoulder:b4,leftstick:b10,leftx:a1~,lefty:a0~,rightshoulder:b6,start:b8,x:b0,y:b3,platform:Linux, +050000007e0500001720000001000000,Nintendo Switch SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b2,y:b3,platform:Linux, 050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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:b3,y:b2,platform:Linux, 05000000010000000100000003000000,Nintendo Wiimote,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, 030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,platform:Linux, @@ -717,8 +761,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000006f0e0000a802000023020000,PDP Wired Controller for Xbox One,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, 030000006f0e00008501000011010000,PDP Wired Fight Pad 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, 0500000049190000030400001b010000,PG-9099,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,PG-9118,a:b73,b:b74,back:b83,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b79,leftstick:b86,lefttrigger:b81,leftx:a0,lefty:a1,rightshoulder:b80,rightstick:b87,righttrigger:b82,rightx:a2,righty:a3,start:b84,x:b76,y:b77,platform:Linux, +05000000491900000204000000000000,PG-9118,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, 030000004c050000da0c000011010000,Playstation Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux, +030000004c0500003713000011010000,PlayStation Vita,a:b1,b:b2,back:b8,dpdown:b13,dpleft:b15,dpright:b14,dpup:b12,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000c62400000053000000010000,PowerA,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, 03000000c62400003a54000001010000,PowerA 1428124-01,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, 03000000d62000006dca000011010000,PowerA Pro Ex,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, @@ -734,6 +779,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, 030000006f0e00001402000011010000,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, 030000008f0e00000300000010010000,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, +050000004c0500006802000000000000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,platform:Linux, 050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, 050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,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, @@ -755,7 +801,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 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, 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:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, 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,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux, +03000000ff000000cb01000010010000,PSP,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, 03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,platform:Linux, +030000009b2800004200000001010000,Raphnet Technologies Dual NES to USB v2.0,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux, 030000009b2800003200000001010000,Raphnet Technologies GC/N64 to USB v3.4,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 Technologies GC/N64 to USB v3.6,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, 030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux, @@ -792,7 +840,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000a306000020f6000011010000,Saitek PS2700 Rumble Pad,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:a4,start:b9,x:b0,y:b3,platform:Linux, 03000000d81d00000e00000010010000,Savior,a:b0,b:b1,back:b8,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,start:b9,x:b4,y:b5,platform:Linux, -03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,platform:Linux, 03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired 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 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 PS3/PC Wired 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, @@ -800,17 +847,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 03000000632500002305000010010000,ShanWan USB 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, 03000000341a00000908000010010000,SL-6566,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, 030000004c050000e60c000011810000,Sony DualSense,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, -030000004c050000e60c000000006800,Sony DualSense,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux, 050000004c050000e60c000000810000,Sony DualSense ,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, -030000004c050000e60c000000016800,Sony DualSense ,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:b11,dpdown:b12,dpleft:b13,dpright:b14,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:Linux, 03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,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:b8,x:b3,y:b0,platform:Linux, 030000005e0400008e02000073050000,Speedlink TORID Wireless 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, 030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,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, 03000000d11800000094000011010000,Stadia 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:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux, 03000000de2800000112000001000000,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, 03000000de2800000211000001000000,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, -03000000de2800004211000001000000,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, 03000000de2800000211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, +03000000de2800004211000001000000,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, 03000000de2800004211000011010000,Steam Controller,a:b2,b:b3,back:b10,dpdown:b18,dpleft:b19,dpright:b20,dpup:b17,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,paddle1:b15,paddle2:b16,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b5,platform:Linux, 03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,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, 05000000de2800000212000001000000,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, @@ -874,6 +919,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 030000005e040000130b000005050000,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:Linux, 050000005e040000130b000001050000,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:Linux, 050000005e040000130b000005050000,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:Linux, +030000005e040000120b000005050000,XBox Series pad,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, 030000005e0400008e02000000010000,xbox360 Wireless EasySMX,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, 03000000450c00002043000010010000,XEOX Gamepad SL-6556-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, 03000000ac0500005b05000010010000,Xiaoji Gamesir-G3w,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, @@ -900,7 +946,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000003512000020ab000000780f00,8BitDo SNES30 Gamepad,a:b21,b:b20,back:b30,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b26,rightshoulder:b27,start:b31,x:b24,y:b23,platform:Android, 05000000c82d000018900000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, 05000000c82d000030320000ffff0f00,8BitDo Zero 2,a:b1,b:b0,back:b4,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android, -38383337343564366131323064613561,Brook Mars,a:b1,b:b19,x:b0,y:b2,leftshoulder:b3,rightshoulder:b20,lefttrigger:b9,righttrigger:b10,back:b17,start:b18,leftx:a0,lefty:a1,rightx:a2,righty:a3,leftstick:b15,rightstick:b6,platform:Android, +38383337343564366131323064613561,Brook Mars,a:b1,b:b19,back:b17,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b0,y:b2,platform:Android, 05000000bc20000000550000ffff3f00,GameSir G3w,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 0500000031366332860c44aadfff0f00,GS Gamepad,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:b15,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b16,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, @@ -916,9 +962,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000005509000014720000df7f3f00,NVIDIA Controller v01.04,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Android, 050000004c05000068020000dfff3f00,PS3 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 030000004c050000cc09000000006800,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +050000004c050000c405000000783f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 050000004c050000c4050000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000c4050000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, -050000004c050000cc090000fffe3f00,PS4 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, +050000004c050000cc090000fffe3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 050000004c050000cc090000ffff3f00,PS4 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 35643031303033326130316330353564,PS4 Controller,a:b1,b:b17,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:+a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, 050000004c050000e60c0000fffe3f00,PS5 Controller,a:b1,b:b17,back:b15,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b3,leftstick:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b6,righttrigger:a4,rightx:a2,righty:a5,start:b16,x:b0,y:b2,platform:Android, @@ -933,9 +980,14 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 050000004f0400000ed00000fffe3f00,ThrustMaster eSwap PRO Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 5477696e20555342204a6f7973746963,Twin USB Joystick,a:b22,b:b21,back:b28,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b26,leftstick:b30,lefttrigger:b24,leftx:a0,lefty:a1,rightshoulder:b27,rightstick:b31,righttrigger:b25,rightx:a3,righty:a2,start:b29,x:b23,y:b20,platform:Android, 30306539356238653637313730656134,Wireless HORIPAD Switch Pro Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b3,leftstick:b15,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,rightstick:b6,righttrigger:b10,rightx:a2,righty:a3,start:b18,x:b19,y:b2,platform:Android, +050000005e0400008e02000000783f00,Xbox 360 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +050000005e040000000b000000783f00,Xbox One Elite 2 Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, +050000005e040000e002000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, +050000005e040000ea02000000783f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 050000005e040000fd020000ff7f3f00,Xbox One S Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 050000005e040000e00200000ffe3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b3,leftstick:b15,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b18,rightstick:b16,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b17,y:b2,platform:Android, 050000005e040000fd020000ffff3f00,Xbox One Wireless Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, +050000005e040000120b000000783f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, 050000005e040000130b0000ffff3f00,Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,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, 65633038363832353634653836396239,Xbox Series Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android, 050000005e04000091020000ff073f00,Xbox Wireless Controller,a:b0,b:b1,back:b4,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Android, @@ -953,13 +1005,17 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2, 4d466947616d65706164010000000000,MFi Extended Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:iOS, 4d466947616d65706164020000000000,MFi Gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:iOS, 050000004c050000cc090000df070000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, +050000004c050000cc090000df870001,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff070000,PS4 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:iOS, 050000004c050000cc090000ff870001,PS4 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,touchpad:b11,x:b2,y:b3,platform:iOS, 050000004c050000cc090000ff876d01,PS4 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,misc1:b11,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, +050000004c050000e60c0000df870000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,touchpad:b10,x:b2,y:b3,platform:iOS, +050000004c050000e60c0000ff870000,PS5 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,touchpad:b11,x:b2,y:b3,platform:iOS, 05000000ac0500000300000000006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000ac0500000300000043006d03,Remote,a:b0,b:b2,leftx:a0,lefty:a1,platform:iOS, 05000000de2800000511000001000000,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:iOS, 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:iOS, +050000005e040000050b0000df070001,Xbox Elite Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b10,paddle2:b12,paddle3:b11,paddle4:b13,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000050b0000ff070001,Xbox Elite Wireless 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,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:iOS, 050000005e040000e0020000df070000,Xbox Wireless Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:iOS, 050000005e040000e0020000ff070000,Xbox Wireless 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:iOS, diff --git a/core/input/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt index db612f04d2..5985b121c9 100644 --- a/core/input/godotcontrollerdb.txt +++ b/core/input/godotcontrollerdb.txt @@ -29,6 +29,8 @@ Linux0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshould Windows0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a1,dpleft:-a0,dpdown:+a1,dpright:+a0,platform:Javascript MacOSX0e8f3013,HuiJia USB GamePad,a:b2,b:b1,y:b0,x:b3,start:b9,back:b8,leftshoulder:b6,rightshoulder:b7,dpup:-a4,dpleft:-a3,dpdown:+a4,dpright:+a3,platform:Javascript Linux046dc216,046d-c216-Logitech Logitech Dual Action,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Javascript +Linux20d6a713,Bensussen Deutsch & Associates Inc.(BDA) NSW Wired controller,a:b1,b:b2,y:b3,x:b0,start:b9,back:b8,leftstick:b10,rightstick:b11,leftshoulder:b4,rightshoulder:b5,dpup:-a5,dpleft:-a4,dpdown:+a5,dpright:+a4,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7,platform:Javascript +Linux054c05c4,Sony Computer Entertainment Wireless Controller,a:b0,b:b1,y:b2,x:b3,start:b9,back:b8,leftstick:b11,rightstick:b12,leftshoulder:b4,rightshoulder:b5,dpup:-a7,dpleft:-a6,dpdown:+a7,dpright:+a6,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,platform:Javascript # UWP __UWP_GAMEPAD__,Xbox Controller,a:b2,b:b3,x:b4,y:b5,start:b0,back:b1,leftstick:b12,rightstick:b13,leftshoulder:b10,rightshoulder:b11,dpup:b6,dpdown:b7,dpleft:b8,dpright:b9,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a4,righttrigger:a5,platform:UWP, diff --git a/core/input/input.cpp b/core/input/input.cpp index a712394b35..2da50b7dff 100644 --- a/core/input/input.cpp +++ b/core/input/input.cpp @@ -101,7 +101,7 @@ void Input::_bind_methods() { ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action", "exact_match"), &Input::is_action_just_pressed, DEFVAL(false)); ClassDB::bind_method(D_METHOD("is_action_just_released", "action", "exact_match"), &Input::is_action_just_released, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_action_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_strength, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_action_raw_strength", "action", "exact_match"), &Input::get_action_raw_strength, DEFVAL(false)); ClassDB::bind_method(D_METHOD("get_axis", "negative_action", "positive_action"), &Input::get_axis); ClassDB::bind_method(D_METHOD("get_vector", "negative_x", "positive_x", "negative_y", "positive_y", "deadzone"), &Input::get_vector, DEFVAL(-1.0f)); ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); @@ -169,13 +169,12 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S 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_axis" || pf == "get_vector")) { + pf == "get_action_strength" || pf == "get_action_raw_strength" || + pf == "get_axis" || pf == "get_vector")) { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - const PropertyInfo &pi = E->get(); - + for (const PropertyInfo &pi : pinfo) { if (!pi.name.begins_with("input/")) { continue; } @@ -222,7 +221,7 @@ Input::SpeedTrack::SpeedTrack() { reset(); } -bool Input::is_key_pressed(int p_keycode) const { +bool Input::is_key_pressed(Key p_keycode) const { _THREAD_SAFE_METHOD_ return keys_pressed.has(p_keycode); } @@ -438,7 +437,7 @@ void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, S } joy_names[p_idx] = js; - emit_signal("joy_connection_changed", p_idx, p_connected); + emit_signal(SNAME("joy_connection_changed"), p_idx, p_connected); } Vector3 Input::get_gravity() const { diff --git a/core/input/input.h b/core/input/input.h index fbcd5836ea..ee991aa725 100644 --- a/core/input/input.h +++ b/core/input/input.h @@ -33,6 +33,7 @@ #include "core/input/input_event.h" #include "core/object/object.h" +#include "core/os/keyboard.h" #include "core/os/thread_safe.h" class Input : public Object { @@ -244,7 +245,7 @@ public: static Input *get_singleton(); - bool is_key_pressed(int p_keycode) const; + bool is_key_pressed(Key p_keycode) const; bool is_mouse_button_pressed(MouseButton p_button) const; bool is_joy_button_pressed(int p_device, JoyButton p_button) const; bool is_action_pressed(const StringName &p_action, bool p_exact = false) const; diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp index 4a2abffae8..6e5c1a58ae 100644 --- a/core/input/input_event.cpp +++ b/core/input/input_event.cpp @@ -32,6 +32,7 @@ #include "core/input/input_map.h" #include "core/os/keyboard.h" +#include "scene/gui/shortcut.h" const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; const int InputEvent::DEVICE_ID_INTERNAL = -2; @@ -306,21 +307,21 @@ bool InputEventKey::is_pressed() const { return pressed; } -void InputEventKey::set_keycode(uint32_t p_keycode) { +void InputEventKey::set_keycode(Key p_keycode) { keycode = p_keycode; emit_changed(); } -uint32_t InputEventKey::get_keycode() const { +Key InputEventKey::get_keycode() const { return keycode; } -void InputEventKey::set_physical_keycode(uint32_t p_keycode) { +void InputEventKey::set_physical_keycode(Key p_keycode) { physical_keycode = p_keycode; emit_changed(); } -uint32_t InputEventKey::get_physical_keycode() const { +Key InputEventKey::get_physical_keycode() const { return physical_keycode; } @@ -386,7 +387,7 @@ String InputEventKey::to_string() { return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e); } -Ref<InputEventKey> InputEventKey::create_reference(uint32_t p_keycode) { +Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode) { Ref<InputEventKey> ie; ie.instantiate(); ie->set_keycode(p_keycode & KEY_CODE_MASK); @@ -1512,3 +1513,26 @@ void InputEventMIDI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_number"), "set_controller_number", "get_controller_number"); ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_value"), "set_controller_value", "get_controller_value"); } + +/////////////////////////////////// + +void InputEventShortcut::set_shortcut(Ref<Shortcut> p_shortcut) { + shortcut = p_shortcut; + emit_changed(); +} + +Ref<Shortcut> InputEventShortcut::get_shortcut() { + return shortcut; +} + +bool InputEventShortcut::is_pressed() const { + return true; +} + +String InputEventShortcut::as_text() const { + return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text()); +} + +String InputEventShortcut::to_string() { + return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text()); +} diff --git a/core/input/input_event.h b/core/input/input_event.h index 76a45c04a4..57b6091123 100644 --- a/core/input/input_event.h +++ b/core/input/input_event.h @@ -34,6 +34,7 @@ #include "core/input/input_enums.h" #include "core/io/resource.h" #include "core/math/transform_2d.h" +#include "core/os/keyboard.h" #include "core/string/ustring.h" #include "core/typedefs.h" @@ -42,6 +43,8 @@ * The events are pretty obvious. */ +class Shortcut; + /** * Input Modifier Status * for keyboard/mouse events. @@ -161,8 +164,8 @@ class InputEventKey : public InputEventWithModifiers { bool pressed = false; /// otherwise release - uint32_t keycode = 0; ///< check keyboard.h , KeyCode enum, without modifier masks - uint32_t physical_keycode = 0; + Key keycode = KEY_NONE; // Key enum, without modifier masks. + Key physical_keycode = KEY_NONE; uint32_t unicode = 0; ///unicode bool echo = false; /// true if this is an echo key @@ -174,11 +177,11 @@ public: void set_pressed(bool p_pressed); virtual bool is_pressed() const override; - void set_keycode(uint32_t p_keycode); - uint32_t get_keycode() const; + void set_keycode(Key p_keycode); + Key get_keycode() const; - void set_physical_keycode(uint32_t p_keycode); - uint32_t get_physical_keycode() const; + void set_physical_keycode(Key p_keycode); + Key get_physical_keycode() const; void set_unicode(uint32_t p_unicode); uint32_t get_unicode() const; @@ -197,7 +200,7 @@ public: virtual String as_text() const override; virtual String to_string() override; - static Ref<InputEventKey> create_reference(uint32_t p_keycode_with_modifier_masks); + static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks); InputEventKey() {} }; @@ -538,4 +541,18 @@ public: InputEventMIDI() {} }; +class InputEventShortcut : public InputEvent { + GDCLASS(InputEventShortcut, InputEvent); + + Ref<Shortcut> shortcut; + +public: + void set_shortcut(Ref<Shortcut> p_shortcut); + Ref<Shortcut> get_shortcut(); + virtual bool is_pressed() const override; + + virtual String as_text() const override; + virtual String to_string() override; +}; + #endif // INPUT_EVENT_H diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp index b5f067d499..15be0f1e36 100644 --- a/core/input/input_map.cpp +++ b/core/input/input_map.cpp @@ -65,11 +65,11 @@ String InputMap::_suggest_actions(const StringName &p_action) const { float closest_similarity = 0.0; // Find the most action with the most similar name. - for (List<StringName>::Element *E = actions.front(); E; E = E->next()) { - const float similarity = String(E->get()).similarity(p_action); + for (const StringName &action : actions) { + const float similarity = String(action).similarity(p_action); if (similarity > closest_similarity) { - closest_action = E->get(); + closest_action = action; closest_similarity = similarity; } } @@ -105,8 +105,8 @@ Array InputMap::_get_actions() { return ret; } - for (const List<StringName>::Element *E = actions.front(); E; E = E->next()) { - ret.push_back(E->get()); + for (const StringName &E : actions) { + ret.push_back(E); } return ret; @@ -129,13 +129,11 @@ List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Re ERR_FAIL_COND_V(!p_event.is_valid(), nullptr); for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) { - const Ref<InputEvent> e = E->get(); - - int device = e->get_device(); + int device = E->get()->get_device(); if (device == ALL_DEVICES || device == p_event->get_device()) { - if (p_exact_match && e->is_match(p_event, true)) { + if (p_exact_match && E->get()->is_match(p_event, true)) { return E; - } else if (!p_exact_match && e->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { + } else if (!p_exact_match && E->get()->action_match(p_event, p_pressed, p_strength, p_raw_strength, p_action.deadzone)) { return E; } } @@ -263,9 +261,7 @@ void InputMap::load_from_project_settings() { List<PropertyInfo> pinfo; ProjectSettings::get_singleton()->get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - const PropertyInfo &pi = E->get(); - + for (const PropertyInfo &pi : pinfo) { if (!pi.name.begins_with("input/")) { continue; } diff --git a/core/io/compression.cpp b/core/io/compression.cpp index 6de626db99..790b6febc0 100644 --- a/core/io/compression.cpp +++ b/core/io/compression.cpp @@ -134,8 +134,9 @@ int Compression::decompress(uint8_t *p_dst, int p_dst_max_size, const uint8_t *p if (p_dst_max_size < 16) { uint8_t dst[16]; - ret_size = fastlz_decompress(p_src, p_src_size, dst, 16); + fastlz_decompress(p_src, p_src_size, dst, 16); memcpy(p_dst, dst, p_dst_max_size); + ret_size = p_dst_max_size; } else { ret_size = fastlz_decompress(p_src, p_src_size, p_dst, p_dst_max_size); } @@ -238,7 +239,10 @@ int Compression::decompress_dynamic(Vector<uint8_t> *p_dst_vect, int p_max_dst_s case Z_DATA_ERROR: case Z_MEM_ERROR: case Z_STREAM_ERROR: - WARN_PRINT(strm.msg); + case Z_BUF_ERROR: + if (strm.msg) { + WARN_PRINT(strm.msg); + } (void)inflateEnd(&strm); p_dst_vect->resize(0); return ret; diff --git a/core/io/config_file.cpp b/core/io/config_file.cpp index 10f68f3cef..aeaf25f321 100644 --- a/core/io/config_file.cpp +++ b/core/io/config_file.cpp @@ -40,8 +40,8 @@ PackedStringArray ConfigFile::_get_sections() const { PackedStringArray arr; arr.resize(s.size()); int idx = 0; - for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); + for (const String &E : s) { + arr.set(idx++, E); } return arr; @@ -53,8 +53,8 @@ PackedStringArray ConfigFile::_get_section_keys(const String &p_section) const { PackedStringArray arr; arr.resize(s.size()); int idx = 0; - for (const List<String>::Element *E = s.front(); E; E = E->next()) { - arr.set(idx++, E->get()); + for (const String &E : s) { + arr.set(idx++, E); } return arr; diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp index dfba00067f..8234adea06 100644 --- a/core/io/dir_access.cpp +++ b/core/io/dir_access.cpp @@ -93,8 +93,8 @@ static Error _erase_recursive(DirAccess *da) { da->list_dir_end(); - for (List<String>::Element *E = dirs.front(); E; E = E->next()) { - Error err = da->change_dir(E->get()); + for (const String &E : dirs) { + Error err = da->change_dir(E); if (err == OK) { err = _erase_recursive(da); if (err) { @@ -105,7 +105,7 @@ static Error _erase_recursive(DirAccess *da) { if (err) { return err; } - err = da->remove(da->get_current_dir().plus_file(E->get())); + err = da->remove(da->get_current_dir().plus_file(E)); if (err) { return err; } @@ -114,8 +114,8 @@ static Error _erase_recursive(DirAccess *da) { } } - for (List<String>::Element *E = files.front(); E; E = E->next()) { - Error err = da->remove(da->get_current_dir().plus_file(E->get())); + for (const String &E : files) { + Error err = da->remove(da->get_current_dir().plus_file(E)); if (err) { return err; } @@ -362,16 +362,15 @@ Error DirAccess::_copy_dir(DirAccess *p_target_da, String p_to, int p_chmod_flag list_dir_end(); - for (List<String>::Element *E = dirs.front(); E; E = E->next()) { - String rel_path = E->get(); + for (const String &rel_path : dirs) { String target_dir = p_to + rel_path; if (!p_target_da->dir_exists(target_dir)) { Error err = p_target_da->make_dir(target_dir); ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot create directory '" + target_dir + "'."); } - Error err = change_dir(E->get()); - ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + E->get() + "'."); + Error err = change_dir(rel_path); + ERR_FAIL_COND_V_MSG(err != OK, err, "Cannot change current directory to '" + rel_path + "'."); err = _copy_dir(p_target_da, p_to + rel_path + "/", p_chmod_flags, p_copy_links); if (err) { diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp index d21c0bd9a2..e6e79dff8a 100644 --- a/core/io/file_access.cpp +++ b/core/io/file_access.cpp @@ -316,52 +316,53 @@ String FileAccess::get_line() const { } Vector<String> FileAccess::get_csv_line(const String &p_delim) const { - ERR_FAIL_COND_V(p_delim.length() != 1, Vector<String>()); + ERR_FAIL_COND_V_MSG(p_delim.length() != 1, Vector<String>(), "Only single character delimiters are supported to parse CSV lines."); + ERR_FAIL_COND_V_MSG(p_delim[0] == '"', Vector<String>(), "The double quotation mark character (\") is not supported as a delimiter for CSV lines."); - String l; + String line; + + // CSV can support entries with line breaks as long as they are enclosed + // in double quotes. So our "line" might be more than a single line in the + // text file. int qc = 0; do { if (eof_reached()) { break; } - - l += get_line() + "\n"; + line += get_line() + "\n"; qc = 0; - for (int i = 0; i < l.length(); i++) { - if (l[i] == '"') { + for (int i = 0; i < line.length(); i++) { + if (line[i] == '"') { qc++; } } - } while (qc % 2); - l = l.substr(0, l.length() - 1); + // Remove the extraneous newline we've added above. + line = line.substr(0, line.length() - 1); Vector<String> strings; bool in_quote = false; String current; - for (int i = 0; i < l.length(); i++) { - char32_t c = l[i]; - char32_t s[2] = { 0, 0 }; - + for (int i = 0; i < line.length(); i++) { + char32_t c = line[i]; + // A delimiter ends the current entry, unless it's in a quoted string. if (!in_quote && c == p_delim[0]) { strings.push_back(current); current = String(); } else if (c == '"') { - if (l[i + 1] == '"' && in_quote) { - s[0] = '"'; - current += s; + // Doubled quotes are escapes for intentional quotes in the string. + if (line[i + 1] == '"' && in_quote) { + current += '"'; i++; } else { in_quote = !in_quote; } } else { - s[0] = c; - current += s; + current += c; } } - strings.push_back(current); return strings; diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp index 8000dd4290..5c1352c1b6 100644 --- a/core/io/http_client.cpp +++ b/core/io/http_client.cpp @@ -93,8 +93,7 @@ Dictionary HTTPClient::_get_response_headers_as_dictionary() { List<String> rh; get_response_headers(&rh); Dictionary ret; - for (const List<String>::Element *E = rh.front(); E; E = E->next()) { - const String &s = E->get(); + for (const String &s : rh) { int sp = s.find(":"); if (sp == -1) { continue; @@ -113,8 +112,8 @@ PackedStringArray HTTPClient::_get_response_headers() { PackedStringArray ret; ret.resize(rh.size()); int idx = 0; - for (const List<String>::Element *E = rh.front(); E; E = E->next()) { - ret.set(idx++, E->get()); + for (const String &E : rh) { + ret.set(idx++, E); } return ret; diff --git a/core/io/http_client_tcp.cpp b/core/io/http_client_tcp.cpp index f9b3165a07..f291086808 100644 --- a/core/io/http_client_tcp.cpp +++ b/core/io/http_client_tcp.cpp @@ -590,6 +590,7 @@ PackedByteArray HTTPClientTCP::read_response_body_chunk() { } } if (err != OK) { + ret.resize(_offset); break; } } diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp index b45e9d26b1..b9fc416f65 100644 --- a/core/io/image_loader.cpp +++ b/core/io/image_loader.cpp @@ -35,8 +35,8 @@ bool ImageFormatLoader::recognize(const String &p_extension) const { List<String> extensions; get_recognized_extensions(&extensions); - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(p_extension) == 0) { + for (const String &E : extensions) { + if (E.nocasecmp_to(p_extension) == 0) { return true; } } diff --git a/core/io/ip.cpp b/core/io/ip.cpp index 001b1c4757..e3102508a3 100644 --- a/core/io/ip.cpp +++ b/core/io/ip.cpp @@ -83,8 +83,23 @@ struct _IP_ResolverPrivate { continue; } - IP::get_singleton()->_resolve_hostname(queue[i].response, queue[i].hostname, queue[i].type); - queue[i].status.set(queue[i].response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); + mutex.lock(); + List<IPAddress> response; + String hostname = queue[i].hostname; + IP::Type type = queue[i].type; + mutex.unlock(); + + // We should not lock while resolving the hostname, + // only when modifying the queue. + IP::get_singleton()->_resolve_hostname(response, hostname, type); + + MutexLock lock(mutex); + // Could have been completed by another function, or deleted. + if (queue[i].status.get() != IP::RESOLVER_STATUS_WAITING) { + continue; + } + queue[i].response = response; + queue[i].status.set(response.is_empty() ? IP::RESOLVER_STATUS_ERROR : IP::RESOLVER_STATUS_DONE); } } @@ -93,8 +108,6 @@ struct _IP_ResolverPrivate { while (!ipr->thread_abort) { ipr->sem.wait(); - - MutexLock lock(ipr->mutex); ipr->resolve_queues(); } } @@ -107,17 +120,23 @@ struct _IP_ResolverPrivate { }; IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { - MutexLock lock(resolver->mutex); - List<IPAddress> res; - String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); + + resolver->mutex.lock(); if (resolver->cache.has(key)) { res = resolver->cache[key]; } else { + // This should be run unlocked so the resolver thread can keep + // resolving other requests. + resolver->mutex.unlock(); _resolve_hostname(res, p_hostname, p_type); + resolver->mutex.lock(); + // We might be overriding another result, but we don't care (they are the + // same hostname). resolver->cache[key] = res; } + resolver->mutex.unlock(); for (int i = 0; i < res.size(); ++i) { if (res[i].is_valid()) { @@ -128,14 +147,23 @@ IPAddress IP::resolve_hostname(const String &p_hostname, IP::Type p_type) { } Array IP::resolve_hostname_addresses(const String &p_hostname, Type p_type) { - MutexLock lock(resolver->mutex); - + List<IPAddress> res; String key = _IP_ResolverPrivate::get_cache_key(p_hostname, p_type); - if (!resolver->cache.has(key)) { - _resolve_hostname(resolver->cache[key], p_hostname, p_type); - } - List<IPAddress> res = resolver->cache[key]; + resolver->mutex.lock(); + if (resolver->cache.has(key)) { + res = resolver->cache[key]; + } else { + // This should be run unlocked so the resolver thread can keep resolving + // other requests. + resolver->mutex.unlock(); + _resolve_hostname(res, p_hostname, p_type); + resolver->mutex.lock(); + // We might be overriding another result, but we don't care (they are the + // same hostname). + resolver->cache[key] = res; + } + resolver->mutex.unlock(); Array result; for (int i = 0; i < res.size(); ++i) { @@ -178,13 +206,12 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ IP::ResolverStatus IP::get_resolve_item_status(ResolverID p_id) const { ERR_FAIL_INDEX_V(p_id, IP::RESOLVER_MAX_QUERIES, IP::RESOLVER_STATUS_NONE); - MutexLock lock(resolver->mutex); - - if (resolver->queue[p_id].status.get() == IP::RESOLVER_STATUS_NONE) { + IP::ResolverStatus res = resolver->queue[p_id].status.get(); + if (res == IP::RESOLVER_STATUS_NONE) { ERR_PRINT("Condition status == IP::RESOLVER_STATUS_NONE"); return IP::RESOLVER_STATUS_NONE; } - return resolver->queue[p_id].status.get(); + return res; } IPAddress IP::get_resolve_item_address(ResolverID p_id) const { @@ -230,8 +257,6 @@ Array IP::get_resolve_item_addresses(ResolverID p_id) const { void IP::erase_resolve_item(ResolverID p_id) { ERR_FAIL_INDEX(p_id, IP::RESOLVER_MAX_QUERIES); - MutexLock lock(resolver->mutex); - resolver->queue[p_id].status.set(IP::RESOLVER_STATUS_NONE); } @@ -252,8 +277,8 @@ Array IP::_get_local_addresses() const { Array addresses; List<IPAddress> ip_addresses; get_local_addresses(&ip_addresses); - for (List<IPAddress>::Element *E = ip_addresses.front(); E; E = E->next()) { - addresses.push_back(E->get()); + for (const IPAddress &E : ip_addresses) { + addresses.push_back(E); } return addresses; @@ -271,8 +296,8 @@ Array IP::_get_local_interfaces() const { rc["index"] = c.index; Array ips; - for (const List<IPAddress>::Element *F = c.ip_addresses.front(); F; F = F->next()) { - ips.push_front(F->get()); + for (const IPAddress &F : c.ip_addresses) { + ips.push_front(F); } rc["addresses"] = ips; @@ -286,8 +311,8 @@ void IP::get_local_addresses(List<IPAddress> *r_addresses) const { Map<String, Interface_Info> interfaces; get_local_interfaces(&interfaces); for (Map<String, Interface_Info>::Element *E = interfaces.front(); E; E = E->next()) { - for (const List<IPAddress>::Element *F = E->get().ip_addresses.front(); F; F = F->next()) { - r_addresses->push_front(F->get()); + for (const IPAddress &F : E->get().ip_addresses) { + r_addresses->push_front(F); } } } diff --git a/core/io/json.cpp b/core/io/json.cpp index b3a2498212..5823afbdcd 100644 --- a/core/io/json.cpp +++ b/core/io/json.cpp @@ -121,14 +121,17 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_ keys.sort(); } - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - if (E != keys.front()) { + bool first_key = true; + for (const Variant &E : keys) { + if (first_key) { + first_key = false; + } else { s += ","; s += end_statement; } - s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(String(E->get()), p_indent, p_cur_indent + 1, p_sort_keys, p_markers); + s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(String(E), p_indent, p_cur_indent + 1, p_sort_keys, p_markers); s += colon; - s += _stringify(d[E->get()], p_indent, p_cur_indent + 1, p_sort_keys, p_markers); + s += _stringify(d[E], p_indent, p_cur_indent + 1, p_sort_keys, p_markers); } s += end_statement + _make_indent(p_indent, p_cur_indent) + "}"; diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp index f342db2dad..e7d5b78d14 100644 --- a/core/io/marshalls.cpp +++ b/core/io/marshalls.cpp @@ -746,7 +746,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_INT64_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - int64_t count = decode_uint64(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); @@ -795,7 +795,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int } break; case Variant::PACKED_FLOAT64_ARRAY: { ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA); - int64_t count = decode_uint64(buf); + int32_t count = decode_uint32(buf); buf += 4; len -= 4; ERR_FAIL_MUL_OF(count, 8, ERR_INVALID_DATA); @@ -804,7 +804,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int Vector<double> data; if (count) { - //const double*rbuf=(const double*)buf; data.resize(count); double *w = data.ptrw(); for (int64_t i = 0; i < count; i++) { @@ -1031,7 +1030,8 @@ static void _encode_string(const String &p_string, uint8_t *&buf, int &r_len) { } } -Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects) { +Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects, int p_depth) { + ERR_FAIL_COND_V_MSG(p_depth > Variant::MAX_RECURSION_DEPTH, ERR_OUT_OF_MEMORY, "Potential inifite recursion detected. Bailing."); uint8_t *buf = r_buffer; r_len = 0; @@ -1358,8 +1358,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo obj->get_property_list(&props); int pc = 0; - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } pc++; @@ -1372,18 +1372,16 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len += 4; - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : props) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - _encode_string(E->get().name, buf, r_len); + _encode_string(E.name, buf, r_len); int len; - Error err = encode_variant(obj->get(E->get().name), buf, len, p_full_objects); - if (err) { - return err; - } + Error err = encode_variant(obj->get(E.name), 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) { @@ -1418,7 +1416,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + for (const Variant &E : keys) { /* CharString utf8 = E->->utf8(); @@ -1433,15 +1431,17 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo r_len++; //pad */ int len; - encode_variant(E->get(), buf, len, p_full_objects); + Error err = encode_variant(E, 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; } - Variant *v = d.getptr(E->get()); + Variant *v = d.getptr(E); ERR_FAIL_COND_V(!v, ERR_BUG); - encode_variant(*v, buf, len, p_full_objects); + err = encode_variant(*v, 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) { @@ -1462,7 +1462,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo for (int i = 0; i < v.size(); i++) { int len; - encode_variant(v.get(i), buf, len, p_full_objects); + Error err = encode_variant(v.get(i), 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) { @@ -1517,7 +1518,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo int datasize = sizeof(int64_t); if (buf) { - encode_uint64(datalen, buf); + encode_uint32(datalen, buf); buf += 4; const int64_t *r = data.ptr(); for (int64_t i = 0; i < datalen; i++) { diff --git a/core/io/marshalls.h b/core/io/marshalls.h index 3ebed914a3..05804d5a46 100644 --- a/core/io/marshalls.h +++ b/core/io/marshalls.h @@ -213,6 +213,6 @@ public: }; Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int *r_len = nullptr, bool p_allow_objects = false); -Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false); +Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bool p_full_objects = false, int p_depth = 0); #endif // MARSHALLS_H diff --git a/core/io/multiplayer_api.cpp b/core/io/multiplayer_api.cpp index 51ba8800e4..f792dc6cb4 100644 --- a/core/io/multiplayer_api.cpp +++ b/core/io/multiplayer_api.cpp @@ -33,6 +33,7 @@ #include "core/debugger/engine_debugger.h" #include "core/io/marshalls.h" #include "scene/main/node.h" +#include "scene/resources/packed_scene.h" #include <stdint.h> @@ -63,7 +64,7 @@ const MultiplayerAPI::RPCConfig _get_rpc_config(const Node *p_node, const String const Vector<MultiplayerAPI::RPCConfig> node_config = p_node->get_node_rpc_methods(); for (int i = 0; i < node_config.size(); i++) { if (node_config[i].name == p_method) { - r_id = ((uint16_t)i) & (1 << 15); + r_id = ((uint16_t)i) | (1 << 15); return node_config[i]; } } @@ -85,61 +86,26 @@ const MultiplayerAPI::RPCConfig _get_rpc_config_by_id(Node *p_node, uint16_t p_i if (id & (1 << 15)) { id = id & ~(1 << 15); config = p_node->get_node_rpc_methods(); - } else { + } else if (p_node->get_script_instance()) { config = p_node->get_script_instance()->get_rpc_methods(); } if (id < config.size()) { - return config[p_id]; + return config[id]; } return MultiplayerAPI::RPCConfig(); } -_FORCE_INLINE_ bool _should_call_local(MultiplayerAPI::RPCMode mode, bool is_master, bool &r_skip_rpc) { - switch (mode) { - case MultiplayerAPI::RPC_MODE_DISABLED: { - // Do nothing. - } break; - case MultiplayerAPI::RPC_MODE_REMOTE: { - // Do nothing. Remote cannot produce a local call. - } break; - case MultiplayerAPI::RPC_MODE_MASTERSYNC: { - if (is_master) { - r_skip_rpc = true; // I am the master, so skip remote call. - } - [[fallthrough]]; - } - case MultiplayerAPI::RPC_MODE_REMOTESYNC: - case MultiplayerAPI::RPC_MODE_PUPPETSYNC: { - // Call it, sync always results in a local call. - return true; - } break; - case MultiplayerAPI::RPC_MODE_MASTER: { - if (is_master) { - r_skip_rpc = true; // I am the master, so skip remote call. - } - return is_master; - } break; - case MultiplayerAPI::RPC_MODE_PUPPET: { - return !is_master; - } break; - } - return false; -} - _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, int p_remote_id) { switch (mode) { case MultiplayerAPI::RPC_MODE_DISABLED: { return false; } break; - case MultiplayerAPI::RPC_MODE_REMOTE: - case MultiplayerAPI::RPC_MODE_REMOTESYNC: { + case MultiplayerAPI::RPC_MODE_REMOTE: { return true; } break; - case MultiplayerAPI::RPC_MODE_MASTERSYNC: case MultiplayerAPI::RPC_MODE_MASTER: { return p_node->is_network_master(); } break; - case MultiplayerAPI::RPC_MODE_PUPPETSYNC: case MultiplayerAPI::RPC_MODE_PUPPET: { return !p_node->is_network_master() && p_remote_id == p_node->get_network_master(); } break; @@ -149,7 +115,7 @@ _FORCE_INLINE_ bool _can_call_mode(Node *p_node, MultiplayerAPI::RPCMode mode, i } void MultiplayerAPI::poll() { - if (!network_peer.is_valid() || network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED) { + if (!network_peer.is_valid() || network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED) { return; } @@ -181,6 +147,7 @@ void MultiplayerAPI::poll() { } void MultiplayerAPI::clear() { + replicated_nodes.clear(); connected_peers.clear(); path_get_cache.clear(); path_send_cache.clear(); @@ -196,13 +163,13 @@ Node *MultiplayerAPI::get_root_node() { return root_node; } -void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer) { +void MultiplayerAPI::set_network_peer(const Ref<MultiplayerPeer> &p_peer) { if (p_peer == network_peer) { return; // Nothing to do } - ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, - "Supplied NetworkedMultiplayerPeer must be connecting or connected."); + ERR_FAIL_COND_MSG(p_peer.is_valid() && p_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, + "Supplied MultiplayerPeer must be connecting or connected."); if (network_peer.is_valid()) { network_peer->disconnect("peer_connected", callable_mp(this, &MultiplayerAPI::_add_peer)); @@ -224,7 +191,7 @@ void MultiplayerAPI::set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_pee } } -Ref<NetworkedMultiplayerPeer> MultiplayerAPI::get_network_peer() const { +Ref<MultiplayerPeer> MultiplayerAPI::get_network_peer() const { return network_peer; } @@ -357,6 +324,12 @@ void MultiplayerAPI::_process_packet(int p_from, const uint8_t *p_packet, int p_ case NETWORK_COMMAND_RAW: { _process_raw(p_from, p_packet, p_packet_len); } break; + case NETWORK_COMMAND_SPAWN: { + _process_spawn_despawn(p_from, p_packet, p_packet_len, true); + } break; + case NETWORK_COMMAND_DESPAWN: { + _process_spawn_despawn(p_from, p_packet, p_packet_len, false); + } break; } } @@ -513,7 +486,8 @@ void MultiplayerAPI::_process_simplify_path(int p_from, const uint8_t *p_packet, packet.write[1] = valid_rpc_checksum; encode_cstring(pname.get_data(), &packet.write[2]); - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->set_target_peer(p_from); network_peer->put_packet(packet.ptr(), packet.size()); } @@ -540,6 +514,64 @@ void MultiplayerAPI::_process_confirm_path(int p_from, const uint8_t *p_packet, E->get() = true; } +void MultiplayerAPI::_process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn) { + ERR_FAIL_COND_MSG(p_packet_len < 18, "Invalid spawn packet received"); + int ofs = 1; + ResourceUID::ID id = decode_uint64(&p_packet[ofs]); + ofs += 8; + ERR_FAIL_COND_MSG(!spawnables.has(id), "Invalid spawn ID received " + itos(id)); + + uint32_t node_target = decode_uint32(&p_packet[ofs]); + Node *parent = _process_get_node(p_from, nullptr, node_target, 0); + ofs += 4; + ERR_FAIL_COND_MSG(parent == nullptr, "Invalid packet received. Requested node was not found."); + + uint32_t name_len = decode_uint32(&p_packet[ofs]); + ofs += 4; + ERR_FAIL_COND_MSG(name_len > uint32_t(p_packet_len - ofs), vformat("Invalid spawn packet size: %d, wants: %d", p_packet_len, ofs + name_len)); + ERR_FAIL_COND_MSG(name_len < 1, "Zero spawn name size."); + + const String name = String::utf8((const char *)&p_packet[ofs], name_len); + // We need to make sure no trickery happens here (e.g. despawning a subpath), but we want to allow autogenerated ("@") node names. + ERR_FAIL_COND_MSG(name.validate_node_name() != name.replace("@", ""), vformat("Invalid node name received: '%s'", name)); + ofs += name_len; + PackedByteArray data; + if (p_packet_len > ofs) { + data.resize(p_packet_len - ofs); + memcpy(data.ptrw(), &p_packet[ofs], data.size()); + } + + SpawnMode mode = spawnables[id]; + if (mode == SPAWN_MODE_SERVER && p_from == 1) { + String scene_path = ResourceUID::get_singleton()->get_id_path(id); + if (p_spawn) { + ERR_FAIL_COND_MSG(parent->has_node(name), vformat("Unable to spawn node. Node already exists: %s/%s", parent->get_path(), name)); + RES res = ResourceLoader::load(scene_path); + ERR_FAIL_COND_MSG(!res.is_valid(), "Unable to load scene to spawn at path: " + scene_path); + PackedScene *scene = Object::cast_to<PackedScene>(res.ptr()); + ERR_FAIL_COND(!scene); + Node *node = scene->instantiate(); + ERR_FAIL_COND(!node); + replicated_nodes[node->get_instance_id()] = id; + parent->_add_child_nocheck(node, name); + emit_signal(SNAME("network_spawn"), p_from, id, node, data); + } else { + ERR_FAIL_COND_MSG(!parent->has_node(name), vformat("Path not found: %s/%s", parent->get_path(), name)); + Node *node = parent->get_node(name); + ERR_FAIL_COND_MSG(!replicated_nodes.has(node->get_instance_id()), vformat("Trying to despawn a Node that was not replicated: %s/%s", parent->get_path(), name)); + emit_signal(SNAME("network_despawn"), p_from, id, node, data); + replicated_nodes.erase(node->get_instance_id()); + node->queue_delete(); + } + } else { + if (p_spawn) { + emit_signal(SNAME("network_spawn_request"), p_from, id, parent, name, data); + } else { + emit_signal(SNAME("network_despawn_request"), p_from, id, parent, name, data); + } + } +} + bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentCache *psc, int p_target) { bool has_all_peers = true; List<int> peers_to_add; // If one is missing, take note to add it. @@ -590,12 +622,13 @@ bool MultiplayerAPI::_send_confirm_path(Node *p_node, NodePath p_path, PathSentC ofs += encode_cstring(path.get_data(), &packet.write[ofs]); - for (List<int>::Element *E = peers_to_add.front(); E; E = E->next()) { - network_peer->set_target_peer(E->get()); // To all of you. - network_peer->set_transfer_mode(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + for (int &E : peers_to_add) { + network_peer->set_target_peer(E); // To all of you. + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); network_peer->put_packet(packet.ptr(), packet.size()); - psc->confirmed_peers.insert(E->get(), false); // Insert into confirmed, but as false since it was not confirmed. + psc->confirmed_peers.insert(E, false); // Insert into confirmed, but as false since it was not confirmed. } } @@ -760,9 +793,9 @@ Error MultiplayerAPI::_decode_and_decompress_variant(Variant &r_variant, const u void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const RPCConfig &p_config, const StringName &p_name, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_MSG(network_peer.is_null(), "Attempt to remote call/set when networking is not active in SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_CONNECTING, "Attempt to remote call/set when networking is not connected yet in SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() == NetworkedMultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() == MultiplayerPeer::CONNECTION_DISCONNECTED, "Attempt to remote call/set when networking is disconnected."); ERR_FAIL_COND_MSG(p_argcount > 255, "Too many arguments >255."); @@ -893,6 +926,7 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const #endif // Take chance and set transfer mode, since all send methods will use it. + network_peer->set_transfer_channel(p_config.channel); network_peer->set_transfer_mode(p_config.transfer_mode); if (has_all_peers) { @@ -941,7 +975,17 @@ void MultiplayerAPI::_send_rpc(Node *p_from, int p_to, uint16_t p_rpc_id, const void MultiplayerAPI::_add_peer(int p_id) { connected_peers.insert(p_id); path_get_cache.insert(p_id, PathGetCache()); - emit_signal("network_peer_connected", p_id); + if (is_network_server()) { + for (const KeyValue<ObjectID, ResourceUID::ID> &E : replicated_nodes) { + // Only server mode adds to replicated_nodes, no need to check it. + Object *obj = ObjectDB::get_instance(E.key); + ERR_CONTINUE(!obj); + Node *node = Object::cast_to<Node>(obj); + ERR_CONTINUE(!node); + _send_spawn_despawn(p_id, E.value, node->get_path(), nullptr, 0, true); + } + } + emit_signal(SNAME("network_peer_connected"), p_id); } void MultiplayerAPI::_del_peer(int p_id) { @@ -952,48 +996,50 @@ void MultiplayerAPI::_del_peer(int p_id) { // Some refactoring is needed to make this faster and do paths GC. List<NodePath> keys; path_send_cache.get_key_list(&keys); - for (List<NodePath>::Element *E = keys.front(); E; E = E->next()) { - PathSentCache *psc = path_send_cache.getptr(E->get()); + for (const NodePath &E : keys) { + PathSentCache *psc = path_send_cache.getptr(E); psc->confirmed_peers.erase(p_id); } - emit_signal("network_peer_disconnected", p_id); + if (p_id == 1) { + // Erase server replicated nodes, but do not queue them for deletion. + replicated_nodes.clear(); + } + emit_signal(SNAME("network_peer_disconnected"), p_id); } void MultiplayerAPI::_connected_to_server() { - emit_signal("connected_to_server"); + emit_signal(SNAME("connected_to_server")); } void MultiplayerAPI::_connection_failed() { - emit_signal("connection_failed"); + emit_signal(SNAME("connection_failed")); } void MultiplayerAPI::_server_disconnected() { - emit_signal("server_disconnected"); + emit_signal(SNAME("server_disconnected")); } void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount) { ERR_FAIL_COND_MSG(!network_peer.is_valid(), "Trying to call an RPC while no network peer is active."); ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "Trying to call an RPC on a node which is not inside SceneTree."); - ERR_FAIL_COND_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); + ERR_FAIL_COND_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, "Trying to call an RPC via a network peer which is not connected."); int node_id = network_peer->get_unique_id(); - bool skip_rpc = node_id == p_peer_id; bool call_local_native = false; bool call_local_script = false; - bool is_master = p_node->is_network_master(); uint16_t rpc_id = UINT16_MAX; const RPCConfig config = _get_rpc_config(p_node, p_method, rpc_id); ERR_FAIL_COND_MSG(config.name == StringName(), vformat("Unable to get the RPC configuration for the function \"%s\" at path: \"%s\". This happens when the method is not marked for RPCs.", p_method, p_node->get_path())); if (p_peer_id == 0 || p_peer_id == node_id || (p_peer_id < 0 && p_peer_id != -node_id)) { if (rpc_id & (1 << 15)) { - call_local_native = _should_call_local(config.rpc_mode, is_master, skip_rpc); + call_local_native = config.sync; } else { - call_local_script = _should_call_local(config.rpc_mode, is_master, skip_rpc); + call_local_script = config.sync; } } - if (!skip_rpc) { + if (p_peer_id != node_id) { #ifdef DEBUG_ENABLED _profile_node_data("out_rpc", p_node->get_instance_id()); #endif @@ -1030,13 +1076,13 @@ void MultiplayerAPI::rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const } } - ERR_FAIL_COND_MSG(skip_rpc && !(call_local_native || call_local_script), "RPC '" + p_method + "' on yourself is not allowed by selected mode."); + ERR_FAIL_COND_MSG(p_peer_id == node_id && !config.sync, "RPC '" + p_method + "' on yourself is not allowed by selected mode."); } -Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMultiplayerPeer::TransferMode p_mode) { +Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, MultiplayerPeer::TransferMode p_mode, int p_channel) { ERR_FAIL_COND_V_MSG(p_data.size() < 1, ERR_INVALID_DATA, "Trying to send an empty raw packet."); ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), ERR_UNCONFIGURED, "Trying to send a raw packet while no network peer is active."); - ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != NetworkedMultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); + ERR_FAIL_COND_V_MSG(network_peer->get_connection_status() != MultiplayerPeer::CONNECTION_CONNECTED, ERR_UNCONFIGURED, "Trying to send a raw packet via a network peer which is not connected."); MAKE_ROOM(p_data.size() + 1); const uint8_t *r = p_data.ptr(); @@ -1044,6 +1090,7 @@ Error MultiplayerAPI::send_bytes(Vector<uint8_t> p_data, int p_to, NetworkedMult memcpy(&packet_cache.write[1], &r[0], p_data.size()); network_peer->set_target_peer(p_to); + network_peer->set_transfer_channel(p_channel); network_peer->set_transfer_mode(p_mode); return network_peer->put_packet(packet_cache.ptr(), p_data.size() + 1); @@ -1059,7 +1106,7 @@ void MultiplayerAPI::_process_raw(int p_from, const uint8_t *p_packet, int p_pac uint8_t *w = out.ptrw(); memcpy(&w[0], &p_packet[1], len); } - emit_signal("network_peer_packet", p_from, out); + emit_signal(SNAME("network_peer_packet"), p_from, out); } int MultiplayerAPI::get_network_unique_id() const { @@ -1068,9 +1115,7 @@ int MultiplayerAPI::get_network_unique_id() const { } bool MultiplayerAPI::is_network_server() const { - // XXX Maybe fail silently? Maybe should actually return true to make development of both local and online multiplayer easier? - ERR_FAIL_COND_V_MSG(!network_peer.is_valid(), false, "No network peer is assigned. I can't be a server."); - return network_peer->is_server(); + return network_peer.is_valid() && network_peer->is_server(); } void MultiplayerAPI::set_refuse_new_network_connections(bool p_refuse) { @@ -1102,10 +1147,103 @@ bool MultiplayerAPI::is_object_decoding_allowed() const { return allow_object_decoding; } +Error MultiplayerAPI::spawnable_config(const ResourceUID::ID &p_id, SpawnMode p_mode) { + ERR_FAIL_COND_V(p_mode < SPAWN_MODE_NONE || p_mode > SPAWN_MODE_CUSTOM, ERR_INVALID_PARAMETER); + ERR_FAIL_COND_V(!ResourceUID::get_singleton()->has_id(p_id), ERR_INVALID_PARAMETER); +#ifdef TOOLS_ENABLED + String path = ResourceUID::get_singleton()->get_id_path(p_id); + RES res = ResourceLoader::load(path); + ERR_FAIL_COND_V(!res->is_class("PackedScene"), ERR_INVALID_PARAMETER); +#endif + if (p_mode == SPAWN_MODE_NONE) { + if (spawnables.has(p_id)) { + spawnables.erase(p_id); + } + } else { + spawnables[p_id] = p_mode; + } + return OK; +} + +Error MultiplayerAPI::send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) { + return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), false); +} + +Error MultiplayerAPI::send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data) { + return _send_spawn_despawn(p_peer_id, p_scene_id, p_path, p_data.ptr(), p_data.size(), true); +} + +Error MultiplayerAPI::_send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const uint8_t *p_data, int p_data_len, bool p_spawn) { + ERR_FAIL_COND_V(!root_node, ERR_UNCONFIGURED); + ERR_FAIL_COND_V_MSG(!spawnables.has(p_scene_id), ERR_INVALID_PARAMETER, vformat("Spawnable not found: %d", p_scene_id)); + + NodePath rel_path = (root_node->get_path()).rel_path_to(p_path); + const Vector<StringName> names = rel_path.get_names(); + ERR_FAIL_COND_V(names.size() < 2, ERR_INVALID_PARAMETER); + + NodePath parent = NodePath(names.subarray(0, names.size() - 2), false); + ERR_FAIL_COND_V_MSG(!root_node->has_node(parent), ERR_INVALID_PARAMETER, "Path not found: " + parent); + + // See if the path is cached. + PathSentCache *psc = path_send_cache.getptr(parent); + if (!psc) { + // Path is not cached, create. + path_send_cache[parent] = PathSentCache(); + psc = path_send_cache.getptr(parent); + psc->id = last_send_cache_id++; + } + _send_confirm_path(root_node->get_node(parent), parent, psc, p_peer_id); + + const CharString cname = String(names[names.size() - 1]).utf8(); + int nlen = encode_cstring(cname.get_data(), nullptr); + MAKE_ROOM(1 + 8 + 4 + 4 + nlen + p_data_len); + uint8_t *ptr = packet_cache.ptrw(); + ptr[0] = p_spawn ? NETWORK_COMMAND_SPAWN : NETWORK_COMMAND_DESPAWN; + int ofs = 1; + ofs += encode_uint64(p_scene_id, &ptr[ofs]); + ofs += encode_uint32(psc->id, &ptr[ofs]); + ofs += encode_uint32(nlen, &ptr[ofs]); + ofs += encode_cstring(cname.get_data(), &ptr[ofs]); + memcpy(&ptr[ofs], p_data, p_data_len); + network_peer->set_target_peer(p_peer_id); + network_peer->set_transfer_channel(0); + network_peer->set_transfer_mode(MultiplayerPeer::TRANSFER_MODE_RELIABLE); + return network_peer->put_packet(ptr, ofs + p_data_len); +} + +void MultiplayerAPI::scene_enter_exit_notify(const String &p_scene, const Node *p_node, bool p_enter) { + if (!has_network_peer()) { + return; + } + ERR_FAIL_COND(!p_node || !p_node->get_parent() || !root_node); + NodePath path = (root_node->get_path()).rel_path_to(p_node->get_parent()->get_path()); + if (path.is_empty()) { + return; + } + ResourceUID::ID id = ResourceLoader::get_resource_uid(p_scene); + if (!spawnables.has(id)) { + return; + } + SpawnMode mode = spawnables[id]; + if (p_enter) { + if (mode == SPAWN_MODE_SERVER && is_network_server()) { + replicated_nodes[p_node->get_instance_id()] = id; + _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, true); + } + emit_signal(SNAME("network_spawnable_added"), id, p_node); + } else { + if (mode == SPAWN_MODE_SERVER && is_network_server() && replicated_nodes.has(p_node->get_instance_id())) { + replicated_nodes.erase(p_node->get_instance_id()); + _send_spawn_despawn(0, id, p_node->get_path(), nullptr, 0, false); + } + emit_signal(SNAME("network_spawnable_removed"), id, p_node); + } +} + void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_root_node", "node"), &MultiplayerAPI::set_root_node); ClassDB::bind_method(D_METHOD("get_root_node"), &MultiplayerAPI::get_root_node); - ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode"), &MultiplayerAPI::send_bytes, DEFVAL(NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE)); + ClassDB::bind_method(D_METHOD("send_bytes", "bytes", "id", "mode", "channel"), &MultiplayerAPI::send_bytes, DEFVAL(MultiplayerPeer::TARGET_PEER_BROADCAST), DEFVAL(MultiplayerPeer::TRANSFER_MODE_RELIABLE), DEFVAL(0)); ClassDB::bind_method(D_METHOD("has_network_peer"), &MultiplayerAPI::has_network_peer); ClassDB::bind_method(D_METHOD("get_network_peer"), &MultiplayerAPI::get_network_peer); ClassDB::bind_method(D_METHOD("get_network_unique_id"), &MultiplayerAPI::get_network_unique_id); @@ -1120,10 +1258,13 @@ void MultiplayerAPI::_bind_methods() { ClassDB::bind_method(D_METHOD("is_refusing_new_network_connections"), &MultiplayerAPI::is_refusing_new_network_connections); ClassDB::bind_method(D_METHOD("set_allow_object_decoding", "enable"), &MultiplayerAPI::set_allow_object_decoding); ClassDB::bind_method(D_METHOD("is_object_decoding_allowed"), &MultiplayerAPI::is_object_decoding_allowed); + ClassDB::bind_method(D_METHOD("spawnable_config", "scene_id", "spawn_mode"), &MultiplayerAPI::spawnable_config); + ClassDB::bind_method(D_METHOD("send_despawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_despawn, DEFVAL(PackedByteArray())); + ClassDB::bind_method(D_METHOD("send_spawn", "peer_id", "scene_id", "path", "data"), &MultiplayerAPI::send_spawn, DEFVAL(PackedByteArray())); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "allow_object_decoding"), "set_allow_object_decoding", "is_object_decoding_allowed"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_network_connections"), "set_refuse_new_network_connections", "is_refusing_new_network_connections"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "NetworkedMultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "network_peer", PROPERTY_HINT_RESOURCE_TYPE, "MultiplayerPeer", PROPERTY_USAGE_NONE), "set_network_peer", "get_network_peer"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "root_node", PROPERTY_HINT_RESOURCE_TYPE, "Node", PROPERTY_USAGE_NONE), "set_root_node", "get_root_node"); ADD_PROPERTY_DEFAULT("refuse_new_network_connections", false); @@ -1133,14 +1274,21 @@ void MultiplayerAPI::_bind_methods() { ADD_SIGNAL(MethodInfo("connected_to_server")); ADD_SIGNAL(MethodInfo("connection_failed")); ADD_SIGNAL(MethodInfo("server_disconnected")); + ADD_SIGNAL(MethodInfo("network_despawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("network_spawn", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("network_despawn_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("network_spawn_request", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "parent", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::STRING, "name"), PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("network_spawnable_added", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("network_spawnable_removed", PropertyInfo(Variant::INT, "scene_id"), PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); BIND_ENUM_CONSTANT(RPC_MODE_DISABLED); BIND_ENUM_CONSTANT(RPC_MODE_REMOTE); BIND_ENUM_CONSTANT(RPC_MODE_MASTER); BIND_ENUM_CONSTANT(RPC_MODE_PUPPET); - BIND_ENUM_CONSTANT(RPC_MODE_REMOTESYNC); - BIND_ENUM_CONSTANT(RPC_MODE_MASTERSYNC); - BIND_ENUM_CONSTANT(RPC_MODE_PUPPETSYNC); + + BIND_ENUM_CONSTANT(SPAWN_MODE_NONE); + BIND_ENUM_CONSTANT(SPAWN_MODE_SERVER); + BIND_ENUM_CONSTANT(SPAWN_MODE_CUSTOM); } MultiplayerAPI::MultiplayerAPI() { diff --git a/core/io/multiplayer_api.h b/core/io/multiplayer_api.h index 43804a20ec..9552a0bf35 100644 --- a/core/io/multiplayer_api.h +++ b/core/io/multiplayer_api.h @@ -31,7 +31,8 @@ #ifndef MULTIPLAYER_API_H #define MULTIPLAYER_API_H -#include "core/io/networked_multiplayer_peer.h" +#include "core/io/multiplayer_peer.h" +#include "core/io/resource_uid.h" #include "core/object/ref_counted.h" class MultiplayerAPI : public RefCounted { @@ -43,15 +44,19 @@ public: RPC_MODE_REMOTE, // Using rpc() on it will call method in all remote peers RPC_MODE_MASTER, // Using rpc() on it will call method on wherever the master is, be it local or remote RPC_MODE_PUPPET, // Using rpc() on it will call method for all puppets - RPC_MODE_REMOTESYNC, // Using rpc() on it will call method in all remote peers and locally - RPC_MODE_MASTERSYNC, // Using rpc() on it will call method in the master peer and locally - RPC_MODE_PUPPETSYNC, // Using rpc() on it will call method in all puppets peers and locally + }; + + enum SpawnMode { + SPAWN_MODE_NONE, + SPAWN_MODE_SERVER, + SPAWN_MODE_CUSTOM, }; struct RPCConfig { StringName name; RPCMode rpc_mode = RPC_MODE_DISABLED; - NetworkedMultiplayerPeer::TransferMode transfer_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE; + bool sync = false; + MultiplayerPeer::TransferMode transfer_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE; int channel = 0; bool operator==(RPCConfig const &p_other) const { @@ -83,11 +88,13 @@ private: Map<int, NodeInfo> nodes; }; - Ref<NetworkedMultiplayerPeer> network_peer; + Ref<MultiplayerPeer> network_peer; + Map<ResourceUID::ID, SpawnMode> spawnables; int rpc_sender_id = 0; Set<int> connected_peers; HashMap<NodePath, PathSentCache> path_send_cache; Map<int, PathGetCache> path_get_cache; + Map<ObjectID, ResourceUID::ID> replicated_nodes; int last_send_cache_id; Vector<uint8_t> packet_cache; Node *root_node = nullptr; @@ -99,6 +106,7 @@ protected: void _process_packet(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_simplify_path(int p_from, const uint8_t *p_packet, int p_packet_len); void _process_confirm_path(int p_from, const uint8_t *p_packet, int p_packet_len); + void _process_spawn_despawn(int p_from, const uint8_t *p_packet, int p_packet_len, bool p_spawn); Node *_process_get_node(int p_from, const uint8_t *p_packet, uint32_t p_node_target, int p_packet_len); void _process_rpc(Node *p_node, const uint16_t p_rpc_method_id, int p_from, const uint8_t *p_packet, int p_packet_len, int p_offset); void _process_raw(int p_from, const uint8_t *p_packet, int p_packet_len); @@ -115,6 +123,8 @@ public: NETWORK_COMMAND_SIMPLIFY_PATH, NETWORK_COMMAND_CONFIRM_PATH, NETWORK_COMMAND_RAW, + NETWORK_COMMAND_SPAWN, + NETWORK_COMMAND_DESPAWN, }; enum NetworkNodeIdCompression { @@ -132,9 +142,9 @@ public: void clear(); void set_root_node(Node *p_node); Node *get_root_node(); - void set_network_peer(const Ref<NetworkedMultiplayerPeer> &p_peer); - Ref<NetworkedMultiplayerPeer> get_network_peer() const; - Error send_bytes(Vector<uint8_t> p_data, int p_to = NetworkedMultiplayerPeer::TARGET_PEER_BROADCAST, NetworkedMultiplayerPeer::TransferMode p_mode = NetworkedMultiplayerPeer::TRANSFER_MODE_RELIABLE); + void set_network_peer(const Ref<MultiplayerPeer> &p_peer); + Ref<MultiplayerPeer> get_network_peer() const; + Error send_bytes(Vector<uint8_t> p_data, int p_to = MultiplayerPeer::TARGET_PEER_BROADCAST, MultiplayerPeer::TransferMode p_mode = MultiplayerPeer::TRANSFER_MODE_RELIABLE, int p_channel = 0); // Called by Node.rpc void rpcp(Node *p_node, int p_peer_id, bool p_unreliable, const StringName &p_method, const Variant **p_arg, int p_argcount); @@ -156,10 +166,17 @@ public: void set_allow_object_decoding(bool p_enable); bool is_object_decoding_allowed() const; + Error spawnable_config(const ResourceUID::ID &p_id, SpawnMode p_mode); + Error send_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data = PackedByteArray()); + Error send_spawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const PackedByteArray &p_data = PackedByteArray()); + Error _send_spawn_despawn(int p_peer_id, const ResourceUID::ID &p_scene_id, const NodePath &p_path, const uint8_t *p_data, int p_data_len, bool p_spawn); + void scene_enter_exit_notify(const String &p_scene, const Node *p_node, bool p_enter); + MultiplayerAPI(); ~MultiplayerAPI(); }; VARIANT_ENUM_CAST(MultiplayerAPI::RPCMode); +VARIANT_ENUM_CAST(MultiplayerAPI::SpawnMode); #endif // MULTIPLAYER_API_H diff --git a/core/io/networked_multiplayer_peer.cpp b/core/io/multiplayer_peer.cpp index b6af046e77..83cf24d7e3 100644 --- a/core/io/networked_multiplayer_peer.cpp +++ b/core/io/multiplayer_peer.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* networked_multiplayer_peer.cpp */ +/* multiplayer_peer.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,25 +28,52 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "networked_multiplayer_peer.h" +#include "multiplayer_peer.h" -void NetworkedMultiplayerPeer::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &NetworkedMultiplayerPeer::set_transfer_mode); - ClassDB::bind_method(D_METHOD("get_transfer_mode"), &NetworkedMultiplayerPeer::get_transfer_mode); - ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &NetworkedMultiplayerPeer::set_target_peer); +#include "core/os/os.h" - ClassDB::bind_method(D_METHOD("get_packet_peer"), &NetworkedMultiplayerPeer::get_packet_peer); +uint32_t MultiplayerPeer::generate_unique_id() const { + uint32_t hash = 0; - ClassDB::bind_method(D_METHOD("poll"), &NetworkedMultiplayerPeer::poll); + while (hash == 0 || hash == 1) { + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_ticks_usec()); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_unix_time(), hash); + hash = hash_djb2_one_32( + (uint32_t)OS::get_singleton()->get_user_data_dir().hash64(), hash); + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)this), hash); // Rely on ASLR heap + hash = hash_djb2_one_32( + (uint32_t)((uint64_t)&hash), hash); // Rely on ASLR stack - ClassDB::bind_method(D_METHOD("get_connection_status"), &NetworkedMultiplayerPeer::get_connection_status); - ClassDB::bind_method(D_METHOD("get_unique_id"), &NetworkedMultiplayerPeer::get_unique_id); + hash = hash & 0x7FFFFFFF; // Make it compatible with unsigned, since negative ID is used for exclusion + } - ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &NetworkedMultiplayerPeer::set_refuse_new_connections); - ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &NetworkedMultiplayerPeer::is_refusing_new_connections); + return hash; +} + +void MultiplayerPeer::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_transfer_channel", "channel"), &MultiplayerPeer::set_transfer_channel); + ClassDB::bind_method(D_METHOD("get_transfer_channel"), &MultiplayerPeer::get_transfer_channel); + ClassDB::bind_method(D_METHOD("set_transfer_mode", "mode"), &MultiplayerPeer::set_transfer_mode); + ClassDB::bind_method(D_METHOD("get_transfer_mode"), &MultiplayerPeer::get_transfer_mode); + ClassDB::bind_method(D_METHOD("set_target_peer", "id"), &MultiplayerPeer::set_target_peer); + + ClassDB::bind_method(D_METHOD("get_packet_peer"), &MultiplayerPeer::get_packet_peer); + + ClassDB::bind_method(D_METHOD("poll"), &MultiplayerPeer::poll); + + ClassDB::bind_method(D_METHOD("get_connection_status"), &MultiplayerPeer::get_connection_status); + ClassDB::bind_method(D_METHOD("get_unique_id"), &MultiplayerPeer::get_unique_id); + ClassDB::bind_method(D_METHOD("generate_unique_id"), &MultiplayerPeer::generate_unique_id); + + ClassDB::bind_method(D_METHOD("set_refuse_new_connections", "enable"), &MultiplayerPeer::set_refuse_new_connections); + ClassDB::bind_method(D_METHOD("is_refusing_new_connections"), &MultiplayerPeer::is_refusing_new_connections); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "refuse_new_connections"), "set_refuse_new_connections", "is_refusing_new_connections"); ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_mode", PROPERTY_HINT_ENUM, "Unreliable,Unreliable Ordered,Reliable"), "set_transfer_mode", "get_transfer_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transfer_channel", PROPERTY_HINT_RANGE, "0,255,1"), "set_transfer_channel", "get_transfer_channel"); BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE); BIND_ENUM_CONSTANT(TRANSFER_MODE_UNRELIABLE_ORDERED); diff --git a/core/io/networked_multiplayer_peer.h b/core/io/multiplayer_peer.h index 7c90f97d88..7ca4e7930b 100644 --- a/core/io/networked_multiplayer_peer.h +++ b/core/io/multiplayer_peer.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* networked_multiplayer_peer.h */ +/* multiplayer_peer.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -33,8 +33,8 @@ #include "core/io/packet_peer.h" -class NetworkedMultiplayerPeer : public PacketPeer { - GDCLASS(NetworkedMultiplayerPeer, PacketPeer); +class MultiplayerPeer : public PacketPeer { + GDCLASS(MultiplayerPeer, PacketPeer); protected: static void _bind_methods(); @@ -56,6 +56,8 @@ public: CONNECTION_CONNECTED, }; + virtual void set_transfer_channel(int p_channel) = 0; + virtual int get_transfer_channel() const = 0; virtual void set_transfer_mode(TransferMode p_mode) = 0; virtual TransferMode get_transfer_mode() const = 0; virtual void set_target_peer(int p_peer_id) = 0; @@ -72,11 +74,12 @@ public: virtual bool is_refusing_new_connections() const = 0; virtual ConnectionStatus get_connection_status() const = 0; + uint32_t generate_unique_id() const; - NetworkedMultiplayerPeer() {} + MultiplayerPeer() {} }; -VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::TransferMode) -VARIANT_ENUM_CAST(NetworkedMultiplayerPeer::ConnectionStatus) +VARIANT_ENUM_CAST(MultiplayerPeer::TransferMode) +VARIANT_ENUM_CAST(MultiplayerPeer::ConnectionStatus) #endif // NETWORKED_MULTIPLAYER_PEER_H diff --git a/core/io/packed_data_container.cpp b/core/io/packed_data_container.cpp index cf6a0b6027..4a76f0191d 100644 --- a/core/io/packed_data_container.cpp +++ b/core/io/packed_data_container.cpp @@ -268,21 +268,21 @@ uint32_t PackedDataContainer::_pack(const Variant &p_data, Vector<uint8_t> &tmpd d.get_key_list(&keys); List<DictKey> sortk; - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + for (const Variant &key : keys) { DictKey dk; - dk.hash = E->get().hash(); - dk.key = E->get(); + dk.hash = key.hash(); + dk.key = key; sortk.push_back(dk); } sortk.sort(); int idx = 0; - for (List<DictKey>::Element *E = sortk.front(); E; E = E->next()) { - encode_uint32(E->get().hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); - uint32_t ofs = _pack(E->get().key, tmpdata, string_cache); + for (const DictKey &E : sortk) { + encode_uint32(E.hash, &tmpdata.write[pos + 8 + idx * 12 + 0]); + uint32_t ofs = _pack(E.key, tmpdata, string_cache); encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 4]); - ofs = _pack(d[E->get().key], tmpdata, string_cache); + ofs = _pack(d[E.key], tmpdata, string_cache); encode_uint32(ofs, &tmpdata.write[pos + 8 + idx * 12 + 8]); idx++; } diff --git a/core/io/resource.cpp b/core/io/resource.cpp index efa622d976..0262655927 100644 --- a/core/io/resource.cpp +++ b/core/io/resource.cpp @@ -33,6 +33,7 @@ #include "core/core_string_names.h" #include "core/io/file_access.h" #include "core/io/resource_loader.h" +#include "core/math/math_funcs.h" #include "core/object/script_language.h" #include "core/os/os.h" #include "scene/main/node.h" //only so casting works @@ -94,12 +95,43 @@ String Resource::get_path() const { return path_cache; } -void Resource::set_subindex(int p_sub_index) { - subindex = p_sub_index; +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. + OS::Date date = OS::get_singleton()->get_date(); + OS::Time time = OS::get_singleton()->get_time(); + uint32_t hash = hash_djb2_one_32(OS::get_singleton()->get_ticks_usec()); + hash = hash_djb2_one_32(date.year, hash); + hash = hash_djb2_one_32(date.month, hash); + hash = hash_djb2_one_32(date.day, hash); + hash = hash_djb2_one_32(time.hour, hash); + hash = hash_djb2_one_32(time.minute, hash); + hash = hash_djb2_one_32(time.second, hash); + hash = hash_djb2_one_32(Math::rand(), hash); + + static constexpr uint32_t characters = 5; + static constexpr uint32_t char_count = ('z' - 'a'); + static constexpr uint32_t base = char_count + ('9' - '0'); + String id; + for (uint32_t i = 0; i < characters; i++) { + uint32_t c = hash % base; + if (c < char_count) { + id += String::chr('a' + c); + } else { + id += String::chr('0' + (c - char_count)); + } + hash /= base; + } + + return id; +} + +void Resource::set_scene_unique_id(const String &p_id) { + scene_unique_id = p_id; } -int Resource::get_subindex() const { - return subindex; +String Resource::get_scene_unique_id() const { + return scene_unique_id; } void Resource::set_name(const String &p_name) { @@ -133,15 +165,15 @@ Error Resource::copy_from(const Ref<Resource> &p_resource) { List<PropertyInfo> pi; p_resource->get_property_list(&pi); - for (List<PropertyInfo>::Element *E = pi.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : pi) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - if (E->get().name == "resource_path") { + if (E.name == "resource_path") { continue; //do not change path } - set(E->get().name, p_resource->get(E->get().name)); + set(E.name, p_resource->get(E.name)); } return OK; } @@ -169,11 +201,11 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res r->local_scene = p_for_scene; - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant p = get(E->get().name); + Variant p = get(E.name); if (p.get_type() == Variant::OBJECT) { RES sr = p; if (sr.is_valid()) { @@ -189,7 +221,7 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res } } - r->set(E->get().name, p); + r->set(E.name, p); } return r; @@ -201,11 +233,11 @@ void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, R local_scene = p_for_scene; - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant p = get(E->get().name); + Variant p = get(E.name); if (p.get_type() == Variant::OBJECT) { RES sr = p; if (sr.is_valid()) { @@ -227,21 +259,21 @@ Ref<Resource> Resource::duplicate(bool p_subresources) const { Ref<Resource> r = (Resource *)ClassDB::instantiate(get_class()); ERR_FAIL_COND_V(r.is_null(), Ref<Resource>()); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_STORAGE)) { + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_STORAGE)) { continue; } - Variant p = get(E->get().name); + Variant p = get(E.name); if ((p.get_type() == Variant::DICTIONARY || p.get_type() == Variant::ARRAY)) { - r->set(E->get().name, p.duplicate(p_subresources)); - } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E->get().usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { + r->set(E.name, p.duplicate(p_subresources)); + } else if (p.get_type() == Variant::OBJECT && (p_subresources || (E.usage & PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE))) { RES sr = p; if (sr.is_valid()) { - r->set(E->get().name, sr->duplicate(p_subresources)); + r->set(E.name, sr->duplicate(p_subresources)); } } else { - r->set(E->get().name, p); + r->set(E.name, p); } } @@ -285,9 +317,9 @@ uint32_t Resource::hash_edited_version() const { List<PropertyInfo> plist; get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE && E->get().type == Variant::OBJECT && E->get().hint == PROPERTY_HINT_RESOURCE_TYPE) { - RES res = get(E->get().name); + for (const PropertyInfo &E : plist) { + if (E.usage & PROPERTY_USAGE_STORAGE && E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_RESOURCE_TYPE) { + RES res = get(E.name); if (res.is_valid()) { hash = hash_djb2_one_32(res->hash_edited_version(), hash); } @@ -350,8 +382,8 @@ bool Resource::is_translation_remapped() const { #ifdef TOOLS_ENABLED //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored -void Resource::set_id_for_path(const String &p_path, int p_id) { - if (p_id == -1) { +void Resource::set_id_for_path(const String &p_path, const String &p_id) { + if (p_id == "") { ResourceCache::path_cache_lock.write_lock(); ResourceCache::resource_path_cache[p_path].erase(get_path()); ResourceCache::path_cache_lock.write_unlock(); @@ -362,15 +394,15 @@ void Resource::set_id_for_path(const String &p_path, int p_id) { } } -int Resource::get_id_for_path(const String &p_path) const { +String Resource::get_id_for_path(const String &p_path) const { ResourceCache::path_cache_lock.read_lock(); if (ResourceCache::resource_path_cache[p_path].has(get_path())) { - int result = ResourceCache::resource_path_cache[p_path][get_path()]; + String result = ResourceCache::resource_path_cache[p_path][get_path()]; ResourceCache::path_cache_lock.read_unlock(); return result; } else { ResourceCache::path_cache_lock.read_unlock(); - return -1; + return ""; } } #endif @@ -414,7 +446,7 @@ Resource::~Resource() { HashMap<String, Resource *> ResourceCache::resources; #ifdef TOOLS_ENABLED -HashMap<String, HashMap<String, int>> ResourceCache::resource_path_cache; +HashMap<String, HashMap<String, String>> ResourceCache::resource_path_cache; #endif RWLock ResourceCache::lock; @@ -520,5 +552,7 @@ void ResourceCache::dump(const char *p_file, bool p_short) { } lock.read_unlock(); +#else + WARN_PRINT("ResourceCache::dump only with in debug builds."); #endif } diff --git a/core/io/resource.h b/core/io/resource.h index 028fed1c6e..9ccc247887 100644 --- a/core/io/resource.h +++ b/core/io/resource.h @@ -31,6 +31,7 @@ #ifndef RESOURCE_H #define RESOURCE_H +#include "core/io/resource_uid.h" #include "core/object/class_db.h" #include "core/object/ref_counted.h" #include "core/templates/safe_refcount.h" @@ -59,9 +60,7 @@ private: String name; String path_cache; - int subindex = 0; - - virtual bool _use_builtin_script() const { return true; } + String scene_unique_id; #ifdef TOOLS_ENABLED uint64_t last_modified_time = 0; @@ -105,8 +104,9 @@ public: virtual void set_path(const String &p_path, bool p_take_over = false); String get_path() const; - void set_subindex(int p_sub_index); - int get_subindex() const; + static String generate_scene_unique_id(); + void set_scene_unique_id(const String &p_id); + 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, Map<Ref<Resource>, Ref<Resource>> &remap_cache); @@ -140,8 +140,8 @@ public: #ifdef TOOLS_ENABLED //helps keep IDs same number when loading/saving scenes. -1 clears ID and it Returns -1 when no id stored - void set_id_for_path(const String &p_path, int p_id); - int get_id_for_path(const String &p_path) const; + void set_id_for_path(const String &p_path, const String &p_id); + String get_id_for_path(const String &p_path) const; #endif Resource(); @@ -156,7 +156,7 @@ class ResourceCache { static RWLock lock; static HashMap<String, Resource *> resources; #ifdef TOOLS_ENABLED - static HashMap<String, HashMap<String, int>> resource_path_cache; // each tscn has a set of resource paths and IDs + static HashMap<String, HashMap<String, String>> resource_path_cache; // Each tscn has a set of resource paths and IDs. static RWLock path_cache_lock; #endif // TOOLS_ENABLED friend void unregister_core_types(); diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp index 0e9815245f..00d4d093da 100644 --- a/core/io/resource_format_binary.cpp +++ b/core/io/resource_format_binary.cpp @@ -84,9 +84,10 @@ enum { OBJECT_EXTERNAL_RESOURCE = 1, OBJECT_INTERNAL_RESOURCE = 2, OBJECT_EXTERNAL_RESOURCE_INDEX = 3, - //version 2: added 64 bits support for float and int - //version 3: changed nodepath encoding - FORMAT_VERSION = 3, + // Version 2: added 64 bits support for float and int. + // Version 3: changed nodepath encoding. + // Version 4: new string ID for ext/subresources, breaks forward compat. + FORMAT_VERSION = 4, FORMAT_VERSION_CAN_RENAME_DEPS = 1, FORMAT_VERSION_NO_NODEPATH_PROPERTY = 3, }; @@ -311,7 +312,14 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } break; case OBJECT_INTERNAL_RESOURCE: { uint32_t index = f->get_32(); - String path = res_path + "::" + itos(index); + String path; + + if (using_named_scene_ids) { // New format. + ERR_FAIL_INDEX_V((int)index, internal_resources.size(), ERR_PARSE_ERROR); + path = internal_resources[index].path; + } else { + path += res_path + "::" + itos(index); + } //always use internal cache for loading internal resources if (!internal_index_cache.has(path)) { @@ -320,7 +328,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { } else { r_v = internal_index_cache[path]; } - } break; case OBJECT_EXTERNAL_RESOURCE: { //old file format, still around for compatibility @@ -378,7 +385,6 @@ Error ResourceLoaderBinary::parse_variant(Variant &r_v) { ERR_FAIL_V(ERR_FILE_CORRUPT); } break; } - } break; case VARIANT_CALLABLE: { r_v = Callable(); @@ -659,15 +665,17 @@ Error ResourceLoaderBinary::load() { //maybe it is loaded already String path; - int subindex = 0; + String id; if (!main) { path = internal_resources[i].path; if (path.begins_with("local://")) { path = path.replace_first("local://", ""); - subindex = path.to_int(); + id = path; path = res_path + "::" + path; + + internal_resources.write[i].path = path; // Update path. } if (cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { @@ -722,7 +730,7 @@ Error ResourceLoaderBinary::load() { if (path != String() && 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 } - r->set_subindex(subindex); + r->set_scene_unique_id(id); } if (!main) { @@ -808,13 +816,18 @@ String ResourceLoaderBinary::get_unicode_string() { } void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types) { - open(p_f); + open(p_f, false, true); if (error) { return; } for (int i = 0; i < external_resources.size(); i++) { - String dep = external_resources[i].path; + String dep; + if (external_resources[i].uid != ResourceUID::INVALID_ID) { + dep = ResourceUID::get_singleton()->id_to_text(external_resources[i].uid); + } else { + dep = external_resources[i].path; + } if (p_add_types && external_resources[i].type != String()) { dep += "::" + external_resources[i].type; @@ -824,7 +837,7 @@ void ResourceLoaderBinary::get_dependencies(FileAccess *p_f, List<String> *p_dep } } -void ResourceLoaderBinary::open(FileAccess *p_f) { +void ResourceLoaderBinary::open(FileAccess *p_f, bool p_no_resources, bool p_keep_uuid_paths) { error = OK; f = p_f; @@ -879,10 +892,29 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { print_bl("type: " + type); importmd_ofs = f->get_64(); - for (int i = 0; i < 14; i++) { + uint32_t flags = f->get_32(); + if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_NAMED_SCENE_IDS) { + using_named_scene_ids = true; + } + if (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS) { + using_uids = true; + } + + if (using_uids) { + uid = f->get_64(); + } else { + f->get_64(); // skip over uid field + uid = ResourceUID::INVALID_ID; + } + + for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { f->get_32(); //skip a few reserved fields } + if (p_no_resources) { + return; + } + uint32_t string_table_size = f->get_32(); string_map.resize(string_table_size); for (uint32_t i = 0; i < string_table_size; i++) { @@ -896,8 +928,18 @@ void ResourceLoaderBinary::open(FileAccess *p_f) { for (uint32_t i = 0; i < ext_resources_size; i++) { ExtResource er; er.type = get_unicode_string(); - er.path = get_unicode_string(); + if (using_uids) { + er.uid = f->get_64(); + if (!p_keep_uuid_paths && er.uid != ResourceUID::INVALID_ID) { + if (ResourceUID::get_singleton()->has_id(er.uid)) { + // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path. + er.path = ResourceUID::get_singleton()->get_id_path(er.uid); + } else { + WARN_PRINT(String(res_path + ": In external resource #" + itos(i) + ", invalid UUID: " + ResourceUID::get_singleton()->id_to_text(er.uid) + " - using text path instead: " + er.path).utf8().get_data()); + } + } + } external_resources.push_back(er); } @@ -1013,8 +1055,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions_for_type(const String extensions.sort(); - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - String ext = E->get().to_lower(); + for (const String &E : extensions) { + String ext = E.to_lower(); p_extensions->push_back(ext); } } @@ -1024,8 +1066,8 @@ void ResourceFormatLoaderBinary::get_recognized_extensions(List<String> *p_exten ClassDB::get_resource_base_extensions(&extensions); extensions.sort(); - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - String ext = E->get().to_lower(); + for (const String &E : extensions) { + String ext = E.to_lower(); p_extensions->push_back(ext); } } @@ -1161,8 +1203,15 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons uint64_t importmd_ofs = f->get_64(); fw->store_64(0); //metadata offset - for (int i = 0; i < 14; i++) { - fw->store_32(0); + uint32_t flags = f->get_32(); + bool using_uids = (flags & ResourceFormatSaverBinaryInstance::FORMAT_FLAG_UIDS); + uint64_t uid_data = f->get_64(); + + fw->store_32(flags); + fw->store_64(uid_data); + + for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { + fw->store_32(0); // reserved f->get_32(); } @@ -1183,6 +1232,16 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons String type = get_ustring(f); String path = get_ustring(f); + if (using_uids) { + ResourceUID::ID uid = f->get_64(); + if (uid != ResourceUID::INVALID_ID) { + if (ResourceUID::get_singleton()->has_id(uid)) { + // If a UID is found and the path is valid, it will be used, otherwise, it falls back to the path. + path = ResourceUID::get_singleton()->get_id_path(uid); + } + } + } + bool relative = false; if (!path.begins_with("res://")) { path = local_path.plus_file(path).simplify_path(); @@ -1194,6 +1253,8 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons path = np; } + String full_path = path; + if (relative) { //restore relative path = local_path.path_to_file(path); @@ -1201,6 +1262,11 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons save_ustring(fw, type); save_ustring(fw, path); + + if (using_uids) { + ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(full_path); + fw->store_64(uid); + } } int64_t size_diff = (int64_t)fw->get_position() - (int64_t)f->get_position(); @@ -1256,6 +1322,28 @@ String ResourceFormatLoaderBinary::get_resource_type(const String &p_path) const return ClassDB::get_compatibility_remapped_class(r); } +ResourceUID::ID ResourceFormatLoaderBinary::get_resource_uid(const String &p_path) const { + String ext = p_path.get_extension().to_lower(); + if (!ClassDB::is_resource_extension(ext)) { + return ResourceUID::INVALID_ID; + } + + FileAccess *f = FileAccess::open(p_path, FileAccess::READ); + if (!f) { + return ResourceUID::INVALID_ID; //could not read + } + + ResourceLoaderBinary loader; + loader.local_path = ProjectSettings::get_singleton()->localize_path(p_path); + loader.res_path = loader.local_path; + //loader.set_local_path( Globals::get_singleton()->localize_path(p_path) ); + loader.open(f, true); + if (loader.error != OK) { + return ResourceUID::INVALID_ID; //could not read + } + return loader.uid; +} + /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// @@ -1269,11 +1357,7 @@ void ResourceFormatSaverBinaryInstance::_pad_buffer(FileAccess *f, int p_bytes) } } -void ResourceFormatSaverBinaryInstance::_write_variant(const Variant &p_property, const PropertyInfo &p_hint) { - write_variant(f, p_property, resource_set, external_resources, string_map, p_hint); -} - -void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { +void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint) { switch (p_property.get_type()) { case Variant::NIL: { f->store_32(VARIANT_NIL); @@ -1492,13 +1576,13 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia f->store_32(OBJECT_EXTERNAL_RESOURCE_INDEX); f->store_32(external_resources[res]); } else { - if (!resource_set.has(res)) { + if (!resource_map.has(res)) { f->store_32(OBJECT_EMPTY); ERR_FAIL_MSG("Resource was not pre cached for the resource section, most likely due to circular reference."); } f->store_32(OBJECT_INTERNAL_RESOURCE); - f->store_32(res->get_subindex()); + f->store_32(resource_map[res]); //internal resource } @@ -1520,14 +1604,14 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + for (const Variant &E : keys) { /* - if (!_check_type(dict[E->get()])) + if (!_check_type(dict[E])) continue; */ - write_variant(f, E->get(), resource_set, external_resources, string_map); - write_variant(f, d[E->get()], resource_set, external_resources, string_map); + write_variant(f, E, resource_map, external_resources, string_map); + write_variant(f, d[E], resource_map, external_resources, string_map); } } break; @@ -1536,7 +1620,7 @@ void ResourceFormatSaverBinaryInstance::write_variant(FileAccess *f, const Varia 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_set, external_resources, string_map); + write_variant(f, a[i], resource_map, external_resources, string_map); } } break; @@ -1677,15 +1761,15 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant res->get_property_list(&property_list); - for (List<PropertyInfo>::Element *E = property_list.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { - Variant value = res->get(E->get().name); - if (E->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { + for (const PropertyInfo &E : property_list) { + if (E.usage & PROPERTY_USAGE_STORAGE) { + Variant value = res->get(E.name); + if (E.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { RES sres = value; if (sres.is_valid()) { NonPersistentKey npk; npk.base = res; - npk.property = E->get().name; + npk.property = E.name; non_persistent_map[npk] = sres; resource_set.insert(sres); saved_resources.push_back(sres); @@ -1715,9 +1799,9 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant Dictionary d = p_variant; List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _find_resources(E->get()); - Variant v = d[E->get()]; + for (const Variant &E : keys) { + _find_resources(E); + Variant v = d[E]; _find_resources(v); } } break; @@ -1816,46 +1900,49 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p save_unicode_string(f, p_resource->get_class()); f->store_64(0); //offset to import metadata - for (int i = 0; i < 14; i++) { + f->store_32(FORMAT_FLAG_NAMED_SCENE_IDS | FORMAT_FLAG_UIDS); + ResourceUID::ID uid = ResourceSaver::get_resource_id_for_path(p_path, true); + f->store_64(uid); + for (int i = 0; i < ResourceFormatSaverBinaryInstance::RESERVED_FIELDS; i++) { f->store_32(0); // reserved } List<ResourceData> resources; { - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { + for (const RES &E : saved_resources) { ResourceData &rd = resources.push_back(ResourceData())->get(); - rd.type = E->get()->get_class(); + rd.type = E->get_class(); List<PropertyInfo> property_list; - E->get()->get_property_list(&property_list); + E->get_property_list(&property_list); - for (List<PropertyInfo>::Element *F = property_list.front(); F; F = F->next()) { - if (skip_editor && F->get().name.begins_with("__editor")) { + for (const PropertyInfo &F : property_list) { + if (skip_editor && F.name.begins_with("__editor")) { continue; } - if ((F->get().usage & PROPERTY_USAGE_STORAGE)) { + if ((F.usage & PROPERTY_USAGE_STORAGE)) { Property p; - p.name_idx = get_string_index(F->get().name); + p.name_idx = get_string_index(F.name); - if (F->get().usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { + if (F.usage & PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT) { NonPersistentKey npk; - npk.base = E->get(); - npk.property = F->get().name; + npk.base = E; + npk.property = F.name; if (non_persistent_map.has(npk)) { p.value = non_persistent_map[npk]; } } else { - p.value = E->get()->get(F->get().name); + p.value = E->get(F.name); } - Variant default_value = ClassDB::class_get_default_property_value(E->get()->get_class(), F->get().name); + Variant default_value = ClassDB::class_get_default_property_value(E->get_class(), F.name); if (default_value.get_type() != Variant::NIL && bool(Variant::evaluate(Variant::OP_EQUAL, p.value, default_value))) { continue; } - p.pi = F->get(); + p.pi = F; rd.properties.push_back(p); } @@ -1882,41 +1969,47 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p String path = save_order[i]->get_path(); path = relative_paths ? local_path.path_to_file(path) : path; save_unicode_string(f, path); + ResourceUID::ID ruid = ResourceSaver::get_resource_id_for_path(save_order[i]->get_path(), false); + f->store_64(ruid); } // save internal resource table f->store_32(saved_resources.size()); //amount of internal resources Vector<uint64_t> ofs_pos; - Set<int> used_indices; + Set<String> used_unique_ids; - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); + for (RES &r : saved_resources) { if (r->get_path() == "" || r->get_path().find("::") != -1) { - if (r->get_subindex() != 0) { - if (used_indices.has(r->get_subindex())) { - r->set_subindex(0); //repeated + if (r->get_scene_unique_id() != "") { + if (used_unique_ids.has(r->get_scene_unique_id())) { + r->set_scene_unique_id(""); } else { - used_indices.insert(r->get_subindex()); + used_unique_ids.insert(r->get_scene_unique_id()); } } } } - for (List<RES>::Element *E = saved_resources.front(); E; E = E->next()) { - RES r = E->get(); + Map<RES, int> resource_map; + int res_index = 0; + for (RES &r : saved_resources) { if (r->get_path() == "" || r->get_path().find("::") != -1) { - if (r->get_subindex() == 0) { - int new_subindex = 1; - if (used_indices.size()) { - new_subindex = used_indices.back()->get() + 1; + if (r->get_scene_unique_id() == "") { + String new_id; + + while (true) { + new_id = r->get_class() + "_" + Resource::generate_scene_unique_id(); + if (!used_unique_ids.has(new_id)) { + break; + } } - r->set_subindex(new_subindex); - used_indices.insert(new_subindex); + r->set_scene_unique_id(new_id); + used_unique_ids.insert(new_id); } - save_unicode_string(f, "local://" + itos(r->get_subindex())); + save_unicode_string(f, "local://" + r->get_scene_unique_id()); if (takeover_paths) { - r->set_path(p_path + "::" + itos(r->get_subindex()), true); + r->set_path(p_path + "::" + r->get_scene_unique_id(), true); } #ifdef TOOLS_ENABLED r->set_edited(false); @@ -1926,22 +2019,20 @@ Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const RES &p } ofs_pos.push_back(f->get_position()); f->store_64(0); //offset in 64 bits + resource_map[r] = res_index++; } Vector<uint64_t> ofs_table; //now actually save the resources - for (List<ResourceData>::Element *E = resources.front(); E; E = E->next()) { - ResourceData &rd = E->get(); - + for (const ResourceData &rd : resources) { ofs_table.push_back(f->get_position()); save_unicode_string(f, rd.type); f->store_32(rd.properties.size()); - for (List<Property>::Element *F = rd.properties.front(); F; F = F->next()) { - Property &p = F->get(); + for (const Property &p : rd.properties) { f->store_32(p.name_idx); - _write_variant(p.value, F->get().pi); + write_variant(f, p.value, resource_map, external_resources, string_map, p.pi); } } diff --git a/core/io/resource_format_binary.h b/core/io/resource_format_binary.h index abc7403935..a6e6d1848e 100644 --- a/core/io/resource_format_binary.h +++ b/core/io/resource_format_binary.h @@ -47,6 +47,8 @@ class ResourceLoaderBinary { uint64_t importmd_ofs = 0; + ResourceUID::ID uid = ResourceUID::INVALID_ID; + Vector<char> str_buf; List<RES> resource_cache; @@ -57,9 +59,12 @@ class ResourceLoaderBinary { struct ExtResource { String path; String type; + ResourceUID::ID uid = ResourceUID::INVALID_ID; RES cache; }; + bool using_named_scene_ids = false; + bool using_uids = false; bool use_sub_threads = false; float *progress = nullptr; Vector<ExtResource> external_resources; @@ -93,7 +98,7 @@ public: void set_translation_remapped(bool p_remapped); void set_remaps(const Map<String, String> &p_remaps) { remaps = p_remaps; } - void open(FileAccess *p_f); + void open(FileAccess *p_f, bool p_no_resources = false, bool p_keep_uuid_paths = false); String recognize(FileAccess *p_f); void get_dependencies(FileAccess *p_f, List<String> *p_dependencies, bool p_add_types); @@ -108,6 +113,7 @@ public: 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 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 Map<String, String> &p_map); }; @@ -150,14 +156,19 @@ class ResourceFormatSaverBinaryInstance { }; static void _pad_buffer(FileAccess *f, int p_bytes); - void _write_variant(const Variant &p_property, const PropertyInfo &p_hint = PropertyInfo()); void _find_resources(const Variant &p_variant, bool p_main = false); static void save_unicode_string(FileAccess *f, const String &p_string, bool p_bit_on_len = false); int get_string_index(const String &p_string); public: + enum { + FORMAT_FLAG_NAMED_SCENE_IDS = 1, + FORMAT_FLAG_UIDS = 2, + // Amount of reserved 32-bit fields in resource header + RESERVED_FIELDS = 11 + }; Error save(const String &p_path, const RES &p_resource, uint32_t p_flags = 0); - static void write_variant(FileAccess *f, const Variant &p_property, Set<RES> &resource_set, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo()); + static void write_variant(FileAccess *f, const Variant &p_property, Map<RES, int> &resource_map, Map<RES, int> &external_resources, Map<StringName, int> &string_map, const PropertyInfo &p_hint = PropertyInfo()); }; class ResourceFormatSaverBinary : public ResourceFormatSaver { diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index b503655edd..1e166015b0 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -93,6 +93,8 @@ Error ResourceFormatImporter::_get_path_and_type(const String &p_path, PathAndTy r_path_and_type.type = ClassDB::get_compatibility_remapped_class(value); } else if (assign == "importer") { r_path_and_type.importer = value; + } else if (assign == "uid") { + r_path_and_type.uid = ResourceUID::get_singleton()->text_to_id(value); } else if (assign == "group_file") { r_path_and_type.group_file = value; } else if (assign == "metadata") { @@ -146,10 +148,10 @@ void ResourceFormatImporter::get_recognized_extensions(List<String> *p_extension for (int i = 0; i < importers.size(); i++) { List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); - for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { - if (!found.has(F->get())) { - p_extensions->push_back(F->get()); - found.insert(F->get()); + for (const String &F : local_exts) { + if (!found.has(F)) { + p_extensions->push_back(F); + found.insert(F); } } } @@ -175,10 +177,10 @@ void ResourceFormatImporter::get_recognized_extensions_for_type(const String &p_ List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); - for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { - if (!found.has(F->get())) { - p_extensions->push_back(F->get()); - found.insert(F->get()); + for (const String &F : local_exts) { + if (!found.has(F)) { + p_extensions->push_back(F); + found.insert(F); } } } @@ -336,6 +338,17 @@ String ResourceFormatImporter::get_resource_type(const String &p_path) const { return pat.type; } +ResourceUID::ID ResourceFormatImporter::get_resource_uid(const String &p_path) const { + PathAndType pat; + Error err = _get_path_and_type(p_path, pat); + + if (err != OK) { + return ResourceUID::INVALID_ID; + } + + return pat.uid; +} + Variant ResourceFormatImporter::get_resource_metadata(const String &p_path) const { PathAndType pat; Error err = _get_path_and_type(p_path, pat); @@ -372,8 +385,8 @@ void ResourceFormatImporter::get_importers_for_extension(const String &p_extensi for (int i = 0; i < importers.size(); i++) { List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); - for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { - if (p_extension.to_lower() == F->get()) { + for (const String &F : local_exts) { + if (p_extension.to_lower() == F) { r_importers->push_back(importers[i]); } } @@ -393,8 +406,8 @@ Ref<ResourceImporter> ResourceFormatImporter::get_importer_by_extension(const St for (int i = 0; i < importers.size(); i++) { List<String> local_exts; importers[i]->get_recognized_extensions(&local_exts); - for (List<String>::Element *F = local_exts.front(); F; F = F->next()) { - if (p_extension.to_lower() == F->get() && importers[i]->get_priority() > priority) { + for (const String &F : local_exts) { + if (p_extension.to_lower() == F && importers[i]->get_priority() > priority) { importer = importers[i]; priority = importers[i]->get_priority(); } @@ -445,3 +458,8 @@ ResourceFormatImporter *ResourceFormatImporter::singleton = nullptr; ResourceFormatImporter::ResourceFormatImporter() { singleton = this; } + +void ResourceImporter::_bind_methods() { + BIND_ENUM_CONSTANT(IMPORT_ORDER_DEFAULT); + BIND_ENUM_CONSTANT(IMPORT_ORDER_SCENE); +} diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 2ceeb176e5..a1cacbd306 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -42,6 +42,7 @@ class ResourceFormatImporter : public ResourceFormatLoader { String importer; String group_file; Variant metadata; + uint64_t uid = ResourceUID::INVALID_ID; }; Error _get_path_and_type(const String &p_path, PathAndType &r_path_and_type, bool *r_valid = nullptr) const; @@ -63,6 +64,8 @@ public: 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 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); @@ -96,6 +99,9 @@ public: class ResourceImporter : public RefCounted { GDCLASS(ResourceImporter, RefCounted); +protected: + static void _bind_methods(); + public: virtual String get_importer_name() const = 0; virtual String get_visible_name() const = 0; @@ -103,7 +109,7 @@ public: virtual String get_save_extension() const = 0; virtual String get_resource_type() const = 0; virtual float get_priority() const { return 1.0; } - virtual int get_import_order() const { return 0; } + virtual int get_import_order() const { return IMPORT_ORDER_DEFAULT; } virtual int get_format_version() const { return 0; } struct ImportOption { @@ -117,6 +123,11 @@ public: ImportOption() {} }; + enum ImportOrder { + IMPORT_ORDER_DEFAULT = 0, + IMPORT_ORDER_SCENE = 100, + }; + virtual bool has_advanced_options() const { return false; } virtual void show_advanced_options(const String &p_path) {} @@ -137,4 +148,6 @@ public: virtual String get_import_settings_string() const { return String(); } }; +VARIANT_ENUM_CAST(ResourceImporter::ImportOrder); + #endif // RESOURCE_IMPORTER_H diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index c5dfe1f2b0..d02d827443 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -58,8 +58,8 @@ bool ResourceFormatLoader::recognize_path(const String &p_path, const String &p_ get_recognized_extensions_for_type(p_for_type, &extensions); } - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(extension) == 0) { + for (const String &E : extensions) { + if (E.nocasecmp_to(extension) == 0) { return true; } } @@ -84,6 +84,14 @@ String ResourceFormatLoader::get_resource_type(const String &p_path) const { return ""; } +ResourceUID::ID ResourceFormatLoader::get_resource_uid(const String &p_path) const { + if (get_script_instance() && get_script_instance()->has_method("_get_resource_uid")) { + return get_script_instance()->call("_get_resource_uid", p_path); + } + + return ResourceUID::INVALID_ID; +} + void ResourceFormatLoader::get_recognized_extensions_for_type(const String &p_type, List<String> *p_extensions) const { if (p_type == "" || handles_type(p_type)) { get_recognized_extensions(p_extensions); @@ -270,13 +278,18 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { thread_load_mutex->unlock(); } -Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) { - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; +static String _validate_local_path(const String &p_path) { + ResourceUID::ID uid = ResourceUID::get_singleton()->text_to_id(p_path); + if (uid != ResourceUID::INVALID_ID) { + return ResourceUID::get_singleton()->get_id_path(uid); + } else if (p_path.is_rel_path()) { + return "res://" + p_path; } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); + return ProjectSettings::get_singleton()->localize_path(p_path); } +} +Error ResourceLoader::load_threaded_request(const String &p_path, const String &p_type_hint, bool p_use_sub_threads, ResourceFormatLoader::CacheMode p_cache_mode, const String &p_source_resource) { + String local_path = _validate_local_path(p_path); thread_load_mutex->lock(); @@ -399,12 +412,7 @@ float ResourceLoader::_dependency_get_progress(const String &p_path) { } ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const String &p_path, float *r_progress) { - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - } + String local_path = _validate_local_path(p_path); thread_load_mutex->lock(); if (!thread_load_tasks.has(local_path)) { @@ -424,12 +432,7 @@ ResourceLoader::ThreadLoadStatus ResourceLoader::load_threaded_get_status(const } RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) { - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - } + String local_path = _validate_local_path(p_path); thread_load_mutex->lock(); if (!thread_load_tasks.has(local_path)) { @@ -510,12 +513,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour *r_error = ERR_CANT_OPEN; } - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - } + String local_path = _validate_local_path(p_path); if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { thread_load_mutex->lock(); @@ -612,12 +610,7 @@ RES ResourceLoader::load(const String &p_path, const String &p_type_hint, Resour } bool ResourceLoader::exists(const String &p_path, const String &p_type_hint) { - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - } + String local_path = _validate_local_path(p_path); if (ResourceCache::has(local_path)) { return true; // If cached, it probably exists @@ -677,14 +670,7 @@ void ResourceLoader::remove_resource_format_loader(Ref<ResourceFormatLoader> p_f } int ResourceLoader::get_import_order(const String &p_path) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -702,14 +688,7 @@ int ResourceLoader::get_import_order(const String &p_path) { } String ResourceLoader::get_import_group_file(const String &p_path) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -727,14 +706,7 @@ String ResourceLoader::get_import_group_file(const String &p_path) { } bool ResourceLoader::is_import_valid(const String &p_path) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -752,14 +724,7 @@ bool ResourceLoader::is_import_valid(const String &p_path) { } bool ResourceLoader::is_imported(const String &p_path) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -777,14 +742,7 @@ bool ResourceLoader::is_imported(const String &p_path) { } void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -800,14 +758,7 @@ void ResourceLoader::get_dependencies(const String &p_path, List<String> *p_depe } Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String, String> &p_map) { - String path = _path_remap(p_path); - - String local_path; - if (path.is_rel_path()) { - local_path = "res://" + path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(path); - } + String local_path = _path_remap(_validate_local_path(p_path)); for (int i = 0; i < loader_count; i++) { if (!loader[i]->recognize_path(local_path)) { @@ -825,12 +776,7 @@ Error ResourceLoader::rename_dependencies(const String &p_path, const Map<String } String ResourceLoader::get_resource_type(const String &p_path) { - String local_path; - if (p_path.is_rel_path()) { - local_path = "res://" + p_path; - } else { - local_path = ProjectSettings::get_singleton()->localize_path(p_path); - } + String local_path = _validate_local_path(p_path); for (int i = 0; i < loader_count; i++) { String result = loader[i]->get_resource_type(local_path); @@ -842,6 +788,19 @@ String ResourceLoader::get_resource_type(const String &p_path) { return ""; } +ResourceUID::ID ResourceLoader::get_resource_uid(const String &p_path) { + String local_path = _validate_local_path(p_path); + + for (int i = 0; i < loader_count; i++) { + ResourceUID::ID id = loader[i]->get_resource_uid(local_path); + if (id != ResourceUID::INVALID_ID) { + return id; + } + } + + return ResourceUID::INVALID_ID; +} + String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_remapped) { String new_path = p_path; @@ -978,15 +937,15 @@ void ResourceLoader::load_translation_remaps() { Dictionary remaps = ProjectSettings::get_singleton()->get("internationalization/locale/translation_remaps"); List<Variant> keys; remaps.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - Array langs = remaps[E->get()]; + for (const Variant &E : keys) { + 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]; } - translation_remaps[String(E->get())] = lang_remaps; + translation_remaps[String(E)] = lang_remaps; } } @@ -1071,8 +1030,7 @@ void ResourceLoader::add_custom_loaders() { List<StringName> global_classes; ScriptServer::get_global_class_list(&global_classes); - for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); + for (const StringName &class_name : global_classes) { StringName base_class = ScriptServer::get_global_class_native_base(class_name); if (base_class == custom_loader_base_class) { diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h index c656b9a69c..e525e80a9d 100644 --- a/core/io/resource_loader.h +++ b/core/io/resource_loader.h @@ -40,9 +40,9 @@ class ResourceFormatLoader : public 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 and subresource use path cache, but replace existing loaded resources when available with information from disk + 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. }; protected: @@ -56,6 +56,7 @@ public: 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 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 Map<String, String> &p_map); virtual bool is_import_valid(const String &p_path) const { return true; } @@ -107,7 +108,7 @@ private: friend class ResourceFormatImporter; friend class ResourceInteractiveLoader; - //internal load function + // Internal load function. static RES _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); static ResourceLoadedCallback _loaded_callback; @@ -157,6 +158,7 @@ public: static void add_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader, bool p_at_front = false); static void remove_resource_format_loader(Ref<ResourceFormatLoader> p_format_loader); static String get_resource_type(const String &p_path); + static ResourceUID::ID get_resource_uid(const String &p_path); static void get_dependencies(const String &p_path, List<String> *p_dependencies, bool p_add_types = false); static Error rename_dependencies(const String &p_path, const Map<String, String> &p_map); static bool is_import_valid(const String &p_path); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 80cb85fba3..564de5ee69 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -39,6 +39,7 @@ Ref<ResourceFormatSaver> ResourceSaver::saver[MAX_SAVERS]; int ResourceSaver::saver_count = 0; bool ResourceSaver::timestamp_on_save = false; ResourceSavedCallback ResourceSaver::save_callback = nullptr; +ResourceSaverGetResourceIDForPath ResourceSaver::save_get_id_for_path = nullptr; Error ResourceFormatSaver::save(const String &p_path, const RES &p_resource, uint32_t p_flags) { if (get_script_instance() && get_script_instance()->has_method("_save")) { @@ -94,8 +95,8 @@ Error ResourceSaver::save(const String &p_path, const RES &p_resource, uint32_t bool recognized = false; saver[i]->get_recognized_extensions(p_resource, &extensions); - for (List<String>::Element *E = extensions.front(); E; E = E->next()) { - if (E->get().nocasecmp_to(extension) == 0) { + for (const String &E : extensions) { + if (E.nocasecmp_to(extension) == 0) { recognized = true; } } @@ -236,8 +237,7 @@ void ResourceSaver::add_custom_savers() { List<StringName> global_classes; ScriptServer::get_global_class_list(&global_classes); - for (List<StringName>::Element *E = global_classes.front(); E; E = E->next()) { - StringName class_name = E->get(); + for (const StringName &class_name : global_classes) { StringName base_class = ScriptServer::get_global_class_native_base(class_name); if (base_class == custom_saver_base_class) { @@ -259,3 +259,14 @@ void ResourceSaver::remove_custom_savers() { remove_resource_format_saver(custom_savers[i]); } } + +ResourceUID::ID ResourceSaver::get_resource_id_for_path(const String &p_path, bool p_generate) { + if (save_get_id_for_path) { + return save_get_id_for_path(p_path, p_generate); + } + return ResourceUID::INVALID_ID; +} + +void ResourceSaver::set_get_resource_id_for_path(ResourceSaverGetResourceIDForPath p_callback) { + save_get_id_for_path = p_callback; +} diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 07154aac4d..2fc8d32126 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -48,6 +48,7 @@ public: }; typedef void (*ResourceSavedCallback)(Ref<Resource> p_resource, const String &p_path); +typedef ResourceUID::ID (*ResourceSaverGetResourceIDForPath)(const String &p_path, bool p_generate); class ResourceSaver { enum { @@ -58,6 +59,7 @@ class ResourceSaver { static int saver_count; static bool timestamp_on_save; static ResourceSavedCallback save_callback; + static ResourceSaverGetResourceIDForPath save_get_id_for_path; static Ref<ResourceFormatSaver> _find_custom_resource_format_saver(String path); @@ -80,7 +82,10 @@ public: static void set_timestamp_on_save(bool p_timestamp) { timestamp_on_save = p_timestamp; } static bool get_timestamp_on_save() { return timestamp_on_save; } + static ResourceUID::ID get_resource_id_for_path(const String &p_path, bool p_generate = false); + 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 void remove_custom_resource_format_saver(String script_path); diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp new file mode 100644 index 0000000000..97d683f415 --- /dev/null +++ b/core/io/resource_uid.cpp @@ -0,0 +1,262 @@ +/*************************************************************************/ +/* resource_uid.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 "resource_uid.h" +#include "core/crypto/crypto.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" + +static constexpr uint32_t char_count = ('z' - 'a'); +static constexpr uint32_t base = char_count + ('9' - '0'); + +const char *ResourceUID::CACHE_FILE = "res://.godot/uid_cache.bin"; + +String ResourceUID::id_to_text(ID p_id) const { + if (p_id < 0) { + return "uid://<invalid>"; + } + String txt; + + while (p_id) { + uint32_t c = p_id % base; + if (c < char_count) { + txt = String::chr('a' + c) + txt; + } else { + txt = String::chr('0' + (c - char_count)) + txt; + } + p_id /= base; + } + + return "uid://" + txt; +} + +ResourceUID::ID ResourceUID::text_to_id(const String &p_text) const { + if (!p_text.begins_with("uid://") || p_text == "uid://<invalid>") { + return INVALID_ID; + } + + uint32_t l = p_text.length(); + uint64_t uid = 0; + for (uint32_t i = 6; i < l; i++) { + uid *= base; + uint32_t c = p_text[i]; + if (c >= 'a' && c <= 'z') { + uid += c - 'a'; + } else if (c >= '0' && c <= '9') { + uid += c - '0' + char_count; + } else { + return INVALID_ID; + } + } + return ID(uid & 0x7FFFFFFFFFFFFFFF); +} + +ResourceUID::ID ResourceUID::create_id() const { + mutex.lock(); + if (crypto.is_null()) { + crypto = Ref<Crypto>(Crypto::create()); + } + mutex.unlock(); + while (true) { + PackedByteArray bytes = crypto->generate_random_bytes(8); + ERR_FAIL_COND_V(bytes.size() != 8, INVALID_ID); + const uint64_t *ptr64 = (const uint64_t *)bytes.ptr(); + ID id = int64_t((*ptr64) & 0x7FFFFFFFFFFFFFFF); + mutex.lock(); + bool exists = unique_ids.has(id); + mutex.unlock(); + if (!exists) { + return id; + } + } +} + +bool ResourceUID::has_id(ID p_id) const { + MutexLock l(mutex); + return unique_ids.has(p_id); +} +void ResourceUID::add_id(ID p_id, const String &p_path) { + MutexLock l(mutex); + ERR_FAIL_COND(unique_ids.has(p_id)); + Cache c; + c.cs = p_path.utf8(); + unique_ids[p_id] = c; + changed = true; +} + +void ResourceUID::set_id(ID p_id, const String &p_path) { + MutexLock l(mutex); + ERR_FAIL_COND(!unique_ids.has(p_id)); + CharString cs = p_path.utf8(); + if (strcmp(cs.ptr(), unique_ids[p_id].cs.ptr()) != 0) { + unique_ids[p_id].cs = cs; + unique_ids[p_id].saved_to_cache = false; //changed + changed = true; + } +} + +String ResourceUID::get_id_path(ID p_id) const { + MutexLock l(mutex); + ERR_FAIL_COND_V(!unique_ids.has(p_id), String()); + const CharString &cs = unique_ids[p_id].cs; + String s(cs.ptr()); + return s; +} +void ResourceUID::remove_id(ID p_id) { + MutexLock l(mutex); + ERR_FAIL_COND(!unique_ids.has(p_id)); + unique_ids.erase(p_id); +} + +Error ResourceUID::save_to_cache() { + if (!FileAccess::exists(CACHE_FILE)) { + DirAccessRef d = DirAccess::create(DirAccess::ACCESS_RESOURCES); + d->make_dir_recursive(String(CACHE_FILE).get_base_dir()); //ensure base dir exists + } + + FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::WRITE); + if (!f) { + return ERR_CANT_OPEN; + } + + MutexLock l(mutex); + f->store_32(unique_ids.size()); + + cache_entries = 0; + + for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) { + f->store_64(E.key()); + uint32_t s = E.get().cs.length(); + f->store_32(s); + f->store_buffer((const uint8_t *)E.get().cs.ptr(), s); + E.get().saved_to_cache = true; + cache_entries++; + } + + changed = false; + return OK; +} + +Error ResourceUID::load_from_cache() { + FileAccessRef f = FileAccess::open(CACHE_FILE, FileAccess::READ); + if (!f) { + return ERR_CANT_OPEN; + } + + MutexLock l(mutex); + unique_ids.clear(); + + uint32_t entry_count = f->get_32(); + for (uint32_t i = 0; i < entry_count; i++) { + int64_t id = f->get_64(); + int32_t len = f->get_32(); + Cache c; + c.cs.resize(len + 1); + ERR_FAIL_COND_V(c.cs.size() != len + 1, ERR_FILE_CORRUPT); // out of memory + c.cs[len] = 0; + int32_t rl = f->get_buffer((uint8_t *)c.cs.ptrw(), len); + ERR_FAIL_COND_V(rl != len, ERR_FILE_CORRUPT); + + c.saved_to_cache = true; + unique_ids[id] = c; + } + + cache_entries = entry_count; + changed = false; + return OK; +} + +Error ResourceUID::update_cache() { + if (!changed) { + return OK; + } + + if (cache_entries == 0) { + return save_to_cache(); + } + MutexLock l(mutex); + + FileAccess *f = nullptr; + for (OrderedHashMap<ID, Cache>::Element E = unique_ids.front(); E; E = E.next()) { + if (!E.get().saved_to_cache) { + if (f == nullptr) { + f = FileAccess::open(CACHE_FILE, FileAccess::READ_WRITE); //append + if (!f) { + return ERR_CANT_OPEN; + } + f->seek_end(); + } + f->store_64(E.key()); + uint32_t s = E.get().cs.length(); + f->store_32(s); + f->store_buffer((const uint8_t *)E.get().cs.ptr(), s); + E.get().saved_to_cache = true; + cache_entries++; + } + } + + if (f != nullptr) { + f->seek(0); + f->store_32(cache_entries); //update amount of entries + f->close(); + memdelete(f); + } + + changed = false; + + return OK; +} + +void ResourceUID::clear() { + cache_entries = 0; + unique_ids.clear(); + changed = false; +} +void ResourceUID::_bind_methods() { + ClassDB::bind_method(D_METHOD("id_to_text", "id"), &ResourceUID::id_to_text); + ClassDB::bind_method(D_METHOD("text_to_id", "text_id"), &ResourceUID::text_to_id); + + ClassDB::bind_method(D_METHOD("create_id"), &ResourceUID::create_id); + + ClassDB::bind_method(D_METHOD("has_id", "id"), &ResourceUID::has_id); + ClassDB::bind_method(D_METHOD("add_id", "id", "path"), &ResourceUID::add_id); + ClassDB::bind_method(D_METHOD("set_id", "id", "path"), &ResourceUID::set_id); + ClassDB::bind_method(D_METHOD("get_id_path", "id"), &ResourceUID::get_id_path); + ClassDB::bind_method(D_METHOD("remove_id", "id"), &ResourceUID::remove_id); + + BIND_CONSTANT(INVALID_ID) +} +ResourceUID *ResourceUID::singleton = nullptr; +ResourceUID::ResourceUID() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} +ResourceUID::~ResourceUID() { +} diff --git a/core/io/resource_uid.h b/core/io/resource_uid.h new file mode 100644 index 0000000000..b12138425a --- /dev/null +++ b/core/io/resource_uid.h @@ -0,0 +1,89 @@ +/*************************************************************************/ +/* resource_uid.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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 RESOURCE_UUID_H +#define RESOURCE_UUID_H + +#include "core/object/ref_counted.h" +#include "core/string/string_name.h" +#include "core/templates/ordered_hash_map.h" + +class Crypto; +class ResourceUID : public Object { + GDCLASS(ResourceUID, Object) +public: + typedef int64_t ID; + enum { + INVALID_ID = -1 + }; + + static const char *CACHE_FILE; + +private: + mutable Ref<Crypto> crypto; + Mutex mutex; + struct Cache { + CharString cs; + bool saved_to_cache = false; + }; + + OrderedHashMap<ID, Cache> unique_ids; //unique IDs and utf8 paths (less memory used) + static ResourceUID *singleton; + + uint32_t cache_entries = 0; + bool changed = false; + +protected: + static void _bind_methods(); + +public: + String id_to_text(ID p_id) const; + ID text_to_id(const String &p_text) const; + + ID create_id() const; + bool has_id(ID p_id) const; + void add_id(ID p_id, const String &p_path); + void set_id(ID p_id, const String &p_path); + String get_id_path(ID p_id) const; + void remove_id(ID p_id); + + Error load_from_cache(); + Error save_to_cache(); + Error update_cache(); + + void clear(); + + static ResourceUID *get_singleton() { return singleton; } + + ResourceUID(); + ~ResourceUID(); +}; + +#endif // RESOURCEUUID_H diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp index 88e11a630c..322eb7ac61 100644 --- a/core/math/a_star.cpp +++ b/core/math/a_star.cpp @@ -35,18 +35,12 @@ #include "scene/scene_string_names.h" int AStar::get_available_point_id() const { - if (points.is_empty()) { - return 1; - } - - // calculate our new next available point id if bigger than before or next id already contained in set of points. if (points.has(last_free_id)) { - int cur_new_id = last_free_id; + int cur_new_id = last_free_id + 1; while (points.has(cur_new_id)) { cur_new_id++; } - int &non_const = const_cast<int &>(last_free_id); - non_const = cur_new_id; + const_cast<int &>(last_free_id) = cur_new_id; } return last_free_id; diff --git a/core/math/basis.cpp b/core/math/basis.cpp index aa3831d4cf..b5e25fb837 100644 --- a/core/math/basis.cpp +++ b/core/math/basis.cpp @@ -1129,3 +1129,21 @@ void Basis::rotate_sh(real_t *p_values) { p_values[7] = -d3; p_values[8] = d4 * s_scale_dst4; } + +Basis Basis::looking_at(const Vector3 &p_target, const Vector3 &p_up) { +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(p_target.is_equal_approx(Vector3()), Basis(), "The target vector can't be zero."); + ERR_FAIL_COND_V_MSG(p_up.is_equal_approx(Vector3()), Basis(), "The up vector can't be zero."); +#endif + Vector3 v_z = -p_target.normalized(); + Vector3 v_x = p_up.cross(v_z); +#ifdef MATH_CHECKS + ERR_FAIL_COND_V_MSG(v_x.is_equal_approx(Vector3()), Basis(), "The target vector and up vector can't be parallel to each other."); +#endif + v_x.normalize(); + Vector3 v_y = v_z.cross(v_x); + + Basis basis; + basis.set(v_x, v_y, v_z); + return basis; +} diff --git a/core/math/basis.h b/core/math/basis.h index 2889a4aa5e..3db2227b70 100644 --- a/core/math/basis.h +++ b/core/math/basis.h @@ -242,6 +242,8 @@ public: operator Quaternion() const { return get_quaternion(); } + static Basis looking_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + Basis(const Quaternion &p_quaternion) { set_quaternion(p_quaternion); }; Basis(const Quaternion &p_quaternion, const Vector3 &p_scale) { set_quaternion_scale(p_quaternion, p_scale); } diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h index 6f7209556e..81adf4d19a 100644 --- a/core/math/delaunay_3d.h +++ b/core/math/delaunay_3d.h @@ -375,8 +375,7 @@ public: OutputSimplex *ret_simplicesw = ret_simplices.ptrw(); uint32_t simplices_written = 0; - for (List<Simplex *>::Element *E = simplex_list.front(); E; E = E->next()) { - Simplex *simplex = E->get(); + for (Simplex *simplex : simplex_list) { bool invalid = false; for (int j = 0; j < 4; j++) { if (simplex->points[j] >= point_count) { diff --git a/core/math/dynamic_bvh.cpp b/core/math/dynamic_bvh.cpp index 8e596f0f9d..f3fb473981 100644 --- a/core/math/dynamic_bvh.cpp +++ b/core/math/dynamic_bvh.cpp @@ -181,7 +181,7 @@ DynamicBVH::Volume DynamicBVH::_bounds(Node **leaves, int p_count) { void DynamicBVH::_bottom_up(Node **leaves, int p_count) { while (p_count > 1) { - real_t minsize = Math_INF; + real_t minsize = INFINITY; int minidx[2] = { -1, -1 }; for (int i = 0; i < p_count; ++i) { for (int j = i + 1; j < p_count; ++j) { diff --git a/core/math/expression.cpp b/core/math/expression.cpp index 0146c345f0..05f2c8dac9 100644 --- a/core/math/expression.cpp +++ b/core/math/expression.cpp @@ -397,10 +397,10 @@ Error Expression::_get_token(Token &r_token) { r_token.value = Math_TAU; } else if (id == "INF") { r_token.type = TK_CONSTANT; - r_token.value = Math_INF; + r_token.value = INFINITY; } else if (id == "NAN") { r_token.type = TK_CONSTANT; - r_token.value = Math_NAN; + r_token.value = NAN; } else if (id == "not") { r_token.type = TK_OP_NOT; } else if (id == "or") { diff --git a/core/math/face3.h b/core/math/face3.h index 5091b338ef..9e9026e54e 100644 --- a/core/math/face3.h +++ b/core/math/face3.h @@ -50,8 +50,8 @@ public: /** * * @param p_plane plane used to split the face - * @param p_res array of at least 3 faces, amount used in functio return - * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in functio return + * @param p_res array of at least 3 faces, amount used in function return + * @param p_is_point_over array of at least 3 booleans, determining which face is over the plane, amount used in function return * @param _epsilon constant used for numerical error rounding, to add "thickness" to the plane (so coplanar points can happen) * @return amount of faces generated by the split, either 0 (means no split possible), 2 or 3 */ diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h index 4ef9b4dbe6..766689e222 100644 --- a/core/math/geometry_3d.h +++ b/core/math/geometry_3d.h @@ -40,7 +40,7 @@ class Geometry3D { public: static void get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2, Vector3 &c1, Vector3 &c2) { -// Do the function 'd' as defined by pb. I think is is dot product of some sort. +// Do the function 'd' as defined by pb. I think it's a dot product of some sort. #define d_of(m, n, o, p) ((m.x - n.x) * (o.x - p.x) + (m.y - n.y) * (o.y - p.y) + (m.z - n.z) * (o.z - p.z)) // Calculate the parametric position on the 2 curves, mua and mub. diff --git a/core/math/math_defs.h b/core/math/math_defs.h index df2223fb78..c3a8f910c0 100644 --- a/core/math/math_defs.h +++ b/core/math/math_defs.h @@ -43,8 +43,6 @@ #define Math_TAU 6.2831853071795864769252867666 #define Math_PI 3.1415926535897932384626433833 #define Math_E 2.7182818284590452353602874714 -#define Math_INF INFINITY -#define Math_NAN NAN #ifdef DEBUG_ENABLED #define MATH_CHECKS @@ -83,6 +81,26 @@ enum VAlign { VALIGN_BOTTOM }; +enum InlineAlign { + // Image alignment points. + INLINE_ALIGN_TOP_TO = 0b0000, + INLINE_ALIGN_CENTER_TO = 0b0001, + INLINE_ALIGN_BOTTOM_TO = 0b0010, + INLINE_ALIGN_IMAGE_MASK = 0b0011, + + // Text alignment points. + INLINE_ALIGN_TO_TOP = 0b0000, + INLINE_ALIGN_TO_CENTER = 0b0100, + INLINE_ALIGN_TO_BASELINE = 0b1000, + INLINE_ALIGN_TO_BOTTOM = 0b1100, + INLINE_ALIGN_TEXT_MASK = 0b1100, + + // Presets. + INLINE_ALIGN_TOP = INLINE_ALIGN_TOP_TO | INLINE_ALIGN_TO_TOP, + INLINE_ALIGN_CENTER = INLINE_ALIGN_CENTER_TO | INLINE_ALIGN_TO_CENTER, + INLINE_ALIGN_BOTTOM = INLINE_ALIGN_BOTTOM_TO | INLINE_ALIGN_TO_BOTTOM +}; + enum Side { SIDE_LEFT, SIDE_TOP, diff --git a/core/math/math_funcs.cpp b/core/math/math_funcs.cpp index e92bb9f4aa..bbed257f60 100644 --- a/core/math/math_funcs.cpp +++ b/core/math/math_funcs.cpp @@ -88,16 +88,6 @@ int Math::range_step_decimals(double p_step) { return step_decimals(p_step); } -double Math::dectime(double p_value, double p_amount, double p_step) { - double sgn = p_value < 0 ? -1.0 : 1.0; - double val = Math::abs(p_value); - val -= p_amount * p_step; - if (val < 0.0) { - val = 0.0; - } - return val * sgn; -} - double Math::ease(double p_x, double p_c) { if (p_x < 0) { p_x = 0; diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h index 3389407e72..4e4f566517 100644 --- a/core/math/math_funcs.h +++ b/core/math/math_funcs.h @@ -296,7 +296,6 @@ public: static int step_decimals(double p_step); static int range_step_decimals(double p_step); static double snapped(double p_value, double p_step); - static double dectime(double p_value, double p_amount, double p_step); static uint32_t larger_prime(uint32_t p_val); diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index 0d77bfe933..0960fe19a6 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -192,9 +192,9 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ continue; } - for (List<Face>::Element *E = faces.front(); E; E = E->next()) { - if (E->get().plane.distance_to(p_points[i]) > over_tolerance) { - E->get().points_over.push_back(i); + for (Face &E : faces) { + if (E.plane.distance_to(p_points[i]) > over_tolerance) { + E.points_over.push_back(i); break; } } @@ -292,8 +292,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ //distribute points into new faces - for (List<List<Face>::Element *>::Element *F = lit_faces.front(); F; F = F->next()) { - Face &lf = F->get()->get(); + for (List<Face>::Element *&F : lit_faces) { + Face &lf = F->get(); for (int i = 0; i < lf.points_over.size(); i++) { if (lf.points_over[i] == f.points_over[next]) { //do not add current one @@ -301,8 +301,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ } Vector3 p = p_points[lf.points_over[i]]; - for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) { - Face &f2 = E->get()->get(); + for (List<Face>::Element *&E : new_faces) { + Face &f2 = E->get(); if (f2.plane.distance_to(p) > over_tolerance) { f2.points_over.push_back(lf.points_over[i]); break; @@ -320,10 +320,10 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ //put faces that contain no points on the front - for (List<List<Face>::Element *>::Element *E = new_faces.front(); E; E = E->next()) { - Face &f2 = E->get()->get(); + for (List<Face>::Element *&E : new_faces) { + Face &f2 = E->get(); if (f2.points_over.size() == 0) { - faces.move_to_front(E->get()); + faces.move_to_front(E); } } @@ -336,19 +336,19 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ Map<Edge, RetFaceConnect> ret_edges; List<Geometry3D::MeshData::Face> ret_faces; - for (List<Face>::Element *E = faces.front(); E; E = E->next()) { + for (const Face &E : faces) { Geometry3D::MeshData::Face f; - f.plane = E->get().plane; + f.plane = E.plane; for (int i = 0; i < 3; i++) { - f.indices.push_back(E->get().vertices[i]); + f.indices.push_back(E.vertices[i]); } List<Geometry3D::MeshData::Face>::Element *F = ret_faces.push_back(f); for (int i = 0; i < 3; i++) { - uint32_t a = E->get().vertices[i]; - uint32_t b = E->get().vertices[(i + 1) % 3]; + uint32_t a = E.vertices[i]; + uint32_t b = E.vertices[(i + 1) % 3]; Edge e(a, b); Map<Edge, RetFaceConnect>::Element *G = ret_edges.find(e); @@ -439,8 +439,8 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry3D::MeshData &r_ r_mesh.faces.resize(ret_faces.size()); int idx = 0; - for (List<Geometry3D::MeshData::Face>::Element *E = ret_faces.front(); E; E = E->next()) { - r_mesh.faces.write[idx++] = E->get(); + for (const Geometry3D::MeshData::Face &E : ret_faces) { + r_mesh.faces.write[idx++] = E; } r_mesh.edges.resize(ret_edges.size()); idx = 0; diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp index 51766b39f4..4f4943c8ef 100644 --- a/core/math/transform_3d.cpp +++ b/core/math/transform_3d.cpp @@ -71,40 +71,12 @@ void Transform3D::rotate_basis(const Vector3 &p_axis, real_t p_phi) { Transform3D Transform3D::looking_at(const Vector3 &p_target, const Vector3 &p_up) const { Transform3D t = *this; - t.set_look_at(origin, p_target, p_up); + t.basis = Basis::looking_at(p_target - origin, p_up); return t; } void Transform3D::set_look_at(const Vector3 &p_eye, const Vector3 &p_target, const Vector3 &p_up) { -#ifdef MATH_CHECKS - ERR_FAIL_COND(p_eye == p_target); - ERR_FAIL_COND(p_up.length() == 0); -#endif - // Reference: MESA source code - Vector3 v_x, v_y, v_z; - - /* Make rotation matrix */ - - /* Z vector */ - v_z = p_eye - p_target; - - v_z.normalize(); - - v_y = p_up; - - v_x = v_y.cross(v_z); -#ifdef MATH_CHECKS - ERR_FAIL_COND(v_x.length() == 0); -#endif - - /* Recompute Y = Z cross X */ - v_y = v_z.cross(v_x); - - v_x.normalize(); - v_y.normalize(); - - basis.set(v_x, v_y, v_z); - + basis = Basis::looking_at(p_target - p_eye, p_up); origin = p_eye; } diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h index 3d8e70cec7..cadfdc13d1 100644 --- a/core/math/transform_3d.h +++ b/core/math/transform_3d.h @@ -75,16 +75,24 @@ public: bool operator!=(const Transform3D &p_transform) const; _FORCE_INLINE_ Vector3 xform(const Vector3 &p_vector) const; + _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; + _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const; + + // NOTE: These are UNSAFE with non-uniform scaling, and will produce incorrect results. + // They use the transpose. + // For safe inverse transforms, xform by the affine_inverse. _FORCE_INLINE_ Vector3 xform_inv(const Vector3 &p_vector) const; + _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; + _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const; + // Safe with non-uniform scaling (uses affine_inverse). _FORCE_INLINE_ Plane xform(const Plane &p_plane) const; _FORCE_INLINE_ Plane xform_inv(const Plane &p_plane) const; - _FORCE_INLINE_ AABB xform(const AABB &p_aabb) const; - _FORCE_INLINE_ AABB xform_inv(const AABB &p_aabb) const; - - _FORCE_INLINE_ Vector<Vector3> xform(const Vector<Vector3> &p_array) const; - _FORCE_INLINE_ Vector<Vector3> xform_inv(const Vector<Vector3> &p_array) const; + // These fast versions use precomputed affine inverse, and should be used in bottleneck areas where + // multiple planes are to be transformed. + _FORCE_INLINE_ Plane xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const; + static _FORCE_INLINE_ Plane xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose); void operator*=(const Transform3D &p_transform); Transform3D operator*(const Transform3D &p_transform) const; @@ -130,30 +138,20 @@ _FORCE_INLINE_ Vector3 Transform3D::xform_inv(const Vector3 &p_vector) const { (basis.elements[0][2] * v.x) + (basis.elements[1][2] * v.y) + (basis.elements[2][2] * v.z)); } +// Neither the plane regular xform or xform_inv are particularly efficient, +// as they do a basis inverse. For xforming a large number +// of planes it is better to pre-calculate the inverse transpose basis once +// and reuse it for each plane, by using the 'fast' version of the functions. _FORCE_INLINE_ Plane Transform3D::xform(const Plane &p_plane) const { - Vector3 point = p_plane.normal * p_plane.d; - Vector3 point_dir = point + p_plane.normal; - point = xform(point); - point_dir = xform(point_dir); - - Vector3 normal = point_dir - point; - normal.normalize(); - real_t d = normal.dot(point); - - return Plane(normal, d); + Basis b = basis.inverse(); + b.transpose(); + return xform_fast(p_plane, b); } _FORCE_INLINE_ Plane Transform3D::xform_inv(const Plane &p_plane) const { - Vector3 point = p_plane.normal * p_plane.d; - Vector3 point_dir = point + p_plane.normal; - point = xform_inv(point); - point_dir = xform_inv(point_dir); - - Vector3 normal = point_dir - point; - normal.normalize(); - real_t d = normal.dot(point); - - return Plane(normal, d); + Transform3D inv = affine_inverse(); + Basis basis_transpose = basis.transposed(); + return xform_inv_fast(p_plane, inv, basis_transpose); } _FORCE_INLINE_ AABB Transform3D::xform(const AABB &p_aabb) const { @@ -231,4 +229,37 @@ Vector<Vector3> Transform3D::xform_inv(const Vector<Vector3> &p_array) const { return array; } +_FORCE_INLINE_ Plane Transform3D::xform_fast(const Plane &p_plane, const Basis &p_basis_inverse_transpose) const { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = xform(point); + + // Use inverse transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_inverse_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + +_FORCE_INLINE_ Plane Transform3D::xform_inv_fast(const Plane &p_plane, const Transform3D &p_inverse, const Basis &p_basis_transpose) { + // Transform a single point on the plane. + Vector3 point = p_plane.normal * p_plane.d; + point = p_inverse.xform(point); + + // Note that instead of precalculating the transpose, an alternative + // would be to use the transpose for the basis transform. + // However that would be less SIMD friendly (requiring a swizzle). + // So the cost is one extra precalced value in the calling code. + // This is probably worth it, as this could be used in bottleneck areas. And + // where it is not a bottleneck, the non-fast method is fine. + + // Use transpose for correct normals with non-uniform scaling. + Vector3 normal = p_basis_transpose.xform(p_plane.normal); + normal.normalize(); + + real_t d = normal.dot(point); + return Plane(normal, d); +} + #endif // TRANSFORM_H diff --git a/core/math/triangle_mesh.cpp b/core/math/triangle_mesh.cpp index 903d5951a8..bf06c848c5 100644 --- a/core/math/triangle_mesh.cpp +++ b/core/math/triangle_mesh.cpp @@ -32,9 +32,9 @@ #include "core/templates/sort_array.h" -int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &max_depth, int &max_alloc) { - if (p_depth > max_depth) { - max_depth = p_depth; +int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, int p_depth, int &r_max_depth, int &r_max_alloc) { + if (p_depth > r_max_depth) { + r_max_depth = p_depth; } if (p_size == 1) { @@ -70,10 +70,10 @@ int TriangleMesh::_create_bvh(BVH *p_bvh, BVH **p_bb, int p_from, int p_size, in } break; } - int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, max_depth, max_alloc); - int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, max_depth, max_alloc); + int left = _create_bvh(p_bvh, p_bb, p_from, p_size / 2, p_depth + 1, r_max_depth, r_max_alloc); + int right = _create_bvh(p_bvh, p_bb, p_from + p_size / 2, p_size - p_size / 2, p_depth + 1, r_max_depth, r_max_alloc); - int index = max_alloc++; + int index = r_max_alloc++; BVH *_new = &p_bvh[index]; _new->aabb = aabb; _new->center = aabb.position + aabb.size * 0.5; diff --git a/core/math/vector2.h b/core/math/vector2.h index 78deb473b4..4d9f3126e9 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -300,6 +300,14 @@ struct Vector2i { return p_idx ? y : x; } + _FORCE_INLINE_ int min_axis() const { + return x < y ? 0 : 1; + } + + _FORCE_INLINE_ int max_axis() const { + return x < y ? 1 : 0; + } + Vector2i min(const Vector2i &p_vector2i) const { return Vector2(MIN(x, p_vector2i.x), MIN(y, p_vector2i.y)); } diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp index a10405dfae..c58fe7bc24 100644 --- a/core/object/class_db.cpp +++ b/core/object/class_db.cpp @@ -359,9 +359,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { //must be alphabetically sorted for hash to compute names.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - ClassInfo *t = classes.getptr(E->get()); - ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E->get()) + "'."); + for (const StringName &E : names) { + ClassInfo *t = classes.getptr(E); + ERR_FAIL_COND_V_MSG(!t, 0, "Cannot get class '" + String(E) + "'."); if (t->api != p_api || !t->exposed) { continue; } @@ -388,8 +388,8 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodBind *mb = t->method_map[F->get()]; + for (const StringName &F : snames) { + MethodBind *mb = t->method_map[F]; hash = hash_djb2_one_64(mb->get_name().hash(), hash); hash = hash_djb2_one_64(mb->get_argument_count(), hash); hash = hash_djb2_one_64(mb->get_argument_type(-1), hash); //return @@ -426,9 +426,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().hash(), hash); - hash = hash_djb2_one_64(t->constant_map[F->get()], hash); + for (const StringName &F : snames) { + hash = hash_djb2_one_64(F.hash(), hash); + hash = hash_djb2_one_64(t->constant_map[F], hash); } } @@ -444,9 +444,9 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - MethodInfo &mi = t->signal_map[F->get()]; - hash = hash_djb2_one_64(F->get().hash(), hash); + for (const StringName &F : snames) { + MethodInfo &mi = t->signal_map[F]; + hash = hash_djb2_one_64(F.hash(), hash); for (int i = 0; i < mi.arguments.size(); i++) { hash = hash_djb2_one_64(mi.arguments[i].type, hash); } @@ -465,23 +465,23 @@ uint64_t ClassDB::get_api_hash(APIType p_api) { snames.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *F = snames.front(); F; F = F->next()) { - PropertySetGet *psg = t->property_setget.getptr(F->get()); + for (const StringName &F : snames) { + PropertySetGet *psg = t->property_setget.getptr(F); ERR_FAIL_COND_V(!psg, 0); - hash = hash_djb2_one_64(F->get().hash(), hash); + hash = hash_djb2_one_64(F.hash(), hash); hash = hash_djb2_one_64(psg->setter.hash(), hash); hash = hash_djb2_one_64(psg->getter.hash(), hash); } } //property list - for (List<PropertyInfo>::Element *F = t->property_list.front(); F; F = F->next()) { - hash = hash_djb2_one_64(F->get().name.hash(), hash); - hash = hash_djb2_one_64(F->get().type, hash); - hash = hash_djb2_one_64(F->get().hint, hash); - hash = hash_djb2_one_64(F->get().hint_string.hash(), hash); - hash = hash_djb2_one_64(F->get().usage, hash); + for (const PropertyInfo &F : t->property_list) { + hash = hash_djb2_one_64(F.name.hash(), hash); + hash = hash_djb2_one_64(F.type, hash); + hash = hash_djb2_one_64(F.hint, hash); + hash = hash_djb2_one_64(F.hint_string.hash(), hash); + hash = hash_djb2_one_64(F.usage, hash); } } @@ -505,11 +505,12 @@ thread_local bool initializing_with_extension = false; thread_local ObjectNativeExtension *initializing_extension = nullptr; thread_local GDExtensionClassInstancePtr initializing_extension_instance = nullptr; -void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance) { +void ClassDB::instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base) { if (initializing_with_extension) { *r_extension = initializing_extension; *r_extension_instance = initializing_extension_instance; initializing_with_extension = false; + initializing_extension->set_object_instance(*r_extension_instance, p_base); } else { *r_extension = nullptr; *r_extension_instance = nullptr; @@ -602,7 +603,7 @@ static MethodInfo info_from_bind(MethodBind *p_method) { } #endif -void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { +void ClassDB::get_method_list(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -619,16 +620,16 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b #ifdef DEBUG_METHODS_ENABLED - for (List<MethodInfo>::Element *E = type->virtual_methods.front(); E; E = E->next()) { - p_methods->push_back(E->get()); + for (const MethodInfo &E : type->virtual_methods) { + p_methods->push_back(E); } - for (List<StringName>::Element *E = type->method_order.front(); E; E = E->next()) { - if (p_exclude_from_properties && type->methods_in_properties.has(E->get())) { + for (const StringName &E : type->method_order) { + if (p_exclude_from_properties && type->methods_in_properties.has(E)) { continue; } - MethodBind *method = type->method_map.get(E->get()); + MethodBind *method = type->method_map.get(E); MethodInfo minfo = info_from_bind(method); p_methods->push_back(minfo); @@ -655,7 +656,7 @@ void ClassDB::get_method_list(StringName p_class, List<MethodInfo> *p_methods, b } } -bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { +bool ClassDB::get_method_info(const StringName &p_class, const StringName &p_method, MethodInfo *r_info, bool p_no_inheritance, bool p_exclude_from_properties) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -706,7 +707,7 @@ bool ClassDB::get_method_info(StringName p_class, StringName p_method, MethodInf return false; } -MethodBind *ClassDB::get_method(StringName p_class, StringName p_name) { +MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_name) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -763,8 +764,8 @@ void ClassDB::get_integer_constant_list(const StringName &p_class, List<String> while (type) { #ifdef DEBUG_METHODS_ENABLED - for (List<StringName>::Element *E = type->constant_order.front(); E; E = E->next()) { - p_constants->push_back(E->get()); + for (const StringName &E : type->constant_order) { + p_constants->push_back(E); } #else const StringName *K = nullptr; @@ -910,7 +911,7 @@ bool ClassDB::has_enum(const StringName &p_class, const StringName &p_name, bool return false; } -void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { +void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); @@ -929,7 +930,7 @@ void ClassDB::add_signal(StringName p_class, const MethodInfo &p_signal) { type->signal_map[sname] = p_signal; } -void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { +void ClassDB::get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); @@ -951,7 +952,7 @@ void ClassDB::get_signal_list(StringName p_class, List<MethodInfo> *p_signals, b } } -bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance) { +bool ClassDB::has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -968,7 +969,7 @@ bool ClassDB::has_signal(StringName p_class, StringName p_signal, bool p_no_inhe return false; } -bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal) { +bool ClassDB::get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -985,7 +986,7 @@ bool ClassDB::get_signal(StringName p_class, StringName p_signal, MethodInfo *r_ return false; } -void ClassDB::add_property_group(StringName p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -993,7 +994,7 @@ void ClassDB::add_property_group(StringName p_class, const String &p_name, const type->property_list.push_back(PropertyInfo(Variant::NIL, p_name, PROPERTY_HINT_NONE, p_prefix, PROPERTY_USAGE_GROUP)); } -void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix) { +void ClassDB::add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ERR_FAIL_COND(!type); @@ -1002,7 +1003,7 @@ void ClassDB::add_property_subgroup(StringName p_class, const String &p_name, co } // NOTE: For implementation simplicity reasons, this method doesn't allow setters to have optional arguments at the end. -void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { +void ClassDB::add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index) { lock.read_lock(); ClassInfo *type = classes.getptr(p_class); lock.read_unlock(); @@ -1060,26 +1061,27 @@ void ClassDB::add_property(StringName p_class, const PropertyInfo &p_pinfo, cons type->property_setget[p_pinfo.name] = psg; } -void ClassDB::set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default) { +void ClassDB::set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default) { if (!default_values.has(p_class)) { default_values[p_class] = HashMap<StringName, Variant>(); } default_values[p_class][p_name] = p_default; } -void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { +void ClassDB::get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<PropertyInfo>::Element *E = check->property_list.front(); E; E = E->next()) { + for (const PropertyInfo &pi : check->property_list) { if (p_validator) { - PropertyInfo pi = E->get(); - p_validator->_validate_property(pi); - p_list->push_back(pi); + // Making a copy as we may modify it. + PropertyInfo pi_mut = pi; + p_validator->_validate_property(pi_mut); + p_list->push_back(pi_mut); } else { - p_list->push_back(E->get()); + p_list->push_back(pi); } } @@ -1090,7 +1092,7 @@ void ClassDB::get_property_list(StringName p_class, List<PropertyInfo> *p_list, } } -bool ClassDB::get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { +bool ClassDB::get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance, const Object *p_validator) { OBJTYPE_RLOCK; ClassInfo *check = classes.getptr(p_class); @@ -1258,7 +1260,7 @@ Variant::Type ClassDB::get_property_type(const StringName &p_class, const String return Variant::NIL; } -StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_property) { +StringName ClassDB::get_property_setter(const StringName &p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1273,7 +1275,7 @@ StringName ClassDB::get_property_setter(StringName p_class, const StringName &p_ return StringName(); } -StringName ClassDB::get_property_getter(StringName p_class, const StringName &p_property) { +StringName ClassDB::get_property_getter(const StringName &p_class, const StringName &p_property) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1305,7 +1307,7 @@ bool ClassDB::has_property(const StringName &p_class, const StringName &p_proper return false; } -void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_flags) { +void ClassDB::set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags) { OBJTYPE_WLOCK; ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; @@ -1314,7 +1316,7 @@ void ClassDB::set_method_flags(StringName p_class, StringName p_method, int p_fl check->method_map[p_method]->set_hint_flags(p_flags); } -bool ClassDB::has_method(StringName p_class, StringName p_method, bool p_no_inheritance) { +bool ClassDB::has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) { ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { @@ -1429,8 +1431,8 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p ClassInfo *type = classes.getptr(p_class); ClassInfo *check = type; while (check) { - for (List<MethodInfo>::Element *E = check->virtual_methods.front(); E; E = E->next()) { - p_methods->push_back(E->get()); + for (const MethodInfo &E : check->virtual_methods) { + p_methods->push_back(E); } if (p_no_inheritance) { @@ -1442,14 +1444,14 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p #endif } -void ClassDB::set_class_enabled(StringName p_class, bool p_enable) { +void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) { OBJTYPE_WLOCK; ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'."); classes[p_class].disabled = !p_enable; } -bool ClassDB::is_class_enabled(StringName p_class) { +bool ClassDB::is_class_enabled(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1463,7 +1465,7 @@ bool ClassDB::is_class_enabled(StringName p_class) { return !ti->disabled; } -bool ClassDB::is_class_exposed(StringName p_class) { +bool ClassDB::is_class_exposed(const StringName &p_class) { OBJTYPE_RLOCK; ClassInfo *ti = classes.getptr(p_class); @@ -1496,6 +1498,10 @@ void ClassDB::get_resource_base_extensions(List<String> *p_extensions) { } } +bool ClassDB::is_resource_extension(const StringName &p_extension) { + return resource_base_extensions.has(p_extension); +} + void ClassDB::get_extensions_for_type(const StringName &p_class, List<String> *p_extensions) { const StringName *K = nullptr; @@ -1530,11 +1536,11 @@ Variant ClassDB::class_get_default_property_value(const StringName &p_class, con if (c) { List<PropertyInfo> plist; c->get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (E->get().usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { - if (!default_values[p_class].has(E->get().name)) { - Variant v = c->get(E->get().name); - default_values[p_class][E->get().name] = v; + for (const PropertyInfo &E : plist) { + if (E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR)) { + if (!default_values[p_class].has(E.name)) { + Variant v = c->get(E.name); + default_values[p_class][E.name] = v; } } } @@ -1587,7 +1593,7 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { GLOBAL_LOCK_FUNCTION; ERR_FAIL_COND_MSG(classes.has(p_extension->class_name), "Class already registered: " + String(p_extension->class_name)); - ERR_FAIL_COND_MSG(classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); + ERR_FAIL_COND_MSG(!classes.has(p_extension->parent_class_name), "Parent class name for extension class not found: " + String(p_extension->parent_class_name)); ClassInfo *parent = classes.getptr(p_extension->parent_class_name); @@ -1599,6 +1605,7 @@ void ClassDB::register_extension_class(ObjectNativeExtension *p_extension) { c.inherits = parent->name; c.class_ptr = parent->class_ptr; c.inherits_ptr = parent; + c.exposed = true; classes[p_extension->class_name] = c; } diff --git a/core/object/class_db.h b/core/object/class_db.h index af528bfde7..bfc9d6f283 100644 --- a/core/object/class_db.h +++ b/core/object/class_db.h @@ -233,7 +233,7 @@ public: static bool is_parent_class(const StringName &p_class, const StringName &p_inherits); static bool can_instantiate(const StringName &p_class); static Object *instantiate(const StringName &p_class); - static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance); + static void instance_get_native_extension_data(ObjectNativeExtension **r_extension, GDExtensionClassInstancePtr *r_extension_instance, Object *p_base); static APIType get_api_type(const StringName &p_class); @@ -311,7 +311,7 @@ public: } template <class M> - static MethodBind *bind_vararg_method(uint32_t p_flags, 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) { + 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); @@ -345,31 +345,31 @@ public: static void bind_method_custom(const StringName &p_class, MethodBind *p_method); - static void add_signal(StringName p_class, const MethodInfo &p_signal); - static bool has_signal(StringName p_class, StringName p_signal, bool p_no_inheritance = false); - static bool get_signal(StringName p_class, StringName p_signal, MethodInfo *r_signal); - static void get_signal_list(StringName p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); - - static void add_property_group(StringName p_class, const String &p_name, const String &p_prefix = ""); - static void add_property_subgroup(StringName p_class, const String &p_name, const String &p_prefix = ""); - static void add_property(StringName p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); - static void set_property_default_value(StringName p_class, const StringName &p_name, const Variant &p_default); - static void get_property_list(StringName p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); - static bool get_property_info(StringName p_class, StringName p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static void add_signal(const StringName &p_class, const MethodInfo &p_signal); + static bool has_signal(const StringName &p_class, const StringName &p_signal, bool p_no_inheritance = false); + static bool get_signal(const StringName &p_class, const StringName &p_signal, MethodInfo *r_signal); + static void get_signal_list(const StringName &p_class, List<MethodInfo> *p_signals, bool p_no_inheritance = false); + + static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix = ""); + static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); + static void set_property_default_value(const StringName &p_class, const StringName &p_name, const Variant &p_default); + static void get_property_list(const StringName &p_class, List<PropertyInfo> *p_list, bool p_no_inheritance = false, const Object *p_validator = nullptr); + static bool get_property_info(const StringName &p_class, const StringName &p_property, PropertyInfo *r_info, bool p_no_inheritance = false, const Object *p_validator = nullptr); static bool set_property(Object *p_object, const StringName &p_property, const Variant &p_value, bool *r_valid = nullptr); static bool get_property(Object *p_object, const StringName &p_property, Variant &r_value); static bool has_property(const StringName &p_class, const StringName &p_property, bool p_no_inheritance = false); static int get_property_index(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr); static Variant::Type get_property_type(const StringName &p_class, const StringName &p_property, bool *r_is_valid = nullptr); - static StringName get_property_setter(StringName p_class, const StringName &p_property); - static StringName get_property_getter(StringName p_class, const StringName &p_property); + static StringName get_property_setter(const StringName &p_class, const StringName &p_property); + static StringName get_property_getter(const StringName &p_class, const StringName &p_property); - static bool has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false); - static void set_method_flags(StringName p_class, StringName p_method, int p_flags); + static bool has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false); + static void set_method_flags(const StringName &p_class, const StringName &p_method, int p_flags); - static void get_method_list(StringName p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false, bool p_exclude_from_properties = false); - static bool get_method_info(StringName p_class, StringName p_method, MethodInfo *r_info, bool p_no_inheritance = false, bool p_exclude_from_properties = false); - static MethodBind *get_method(StringName p_class, StringName p_name); + 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 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 MethodBind *get_method(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); static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false); @@ -388,14 +388,15 @@ public: static StringName get_category(const StringName &p_node); - static void set_class_enabled(StringName p_class, bool p_enable); - static bool is_class_enabled(StringName p_class); + static void set_class_enabled(const StringName &p_class, bool p_enable); + static bool is_class_enabled(const StringName &p_class); - static bool is_class_exposed(StringName p_class); + static bool is_class_exposed(const StringName &p_class); static void add_resource_base_extension(const StringName &p_extension, const StringName &p_class); static void get_resource_base_extensions(List<String> *p_extensions); static void get_extensions_for_type(const StringName &p_class, List<String> *p_extensions); + static bool is_resource_extension(const StringName &p_extension); static void add_compatibility_class(const StringName &p_class, const StringName &p_fallback); @@ -434,4 +435,15 @@ public: #endif +#define GDREGISTER_CLASS(m_class) \ + if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ + ClassDB::register_class<m_class>(); \ + } +#define GDREGISTER_VIRTUAL_CLASS(m_class) \ + if (!GD_IS_DEFINED(ClassDB_Disable_##m_class)) { \ + ClassDB::register_virtual_class<m_class>(); \ + } + +#include "core/disabled_classes.gen.h" + #endif // CLASS_DB_H diff --git a/core/object/object.cpp b/core/object/object.cpp index 296d876701..c191109a8f 100644 --- a/core/object/object.cpp +++ b/core/object/object.cpp @@ -922,11 +922,11 @@ Variant Object::get_script() const { return script; } -bool Object::has_meta(const String &p_name) const { +bool Object::has_meta(const StringName &p_name) const { return metadata.has(p_name); } -void Object::set_meta(const String &p_name, const Variant &p_value) { +void Object::set_meta(const StringName &p_name, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { metadata.erase(p_name); return; @@ -935,12 +935,12 @@ void Object::set_meta(const String &p_name, const Variant &p_value) { metadata[p_name] = p_value; } -Variant Object::get_meta(const String &p_name) const { +Variant Object::get_meta(const StringName &p_name) const { ERR_FAIL_COND_V_MSG(!metadata.has(p_name), Variant(), "The object does not have any 'meta' values with the key '" + p_name + "'."); return metadata[p_name]; } -void Object::remove_meta(const String &p_name) { +void Object::remove_meta(const StringName &p_name) { metadata.erase(p_name); } @@ -964,23 +964,23 @@ Array Object::_get_method_list_bind() const { return ret; } -Vector<String> Object::_get_meta_list_bind() const { - Vector<String> _metaret; +Vector<StringName> Object::_get_meta_list_bind() const { + Vector<StringName> _metaret; List<Variant> keys; metadata.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _metaret.push_back(E->get()); + for (const Variant &E : keys) { + _metaret.push_back(E); } return _metaret; } -void Object::get_meta_list(List<String> *p_list) const { +void Object::get_meta_list(List<StringName> *p_list) const { List<Variant> keys; metadata.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - p_list->push_back(E->get()); + for (const Variant &E : keys) { + p_list->push_back(E); } } @@ -1184,8 +1184,8 @@ Array Object::_get_signal_list() const { get_signal_list(&signal_list); Array ret; - for (List<MethodInfo>::Element *E = signal_list.front(); E; E = E->next()) { - ret.push_back(Dictionary(E->get())); + for (const MethodInfo &E : signal_list) { + ret.push_back(Dictionary(E)); } return ret; @@ -1197,8 +1197,7 @@ Array Object::_get_signal_connection_list(const String &p_signal) const { Array ret; - for (List<Connection>::Element *E = conns.front(); E; E = E->next()) { - Connection &c = E->get(); + for (const Connection &c : conns) { if (c.signal.get_name() == p_signal) { ret.push_back(c); } @@ -1297,8 +1296,8 @@ int Object::get_persistent_signal_connection_count() const { } void Object::get_signals_connected_to_this(List<Connection> *p_connections) const { - for (const List<Connection>::Element *E = connections.front(); E; E = E->next()) { - p_connections->push_back(E->get()); + for (const Connection &E : connections) { + p_connections->push_back(E); } } @@ -1500,9 +1499,9 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) { List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - _clear_internal_resource_paths(E->get()); - _clear_internal_resource_paths(d[E->get()]); + for (const Variant &E : keys) { + _clear_internal_resource_paths(E); + _clear_internal_resource_paths(d[E]); } } break; default: { @@ -1531,8 +1530,8 @@ void Object::clear_internal_resource_paths() { get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - _clear_internal_resource_paths(get(E->get().name)); + for (const PropertyInfo &E : pinfo) { + _clear_internal_resource_paths(get(E.name)); } } @@ -1666,12 +1665,12 @@ void Object::get_translatable_strings(List<String> *p_strings) const { List<PropertyInfo> plist; get_property_list(&plist); - for (List<PropertyInfo>::Element *E = plist.front(); E; E = E->next()) { - if (!(E->get().usage & PROPERTY_USAGE_INTERNATIONALIZED)) { + for (const PropertyInfo &E : plist) { + if (!(E.usage & PROPERTY_USAGE_INTERNATIONALIZED)) { continue; } - String text = get(E->get().name); + String text = get(E.name); if (text == "") { continue; @@ -1769,6 +1768,17 @@ uint32_t Object::get_edited_version() const { } #endif +void Object::set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks) { + // This is only meant to be used on creation by the binder. + ERR_FAIL_COND(_instance_bindings != nullptr); + _instance_bindings = (InstanceBinding *)memalloc(sizeof(InstanceBinding)); + _instance_bindings[0].binding = p_binding; + _instance_bindings[0].free_callback = p_callbacks->free_callback; + _instance_bindings[0].reference_callback = p_callbacks->reference_callback; + _instance_bindings[0].token = p_token; + _instance_binding_count = 1; +} + void *Object::get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks) { void *binding = nullptr; _instance_binding_mutex.lock(); @@ -1805,7 +1815,7 @@ void Object::_construct_object(bool p_reference) { type_is_reference = p_reference; _instance_id = ObjectDB::add_instance(this); - ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance); + ClassDB::instance_get_native_extension_data(&_extension, &_extension_instance, this); #ifdef DEBUG_ENABLED _lock_index.init(1); @@ -1866,7 +1876,7 @@ Object::~Object() { if (_instance_bindings != nullptr) { for (uint32_t i = 0; i < _instance_binding_count; i++) { if (_instance_bindings[i].free_callback) { - _instance_bindings[i].free_callback(_instance_bindings[i].token, _instance_bindings[i].binding, this); + _instance_bindings[i].free_callback(_instance_bindings[i].token, this, _instance_bindings[i].binding); } } memfree(_instance_bindings); diff --git a/core/object/object.h b/core/object/object.h index 8389d80afc..1f5e17c99f 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -44,13 +44,13 @@ #include "core/variant/callable_bind.h" #include "core/variant/variant.h" -#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant() -#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5 -#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5 -#define VARIANT_ARG_MAX 5 -#define VARIANT_ARGPTRS const Variant *argptr[5] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5 }; -#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4] -#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4] +#define VARIANT_ARG_LIST const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant() +#define VARIANT_ARG_PASS p_arg1, p_arg2, p_arg3, p_arg4, p_arg5, p_arg6, p_arg7, p_arg8 +#define VARIANT_ARG_DECLARE const Variant &p_arg1, const Variant &p_arg2, const Variant &p_arg3, const Variant &p_arg4, const Variant &p_arg5, const Variant &p_arg6, const Variant &p_arg7, const Variant &p_arg8 +#define VARIANT_ARG_MAX 8 +#define VARIANT_ARGPTRS const Variant *argptr[8] = { &p_arg1, &p_arg2, &p_arg3, &p_arg4, &p_arg5, &p_arg6, &p_arg7, &p_arg8 }; +#define VARIANT_ARGPTRS_PASS *argptr[0], *argptr[1], *argptr[2], *argptr[3], *argptr[4], *argptr[5], *argptr[6]], *argptr[7] +#define VARIANT_ARGS_FROM_ARRAY(m_arr) m_arr[0], m_arr[1], m_arr[2], m_arr[3], m_arr[4], m_arr[5], m_arr[6], m_arr[7] /** @author Juan Linietsky <reduzio@gmail.com> @@ -60,6 +60,7 @@ enum PropertyHint { PROPERTY_HINT_NONE, ///< no hint provided. PROPERTY_HINT_RANGE, ///< hint_text = "min,max[,step][,or_greater][,or_lesser][,noslider][,radians][,degrees][,exp][,suffix:<keyword>] range. PROPERTY_HINT_ENUM, ///< hint_text= "val1,val2,val3,etc" + PROPERTY_HINT_ENUM_SUGGESTION, ///< hint_text= "val1,val2,val3,etc" PROPERTY_HINT_EXP_EASING, /// exponential easing function (Math::ease) use "attenuation" hint string to revert (flip h), "full" to also include in/out. (ie: "attenuation,inout") PROPERTY_HINT_LENGTH, ///< hint_text= "length" (as integer) PROPERTY_HINT_KEY_ACCEL, ///< hint_text= "length" (as integer) @@ -271,6 +272,7 @@ struct ObjectNativeExtension { GDNativeExtensionClassCreateInstance create_instance; GDNativeExtensionClassFreeInstance free_instance; + GDNativeExtensionClassObjectInstance set_object_instance; GDNativeExtensionClassGetVirtual get_virtual; }; @@ -614,7 +616,7 @@ protected: return &_class_name; } - Vector<String> _get_meta_list_bind() const; + Vector<StringName> _get_meta_list_bind() const; Array _get_property_list_bind() const; Array _get_method_list_bind() const; @@ -742,11 +744,11 @@ public: /* SCRIPT */ - bool has_meta(const String &p_name) const; - void set_meta(const String &p_name, const Variant &p_value); - void remove_meta(const String &p_name); - Variant get_meta(const String &p_name) const; - void get_meta_list(List<String> *p_list) const; + bool has_meta(const StringName &p_name) const; + void set_meta(const StringName &p_name, const Variant &p_value); + void remove_meta(const StringName &p_name); + Variant get_meta(const StringName &p_name) const; + void get_meta_list(List<StringName> *p_list) const; #ifdef TOOLS_ENABLED void set_edited(bool p_edited); @@ -803,8 +805,10 @@ public: #endif - //used by script languages to store binding data + // Used by script languages to store binding data. void *get_instance_binding(void *p_token, const GDNativeInstanceBindingCallbacks *p_callbacks); + // Used on creation by binding only. + void set_instance_binding(void *p_token, void *p_binding, const GDNativeInstanceBindingCallbacks *p_callbacks); void clear_internal_resource_paths(); diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp index 626a7413e7..0fb8c7350c 100644 --- a/core/object/script_language.cpp +++ b/core/object/script_language.cpp @@ -63,8 +63,8 @@ Array Script::_get_script_property_list() { Array ret; List<PropertyInfo> list; get_script_property_list(&list); - for (List<PropertyInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const PropertyInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -73,8 +73,8 @@ Array Script::_get_script_method_list() { Array ret; List<MethodInfo> list; get_script_method_list(&list); - for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const MethodInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -83,8 +83,8 @@ Array Script::_get_script_signal_list() { Array ret; List<MethodInfo> list; get_script_signal_list(&list); - for (List<MethodInfo>::Element *E = list.front(); E; E = E->next()) { - ret.append(E->get().operator Dictionary()); + for (const MethodInfo &E : list) { + ret.append(E.operator Dictionary()); } return ret; } @@ -257,8 +257,8 @@ void ScriptServer::get_global_class_list(List<StringName> *r_global_classes) { classes.push_back(*K); } classes.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = classes.front(); E; E = E->next()) { - r_global_classes->push_back(E->get()); + for (const StringName &E : classes) { + r_global_classes->push_back(E); } } @@ -266,12 +266,12 @@ void ScriptServer::save_global_classes() { List<StringName> gc; get_global_class_list(&gc); Array gcarr; - for (List<StringName>::Element *E = gc.front(); E; E = E->next()) { + for (const StringName &E : gc) { Dictionary d; - d["class"] = E->get(); - d["language"] = global_classes[E->get()].language; - d["path"] = global_classes[E->get()].path; - d["base"] = global_classes[E->get()].base; + d["class"] = E; + d["language"] = global_classes[E].language; + d["path"] = global_classes[E].path; + d["base"] = global_classes[E].base; gcarr.push_back(d); } @@ -297,10 +297,10 @@ void ScriptServer::save_global_classes() { void ScriptInstance::get_property_state(List<Pair<StringName, Variant>> &state) { List<PropertyInfo> pinfo; get_property_list(&pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE) { + for (const PropertyInfo &E : pinfo) { + if (E.usage & PROPERTY_USAGE_STORAGE) { Pair<StringName, Variant> p; - p.first = E->get().name; + p.first = E.name; if (get(p.first, p.second)) { state.push_back(p); } @@ -430,16 +430,16 @@ bool PlaceHolderScriptInstance::get(const StringName &p_name, Variant &r_ret) co void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properties) const { if (script->is_placeholder_fallback_enabled()) { - for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - p_properties->push_back(E->get()); + for (const PropertyInfo &E : properties) { + p_properties->push_back(E); } } else { - for (const List<PropertyInfo>::Element *E = properties.front(); E; E = E->next()) { - PropertyInfo pinfo = E->get(); + 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->get()); + p_properties->push_back(E); } } } @@ -489,11 +489,11 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const { void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const Map<StringName, Variant> &p_values) { Set<StringName> new_values; - for (const List<PropertyInfo>::Element *E = p_properties.front(); E; E = E->next()) { - StringName n = E->get().name; + for (const PropertyInfo &E : p_properties) { + StringName n = E.name; new_values.insert(n); - if (!values.has(n) || values[n].get_type() != E->get().type) { + if (!values.has(n) || values[n].get_type() != E.type) { if (p_values.has(n)) { values[n] = p_values[n]; } @@ -511,7 +511,7 @@ void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, c Variant defval; if (script->get_property_default_value(E->key(), defval)) { //remove because it's the same as the default value - if (defval == E->get()) { + if (defval == E) { to_remove.push_back(E->key()); } } @@ -542,8 +542,8 @@ void PlaceHolderScriptInstance::property_set_fallback(const StringName &p_name, } bool found = false; - for (const List<PropertyInfo>::Element *F = properties.front(); F; F = F->next()) { - if (F->get().name == p_name) { + for (const PropertyInfo &F : properties) { + if (F.name == p_name) { found = true; break; } diff --git a/core/object/script_language.h b/core/object/script_language.h index 2cbaa0f52e..385bf79c1a 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -310,6 +310,7 @@ public: Ref<Script> script; String class_name; String class_member; + String class_path; int location; }; diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp index 96c96c1efb..adf068eb2f 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -39,11 +39,15 @@ void UndoRedo::_discard_redo() { } for (int i = current_action + 1; i < actions.size(); i++) { - for (List<Operation>::Element *E = actions.write[i].do_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) { - memdelete(obj); + for (Operation &E : actions.write[i].do_ops) { + if (E.type == Operation::TYPE_REFERENCE) { + if (E.ref.is_valid()) { + E.ref.unref(); + } else { + Object *obj = ObjectDB::get_instance(E.object); + if (obj) { + memdelete(obj); + } } } } @@ -65,7 +69,7 @@ bool UndoRedo::_redo(bool p_execute) { _process_operation_list(actions.write[current_action].do_ops.front()); } version++; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); return true; } @@ -240,11 +244,15 @@ void UndoRedo::_pop_history_tail() { return; } - for (List<Operation>::Element *E = actions.write[0].undo_ops.front(); E; E = E->next()) { - if (E->get().type == Operation::TYPE_REFERENCE) { - Object *obj = ObjectDB::get_instance(E->get().object); - if (obj) { - memdelete(obj); + for (Operation &E : actions.write[0].undo_ops) { + if (E.type == Operation::TYPE_REFERENCE) { + if (E.ref.is_valid()) { + E.ref.unref(); + } else { + Object *obj = ObjectDB::get_instance(E.object); + if (obj) { + memdelete(obj); + } } } } @@ -352,7 +360,7 @@ bool UndoRedo::undo() { _process_operation_list(actions.write[current_action].undo_ops.front()); current_action--; version--; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); return true; } @@ -385,7 +393,7 @@ void UndoRedo::clear_history(bool p_increase_version) { if (p_increase_version) { version++; - emit_signal("version_changed"); + emit_signal(SNAME("version_changed")); } } @@ -460,8 +468,8 @@ Variant UndoRedo::_add_do_method(const Variant **p_args, int p_argcount, Callabl v[i] = *p_args[i + 2]; } - static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); - add_do_method(object, method, v[0], v[1], v[2], v[3], v[4]); + static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8"); + add_do_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); return Variant(); } @@ -497,8 +505,8 @@ Variant UndoRedo::_add_undo_method(const Variant **p_args, int p_argcount, Calla v[i] = *p_args[i + 2]; } - static_assert(VARIANT_ARG_MAX == 5, "This code needs to be updated if VARIANT_ARG_MAX != 5"); - add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4]); + static_assert(VARIANT_ARG_MAX == 8, "This code needs to be updated if VARIANT_ARG_MAX != 8"); + add_undo_method(object, method, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); return Variant(); } diff --git a/core/os/keyboard.h b/core/os/keyboard.h index 33f9213c4e..52174432d9 100644 --- a/core/os/keyboard.h +++ b/core/os/keyboard.h @@ -46,6 +46,7 @@ enum { }; enum Key { + KEY_NONE = 0, /* CURSOR/FUNCTION/BROWSER/MULTIMEDIA/MISC KEYS */ KEY_ESCAPE = SPKEY | 0x01, KEY_TAB = SPKEY | 0x02, @@ -314,6 +315,52 @@ enum KeyModifierMask { // bit 31 can't be used because variant uses regular 32 bits int as datatype }; +// To avoid having unnecessary operators, only define the ones that are needed. + +inline Key operator-(uint32_t a, Key b) { + return (Key)(a - (uint32_t)b); +} + +inline Key &operator-=(Key &a, int b) { + return (Key &)((int &)a -= b); +} + +inline Key operator+(Key a, Key b) { + return (Key)((int)a - (int)b); +} + +inline Key &operator|=(Key &a, Key b) { + return (Key &)((int &)a |= (int)b); +} + +inline Key &operator|=(Key &a, KeyModifierMask b) { + return (Key &)((int &)a |= (int)b); +} + +inline Key operator|(Key a, KeyModifierMask b) { + return (Key)((int)a | (int)b); +} + +inline Key operator&(Key a, KeyModifierMask b) { + return (Key)((int)a & (int)b); +} + +inline Key operator+(KeyModifierMask a, Key b) { + return (Key)((int)a + (int)b); +} + +inline Key operator|(KeyModifierMask a, Key b) { + return (Key)((int)a | (int)b); +} + +inline KeyModifierMask operator+(KeyModifierMask a, KeyModifierMask b) { + return (KeyModifierMask)((int)a + (int)b); +} + +inline KeyModifierMask operator|(KeyModifierMask a, KeyModifierMask b) { + return (KeyModifierMask)((int)a | (int)b); +} + String keycode_get_string(uint32_t p_code); bool keycode_has_unicode(uint32_t p_keycode); int find_keycode(const String &p_code); diff --git a/core/os/main_loop.cpp b/core/os/main_loop.cpp index 016d9d0a09..3c0e56f5a8 100644 --- a/core/os/main_loop.cpp +++ b/core/os/main_loop.cpp @@ -66,7 +66,7 @@ void MainLoop::initialize() { } } -bool MainLoop::physics_process(float p_time) { +bool MainLoop::physics_process(double p_time) { if (get_script_instance()) { return get_script_instance()->call("_physics_process", p_time); } @@ -74,7 +74,7 @@ bool MainLoop::physics_process(float p_time) { return false; } -bool MainLoop::process(float p_time) { +bool MainLoop::process(double p_time) { if (get_script_instance()) { return get_script_instance()->call("_process", p_time); } diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 34e944709b..b42e9b18ff 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -60,8 +60,8 @@ public: }; virtual void initialize(); - virtual bool physics_process(float p_time); - virtual bool process(float p_time); + virtual bool physics_process(double p_time); + virtual bool process(double p_time); virtual void finalize(); void set_initialize_script(const Ref<Script> &p_initialize_script); diff --git a/core/os/memory.h b/core/os/memory.h index 10e678103d..9d09626b8c 100644 --- a/core/os/memory.h +++ b/core/os/memory.h @@ -80,7 +80,7 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d #define memalloc(m_size) Memory::alloc_static(m_size) #define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size) -#define memfree(m_size) Memory::free_static(m_size) +#define memfree(m_mem) Memory::free_static(m_mem) _ALWAYS_INLINE_ void postinitialize_handler(void *) {} diff --git a/core/os/os.cpp b/core/os/os.cpp index 535eee4797..76a6da51e1 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -110,6 +110,10 @@ void OS::printerr(const char *p_format, ...) { va_end(argp); } +void OS::alert(const String &p_alert, const String &p_title) { + fprintf(stderr, "%s: %s\n", p_title.utf8().get_data(), p_alert.utf8().get_data()); +} + void OS::set_low_processor_usage_mode(bool p_enabled) { low_processor_usage_mode = p_enabled; } @@ -174,7 +178,7 @@ static void _OS_printres(Object *p_obj) { return; } - String str = itos(res->get_instance_id()) + String(res->get_class()) + ":" + String(res->get_name()) + " - " + res->get_path(); + String str = vformat("%s - %s - %s", res->to_string(), res->get_name(), res->get_path()); if (_OSPRF) { _OSPRF->store_line(str); } else { @@ -211,14 +215,6 @@ void OS::dump_resources_to_file(const char *p_file) { ResourceCache::dump(p_file); } -void OS::set_no_window_mode(bool p_enable) { - _no_window = p_enable; -} - -bool OS::is_no_window_mode_enabled() const { - return _no_window; -} - int OS::get_exit_code() const { return _exit_code; } diff --git a/core/os/os.h b/core/os/os.h index 301718a8b3..0466d94acd 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -53,7 +53,6 @@ class OS { bool _verbose_stdout = false; bool _debug_stdout = false; String _local_clipboard; - bool _no_window = false; int _exit_code = EXIT_FAILURE; // unexpected exit is marked as failure int _orientation; bool _allow_hidpi = false; @@ -120,6 +119,8 @@ public: virtual void open_midi_inputs(); virtual void close_midi_inputs(); + 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) { 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; } @@ -267,9 +268,6 @@ public: virtual Error move_to_trash(const String &p_path) { return FAILED; } - virtual void set_no_window_mode(bool p_enable); - virtual bool is_no_window_mode_enabled() const; - virtual void debug_break(); virtual int get_exit_code() const; diff --git a/core/os/pool_allocator.h b/core/os/pool_allocator.h index 15e50dac90..49f433ba97 100644 --- a/core/os/pool_allocator.h +++ b/core/os/pool_allocator.h @@ -37,7 +37,7 @@ @author Juan Linietsky <reduzio@gmail.com> * Generic Pool Allocator. * This is a generic memory pool allocator, with locking, compacting and alignment. (@TODO alignment) - * It used as a standard way to manage alloction in a specific region of memory, such as texture memory, + * It used as a standard way to manage allocation in a specific region of memory, such as texture memory, * audio sample memory, or just any kind of memory overall. * (@TODO) abstraction should be greater, because in many platforms, you need to manage a nonreachable memory. */ diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index 1cbb0bb597..f801be3db3 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -48,7 +48,7 @@ #include "core/io/json.h" #include "core/io/marshalls.h" #include "core/io/multiplayer_api.h" -#include "core/io/networked_multiplayer_peer.h" +#include "core/io/multiplayer_peer.h" #include "core/io/packed_data_container.h" #include "core/io/packet_peer.h" #include "core/io/packet_peer_dtls.h" @@ -56,6 +56,7 @@ #include "core/io/pck_packer.h" #include "core/io/resource_format_binary.h" #include "core/io/resource_importer.h" +#include "core/io/resource_uid.h" #include "core/io/stream_peer_ssl.h" #include "core/io/tcp_server.h" #include "core/io/translation_loader_po.h" @@ -81,6 +82,7 @@ static Ref<ResourceFormatLoaderImage> resource_format_image; static Ref<TranslationLoaderPO> resource_format_po; static Ref<ResourceFormatSaverCrypto> resource_format_saver_crypto; static Ref<ResourceFormatLoaderCrypto> resource_format_loader_crypto; +static Ref<NativeExtensionResourceLoader> resource_loader_native_extension; static _ResourceLoader *_resource_loader = nullptr; static _ResourceSaver *_resource_saver = nullptr; @@ -102,6 +104,8 @@ static NativeExtensionManager *native_extension_manager = nullptr; extern void register_global_constants(); extern void unregister_global_constants(); +static ResourceUID *resource_uid = nullptr; + void register_core_types() { //consistency check static_assert(sizeof(Callable) <= 16); @@ -131,50 +135,50 @@ void register_core_types() { resource_format_image.instantiate(); ResourceLoader::add_resource_format_loader(resource_format_image); - ClassDB::register_class<Object>(); - - ClassDB::register_virtual_class<Script>(); - - ClassDB::register_class<RefCounted>(); - ClassDB::register_class<WeakRef>(); - ClassDB::register_class<Resource>(); - ClassDB::register_class<Image>(); - - ClassDB::register_virtual_class<InputEvent>(); - ClassDB::register_virtual_class<InputEventWithModifiers>(); - ClassDB::register_virtual_class<InputEventFromWindow>(); - ClassDB::register_class<InputEventKey>(); - ClassDB::register_virtual_class<InputEventMouse>(); - ClassDB::register_class<InputEventMouseButton>(); - ClassDB::register_class<InputEventMouseMotion>(); - ClassDB::register_class<InputEventJoypadButton>(); - ClassDB::register_class<InputEventJoypadMotion>(); - ClassDB::register_class<InputEventScreenDrag>(); - ClassDB::register_class<InputEventScreenTouch>(); - ClassDB::register_class<InputEventAction>(); - ClassDB::register_virtual_class<InputEventGesture>(); - ClassDB::register_class<InputEventMagnifyGesture>(); - ClassDB::register_class<InputEventPanGesture>(); - ClassDB::register_class<InputEventMIDI>(); + GDREGISTER_CLASS(Object); + + GDREGISTER_VIRTUAL_CLASS(Script); + + GDREGISTER_CLASS(RefCounted); + GDREGISTER_CLASS(WeakRef); + GDREGISTER_CLASS(Resource); + GDREGISTER_CLASS(Image); + + GDREGISTER_VIRTUAL_CLASS(InputEvent); + GDREGISTER_VIRTUAL_CLASS(InputEventWithModifiers); + GDREGISTER_VIRTUAL_CLASS(InputEventFromWindow); + GDREGISTER_CLASS(InputEventKey); + GDREGISTER_VIRTUAL_CLASS(InputEventMouse); + GDREGISTER_CLASS(InputEventMouseButton); + GDREGISTER_CLASS(InputEventMouseMotion); + GDREGISTER_CLASS(InputEventJoypadButton); + GDREGISTER_CLASS(InputEventJoypadMotion); + GDREGISTER_CLASS(InputEventScreenDrag); + GDREGISTER_CLASS(InputEventScreenTouch); + GDREGISTER_CLASS(InputEventAction); + GDREGISTER_VIRTUAL_CLASS(InputEventGesture); + GDREGISTER_CLASS(InputEventMagnifyGesture); + GDREGISTER_CLASS(InputEventPanGesture); + GDREGISTER_CLASS(InputEventMIDI); // Network - ClassDB::register_virtual_class<IP>(); + GDREGISTER_VIRTUAL_CLASS(IP); - ClassDB::register_virtual_class<StreamPeer>(); - ClassDB::register_class<StreamPeerBuffer>(); - ClassDB::register_class<StreamPeerTCP>(); - ClassDB::register_class<TCPServer>(); + GDREGISTER_VIRTUAL_CLASS(StreamPeer); + GDREGISTER_CLASS(StreamPeerBuffer); + GDREGISTER_CLASS(StreamPeerTCP); + GDREGISTER_CLASS(TCPServer); - ClassDB::register_virtual_class<PacketPeer>(); - ClassDB::register_class<PacketPeerStream>(); - ClassDB::register_class<PacketPeerUDP>(); - ClassDB::register_class<UDPServer>(); + GDREGISTER_VIRTUAL_CLASS(PacketPeer); + GDREGISTER_CLASS(PacketPeerStream); + GDREGISTER_CLASS(PacketPeerUDP); + GDREGISTER_CLASS(UDPServer); ClassDB::register_custom_instance_class<HTTPClient>(); // Crypto - ClassDB::register_class<HashingContext>(); - ClassDB::register_class<AESContext>(); + GDREGISTER_CLASS(HashingContext); + GDREGISTER_CLASS(AESContext); ClassDB::register_custom_instance_class<X509Certificate>(); ClassDB::register_custom_instance_class<CryptoKey>(); ClassDB::register_custom_instance_class<HMACContext>(); @@ -188,45 +192,52 @@ void register_core_types() { resource_format_loader_crypto.instantiate(); ResourceLoader::add_resource_format_loader(resource_format_loader_crypto); - ClassDB::register_virtual_class<NetworkedMultiplayerPeer>(); - ClassDB::register_class<MultiplayerAPI>(); - ClassDB::register_class<MainLoop>(); - ClassDB::register_class<Translation>(); - ClassDB::register_class<OptimizedTranslation>(); - ClassDB::register_class<UndoRedo>(); - ClassDB::register_class<TriangleMesh>(); + GDREGISTER_VIRTUAL_CLASS(MultiplayerPeer); + GDREGISTER_CLASS(MultiplayerAPI); + GDREGISTER_CLASS(MainLoop); + GDREGISTER_CLASS(Translation); + GDREGISTER_CLASS(OptimizedTranslation); + GDREGISTER_CLASS(UndoRedo); + GDREGISTER_CLASS(TriangleMesh); + + GDREGISTER_CLASS(ResourceFormatLoader); + GDREGISTER_CLASS(ResourceFormatSaver); + + GDREGISTER_CLASS(_File); + GDREGISTER_CLASS(_Directory); + GDREGISTER_CLASS(_Thread); + GDREGISTER_CLASS(_Mutex); + GDREGISTER_CLASS(_Semaphore); - ClassDB::register_class<ResourceFormatLoader>(); - ClassDB::register_class<ResourceFormatSaver>(); + GDREGISTER_CLASS(XMLParser); + GDREGISTER_CLASS(JSON); - ClassDB::register_class<_File>(); - ClassDB::register_class<_Directory>(); - ClassDB::register_class<_Thread>(); - ClassDB::register_class<_Mutex>(); - ClassDB::register_class<_Semaphore>(); + GDREGISTER_CLASS(ConfigFile); - ClassDB::register_class<XMLParser>(); - ClassDB::register_class<JSON>(); + GDREGISTER_CLASS(PCKPacker); - ClassDB::register_class<ConfigFile>(); + GDREGISTER_CLASS(PackedDataContainer); + GDREGISTER_VIRTUAL_CLASS(PackedDataContainerRef); + GDREGISTER_CLASS(AStar); + GDREGISTER_CLASS(AStar2D); + GDREGISTER_CLASS(EncodedObjectAsID); + GDREGISTER_CLASS(RandomNumberGenerator); - ClassDB::register_class<PCKPacker>(); + GDREGISTER_VIRTUAL_CLASS(ResourceImporter); - ClassDB::register_class<PackedDataContainer>(); - ClassDB::register_virtual_class<PackedDataContainerRef>(); - ClassDB::register_class<AStar>(); - ClassDB::register_class<AStar2D>(); - ClassDB::register_class<EncodedObjectAsID>(); - ClassDB::register_class<RandomNumberGenerator>(); + GDREGISTER_CLASS(NativeExtension); - ClassDB::register_virtual_class<ResourceImporter>(); + GDREGISTER_VIRTUAL_CLASS(NativeExtensionManager); - ClassDB::register_class<NativeExtension>(); + GDREGISTER_VIRTUAL_CLASS(ResourceUID); - ClassDB::register_virtual_class<NativeExtensionManager>(); + resource_uid = memnew(ResourceUID); native_extension_manager = memnew(NativeExtensionManager); + resource_loader_native_extension.instantiate(); + ResourceLoader::add_resource_format_loader(resource_loader_native_extension); + ip = IP::create(); _geometry_2d = memnew(_Geometry2D); @@ -253,22 +264,22 @@ void register_core_settings() { } void register_core_singletons() { - ClassDB::register_class<ProjectSettings>(); - ClassDB::register_virtual_class<IP>(); - ClassDB::register_class<_Geometry2D>(); - ClassDB::register_class<_Geometry3D>(); - ClassDB::register_class<_ResourceLoader>(); - ClassDB::register_class<_ResourceSaver>(); - ClassDB::register_class<_OS>(); - ClassDB::register_class<_Engine>(); - ClassDB::register_class<_ClassDB>(); - ClassDB::register_class<_Marshalls>(); - ClassDB::register_class<TranslationServer>(); - ClassDB::register_virtual_class<Input>(); - ClassDB::register_class<InputMap>(); - ClassDB::register_class<Expression>(); - ClassDB::register_class<_EngineDebugger>(); - ClassDB::register_class<Time>(); + GDREGISTER_CLASS(ProjectSettings); + GDREGISTER_VIRTUAL_CLASS(IP); + GDREGISTER_CLASS(_Geometry2D); + GDREGISTER_CLASS(_Geometry3D); + GDREGISTER_CLASS(_ResourceLoader); + GDREGISTER_CLASS(_ResourceSaver); + GDREGISTER_CLASS(_OS); + GDREGISTER_CLASS(_Engine); + GDREGISTER_CLASS(_ClassDB); + GDREGISTER_CLASS(_Marshalls); + GDREGISTER_CLASS(TranslationServer); + GDREGISTER_VIRTUAL_CLASS(Input); + GDREGISTER_CLASS(InputMap); + GDREGISTER_CLASS(Expression); + GDREGISTER_CLASS(_EngineDebugger); + GDREGISTER_CLASS(Time); Engine::get_singleton()->add_singleton(Engine::Singleton("ProjectSettings", ProjectSettings::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("IP", IP::get_singleton(), "IP")); @@ -286,10 +297,12 @@ void register_core_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("EngineDebugger", _EngineDebugger::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("Time", Time::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("NativeExtensionManager", NativeExtensionManager::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton())); } void register_core_extensions() { - //harcoded for now + // Hardcoded for now. + NativeExtension::initialize_native_extensions(); if (ProjectSettings::get_singleton()->has_setting("native_extensions/paths")) { Vector<String> paths = ProjectSettings::get_singleton()->get("native_extensions/paths"); for (int i = 0; i < paths.size(); i++) { @@ -304,6 +317,8 @@ void unregister_core_types() { native_extension_manager->deinitialize_extensions(NativeExtension::INITIALIZATION_LEVEL_CORE); memdelete(native_extension_manager); + + memdelete(resource_uid); memdelete(_resource_loader); memdelete(_resource_saver); memdelete(_os); @@ -339,6 +354,9 @@ void unregister_core_types() { memdelete(ip); } + ResourceLoader::remove_resource_format_loader(resource_loader_native_extension); + resource_loader_native_extension.unref(); + ResourceLoader::finalize(); ClassDB::cleanup_defaults(); diff --git a/core/string/node_path.cpp b/core/string/node_path.cpp index d3afa7b4dd..5fae13779e 100644 --- a/core/string/node_path.cpp +++ b/core/string/node_path.cpp @@ -240,19 +240,26 @@ NodePath NodePath::rel_path_to(const NodePath &p_np) const { common_parent--; Vector<StringName> relpath; + relpath.resize(src_dirs.size() + dst_dirs.size() + 1); - for (int i = src_dirs.size() - 1; i > common_parent; i--) { - relpath.push_back(".."); + StringName *relpath_ptr = relpath.ptrw(); + + int path_size = 0; + StringName back_str(".."); + for (int i = common_parent + 1; i < src_dirs.size(); i++) { + relpath_ptr[path_size++] = back_str; } for (int i = common_parent + 1; i < dst_dirs.size(); i++) { - relpath.push_back(dst_dirs[i]); + relpath_ptr[path_size++] = dst_dirs[i]; } - if (relpath.size() == 0) { - relpath.push_back("."); + if (path_size == 0) { + relpath_ptr[path_size++] = "."; } + relpath.resize(path_size); + return NodePath(relpath, p_np.get_subnames(), false); } diff --git a/core/string/optimized_translation.cpp b/core/string/optimized_translation.cpp index 268562d971..5863bd1c46 100644 --- a/core/string/optimized_translation.cpp +++ b/core/string/optimized_translation.cpp @@ -66,9 +66,9 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { int total_compression_size = 0; int total_string_size = 0; - for (List<StringName>::Element *E = keys.front(); E; E = E->next()) { + for (const StringName &E : keys) { //hash string - CharString cs = E->get().operator String().utf8(); + CharString cs = E.operator String().utf8(); uint32_t h = hash(0, cs.get_data()); Pair<int, CharString> p; p.first = idx; @@ -76,7 +76,7 @@ void OptimizedTranslation::generate(const Ref<Translation> &p_from) { buckets.write[h % size].push_back(p); //compress string - CharString src_s = p_from->get_message(E->get()).operator String().utf8(); + CharString src_s = p_from->get_message(E).operator String().utf8(); CompressedString ps; ps.orig_len = src_s.size(); ps.offset = total_compression_size; diff --git a/core/string/string_name.cpp b/core/string/string_name.cpp index 14b87072bb..9024f60dae 100644 --- a/core/string/string_name.cpp +++ b/core/string/string_name.cpp @@ -41,13 +41,17 @@ StaticCString StaticCString::create(const char *p_ptr) { StringName::_Data *StringName::_table[STRING_TABLE_LEN]; -StringName _scs_create(const char *p_chr) { - return (p_chr[0] ? StringName(StaticCString::create(p_chr)) : StringName()); +StringName _scs_create(const char *p_chr, bool p_static) { + return (p_chr[0] ? StringName(StaticCString::create(p_chr), p_static) : StringName()); } bool StringName::configured = false; Mutex StringName::mutex; +#ifdef DEBUG_ENABLED +bool StringName::debug_stringname = false; +#endif + void StringName::setup() { ERR_FAIL_COND(configured); for (int i = 0; i < STRING_TABLE_LEN; i++) { @@ -59,12 +63,29 @@ void StringName::setup() { void StringName::cleanup() { MutexLock lock(mutex); +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + Vector<_Data *> data; + for (int i = 0; i < STRING_TABLE_LEN; i++) { + _Data *d = _table[i]; + while (d) { + data.push_back(d); + d = d->next; + } + } + print_line("\nStringName Reference Ranking:\n"); + data.sort_custom<DebugSortReferences>(); + for (int i = 0; i < MIN(100, data.size()); i++) { + print_line(itos(i + 1) + ": " + data[i]->get_name() + " - " + itos(data[i]->debug_references)); + } + } +#endif int lost_strings = 0; for (int i = 0; i < STRING_TABLE_LEN; i++) { while (_table[i]) { _Data *d = _table[i]; lost_strings++; - if (OS::get_singleton()->is_stdout_verbose()) { + if (d->static_count.get() != d->refcount.get() && OS::get_singleton()->is_stdout_verbose()) { if (d->cname) { print_line("Orphan StringName: " + String(d->cname)); } else { @@ -79,6 +100,7 @@ void StringName::cleanup() { if (lost_strings) { print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit."); } + configured = false; } void StringName::unref() { @@ -87,6 +109,13 @@ void StringName::unref() { if (_data && _data->refcount.unref()) { MutexLock lock(mutex); + if (_data->static_count.get() > 0) { + if (_data->cname) { + ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname)); + } else { + ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->name)); + } + } if (_data->prev) { _data->prev->next = _data->next; } else { @@ -153,7 +182,7 @@ StringName::StringName(const StringName &p_name) { } } -StringName::StringName(const char *p_name) { +StringName::StringName(const char *p_name, bool p_static) { _data = nullptr; ERR_FAIL_COND(!configured); @@ -181,25 +210,42 @@ StringName::StringName(const char *p_name) { if (_data) { if (_data->refcount.ref()) { // exists - return; + if (p_static) { + _data->static_count.increment(); + } +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif } + + return; } _data = memnew(_Data); _data->name = p_name; _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); _data->hash = hash; _data->idx = idx; _data->cname = nullptr; _data->next = _table[idx]; _data->prev = nullptr; +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif if (_table[idx]) { _table[idx]->prev = _data; } _table[idx] = _data; } -StringName::StringName(const StaticCString &p_static_string) { +StringName::StringName(const StaticCString &p_static_string, bool p_static) { _data = nullptr; ERR_FAIL_COND(!configured); @@ -225,6 +271,14 @@ StringName::StringName(const StaticCString &p_static_string) { if (_data) { if (_data->refcount.ref()) { // exists + if (p_static) { + _data->static_count.increment(); + } +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif return; } } @@ -232,18 +286,26 @@ StringName::StringName(const StaticCString &p_static_string) { _data = memnew(_Data); _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); _data->hash = hash; _data->idx = idx; _data->cname = p_static_string.ptr; _data->next = _table[idx]; _data->prev = nullptr; +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif if (_table[idx]) { _table[idx]->prev = _data; } _table[idx] = _data; } -StringName::StringName(const String &p_name) { +StringName::StringName(const String &p_name, bool p_static) { _data = nullptr; ERR_FAIL_COND(!configured); @@ -269,6 +331,14 @@ StringName::StringName(const String &p_name) { if (_data) { if (_data->refcount.ref()) { // exists + if (p_static) { + _data->static_count.increment(); + } +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif return; } } @@ -276,11 +346,20 @@ StringName::StringName(const String &p_name) { _data = memnew(_Data); _data->name = p_name; _data->refcount.init(); + _data->static_count.set(p_static ? 1 : 0); _data->hash = hash; _data->idx = idx; _data->cname = nullptr; _data->next = _table[idx]; _data->prev = nullptr; +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + // Keep in memory, force static. + _data->refcount.ref(); + _data->static_count.increment(); + } +#endif + if (_table[idx]) { _table[idx]->prev = _data; } @@ -311,6 +390,12 @@ StringName StringName::search(const char *p_name) { } if (_data && _data->refcount.ref()) { +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif + return StringName(_data); } @@ -368,16 +453,17 @@ StringName StringName::search(const String &p_name) { } if (_data && _data->refcount.ref()) { +#ifdef DEBUG_ENABLED + if (unlikely(debug_stringname)) { + _data->debug_references++; + } +#endif return StringName(_data); } return StringName(); //does not exist } -StringName::~StringName() { - unref(); -} - bool operator==(const String &p_name, const StringName &p_string_name) { return p_name == p_string_name.operator String(); } diff --git a/core/string/string_name.h b/core/string/string_name.h index 44d0ea14fa..ce7988744b 100644 --- a/core/string/string_name.h +++ b/core/string/string_name.h @@ -44,16 +44,19 @@ struct StaticCString { class StringName { enum { - STRING_TABLE_BITS = 12, + STRING_TABLE_BITS = 16, STRING_TABLE_LEN = 1 << STRING_TABLE_BITS, STRING_TABLE_MASK = STRING_TABLE_LEN - 1 }; struct _Data { SafeRefCount refcount; + SafeNumeric<uint32_t> static_count; const char *cname = nullptr; String name; - +#ifdef DEBUG_ENABLED + uint32_t debug_references = 0; +#endif String get_name() const { return cname ? String(cname) : name; } int idx = 0; uint32_t hash = 0; @@ -79,6 +82,15 @@ class StringName { static void setup(); static void cleanup(); static bool configured; +#ifdef DEBUG_ENABLED + struct DebugSortReferences { + bool operator()(const _Data *p_left, const _Data *p_right) const { + return p_left->debug_references > p_right->debug_references; + } + }; + + static bool debug_stringname; +#endif StringName(_Data *p_data) { _data = p_data; } @@ -146,12 +158,20 @@ public: }; void operator=(const StringName &p_name); - StringName(const char *p_name); + StringName(const char *p_name, bool p_static = false); StringName(const StringName &p_name); - StringName(const String &p_name); - StringName(const StaticCString &p_static_string); + StringName(const String &p_name, bool p_static = false); + StringName(const StaticCString &p_static_string, bool p_static = false); StringName() {} - ~StringName(); + _FORCE_INLINE_ ~StringName() { + if (likely(configured) && _data) { //only free if configured + unref(); + } + } + +#ifdef DEBUG_ENABLED + static void set_debug_stringnames(bool p_enable) { debug_stringname = p_enable; } +#endif }; bool operator==(const String &p_name, const StringName &p_string_name); @@ -159,6 +179,8 @@ bool operator!=(const String &p_name, const StringName &p_string_name); bool operator==(const char *p_name, const StringName &p_string_name); bool operator!=(const char *p_name, const StringName &p_string_name); -StringName _scs_create(const char *p_chr); +StringName _scs_create(const char *p_chr, bool p_static = false); + +#define SNAME(m_arg) ([]() -> const StringName & { static StringName sname = _scs_create(m_arg, true); return sname; })() #endif // STRING_NAME_H diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 153f0190fd..cb7d924556 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -84,6 +84,7 @@ static const char *locale_list[] = { "ast_ES", // Asturian (Spain) "ayc_PE", // Southern Aymara (Peru) "ay_PE", // Aymara (Peru) + "az", // Azerbaijani "az_AZ", // Azerbaijani (Azerbaijan) "be", // Belarusian "be_BY", // Belarusian (Belarus) @@ -240,6 +241,7 @@ static const char *locale_list[] = { "ka_GE", // Georgian (Georgia) "kk_KZ", // Kazakh (Kazakhstan) "kl_GL", // Kalaallisut (Greenland) + "km", // Central Khmer "km_KH", // Central Khmer (Cambodia) "kn_IN", // Kannada (India) "kok_IN", // Konkani (India) @@ -390,6 +392,7 @@ static const char *locale_list[] = { "tr_CY", // Turkish (Cyprus) "tr_TR", // Turkish (Turkey) "ts_ZA", // Tsonga (South Africa) + "tt", // Tatar "tt_RU", // Tatar (Russia) "tzm", // Central Atlas Tamazight "tzm_MA", // Central Atlas Tamazight (Marrocos) @@ -458,6 +461,7 @@ static const char *locale_names[] = { "Asturian (Spain)", "Southern Aymara (Peru)", "Aymara (Peru)", + "Azerbaijani", "Azerbaijani (Azerbaijan)", "Belarusian", "Belarusian (Belarus)", @@ -614,6 +618,7 @@ static const char *locale_names[] = { "Georgian (Georgia)", "Kazakh (Kazakhstan)", "Kalaallisut (Greenland)", + "Central Khmer", "Central Khmer (Cambodia)", "Kannada (India)", "Konkani (India)", @@ -764,6 +769,7 @@ static const char *locale_names[] = { "Turkish (Cyprus)", "Turkish (Turkey)", "Tsonga (South Africa)", + "Tatar", "Tatar (Russia)", "Central Atlas Tamazight", "Central Atlas Tamazight (Marrocos)", @@ -835,8 +841,8 @@ Vector<String> Translation::_get_message_list() const { void Translation::_set_messages(const Dictionary &p_messages) { List<Variant> keys; p_messages.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - translation_map[E->get()] = p_messages[E->get()]; + for (const Variant &E : keys) { + translation_map[E] = p_messages[E]; } } @@ -923,6 +929,66 @@ void Translation::_bind_methods() { /////////////////////////////////////////////// +struct _character_accent_pair { + const char32_t character; + const char32_t *accented_character; +}; + +static _character_accent_pair _character_to_accented[] = { + { 'A', U"Å" }, + { 'B', U"ß" }, + { 'C', U"Ç" }, + { 'D', U"Ð" }, + { 'E', U"É" }, + { 'F', U"F́" }, + { 'G', U"Ĝ" }, + { 'H', U"Ĥ" }, + { 'I', U"Ĩ" }, + { 'J', U"Ĵ" }, + { 'K', U"ĸ" }, + { 'L', U"Ł" }, + { 'M', U"Ḿ" }, + { 'N', U"й" }, + { 'O', U"Ö" }, + { 'P', U"Ṕ" }, + { 'Q', U"Q́" }, + { 'R', U"Ř" }, + { 'S', U"Ŝ" }, + { 'T', U"Ŧ" }, + { 'U', U"Ũ" }, + { 'V', U"Ṽ" }, + { 'W', U"Ŵ" }, + { 'X', U"X́" }, + { 'Y', U"Ÿ" }, + { 'Z', U"Ž" }, + { 'a', U"á" }, + { 'b', U"ḅ" }, + { 'c', U"ć" }, + { 'd', U"d́" }, + { 'e', U"é" }, + { 'f', U"f́" }, + { 'g', U"ǵ" }, + { 'h', U"h̀" }, + { 'i', U"í" }, + { 'j', U"ǰ" }, + { 'k', U"ḱ" }, + { 'l', U"ł" }, + { 'm', U"m̀" }, + { 'n', U"ή" }, + { 'o', U"ô" }, + { 'p', U"ṕ" }, + { 'q', U"q́" }, + { 'r', U"ŕ" }, + { 's', U"š" }, + { 't', U"ŧ" }, + { 'u', U"ü" }, + { 'v', U"ṽ" }, + { 'w', U"ŵ" }, + { 'x', U"x́" }, + { 'y', U"ý" }, + { 'z', U"ź" }, +}; + bool TranslationServer::is_locale_valid(const String &p_locale) { const char **ptr = locale_list; @@ -1095,10 +1161,10 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin } if (!res) { - return p_message; + return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message; } - return res; + return pseudolocalization_enabled ? pseudolocalize(res) : res; } StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1211,7 +1277,18 @@ void TranslationServer::setup() { } else { set_locale(OS::get_singleton()->get_locale()); } + fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); + pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false); + pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true); + pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false); + pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false); + pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false); + expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0); + pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["); + pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true); + #ifdef TOOLS_ENABLED { String options = ""; @@ -1252,10 +1329,10 @@ StringName TranslationServer::tool_translate(const StringName &p_message, const if (tool_translation.is_valid()) { StringName r = tool_translation->get_message(p_message, p_context); if (r) { - return r; + return editor_pseudolocalization ? tool_pseudolocalize(r) : r; } } - return p_message; + return editor_pseudolocalization ? tool_pseudolocalize(p_message) : p_message; } StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const { @@ -1300,6 +1377,181 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message, return p_message_plural; } +bool TranslationServer::is_pseudolocalization_enabled() const { + return pseudolocalization_enabled; +} + +void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) { + pseudolocalization_enabled = p_enabled; + + 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) { + editor_pseudolocalization = p_enabled; +} + +void TranslationServer::reload_pseudolocalization() { + pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"); + pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels"); + pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"); + pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override"); + expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"); + pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix"); + pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix"); + pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"); + + 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 { + String message = p_message; + int length = message.length(); + if (pseudolocalization_override_enabled) { + message = get_override_string(message); + } + + if (pseudolocalization_double_vowels_enabled) { + message = double_vowels(message); + } + + if (pseudolocalization_accents_enabled) { + message = replace_with_accented_string(message); + } + + if (pseudolocalization_fake_bidi_enabled) { + message = wrap_with_fakebidi_characters(message); + } + + StringName res = add_padding(message, length); + return res; +} + +StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const { + String message = p_message; + message = double_vowels(message); + message = replace_with_accented_string(message); + StringName res = "[!!! " + message + " !!!]"; + return res; +} + +String TranslationServer::get_override_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += '*'; + } + return res; +} + +String TranslationServer::double_vowels(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + res += p_message[i]; + if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' || + p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') { + res += p_message[i]; + } + } + return res; +}; + +String TranslationServer::replace_with_accented_string(String &p_message) const { + String res; + for (int i = 0; i < p_message.size(); i++) { + if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += p_message[i]; + res += p_message[i + 1]; + i++; + continue; + } + const char32_t *accented = get_accented_version(p_message[i]); + if (accented) { + res += accented; + } else { + res += p_message[i]; + } + } + return res; +} + +String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const { + String res; + char32_t fakebidiprefix = U'\u202e'; + 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++) { + if (p_message[i] == '\n') { + res += fakebidisuffix; + res += p_message[i]; + res += fakebidiprefix; + } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { + res += fakebidisuffix; + res += p_message[i]; + res += p_message[i + 1]; + res += fakebidiprefix; + i++; + } else { + res += p_message[i]; + } + } + res += fakebidisuffix; + return res; +} + +String TranslationServer::add_padding(String &p_message, int p_length) const { + String res; + String prefix = pseudolocalization_prefix; + String suffix; + for (int i = 0; i < p_length * expansion_ratio / 2; i++) { + prefix += "_"; + suffix += "_"; + } + suffix += pseudolocalization_suffix; + res += prefix; + res += p_message; + res += suffix; + return res; +} + +const char32_t *TranslationServer::get_accented_version(char32_t p_character) const { + if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) { + return nullptr; + } + + for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) { + if (_character_to_accented[i].character == p_character) { + return _character_to_accented[i].accented_character; + } + } + + return nullptr; +} + +bool TranslationServer::is_placeholder(String &p_message, int p_index) const { + return p_message[p_index] == '%' && p_index < p_message.size() - 1 && + (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'); +} + 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); @@ -1316,6 +1568,12 @@ void TranslationServer::_bind_methods() { ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); + + ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled); + ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization); + ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize); + ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled"); } void TranslationServer::load_translations() { diff --git a/core/string/translation.h b/core/string/translation.h index 72a828227e..4f179ac0fe 100644 --- a/core/string/translation.h +++ b/core/string/translation.h @@ -77,6 +77,26 @@ class TranslationServer : public Object { bool enabled = true; + bool pseudolocalization_enabled = false; + bool pseudolocalization_accents_enabled = false; + bool pseudolocalization_double_vowels_enabled = false; + bool pseudolocalization_fake_bidi_enabled = false; + bool pseudolocalization_override_enabled = false; + bool pseudolocalization_skip_placeholders_enabled = false; + bool editor_pseudolocalization = false; + float expansion_ratio = 0.0; + String pseudolocalization_prefix; + String pseudolocalization_suffix; + + StringName tool_pseudolocalize(const StringName &p_message) const; + String get_override_string(String &p_message) const; + String double_vowels(String &p_message) const; + String replace_with_accented_string(String &p_message) const; + String wrap_with_fakebidi_characters(String &p_message) const; + String add_padding(String &p_message, int p_length) const; + const char32_t *get_accented_version(char32_t p_character) const; + bool is_placeholder(String &p_message, int p_index) const; + static TranslationServer *singleton; bool _load_translations(const String &p_from); @@ -104,6 +124,13 @@ public: StringName translate(const StringName &p_message, const StringName &p_context = "") const; StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const; + StringName pseudolocalize(const StringName &p_message) const; + + bool is_pseudolocalization_enabled() const; + void set_pseudolocalization_enabled(bool p_enabled); + void set_editor_pseudolocalization(bool p_enabled); + void reload_pseudolocalization(); + static Vector<String> get_all_locales(); static Vector<String> get_all_locale_names(); static bool is_locale_valid(const String &p_locale); diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp index f9b4e661e4..1da00aa54b 100644 --- a/core/string/translation_po.cpp +++ b/core/string/translation_po.cpp @@ -47,8 +47,7 @@ void TranslationPO::print_translation_map() { List<StringName> context_l; translation_map.get_key_list(&context_l); - for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) { - StringName ctx = E->get(); + for (const StringName &ctx : context_l) { file->store_line(" ===== Context: " + String::utf8(String(ctx).utf8()) + " ===== "); const HashMap<StringName, Vector<StringName>> &inner_map = translation_map[ctx]; @@ -74,8 +73,7 @@ Dictionary TranslationPO::_get_messages() const { List<StringName> context_l; translation_map.get_key_list(&context_l); - for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) { - StringName ctx = E->get(); + for (const StringName &ctx : context_l) { const HashMap<StringName, Vector<StringName>> &id_str_map = translation_map[ctx]; Dictionary d2; @@ -98,8 +96,7 @@ void TranslationPO::_set_messages(const Dictionary &p_messages) { List<Variant> context_l; p_messages.get_key_list(&context_l); - for (List<Variant>::Element *E = context_l.front(); E; E = E->next()) { - StringName ctx = E->get(); + for (const Variant &ctx : context_l) { const Dictionary &id_str_map = p_messages[ctx]; HashMap<StringName, Vector<StringName>> temp_map; @@ -121,8 +118,8 @@ Vector<String> TranslationPO::_get_message_list() const { get_message_list(&msgs); Vector<String> v; - for (List<StringName>::Element *E = msgs.front(); E; E = E->next()) { - v.push_back(E->get()); + for (const StringName &E : msgs) { + v.push_back(E); } return v; @@ -281,13 +278,13 @@ void TranslationPO::get_message_list(List<StringName> *r_messages) const { List<StringName> context_l; translation_map.get_key_list(&context_l); - for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) { - if (String(E->get()) != "") { + for (const StringName &E : context_l) { + if (String(E) != "") { continue; } List<StringName> msgid_l; - translation_map[E->get()].get_key_list(&msgid_l); + translation_map[E].get_key_list(&msgid_l); for (List<StringName>::Element *E2 = msgid_l.front(); E2; E2 = E2->next()) { r_messages->push_back(E2->get()); @@ -300,8 +297,8 @@ int TranslationPO::get_message_count() const { translation_map.get_key_list(&context_l); int count = 0; - for (List<StringName>::Element *E = context_l.front(); E; E = E->next()) { - count += translation_map[E->get()].size(); + for (const StringName &E : context_l) { + count += translation_map[E].size(); } return count; } diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp index 16ecd2b985..7beecdb6b5 100644 --- a/core/string/ustring.cpp +++ b/core/string/ustring.cpp @@ -3397,17 +3397,10 @@ String String::format(const Variant &values, String placeholder) const { if (value_arr.size() == 2) { Variant v_key = value_arr[0]; String key = v_key; - if (key.left(1) == "\"" && key.right(1) == "\"") { - key = key.substr(1, key.length() - 2); - } Variant v_val = value_arr[1]; String val = v_val; - if (val.left(1) == "\"" && val.right(1) == "\"") { - val = val.substr(1, val.length() - 2); - } - new_string = new_string.replace(placeholder.replace("_", key), val); } else { ERR_PRINT(String("STRING.format Inner Array size != 2 ").ascii().get_data()); @@ -3416,10 +3409,6 @@ String String::format(const Variant &values, String placeholder) const { Variant v_val = values_arr[i]; String val = v_val; - if (val.left(1) == "\"" && val.right(1) == "\"") { - val = val.substr(1, val.length() - 2); - } - if (placeholder.find("_") > -1) { new_string = new_string.replace(placeholder.replace("_", i_as_str), val); } else { @@ -3432,19 +3421,8 @@ String String::format(const Variant &values, String placeholder) const { List<Variant> keys; d.get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - String key = E->get(); - String val = d[E->get()]; - - if (key.left(1) == "\"" && key.right(1) == "\"") { - key = key.substr(1, key.length() - 2); - } - - if (val.left(1) == "\"" && val.right(1) == "\"") { - val = val.substr(1, val.length() - 2); - } - - new_string = new_string.replace(placeholder.replace("_", key), val); + for (const Variant &key : keys) { + new_string = new_string.replace(placeholder.replace("_", key), d[key]); } } else { ERR_PRINT(String("Invalid type: use Array or Dictionary.").ascii().get_data()); diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index acc46da0d5..519a896ffc 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -321,7 +321,7 @@ class CommandQueueMT { DECL_CMD(0) SPACE_SEP_LIST(DECL_CMD, 15) - /* comands that return */ + // Commands that return. DECL_CMD_RET(0) SPACE_SEP_LIST(DECL_CMD_RET, 15) diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h index 4572b269cf..2e932f9f26 100644 --- a/core/templates/hashfuncs.h +++ b/core/templates/hashfuncs.h @@ -95,7 +95,7 @@ static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) if (p_in == 0.0f) { u.d = 0.0; } else if (Math::is_nan(p_in)) { - u.d = Math_NAN; + u.d = NAN; } else { u.d = p_in; } @@ -124,7 +124,7 @@ static inline uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 538 if (p_in == 0.0f) { u.d = 0.0; } else if (Math::is_nan(p_in)) { - u.d = Math_NAN; + u.d = NAN; } else { u.d = p_in; } diff --git a/core/templates/list.h b/core/templates/list.h index 6047b89670..c2e17a2f6f 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -176,8 +176,8 @@ 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 ConstIterator &b) const { return E == b.E; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; } _FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; } _FORCE_INLINE_ ConstIterator() {} diff --git a/core/templates/pair.h b/core/templates/pair.h index 31706b6ecb..e30ee8bc56 100644 --- a/core/templates/pair.h +++ b/core/templates/pair.h @@ -62,7 +62,10 @@ bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) { template <class F, class S> struct PairSort { bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const { - return A.first < B.first; + if (A.first != B.first) { + return A.first < B.first; + } + return A.second < B.second; } }; diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h index 4f5c74ca46..8d139551ef 100644 --- a/core/templates/rid_owner.h +++ b/core/templates/rid_owner.h @@ -101,7 +101,7 @@ class RID_Alloc : public RID_AllocBase { //initialize for (uint32_t i = 0; i < elements_in_chunk; i++) { - //dont initialize chunk + // Don't initialize chunk. validator_chunks[chunk_count][i] = 0xFFFFFFFF; free_list_chunks[chunk_count][i] = alloc_count + i; } diff --git a/core/templates/set.h b/core/templates/set.h index 245c174862..9261d2d3d2 100644 --- a/core/templates/set.h +++ b/core/templates/set.h @@ -71,6 +71,9 @@ public: Element *prev() { return _prev; } + T &get() { + return value; + } const T &get() const { return value; }; @@ -118,8 +121,8 @@ 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 ConstIterator &b) const { return E == b.E; } + _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; } _FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; } _FORCE_INLINE_ ConstIterator() {} diff --git a/core/typedefs.h b/core/typedefs.h index cdbfb34e56..dde254af23 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -281,4 +281,11 @@ struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {}; #define DEBUG_METHODS_ENABLED #endif +// Macro GD_IS_DEFINED() allows to check if a macro is defined. It needs to be defined to anything (say 1) to work. +#define __GDARG_PLACEHOLDER_1 0, +#define __gd_take_second_arg(__ignored, val, ...) val +#define ____gd_is_defined(arg1_or_junk) __gd_take_second_arg(arg1_or_junk 1, 0) +#define ___gd_is_defined(val) ____gd_is_defined(__GDARG_PLACEHOLDER_##val) +#define GD_IS_DEFINED(x) ___gd_is_defined(x) + #endif // TYPEDEFS_H diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index ef5867c685..3b2c837096 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -33,6 +33,7 @@ #include "core/input/input_enums.h" #include "core/object/object.h" +#include "core/os/keyboard.h" #include "core/templates/list.h" #include "core/templates/simple_type.h" #include "core/typedefs.h" @@ -69,17 +70,17 @@ struct VariantCaster<const T &> { template <> \ struct VariantCaster<m_enum> { \ static _FORCE_INLINE_ m_enum cast(const Variant &p_variant) { \ - return (m_enum)p_variant.operator int(); \ + return (m_enum)p_variant.operator int64_t(); \ } \ }; \ template <> \ struct PtrToArg<m_enum> { \ _FORCE_INLINE_ static m_enum convert(const void *p_ptr) { \ - return m_enum(*reinterpret_cast<const int *>(p_ptr)); \ + return m_enum(*reinterpret_cast<const int64_t *>(p_ptr)); \ } \ typedef int64_t EncodeT; \ _FORCE_INLINE_ static void encode(m_enum p_val, const void *p_ptr) { \ - *(int *)p_ptr = p_val; \ + *(int64_t *)p_ptr = p_val; \ } \ }; @@ -96,11 +97,14 @@ VARIANT_ENUM_CAST(HatDir); VARIANT_ENUM_CAST(HatMask); VARIANT_ENUM_CAST(JoyAxis); VARIANT_ENUM_CAST(JoyButton); +VARIANT_ENUM_CAST(Key); +VARIANT_ENUM_CAST(KeyModifierMask); VARIANT_ENUM_CAST(MIDIMessage); VARIANT_ENUM_CAST(MouseButton); VARIANT_ENUM_CAST(Orientation); VARIANT_ENUM_CAST(HAlign); VARIANT_ENUM_CAST(VAlign); +VARIANT_ENUM_CAST(InlineAlign); VARIANT_ENUM_CAST(PropertyHint); VARIANT_ENUM_CAST(PropertyUsageFlags); VARIANT_ENUM_CAST(Variant::Type); diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp index ca6f3d615e..f487e718f4 100644 --- a/core/variant/callable.cpp +++ b/core/variant/callable.cpp @@ -407,8 +407,8 @@ Array Signal::get_connections() const { object->get_signal_connection_list(name, &connections); Array arr; - for (List<Object::Connection>::Element *E = connections.front(); E; E = E->next()) { - arr.push_back(E->get()); + for (const Object::Connection &E : connections) { + arr.push_back(E); } return arr; } diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index 7852187b77..8836e257a9 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -105,7 +105,7 @@ struct PtrToArg {}; } \ } -MAKE_PTRARGCONV(bool, uint32_t); +MAKE_PTRARGCONV(bool, uint8_t); // Integer types. MAKE_PTRARGCONV(uint8_t, int64_t); MAKE_PTRARGCONV(int8_t, int64_t); diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index badb5ba103..d538b9faff 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -1681,10 +1681,10 @@ String Variant::stringify(List<const void *> &stack) const { Vector<_VariantStrPair> pairs; - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { + for (const Variant &E : keys) { _VariantStrPair sp; - sp.key = E->get().stringify(stack); - sp.value = d[E->get()].stringify(stack); + sp.key = E.stringify(stack); + sp.value = d[E].stringify(stack); pairs.push_back(sp); } @@ -3533,12 +3533,13 @@ void Variant::register_types() { _register_variant_methods(); _register_variant_setters_getters(); _register_variant_constructors(); + _register_variant_destructors(); _register_variant_utility_functions(); } void Variant::unregister_types() { _unregister_variant_operators(); _unregister_variant_methods(); _unregister_variant_setters_getters(); - _unregister_variant_constructors(); + _unregister_variant_destructors(); _unregister_variant_utility_functions(); } diff --git a/core/variant/variant.h b/core/variant/variant.h index 373fe32921..9ec131a1b8 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -118,6 +118,11 @@ public: VARIANT_MAX }; + enum { + // Maximum recursion depth allowed when serializing variants. + MAX_RECURSION_DEPTH = 1024, + }; + private: friend struct _VariantCall; friend class VariantInternal; @@ -203,7 +208,7 @@ private: Transform3D *_transform3d; PackedArrayRefBase *packed_array; void *_ptr; //generic pointer - uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]; + uint8_t _mem[sizeof(ObjData) > (sizeof(real_t) * 4) ? sizeof(ObjData) : (sizeof(real_t) * 4)]{ 0 }; } _data alignas(8); void reference(const Variant &p_variant); @@ -253,7 +258,7 @@ private: true, //PACKED_COLOR_ARRAY, }; - if (unlikely(needs_deinit[type])) { //make it fast for types that dont need deinit + if (unlikely(needs_deinit[type])) { // Make it fast for types that don't need deinit. _clear_internal(); } type = NIL; @@ -266,6 +271,8 @@ private: static void _register_variant_setters_getters(); static void _unregister_variant_setters_getters(); static void _register_variant_constructors(); + static void _unregister_variant_destructors(); + static void _register_variant_destructors(); static void _unregister_variant_constructors(); static void _register_variant_utility_functions(); static void _unregister_variant_utility_functions(); @@ -502,7 +509,7 @@ public: static uint32_t get_builtin_method_hash(Variant::Type p_type, const StringName &p_method); void call(const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); - Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant()); + Variant call(const StringName &p_method, const Variant &p_arg1 = Variant(), const Variant &p_arg2 = Variant(), const Variant &p_arg3 = Variant(), const Variant &p_arg4 = Variant(), const Variant &p_arg5 = Variant(), const Variant &p_arg6 = Variant(), const Variant &p_arg7 = Variant(), const Variant &p_arg8 = Variant()); static void call_static(Variant::Type p_type, const StringName &p_method, const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error); @@ -529,6 +536,14 @@ public: static void get_constructor_list(Type p_type, List<MethodInfo> *r_list); //convenience + /* Destructors */ + + // Only ptrcall is available. + typedef void (*PTRDestructor)(void *base); + + static PTRDestructor get_ptr_destructor(Variant::Type p_type); + static bool has_destructor(Variant::Type p_type); + /* Properties */ void set_named(const StringName &p_member, const Variant &p_value, bool &r_valid); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 733361fe58..c3481d4896 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -750,6 +750,42 @@ struct _VariantCall { return 0; } + static PackedInt32Array func_PackedByteArray_decode_s32_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + const uint8_t *r = p_instance->ptr(); + PackedInt32Array dest; + dest.resize(size / sizeof(int32_t)); + memcpy(dest.ptrw(), r, size); + return dest; + } + + static PackedInt64Array func_PackedByteArray_decode_s64_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + const uint8_t *r = p_instance->ptr(); + PackedInt64Array dest; + dest.resize(size / sizeof(int64_t)); + memcpy(dest.ptrw(), r, size); + return dest; + } + + static PackedFloat32Array func_PackedByteArray_decode_float_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + const uint8_t *r = p_instance->ptr(); + PackedFloat32Array dest; + dest.resize(size / sizeof(float)); + memcpy(dest.ptrw(), r, size); + return dest; + } + + static PackedFloat64Array func_PackedByteArray_decode_double_array(PackedByteArray *p_instance) { + uint64_t size = p_instance->size(); + const uint8_t *r = p_instance->ptr(); + PackedFloat64Array dest; + dest.resize(size / sizeof(double)); + memcpy(dest.ptrw(), r, size); + return dest; + } + static void func_PackedByteArray_encode_u8(PackedByteArray *p_instance, int64_t p_offset, int64_t p_value) { uint64_t size = p_instance->size(); ERR_FAIL_COND(p_offset < 0 || p_offset > int64_t(size) - 1); @@ -1088,8 +1124,8 @@ bool Variant::has_builtin_method_return_value(Variant::Type p_type, const String void Variant::get_builtin_method_list(Variant::Type p_type, List<StringName> *p_list) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); - for (List<StringName>::Element *E = builtin_method_names[p_type].front(); E; E = E->next()) { - p_list->push_back(E->get()); + for (const StringName &E : builtin_method_names[p_type]) { + p_list->push_back(E); } } @@ -1152,12 +1188,12 @@ void Variant::get_method_list(List<MethodInfo> *p_list) const { obj->get_method_list(p_list); } } else { - for (List<StringName>::Element *E = builtin_method_names[type].front(); E; E = E->next()) { - const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E->get()); + 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->get(); + mi.name = E; //return type if (method->has_return_type) { @@ -1673,7 +1709,7 @@ static void _register_variant_builtin_methods() { bind_method(Transform2D, interpolate_with, sarray("xform", "weight"), varray()); bind_method(Transform2D, is_equal_approx, sarray("xform"), varray()); bind_method(Transform2D, set_rotation, sarray("rotation"), varray()); - bind_method(Transform2D, looking_at, sarray("target"), varray(Transform2D())); + bind_method(Transform2D, looking_at, sarray("target"), varray(Vector2())); /* Basis */ @@ -1692,6 +1728,7 @@ static void _register_variant_builtin_methods() { bind_method(Basis, slerp, sarray("to", "weight"), varray()); bind_method(Basis, is_equal_approx, sarray("b"), varray()); bind_method(Basis, get_rotation_quaternion, sarray(), varray()); + bind_static_method(Basis, looking_at, sarray("target", "up"), varray(Vector3(0, 1, 0))); /* AABB */ @@ -1824,6 +1861,11 @@ static void _register_variant_builtin_methods() { bind_function(PackedByteArray, decode_var, _VariantCall::func_PackedByteArray_decode_var, sarray("byte_offset", "allow_objects"), varray(false)); bind_function(PackedByteArray, decode_var_size, _VariantCall::func_PackedByteArray_decode_var_size, sarray("byte_offset", "allow_objects"), varray(false)); + bind_function(PackedByteArray, to_int32_array, _VariantCall::func_PackedByteArray_decode_s32_array, sarray(), varray()); + bind_function(PackedByteArray, to_int64_array, _VariantCall::func_PackedByteArray_decode_s64_array, sarray(), varray()); + bind_function(PackedByteArray, to_float32_array, _VariantCall::func_PackedByteArray_decode_float_array, sarray(), varray()); + bind_function(PackedByteArray, to_float64_array, _VariantCall::func_PackedByteArray_decode_double_array, sarray(), varray()); + bind_functionnc(PackedByteArray, encode_u8, _VariantCall::func_PackedByteArray_encode_u8, sarray("byte_offset", "value"), varray()); bind_functionnc(PackedByteArray, encode_s8, _VariantCall::func_PackedByteArray_encode_s8, sarray("byte_offset", "value"), varray()); bind_functionnc(PackedByteArray, encode_u16, _VariantCall::func_PackedByteArray_encode_u16, sarray("byte_offset", "value"), varray()); @@ -2002,7 +2044,7 @@ static void _register_variant_builtin_methods() { _VariantCall::add_variant_constant(Variant::VECTOR3, "ZERO", Vector3(0, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "ONE", Vector3(1, 1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(Math_INF, Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR3, "INF", Vector3(INFINITY, INFINITY, INFINITY)); _VariantCall::add_variant_constant(Variant::VECTOR3, "LEFT", Vector3(-1, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "RIGHT", Vector3(1, 0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR3, "UP", Vector3(0, 1, 0)); @@ -2031,7 +2073,7 @@ static void _register_variant_builtin_methods() { _VariantCall::add_variant_constant(Variant::VECTOR2, "ZERO", Vector2(0, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "ONE", Vector2(1, 1)); - _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(Math_INF, Math_INF)); + _VariantCall::add_variant_constant(Variant::VECTOR2, "INF", Vector2(INFINITY, INFINITY)); _VariantCall::add_variant_constant(Variant::VECTOR2, "LEFT", Vector2(-1, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "RIGHT", Vector2(1, 0)); _VariantCall::add_variant_constant(Variant::VECTOR2, "UP", Vector2(0, -1)); diff --git a/core/variant/variant_destruct.cpp b/core/variant/variant_destruct.cpp new file mode 100644 index 0000000000..366b71df3a --- /dev/null +++ b/core/variant/variant_destruct.cpp @@ -0,0 +1,78 @@ +/*************************************************************************/ +/* variant_destruct.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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_destruct.h" + +#include "core/templates/local_vector.h" + +static Variant::PTRDestructor destruct_pointers[Variant::VARIANT_MAX] = { nullptr }; + +template <class T> +static void add_destructor() { + destruct_pointers[T::get_base_type()] = T::ptr_destruct; +} + +void Variant::_register_variant_destructors() { + add_destructor<VariantDestruct<String>>(); + add_destructor<VariantDestruct<Transform2D>>(); + add_destructor<VariantDestruct<::AABB>>(); + add_destructor<VariantDestruct<Basis>>(); + add_destructor<VariantDestruct<Transform3D>>(); + add_destructor<VariantDestruct<StringName>>(); + add_destructor<VariantDestruct<NodePath>>(); + add_destructor<VariantDestruct<::RID>>(); + add_destructor<VariantDestruct<Callable>>(); + add_destructor<VariantDestruct<Signal>>(); + add_destructor<VariantDestruct<Dictionary>>(); + add_destructor<VariantDestruct<Array>>(); + add_destructor<VariantDestruct<PackedByteArray>>(); + add_destructor<VariantDestruct<PackedInt32Array>>(); + add_destructor<VariantDestruct<PackedInt64Array>>(); + add_destructor<VariantDestruct<PackedFloat32Array>>(); + add_destructor<VariantDestruct<PackedFloat64Array>>(); + add_destructor<VariantDestruct<PackedStringArray>>(); + add_destructor<VariantDestruct<PackedVector2Array>>(); + add_destructor<VariantDestruct<PackedVector3Array>>(); + add_destructor<VariantDestruct<PackedColorArray>>(); +} + +void Variant::_unregister_variant_destructors() { + // Nothing to be done. +} + +Variant::PTRDestructor Variant::get_ptr_destructor(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, nullptr); + return destruct_pointers[p_type]; +} + +bool Variant::has_destructor(Variant::Type p_type) { + ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, false); + return destruct_pointers[p_type] != nullptr; +} diff --git a/core/variant/variant_destruct.h b/core/variant/variant_destruct.h new file mode 100644 index 0000000000..7356e42201 --- /dev/null +++ b/core/variant/variant_destruct.h @@ -0,0 +1,76 @@ +/*************************************************************************/ +/* variant_destruct.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* 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_DESTRUCT_H +#define VARIANT_DESTRUCT_H + +#include "core/variant/variant.h" + +#include "core/object/class_db.h" + +template <class T> +struct VariantDestruct {}; + +#define MAKE_PTRDESTRUCT(m_type) \ + template <> \ + struct VariantDestruct<m_type> { \ + _FORCE_INLINE_ static void ptr_destruct(void *p_ptr) { \ + reinterpret_cast<m_type *>(p_ptr)->~m_type(); \ + } \ + _FORCE_INLINE_ static Variant::Type get_base_type() { \ + return GetTypeInfo<m_type>::VARIANT_TYPE; \ + } \ + } + +MAKE_PTRDESTRUCT(String); +MAKE_PTRDESTRUCT(Transform2D); +MAKE_PTRDESTRUCT(AABB); +MAKE_PTRDESTRUCT(Basis); +MAKE_PTRDESTRUCT(Transform3D); +MAKE_PTRDESTRUCT(StringName); +MAKE_PTRDESTRUCT(NodePath); +MAKE_PTRDESTRUCT(RID); +MAKE_PTRDESTRUCT(Callable); +MAKE_PTRDESTRUCT(Signal); +MAKE_PTRDESTRUCT(Dictionary); +MAKE_PTRDESTRUCT(Array); +MAKE_PTRDESTRUCT(PackedByteArray); +MAKE_PTRDESTRUCT(PackedInt32Array); +MAKE_PTRDESTRUCT(PackedInt64Array); +MAKE_PTRDESTRUCT(PackedFloat32Array); +MAKE_PTRDESTRUCT(PackedFloat64Array); +MAKE_PTRDESTRUCT(PackedStringArray); +MAKE_PTRDESTRUCT(PackedVector2Array); +MAKE_PTRDESTRUCT(PackedVector3Array); +MAKE_PTRDESTRUCT(PackedColorArray); + +#undef MAKE_PTRDESTRUCT + +#endif // VARIANT_DESTRUCT_H diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 78e1ad06ae..566b14736e 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1171,6 +1171,11 @@ struct VariantInitializer<PackedColorArray> { static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::init_color_array(v); } }; +template <> +struct VariantInitializer<Object *> { + static _FORCE_INLINE_ void init(Variant *v) { VariantInternal::object_assign_null(v); } +}; + template <class T> struct VariantZeroAssigner { }; @@ -1385,4 +1390,19 @@ struct VariantTypeAdjust<Object *> { } }; +// GDNative extension helpers. + +template <class T> +struct VariantTypeConstructor { + _FORCE_INLINE_ static void variant_from_type(void *p_variant, void *p_value) { + Variant *variant = reinterpret_cast<Variant *>(p_variant); + VariantInitializer<T>::init(variant); + VariantInternalAccessor<T>::set(variant, *((T *)p_value)); + } + + _FORCE_INLINE_ static void type_from_variant(void *p_value, void *p_variant) { + *((T *)p_value) = VariantInternalAccessor<T>::get(reinterpret_cast<Variant *>(p_variant)); + } +}; + #endif // VARIANT_INTERNAL_H diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h index e744e76ea3..cbdd60f404 100644 --- a/core/variant/variant_op.h +++ b/core/variant/variant_op.h @@ -1261,8 +1261,10 @@ public: const String &a = *VariantGetInternalPtr<String>::get_ptr(&p_left); - b->get(a, &r_valid); - *r_ret = r_valid; + bool exist; + b->get(a, &exist); + *r_ret = exist; + r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); @@ -1293,8 +1295,10 @@ public: const StringName &a = *VariantGetInternalPtr<StringName>::get_ptr(&p_left); - b->get(a, &r_valid); - *r_ret = r_valid; + bool exist; + b->get(a, &exist); + *r_ret = exist; + r_valid = true; } static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) { Object *l = right->get_validated_object(); diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp index 86d5ae7f38..50c48fd386 100644 --- a/core/variant/variant_parser.cpp +++ b/core/variant/variant_parser.cpp @@ -506,9 +506,9 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, } else if (id == "null" || id == "nil") { value = Variant(); } else if (id == "inf") { - value = Math_INF; + value = INFINITY; } else if (id == "nan") { - value = Math_NAN; + value = NAN; } else if (id == "Vector2") { Vector<real_t> args; Error err = _parse_construct<real_t>(p_stream, args, line, r_err_str); @@ -1586,8 +1586,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str List<PropertyInfo> props; obj->get_property_list(&props); bool first = true; - for (List<PropertyInfo>::Element *E = props.front(); E; E = E->next()) { - if (E->get().usage & PROPERTY_USAGE_STORAGE || E->get().usage & PROPERTY_USAGE_SCRIPT_VARIABLE) { + for (const PropertyInfo &E : props) { + if (E.usage & PROPERTY_USAGE_STORAGE || E.usage & PROPERTY_USAGE_SCRIPT_VARIABLE) { //must be serialized if (first) { @@ -1596,8 +1596,8 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str p_store_string_func(p_store_string_ud, ","); } - p_store_string_func(p_store_string_ud, "\"" + E->get().name + "\":"); - write(obj->get(E->get().name), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); + 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); } } @@ -1615,7 +1615,7 @@ 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()) { /* - if (!_check_type(dict[E->get()])) + if (!_check_type(dict[E])) continue; */ write(E->get(), p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud); diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h index 05fc29b5e0..1ba26db6ed 100644 --- a/core/variant/variant_parser.h +++ b/core/variant/variant_parser.h @@ -73,9 +73,9 @@ public: struct ResourceParser { void *userdata = nullptr; - ParseResourceFunc func; - ParseResourceFunc ext_func; - ParseResourceFunc sub_func; + ParseResourceFunc func = nullptr; + ParseResourceFunc ext_func = nullptr; + ParseResourceFunc sub_func = nullptr; }; enum TokenType { diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp index de1deace63..ae3c7685fd 100644 --- a/core/variant/variant_setget.cpp +++ b/core/variant/variant_setget.cpp @@ -1093,9 +1093,9 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { const Dictionary *dic = reinterpret_cast<const Dictionary *>(_data._mem); List<Variant> keys; dic->get_key_list(&keys); - for (List<Variant>::Element *E = keys.front(); E; E = E->next()) { - if (E->get().get_type() == Variant::STRING) { - p_list->push_back(PropertyInfo(Variant::STRING, E->get())); + for (const Variant &E : keys) { + if (E.get_type() == Variant::STRING) { + p_list->push_back(PropertyInfo(Variant::STRING, E)); } } } else if (type == OBJECT) { @@ -1106,10 +1106,10 @@ void Variant::get_property_list(List<PropertyInfo> *p_list) const { } else { List<StringName> members; get_member_list(type, &members); - for (List<StringName>::Element *E = members.front(); E; E = E->next()) { + for (const StringName &E : members) { PropertyInfo pi; - pi.name = E->get(); - pi.type = get_member_type(type, E->get()); + pi.name = E; + pi.type = get_member_type(type, E); p_list->push_back(pi); } } diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index 1f69e81d99..6c57d1de10 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -249,10 +249,6 @@ struct VariantUtilityFunctions { return Math::move_toward(from, to, delta); } - static inline double dectime(double value, double amount, double step) { - return Math::dectime(value, amount, step); - } - static inline double deg2rad(double angle_deg) { return Math::deg2rad(angle_deg); } @@ -488,6 +484,14 @@ struct VariantUtilityFunctions { return str; } + static inline String error_string(Error error) { + if (error < 0 || error >= ERR_MAX) { + return String("(invalid error code)"); + } + + return String(error_names[error]); + } + static inline void print(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (p_arg_count < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1195,7 +1199,6 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(smoothstep, sarray("from", "to", "x"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(move_toward, sarray("from", "to", "delta"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(dectime, sarray("value", "amount", "step"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(deg2rad, sarray("deg"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(rad2deg, sarray("rad"), Variant::UTILITY_FUNC_TYPE_MATH); @@ -1239,6 +1242,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printerr, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(printt, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); @@ -1397,8 +1401,8 @@ uint32_t Variant::get_utility_function_hash(const StringName &p_name) { } void Variant::get_utility_function_list(List<StringName> *r_functions) { - for (List<StringName>::Element *E = utility_function_name_table.front(); E; E = E->next()) { - r_functions->push_back(E->get()); + for (const StringName &E : utility_function_name_table) { + r_functions->push_back(E); } } |