summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/SCsub9
-rw-r--r--core/config/engine.cpp16
-rw-r--r--core/config/engine.h7
-rw-r--r--core/config/project_settings.cpp9
-rw-r--r--core/core_bind.cpp5
-rw-r--r--core/core_bind.h1
-rw-r--r--core/crypto/SCsub13
-rw-r--r--core/crypto/crypto_core.cpp3
-rw-r--r--core/extension/extension_api_dump.cpp24
-rw-r--r--core/extension/gdextension.cpp61
-rw-r--r--core/extension/gdextension.h7
-rw-r--r--core/input/gamecontrollerdb.txt75
-rw-r--r--core/io/dir_access.cpp2
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/json.cpp11
-rw-r--r--core/io/marshalls.cpp235
-rw-r--r--core/io/resource.cpp3
-rw-r--r--core/io/resource_format_binary.cpp9
-rw-r--r--core/io/resource_loader.cpp7
-rw-r--r--core/io/stream_peer_tcp.cpp1
-rw-r--r--core/math/a_star.compat.inc59
-rw-r--r--core/math/a_star.cpp65
-rw-r--r--core/math/a_star.h25
-rw-r--r--core/math/a_star_grid_2d.compat.inc48
-rw-r--r--core/math/a_star_grid_2d.cpp34
-rw-r--r--core/math/a_star_grid_2d.h15
-rw-r--r--core/math/basis.cpp2
-rw-r--r--core/math/basis.h2
-rw-r--r--core/math/bvh_abb.h2
-rw-r--r--core/math/bvh_debug.inc2
-rw-r--r--core/math/bvh_split.inc4
-rw-r--r--core/math/convex_hull.cpp10
-rw-r--r--core/math/delaunay_3d.h5
-rw-r--r--core/math/geometry_3d.h79
-rw-r--r--core/math/projection.cpp17
-rw-r--r--core/math/projection.h6
-rw-r--r--core/math/rect2.h8
-rw-r--r--core/math/vector3.cpp4
-rw-r--r--core/object/class_db.cpp27
-rw-r--r--core/object/object.cpp35
-rw-r--r--core/object/object.h2
-rw-r--r--core/object/script_language.cpp12
-rw-r--r--core/object/script_language_extension.h36
-rw-r--r--core/object/worker_thread_pool.cpp161
-rw-r--r--core/object/worker_thread_pool.h24
-rw-r--r--core/os/os.h3
-rw-r--r--core/os/pool_allocator.cpp5
-rw-r--r--core/string/translation.cpp4
-rw-r--r--core/string/translation.h2
-rw-r--r--core/string/ustring.cpp10
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/templates/command_queue_mt.cpp8
-rw-r--r--core/templates/command_queue_mt.h56
-rw-r--r--core/variant/array.cpp16
-rw-r--r--core/variant/array.h64
-rw-r--r--core/variant/variant.cpp119
-rw-r--r--core/variant/variant.h89
-rw-r--r--core/variant/variant_call.cpp4
-rw-r--r--core/variant/variant_op.cpp10
-rw-r--r--core/variant/variant_parser.cpp105
-rw-r--r--core/variant/variant_parser.h1
61 files changed, 1136 insertions, 546 deletions
diff --git a/core/SCsub b/core/SCsub
index 7edf8ea88d..f7b733a221 100644
--- a/core/SCsub
+++ b/core/SCsub
@@ -3,6 +3,7 @@
Import("env")
import core_builders
+import methods
env.core_sources = []
@@ -35,10 +36,12 @@ if "SCRIPT_AES256_ENCRYPTION_KEY" in os.environ:
)
Exit(255)
-# NOTE: It is safe to generate this file here, since this is still executed serially
-with open("script_encryption_key.gen.cpp", "w", encoding="utf-8", newline="\n") as f:
- f.write('#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n")
+script_encryption_key_contents = (
+ '#include "core/config/project_settings.h"\nuint8_t script_encryption_key[32]={' + txt + "};\n"
+)
+
+methods.write_file_if_needed("script_encryption_key.gen.cpp", script_encryption_key_contents)
# Add required thirdparty code.
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 9f4bff3779..f2f8aebe8b 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -82,6 +82,17 @@ int Engine::get_audio_output_latency() const {
return _audio_output_latency;
}
+void Engine::increment_frames_drawn() {
+ if (frame_server_synced) {
+ server_syncs++;
+ } else {
+ server_syncs = 0;
+ }
+ frame_server_synced = false;
+
+ frames_drawn++;
+}
+
uint64_t Engine::get_frames_drawn() {
return frames_drawn;
}
@@ -364,6 +375,11 @@ Engine *Engine::get_singleton() {
return singleton;
}
+bool Engine::notify_frame_server_synced() {
+ frame_server_synced = true;
+ return server_syncs > SERVER_SYNC_FRAME_COUNT_WARNING;
+}
+
Engine::Engine() {
singleton = this;
}
diff --git a/core/config/engine.h b/core/config/engine.h
index d1495b36c2..8dece803e3 100644
--- a/core/config/engine.h
+++ b/core/config/engine.h
@@ -91,6 +91,10 @@ private:
String write_movie_path;
String shader_cache_path;
+ static constexpr int SERVER_SYNC_FRAME_COUNT_WARNING = 5;
+ int server_syncs = 0;
+ bool frame_server_synced = false;
+
public:
static Engine *get_singleton();
@@ -179,6 +183,9 @@ public:
bool is_generate_spirv_debug_info_enabled() const;
int32_t get_gpu_index() const;
+ void increment_frames_drawn();
+ bool notify_frame_server_synced();
+
Engine();
virtual ~Engine() {}
};
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index ce7fa1074b..104b17961d 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -1330,8 +1330,8 @@ void ProjectSettings::load_scene_groups_cache() {
for (const String &E : scene_paths) {
Array scene_groups = cf->get_value(E, "groups", Array());
HashSet<StringName> cache;
- for (int i = 0; i < scene_groups.size(); ++i) {
- cache.insert(scene_groups[i]);
+ for (const Variant &scene_group : scene_groups) {
+ cache.insert(scene_group);
}
add_scene_groups_cache(E, cache);
}
@@ -1438,6 +1438,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("application/run/disable_stdout", false);
GLOBAL_DEF("application/run/disable_stderr", false);
GLOBAL_DEF("application/run/print_header", true);
+ GLOBAL_DEF("application/run/enable_alt_space_menu", false);
GLOBAL_DEF_RST("application/config/use_hidden_project_data_directory", true);
GLOBAL_DEF("application/config/use_custom_user_dir", false);
GLOBAL_DEF("application/config/custom_user_dir_name", "");
@@ -1523,8 +1524,11 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("debug/settings/crash_handler/message.editor",
String("Please include this when reporting the bug on: https://github.com/godotengine/godot/issues"));
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
+ GLOBAL_DEF_RST("rendering/occlusion_culling/jitter_projection", true);
+
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
+ GLOBAL_DEF_BASIC("internationalization/rendering/root_node_auto_translate", true);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
@@ -1536,6 +1540,7 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::BOOL, "rendering/rendering_device/pipeline_cache/enable"), true);
GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 8c85030783..03c31bee28 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -338,6 +338,10 @@ bool OS::is_process_running(int p_pid) const {
return ::OS::get_singleton()->is_process_running(p_pid);
}
+int OS::get_process_exit_code(int p_pid) const {
+ return ::OS::get_singleton()->get_process_exit_code(p_pid);
+}
+
int OS::get_process_id() const {
return ::OS::get_singleton()->get_process_id();
}
@@ -602,6 +606,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
+ ClassDB::bind_method(D_METHOD("get_process_exit_code", "pid"), &OS::get_process_exit_code);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);
diff --git a/core/core_bind.h b/core/core_bind.h
index d46321cf5c..3c0cdc25ce 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -164,6 +164,7 @@ public:
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);
bool is_process_running(int p_pid) const;
+ int get_process_exit_code(int p_pid) const;
int get_process_id() const;
void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>());
diff --git a/core/crypto/SCsub b/core/crypto/SCsub
index ac79e10d19..8cff3cf679 100644
--- a/core/crypto/SCsub
+++ b/core/crypto/SCsub
@@ -21,9 +21,9 @@ if is_builtin or not has_module:
# to make a "light" build with only the necessary mbedtls files.
if not has_module:
# Minimal mbedTLS config file
- env_crypto.Append(
- CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_core_mbedtls_config.h\\"')]
- )
+ config_path = "thirdparty/mbedtls/include/godot_core_mbedtls_config.h"
+ config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"'
+ env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)])
# Build minimal mbedTLS library (MD5/SHA/Base64/AES).
env_thirdparty = env_crypto.Clone()
env_thirdparty.disable_warnings()
@@ -34,6 +34,7 @@ if not has_module:
"constant_time.c",
"ctr_drbg.c",
"entropy.c",
+ "md.c",
"md5.c",
"sha1.c",
"sha256.c",
@@ -46,9 +47,9 @@ if not has_module:
env.core_sources += thirdparty_obj
elif is_builtin:
# Module mbedTLS config file
- env_crypto.Append(
- CPPDEFINES=[("MBEDTLS_CONFIG_FILE", '\\"thirdparty/mbedtls/include/godot_module_mbedtls_config.h\\"')]
- )
+ config_path = "thirdparty/mbedtls/include/godot_module_mbedtls_config.h"
+ config_path = f"<{config_path}>" if env_crypto["ninja"] and env_crypto.msvc else f'\\"{config_path}\\"'
+ env_crypto.Append(CPPDEFINES=[("MBEDTLS_CONFIG_FILE", config_path)])
# Needed to force rebuilding the core files when the configuration file is updated.
thirdparty_obj = ["#thirdparty/mbedtls/include/godot_module_mbedtls_config.h"]
diff --git a/core/crypto/crypto_core.cpp b/core/crypto/crypto_core.cpp
index 17b34c08e2..69a83284cc 100644
--- a/core/crypto/crypto_core.cpp
+++ b/core/crypto/crypto_core.cpp
@@ -39,6 +39,9 @@
#include <mbedtls/md5.h>
#include <mbedtls/sha1.h>
#include <mbedtls/sha256.h>
+#if MBEDTLS_VERSION_MAJOR >= 3
+#include <mbedtls/compat-2.x.h>
+#endif
// RandomGenerator
CryptoCore::RandomGenerator::RandomGenerator() {
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 543dabfb16..69be2d2a8f 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -1313,9 +1313,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
} else if (p_old_value.get_type() == Variant::DICTIONARY && p_new_value.get_type() == Variant::DICTIONARY) {
Dictionary old_dict = p_old_value;
Dictionary new_dict = p_new_value;
- Array old_keys = old_dict.keys();
- for (int i = 0; i < old_keys.size(); i++) {
- Variant key = old_keys[i];
+ for (const Variant &key : old_dict.keys()) {
if (!new_dict.has(key)) {
failed = true;
print_error(vformat("Validate extension JSON: Error: Field '%s': %s was removed.", p_path, key));
@@ -1328,9 +1326,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
failed = true;
}
}
- Array new_keys = old_dict.keys();
- for (int i = 0; i < new_keys.size(); i++) {
- Variant key = new_keys[i];
+ for (const Variant &key : old_dict.keys()) {
if (!old_dict.has(key)) {
failed = true;
print_error(vformat("Validate extension JSON: Error: Field '%s': %s was added with value %s.", p_path, key, new_dict[key]));
@@ -1356,8 +1352,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
Array new_api = p_new_api[p_base_array];
HashMap<String, Dictionary> new_api_assoc;
- for (int i = 0; i < new_api.size(); i++) {
- Dictionary elem = new_api[i];
+ for (const Variant &var : new_api) {
+ Dictionary elem = var;
ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field));
String name = elem[p_name_field];
if (p_compare_operators && elem.has("right_type")) {
@@ -1367,8 +1363,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
}
Array old_api = p_old_api[p_base_array];
- for (int i = 0; i < old_api.size(); i++) {
- Dictionary old_elem = old_api[i];
+ for (const Variant &var : old_api) {
+ Dictionary old_elem = var;
if (!old_elem.has(p_name_field)) {
failed = true;
print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", base_array, p_name_field));
@@ -1508,16 +1504,16 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered
Array new_api = p_new_api[p_outer];
HashMap<String, Dictionary> new_api_assoc;
- for (int i = 0; i < new_api.size(); i++) {
- Dictionary elem = new_api[i];
+ for (const Variant &var : new_api) {
+ Dictionary elem = var;
ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", p_outer, p_outer_name));
new_api_assoc.insert(elem[p_outer_name], elem);
}
Array old_api = p_old_api[p_outer];
- for (int i = 0; i < old_api.size(); i++) {
- Dictionary old_elem = old_api[i];
+ for (const Variant &var : old_api) {
+ Dictionary old_elem = var;
if (!old_elem.has(p_outer_name)) {
failed = true;
print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", p_outer, p_outer_name));
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 0ed4c3380c..546c40e9cb 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -729,53 +729,18 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const String
Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- // If running on the editor on Windows, we copy the library and open the copy.
- // This is so the original file isn't locked and can be updated by a compiler.
- bool library_copied = false;
- if (Engine::get_singleton()->is_editor_hint()) {
- if (!FileAccess::exists(abs_path)) {
- ERR_PRINT("GDExtension library not found: " + abs_path);
- return ERR_FILE_NOT_FOUND;
- }
-
- // Copy the file to the same directory as the original with a prefix in the name.
- // This is so relative path to dependencies are satisfied.
- String copy_path = abs_path.get_base_dir().path_join("~" + abs_path.get_file());
+ String actual_lib_path;
+ Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &actual_lib_path, Engine::get_singleton()->is_editor_hint());
- // If there's a left-over copy (possibly from a crash) then delete it first.
- if (FileAccess::exists(copy_path)) {
- DirAccess::remove_absolute(copy_path);
- }
-
- Error copy_err = DirAccess::copy_absolute(abs_path, copy_path);
- if (copy_err) {
- ERR_PRINT("Error copying GDExtension library: " + abs_path);
- return ERR_CANT_CREATE;
- }
- FileAccess::set_hidden_attribute(copy_path, true);
- library_copied = true;
-
- // Save the copied path so it can be deleted later.
- temp_lib_path = copy_path;
-
- // Use the copy to open the library.
- abs_path = copy_path;
+ if (actual_lib_path.get_file() != abs_path.get_file()) {
+ // If temporary files are generated, let's change the library path to point at the original,
+ // because that's what we want to check to see if it's changed.
+ library_path = actual_lib_path.get_base_dir().path_join(p_path.get_file());
}
-#endif
- Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &library_path);
ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- // If we copied the file, let's change the library path to point at the original,
- // because that's what we want to check to see if it's changed.
- if (library_copied) {
- library_path = library_path.get_base_dir() + "\\" + p_path.get_file();
- }
-#endif
-
void *entry_funcptr = nullptr;
err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
@@ -803,13 +768,6 @@ void GDExtension::close_library() {
ERR_FAIL_NULL(library);
OS::get_singleton()->close_dynamic_library(library);
-#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
- // Delete temporary copy of library if it exists.
- if (!temp_lib_path.is_empty() && Engine::get_singleton()->is_editor_hint()) {
- DirAccess::remove_absolute(temp_lib_path);
- }
-#endif
-
library = nullptr;
class_icon_paths.clear();
@@ -1014,13 +972,6 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol);
if (err != OK) {
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- // If the DLL fails to load, make sure that temporary DLL copies are cleaned up.
- if (Engine::get_singleton()->is_editor_hint()) {
- DirAccess::remove_absolute(p_extension->get_temp_library_path());
- }
-#endif
-
// Unreference the extension so that this loading can be considered a failure.
p_extension.unref();
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 2d0cb6a5ba..1f48edecf7 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -47,9 +47,6 @@ class GDExtension : public Resource {
void *library = nullptr; // pointer if valid,
String library_path;
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- String temp_lib_path;
-#endif
bool reloadable = false;
struct Extension {
@@ -130,10 +127,6 @@ public:
Error open_library(const String &p_path, const String &p_entry_symbol);
void close_library();
-#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
- String get_temp_library_path() const { return temp_lib_path; }
-#endif
-
enum InitializationLevel {
INITIALIZATION_LEVEL_CORE = GDEXTENSION_INITIALIZATION_CORE,
INITIALIZATION_LEVEL_SERVERS = GDEXTENSION_INITIALIZATION_SERVERS,
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 420de7f9b7..a17e15a62c 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -23,7 +23,7 @@
03000000c82d00001151000000000000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000150000000000000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000151000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a2,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
-03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
+03000000c82d00000650000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00005106000000000000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,guide:b2,leftshoulder:b8,lefttrigger:b9,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Windows,
03000000c82d00002090000000000000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b4,y:b3,platform:Windows,
03000000c82d00000310000000000000,8BitDo N30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Windows,
@@ -104,6 +104,7 @@
030000007c1800000006000000000000,Alienware Dual Compatible PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b3,platform:Windows,
03000000491900001904000000000000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,
03000000710100001904000000000000,Amazon Luna Controller,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b5,leftstick:b8,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b4,rightstick:b7,rightx:a3,righty:a4,start:b6,x:b3,y:b2,platform:Windows,
+0300000008100000e501000000000000,Anbernic Game Pad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000830500000160000000000000,Arcade,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b3,x:b4,y:b4,platform:Windows,
03000000120c0000100e000000000000,Armor 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
03000000490b00004406000000000000,ASCII Seamic Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
@@ -114,6 +115,10 @@
03000000050b00000679000000000000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,start:b3,platform:Windows,
03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,rightx:a3,righty:a4,start:b9,x:b2,y:b3,platform:Windows,
+030000008a3500000102000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
+030000008a3500000201000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
+030000008a3500000302000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
+030000008a3500000402000000000000,Backbone One,a:b4,b:b5,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b10,leftstick:b17,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b18,righttrigger:b13,rightx:a3,righty:a4,start:b15,x:b7,y:b8,platform:Windows,
03000000e4150000103f000000000000,Batarang,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d6200000e557000000000000,Batarang PlayStation Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000c01100001352000000000000,Battalife Joystick,a:b6,b:b7,back:b2,leftshoulder:b0,leftx:a0,lefty:a1,rightshoulder:b1,start:b3,x:b4,y:b5,platform:Windows,
@@ -167,6 +172,7 @@
03000000317300000100000000000000,DualShock 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
030000006f0e00003001000000000000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fc0400000250000000000000,Easy Grip,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Windows,
+03000000bc2000000091000000000000,EasySMX Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000006e0500000a20000000000000,Elecom DUX60 MMO,a:b2,b:b3,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,leftstick:b14,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b15,righttrigger:b13,rightx:a3,righty:a4,start:b20,x:b0,y:b1,platform:Windows,
03000000b80500000410000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,
03000000b80500000610000000000000,Elecom Gamepad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b1,platform:Windows,
@@ -254,6 +260,8 @@
030000000d0f00005f00000000000000,Hori Fighting Commander 4 PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00005e00000000000000,Hori Fighting Commander 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00008400000000000000,Hori Fighting Commander 5,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+030000000d0f00006201000000000000,Hori Fighting Commander Octa,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Windows,
+030000000d0f00006401000000000000,Hori Fighting Commander Octa,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f00005100000000000000,Hori Fighting Commander PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000000d0f00008600000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000000d0f0000ba00000000000000,Hori Fighting Commander Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
@@ -556,6 +564,7 @@
030000009b2800002300000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800006900000000000000,Raphnet 3DO Adapter,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b2,start:b3,platform:Windows,
030000009b2800000800000000000000,Raphnet Dreamcast Adapter,a:b2,b:b1,dpdown:b5,dpleft:b6,dpright:b7,dpup:b4,lefttrigger:a2,leftx:a0,righttrigger:a3,righty:a1,start:b3,x:b10,y:b9,platform:Windows,
+030000009b280000d000000000000000,Raphnet Dreamcast Adapter,a:b1,b:b0,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,lefttrigger:+a5,leftx:a0,lefty:a1,righttrigger:+a2,start:b3,x:b5,y:b4,platform:Windows,
030000009b2800006200000000000000,Raphnet GameCube Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
@@ -567,6 +576,9 @@
030000009b2800004300000000000000,Raphnet Saturn,a:b0,b:b1,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Windows,
030000009b2800000500000000000000,Raphnet Saturn Adapter 2.0,a:b1,b:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b4,rightshoulder:b7,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
030000009b2800000300000000000000,Raphnet SNES Adapter,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
+030000009b2800002600000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
+030000009b2800002e00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
+030000009b2800002f00000000000000,Raphnet SNES Adapter,a:b1,b:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005600000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800005700000000000000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Windows,
030000009b2800001e00000000000000,Raphnet Vectrex Adapter,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a1,lefty:a2,x:b2,y:b3,platform:Windows,
@@ -652,6 +664,7 @@
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+03000000140300000918000000000000,SNES Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
@@ -787,6 +800,7 @@
03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000007d0400000340000000000000,Xterminator Digital Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:-a4,lefttrigger:+a4,leftx:a0,lefty:a1,paddle1:b7,paddle2:b6,rightshoulder:b5,rightstick:b9,righttrigger:b2,rightx:a3,righty:a5,start:b8,x:b3,y:b4,platform:Windows,
+030000002c3600000100000000000000,Yawman Arrow,+rightx:h0.2,+righty:h0.4,-rightx:h0.8,-righty:h0.1,a:b4,b:b5,back:b6,dpdown:b15,dpleft:b14,dpright:b16,dpup:b13,leftshoulder:b10,leftstick:b0,lefttrigger:-a4,leftx:a0,lefty:a1,paddle1:b11,paddle2:b12,rightshoulder:b8,rightstick:b9,righttrigger:+a4,start:b3,x:b1,y:b2,platform:Windows,
03000000790000004f18000000000000,ZDT Android Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000120c00000500000000000000,Zeroplus Adapter,a:b2,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b0,righttrigger:b5,rightx:a3,righty:a2,start:b8,x:b3,y:b0,platform:Windows,
03000000120c0000101e000000000000,Zeroplus P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
@@ -804,7 +818,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00001151000000020000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a30c00002400000006020000,8BitDo M30,a:b2,b:b1,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,guide:b9,leftshoulder:b6,lefttrigger:b5,rightshoulder:b4,righttrigger:b7,start:b8,x:b3,y:b0,platform:Mac OS X,
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
-03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00000650000001000000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00005106000000010000,8BitDo M30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b2,leftshoulder:b6,lefttrigger:a5,rightshoulder:b7,righttrigger:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000451000000010000,8BitDo N30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
@@ -813,7 +827,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00006928000000010000,8BitDo N64,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,platform:Mac OS X,
03000000c82d00002590000000010000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00002590000001000000,8BitDo NEOGEO,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
-03000000c82d00002690000000010000,8BitDo NEOGEOa:b0,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00002690000000010000,8BitDo NEOGEO,+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b10,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
030000003512000012ab000001000000,8BitDo NES30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d000012ab000001000000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c82d00002028000000010000,8BitDo NES30,a:b0,b:b1,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -829,6 +843,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00000331000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000431000001000000,8BitDo Receiver,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00002867000000010000,8BitDo S30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b3,y:b4,platform:Mac OS X,
+03000000c82d00003028000000010000,8Bitdo SFC30 Gamepad,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000102800000900000000000000,8BitDo SFC30 Joystick,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00000351000000010000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000c82d00001290000001000000,8BitDo SN30,a:b1,b:b0,back:b10,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b11,x:b4,y:b3,platform:Mac OS X,
@@ -850,6 +865,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00003032000000010000,8BitDo Zero 2,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,rightx:a2,righty:a31,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000491900001904000001010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
+0300000008100000e501000019040000,Anbernic Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b4,y:b3,platform:Mac OS X,
03000000a30c00002700000003030000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000a30c00002800000003030000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a3,lefty:a4,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
@@ -857,6 +873,11 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b23,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000503200000110000047010000,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b3,start:b2,platform:Mac OS X,
03000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Mac OS X,
+030000008a3500000102000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
+030000008a3500000201000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+030000008a3500000202000000010000,Backbone One,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
+030000008a3500000402000000010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+030000008a3500000302000000010000,Backbone One PlayStationÆ Edition,a:b0,b:b1,back:b16,dpdown:b11,dpleft:b13,dpright:b12,dpup:b10,guide:b17,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3~,start:b15,x:b2,y:b3,platform:Mac OS X,
03000000c62400001a89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b14,leftshoulder:b6,leftstick:b15,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b16,righttrigger:a4,rightx:a2,righty:a3,start:b13,x:b3,y:b4,platform:Mac OS X,
03000000c62400001b89000000010000,BDA MOGA XP5-X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000d62000002a79000000010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -901,6 +922,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000000d0f00006600000000010000,Horipad 4 PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f00006600000000000000,Horipad FPS Plus 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000000d0f0000ee00000000010000,Horipad Mini 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
+030000000d0f0000c100000072050000,Horipad Nintendo Switch Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000242e0000ff0b000000010000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Mac OS X,
03000000790000004e95000000010000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Mac OS X,
03000000830500006020000000000000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Mac OS X,
@@ -947,7 +969,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000001008000001e5000006010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Mac OS X,
030000007e0500000920000000000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
030000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
-050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b3,y:b2,platform:Mac OS X,
+030000007e0500000920000010020000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,
+050000007e05000009200000ff070000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b10,x:b2,y:b3,platform:Mac OS X,
030000007e0500001920000001000000,NSO N64 Controller,+rightx:b8,+righty:b7,-rightx:b3,-righty:b2,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,righttrigger:b10,start:b9,platform:Mac OS X,
030000007e0500001720000001000000,NSO SNES Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b15,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000550900001472000025050000,NVIDIA Controller,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Mac OS X,
@@ -976,6 +999,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000e002000001000000,PXN P30 Pro Mobile,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000222c00000225000000010000,Qanba Dragon Arcade Joystick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000222c00000020000000010000,Qanba Drone Arcade Stick,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
+030000009b2800005600000020020000,Raphnet SNES Adapter,a:b1,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Mac OS X,
+030000009b2800008000000022020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,guide:b10,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a3,righty:a4,start:b3,x:b0,y:b5,platform:Mac OS X,
030000008916000000fd000000000000,Razer Onza TE,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000321500000204000000010000,Razer Panthera PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000321500000104000000010000,Razer Panthera PS4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -986,7 +1011,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Mac OS X,
0300000032150000030a000000000000,Razer Wildcat,a:b0,b:b1,back:b9,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,start:b8,x:b2,y:b3,platform:Mac OS X,
03000000632500008005000000010000,Redgear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Mac OS X,
-030000000d0f0000c100000072050000,Retro Bit Sega Genesis 6B Controller,a:b2,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b8,rightshoulder:b6,righttrigger:b7,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000921200004547000000020000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b2,dpdown:+a2,dpleft:-a0,dpright:+a0,dpup:-a2,lefttrigger:b14,rightshoulder:b10,righttrigger:b4,start:b12,x:b6,y:b8,platform:Mac OS X,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -1063,12 +1087,13 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000130b000009050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000013050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
030000005e040000130b000015050000,Xbox Series Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+030000005e040000130b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
+030000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b15,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000120c0000100e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000120c0000101e000000010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
# Linux
-030000005e0400008e02000020010000,8BitDo Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000c82d00000031000011010000,8BitDo Adapter,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00000951000000010000,8BitDo Dogbone,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightx:a2,righty:a3,start:b11,platform:Linux,
03000000021000000090000011010000,8BitDo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@@ -1079,7 +1104,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c82d00001151000011010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00001151000000010000,8BitDo Lite SE,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
03000000c82d00000151000000010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,start:b11,x:b3,y:b4,platform:Linux,
+03000000c82d00000650000011010000,8BitDo M30,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000c82d00005106000000010000,8BitDo M30,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b8,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b11,x:b3,y:b4,platform:Linux,
03000000c82d00002090000011010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
05000000c82d00002090000000010000,8BitDo Micro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,platform:Linux,
@@ -1151,6 +1176,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000491900000204000021000000,Amazon Fire Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b17,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b12,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000491900001904000011010000,Amazon Luna Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b9,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Linux,
05000000710100001904000000010000,Amazon Luna Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Linux,
+0300000008100000e501000001010000,Anbernic Gamepad,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a4,start:b11,x:b3,y:b4,platform:Linux,
03000000790000003018000011010000,Arcade Fightstick F300,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000a30c00002700000011010000,Astro City Mini,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000a30c00002800000011010000,Astro City Mini,a:b2,b:b1,back:b8,leftx:a0,lefty:a1,rightshoulder:b4,righttrigger:b5,start:b9,x:b3,y:b0,platform:Linux,
@@ -1158,17 +1184,21 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,platform:Linux,
03000000050b00000579000011010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b36,paddle1:b52,paddle2:b53,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000050b00000679000000010000,ASUS ROG Kunai 3,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b21,paddle1:b22,paddle2:b23,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-03000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
-03000000503200000110000011010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
-05000000503200000110000000000000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
-05000000503200000110000044010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
-05000000503200000110000046010000,Atari Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
-03000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,
-03000000503200000210000011010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
-05000000503200000210000000000000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
-05000000503200000210000045010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
-05000000503200000210000046010000,Atari Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
+03000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
+03000000503200000110000011010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
+05000000503200000110000000000000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
+05000000503200000110000044010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
+05000000503200000110000046010000,Atari VCS Classic Controller,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b4,start:b3,platform:Linux,
+03000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,
+03000000503200000210000011010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
+05000000503200000210000000000000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
+05000000503200000210000045010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
+05000000503200000210000046010000,Atari VCS Modern Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b2,platform:Linux,
05000000503200000210000047010000,Atari VCS Modern Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:-a4,rightx:a2,righty:a3,start:b8,x:b2,y:b3,platform:Linux,
+030000008a3500000201000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+030000008a3500000202000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+030000008a3500000302000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+030000008a3500000402000011010000,Backbone One,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000c62400001b89000011010000,BDA MOGA XP5X Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000d62000002a79000011010000,BDA PS4 Fightpad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
@@ -1275,7 +1305,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000fd0500000030000000010000,InterAct GoPad,a:b3,b:b4,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,x:b0,y:b1,platform:Linux,
03000000fd0500002a26000000010000,InterAct HammerHead FX,a:b3,b:b4,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b2,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b5,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b0,y:b1,platform:Linux,
0500000049190000020400001b010000,Ipega PG 9069,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+03000000632500007505000011010000,Ipega PG 9099,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
0500000049190000030400001b010000,Ipega PG9099,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
05000000491900000204000000000000,Ipega PG9118,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000300f00001001000010010000,Jess Tech Dual Analog Rumble,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
@@ -1375,7 +1405,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000004518000010010000,Nexilux GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
030000001008000001e5000010010000,NEXT SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,righttrigger:b6,start:b9,x:b3,y:b0,platform:Linux,
060000007e0500003713000000000000,Nintendo 3DS,a:b0,b:b1,back:b8,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b2,platform:Linux,
-030000009b2800008000000020020000,Nintendo Classic Controller,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,
030000007e0500003703000000016800,Nintendo GameCube Controller,a:b0,b:b2,dpdown:b6,dpleft:b4,dpright:b5,dpup:b7,lefttrigger:a4,leftx:a0,lefty:a1~,rightshoulder:b9,righttrigger:a5,rightx:a2,righty:a3~,start:b8,x:b1,y:b3,platform:Linux,
03000000790000004618000010010000,Nintendo GameCube Controller Adapter,a:b1,b:b0,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a5~,righty:a2~,start:b9,x:b2,y:b3,platform:Linux,
060000004e696e74656e646f20537700,Nintendo Switch Combined Joy-Cons,a:b0,b:b1,back:b9,dpdown:b15,dpleft:b16,dpright:b17,dpup:b14,guide:b11,leftshoulder:b5,leftstick:b12,lefttrigger:b7,leftx:a0,lefty:a1,misc1:b4,rightshoulder:b6,rightstick:b13,righttrigger:b8,rightx:a2,righty:a3,start:b10,x:b3,y:b2,platform:Linux,
@@ -1404,7 +1433,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,
19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,
-03000000c0160000dc27000001010000,OnyxSoft Dual JoyDivision,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b6,x:b2,y:b3,platform:Linux,
05000000362800000100000002010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,
05000000362800000100000003010000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Linux,
05000000362800000100000004010000,OUYA Controller,a:b0,b:b3,back:b14,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,leftshoulder:b4,leftstick:b6,lefttrigger:b12,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,start:b16,x:b1,y:b2,platform:Linux,
@@ -1494,6 +1522,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000009b2800004200000001010000,Raphnet Dual NES Adapter,a:b0,b:b1,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b3,platform:Linux,
030000009b2800003200000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
030000009b2800006000000001010000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Linux,
+030000009b2800008000000020020000,Raphnet Wii Classic Adapter,a:b1,b:b4,back:b2,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,rightshoulder:b7,start:b3,x:b0,y:b5,platform:Linux,
03000000f8270000bf0b000011010000,Razer Kishi,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000321500000204000011010000,Razer Panthera PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
@@ -1565,6 +1594,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b10,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,
+03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux,
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000004e696d6275732b0000000000,SteelSeries Nimbus Plus,a:b0,b:b1,back:b10,guide:b11,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b12,x:b2,y:b3,platform:Linux,
03000000381000003014000075010000,SteelSeries Stratus Duo,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1664,11 +1694,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+050000005e040000130b000017050000,Xbox Series X Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+050000005e040000200b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+050000005e040000220b000017050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,
03000000c0160000e105000001010000,XinMo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,platform:Linux,
+030000005e0400008e02000020010000,XInput Adapter,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000120c0000100e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000120c0000101e000011010000,Zeroplus P4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
@@ -1755,6 +1789,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
63396666386564393334393236386630,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b3,y:b2,platform:Android,
63633435623263373466343461646430,8BitDo Zero 2,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftx:a0,lefty:a1,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android,
32333634613735616163326165323731,Amazon Luna Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,x:b2,y:b3,platform:Android,
+4c696e757820342e31392e3137322077,Anbernic Gamepad,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a4,start:b6,x:b2,y:b3,platform:Android,
417374726f2063697479206d696e6920,Astro City Mini,a:b23,b:b22,back:b29,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,rightshoulder:b25,righttrigger:b26,start:b30,x:b24,y:b21,platform:Android,
35643263313264386134376362363435,Atari VCS Classic Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,start:b6,platform:Android,
32353831643566306563643065356239,Atari VCS Modern Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
diff --git a/core/io/dir_access.cpp b/core/io/dir_access.cpp
index 680a653dfc..e99885befa 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -84,7 +84,7 @@ static Error _erase_recursive(DirAccess *da) {
String n = da->get_next();
while (!n.is_empty()) {
if (n != "." && n != "..") {
- if (da->current_is_dir()) {
+ if (da->current_is_dir() && !da->is_link(n)) {
dirs.push_back(n);
} else {
files.push_back(n);
diff --git a/core/io/image.cpp b/core/io/image.cpp
index c454f06d67..6096211cff 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -4114,7 +4114,7 @@ Dictionary Image::compute_image_metrics(const Ref<Image> p_compared_image, bool
continue;
}
- image_metric_max = MAX(image_metric_max, i);
+ image_metric_max = i;
double x = i * hist[i];
diff --git a/core/io/json.cpp b/core/io/json.cpp
index 496400a5ea..5a1fe45f70 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -86,7 +86,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
case Variant::PACKED_STRING_ARRAY:
case Variant::ARRAY: {
Array a = p_var;
- if (a.size() == 0) {
+ if (a.is_empty()) {
return "[]";
}
String s = "[";
@@ -95,12 +95,15 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON.");
p_markers.insert(a.id());
- for (int i = 0; i < a.size(); i++) {
- if (i > 0) {
+ bool first = true;
+ for (const Variant &var : a) {
+ if (first) {
+ first = false;
+ } else {
s += ",";
s += end_statement;
}
- s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(a[i], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
+ s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(var, p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
}
s += end_statement + _make_indent(p_indent, p_cur_indent) + "]";
p_markers.erase(a.id());
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index bc2493d360..18dbac991c 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -30,7 +30,10 @@
#include "marshalls.h"
+#include "core/core_string_names.h"
+#include "core/io/resource_loader.h"
#include "core/object/ref_counted.h"
+#include "core/object/script_language.h"
#include "core/os/keyboard.h"
#include "core/string/print_string.h"
@@ -55,9 +58,22 @@ ObjectID EncodedObjectAsID::get_object_id() const {
#define ERR_FAIL_ADD_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(b)) < 0 || ((int32_t)(a)) < 0 || ((int32_t)(a)) > INT_MAX - ((int32_t)(b)), err)
#define ERR_FAIL_MUL_OF(a, b, err) ERR_FAIL_COND_V(((int32_t)(a)) < 0 || ((int32_t)(b)) <= 0 || ((int32_t)(a)) > INT_MAX / ((int32_t)(b)), err)
-#define ENCODE_MASK 0xFF
-#define ENCODE_FLAG_64 1 << 16
-#define ENCODE_FLAG_OBJECT_AS_ID 1 << 16
+// Byte 0: `Variant::Type`, byte 1: unused, bytes 2 and 3: additional data.
+#define HEADER_TYPE_MASK 0xFF
+
+// For `Variant::INT`, `Variant::FLOAT` and other math types.
+#define HEADER_DATA_FLAG_64 (1 << 16)
+
+// For `Variant::OBJECT`.
+#define HEADER_DATA_FLAG_OBJECT_AS_ID (1 << 16)
+
+// For `Variant::ARRAY`.
+// Occupies bits 16 and 17.
+#define HEADER_DATA_FIELD_TYPED_ARRAY_MASK (0b11 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_NONE (0b00 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN (0b01 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME (0b10 << 16)
+#define HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT (0b11 << 16)
static Error _decode_string(const uint8_t *&buf, int &len, int *r_len, String &r_string) {
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
@@ -101,9 +117,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
- uint32_t type = decode_uint32(buf);
+ uint32_t header = decode_uint32(buf);
- ERR_FAIL_COND_V((type & ENCODE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V((header & HEADER_TYPE_MASK) >= Variant::VARIANT_MAX, ERR_INVALID_DATA);
buf += 4;
len -= 4;
@@ -114,7 +130,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
// Note: We cannot use sizeof(real_t) for decoding, in case a different size is encoded.
// Decoding math types always checks for the encoded size, while encoding always uses compilation setting.
// This does lead to some code duplication for decoding, but compatibility is the priority.
- switch (type & ENCODE_MASK) {
+ switch (header & HEADER_TYPE_MASK) {
case Variant::NIL: {
r_variant = Variant();
} break;
@@ -127,7 +143,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
} break;
case Variant::INT: {
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
int64_t val = decode_uint64(buf);
r_variant = val;
@@ -146,7 +162,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::FLOAT: {
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double), ERR_INVALID_DATA);
double val = decode_double(buf);
r_variant = val;
@@ -176,7 +192,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
// math types
case Variant::VECTOR2: {
Vector2 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 2, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -210,7 +226,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::RECT2: {
Rect2 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.position.x = decode_double(&buf[0]);
val.position.y = decode_double(&buf[sizeof(double)]);
@@ -250,7 +266,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::VECTOR3: {
Vector3 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 3, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -287,7 +303,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::VECTOR4: {
Vector4 val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -327,7 +343,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::TRANSFORM2D: {
Transform2D val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
@@ -355,7 +371,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PLANE: {
Plane val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.normal.x = decode_double(&buf[0]);
val.normal.y = decode_double(&buf[sizeof(double)]);
@@ -381,7 +397,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::QUATERNION: {
Quaternion val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 4, ERR_INVALID_DATA);
val.x = decode_double(&buf[0]);
val.y = decode_double(&buf[sizeof(double)]);
@@ -407,7 +423,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::AABB: {
AABB val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 6, ERR_INVALID_DATA);
val.position.x = decode_double(&buf[0]);
val.position.y = decode_double(&buf[sizeof(double)]);
@@ -437,7 +453,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::BASIS: {
Basis val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 9, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -465,7 +481,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::TRANSFORM3D: {
Transform3D val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 12, ERR_INVALID_DATA);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
@@ -499,7 +515,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::PROJECTION: {
Projection val;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_COND_V((size_t)len < sizeof(double) * 16, ERR_INVALID_DATA);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
@@ -560,12 +576,12 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
uint32_t namecount = strlen &= 0x7FFFFFFF;
uint32_t subnamecount = decode_uint32(buf + 4);
- uint32_t flags = decode_uint32(buf + 8);
+ uint32_t np_flags = decode_uint32(buf + 8);
len -= 12;
buf += 12;
- if (flags & 2) { // Obsolete format with property separate from subpath
+ if (np_flags & 2) { // Obsolete format with property separate from subpath.
subnamecount++;
}
@@ -589,7 +605,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
}
- r_variant = NodePath(names, subnames, flags & 1);
+ r_variant = NodePath(names, subnames, np_flags & 1);
} else {
//old format, just a string
@@ -608,8 +624,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = RID::from_uint64(id);
} break;
case Variant::OBJECT: {
- if (type & ENCODE_FLAG_OBJECT_AS_ID) {
- //this _is_ allowed
+ if (header & HEADER_DATA_FLAG_OBJECT_AS_ID) {
+ // This _is_ allowed.
ERR_FAIL_COND_V(len < 8, ERR_INVALID_DATA);
ObjectID val = ObjectID(decode_uint64(buf));
if (r_len) {
@@ -625,7 +641,6 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
r_variant = obj_as_id;
}
-
} else {
ERR_FAIL_COND_V(!p_allow_objects, ERR_UNAUTHORIZED);
@@ -641,10 +656,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA);
Object *obj = ClassDB::instantiate(str);
-
ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE);
- ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+ // Avoid premature free `RefCounted`. This must be done before properties are initialized,
+ // since script functions (setters, implicit initializer) may be called. See GH-68666.
+ Variant variant;
+ if (Object::cast_to<RefCounted>(obj)) {
+ Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
+ variant = ref;
+ } else {
+ variant = obj;
+ }
+
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
int32_t count = decode_uint32(buf);
buf += 4;
len -= 4;
@@ -672,15 +696,19 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
(*r_len) += used;
}
- obj->set(str, value);
+ if (str == "script" && value.get_type() != Variant::NIL) {
+ ERR_FAIL_COND_V_MSG(value.get_type() != Variant::STRING, ERR_INVALID_DATA, "Invalid value for \"script\" property, expected script path as String.");
+ String path = value;
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ Ref<Script> script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+ obj->set_script(script);
+ } else {
+ obj->set(str, value);
+ }
}
- if (Object::cast_to<RefCounted>(obj)) {
- Ref<RefCounted> ref = Ref<RefCounted>(Object::cast_to<RefCounted>(obj));
- r_variant = ref;
- } else {
- r_variant = obj;
- }
+ r_variant = variant;
}
}
@@ -747,7 +775,66 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
} break;
case Variant::ARRAY: {
+ Variant::Type builtin_type = Variant::VARIANT_MAX;
+ StringName class_name;
+ Ref<Script> script;
+
+ switch (header & HEADER_DATA_FIELD_TYPED_ARRAY_MASK) {
+ case HEADER_DATA_FIELD_TYPED_ARRAY_NONE:
+ break; // Untyped array.
+ case HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN: {
+ ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
+ int32_t bt = decode_uint32(buf);
+ buf += 4;
+ len -= 4;
+ if (r_len) {
+ (*r_len) += 4;
+ }
+
+ ERR_FAIL_INDEX_V(bt, Variant::VARIANT_MAX, ERR_INVALID_DATA);
+ builtin_type = (Variant::Type)bt;
+ if (!p_allow_objects && builtin_type == Variant::OBJECT) {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
+ } break;
+ case HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME: {
+ String str;
+ Error err = _decode_string(buf, len, r_len, str);
+ if (err) {
+ return err;
+ }
+
+ builtin_type = Variant::OBJECT;
+ if (p_allow_objects) {
+ class_name = str;
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
+ } break;
+ case HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT: {
+ String path;
+ Error err = _decode_string(buf, len, r_len, path);
+ if (err) {
+ return err;
+ }
+
+ builtin_type = Variant::OBJECT;
+ if (p_allow_objects) {
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://") || !ResourceLoader::exists(path, "Script"), ERR_INVALID_DATA, "Invalid script path: '" + path + "'.");
+ script = ResourceLoader::load(path, "Script");
+ ERR_FAIL_COND_V_MSG(script.is_null(), ERR_INVALID_DATA, "Can't load script at path: '" + path + "'.");
+ class_name = script->get_instance_base_type();
+ } else {
+ class_name = EncodedObjectAsID::get_class_static();
+ }
+ } break;
+ default:
+ ERR_FAIL_V(ERR_INVALID_DATA); // Future proofing.
+ }
+
ERR_FAIL_COND_V(len < 4, ERR_INVALID_DATA);
+
int32_t count = decode_uint32(buf);
// bool shared = count&0x80000000;
count &= 0x7FFFFFFF;
@@ -760,6 +847,9 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
}
Array varr;
+ if (builtin_type != Variant::VARIANT_MAX) {
+ varr.set_typed(builtin_type, class_name, script);
+ }
for (int i = 0; i < count; i++) {
int used = 0;
@@ -936,7 +1026,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<Vector2> varray;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_MUL_OF(count, sizeof(double) * 2, ERR_INVALID_DATA);
ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 2 > (size_t)len, ERR_INVALID_DATA);
@@ -996,7 +1086,7 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
Vector<Vector3> varray;
- if (type & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
ERR_FAIL_MUL_OF(count, sizeof(double) * 3, ERR_INVALID_DATA);
ERR_FAIL_COND_V(count < 0 || count * sizeof(double) * 3 > (size_t)len, ERR_INVALID_DATA);
@@ -1122,20 +1212,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len = 0;
- uint32_t flags = 0;
+ uint32_t header = p_variant.get_type();
switch (p_variant.get_type()) {
case Variant::INT: {
int64_t val = p_variant;
if (val > (int64_t)INT_MAX || val < (int64_t)INT_MIN) {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
}
} break;
case Variant::FLOAT: {
double d = p_variant;
float f = d;
if (double(f) != d) {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
}
} break;
case Variant::OBJECT: {
@@ -1151,7 +1241,20 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
if (!p_full_objects) {
- flags |= ENCODE_FLAG_OBJECT_AS_ID;
+ header |= HEADER_DATA_FLAG_OBJECT_AS_ID;
+ }
+ } break;
+ case Variant::ARRAY: {
+ Array array = p_variant;
+ if (array.is_typed()) {
+ Ref<Script> script = array.get_typed_script();
+ if (script.is_valid()) {
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_SCRIPT;
+ } else if (array.get_typed_class_name() != StringName()) {
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_CLASS_NAME;
+ } else {
+ header |= HEADER_DATA_FIELD_TYPED_ARRAY_BUILTIN;
+ }
}
} break;
#ifdef REAL_T_IS_DOUBLE
@@ -1168,7 +1271,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
case Variant::BASIS:
case Variant::RECT2:
case Variant::AABB: {
- flags |= ENCODE_FLAG_64;
+ header |= HEADER_DATA_FLAG_64;
} break;
#endif // REAL_T_IS_DOUBLE
default: {
@@ -1176,7 +1279,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
if (buf) {
- encode_uint32(p_variant.get_type() | flags, buf);
+ encode_uint32(header, buf);
buf += 4;
}
r_len += 4;
@@ -1194,7 +1297,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
case Variant::INT: {
- if (flags & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
//64 bits
if (buf) {
encode_uint64(p_variant.operator int64_t(), buf);
@@ -1210,7 +1313,7 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
}
} break;
case Variant::FLOAT: {
- if (flags & ENCODE_FLAG_64) {
+ if (header & HEADER_DATA_FLAG_64) {
if (buf) {
encode_double(p_variant.operator double(), buf);
}
@@ -1523,8 +1626,21 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
_encode_string(E.name, buf, r_len);
+ Variant value;
+
+ if (E.name == CoreStringNames::get_singleton()->_script) {
+ Ref<Script> script = obj->get_script();
+ if (script.is_valid()) {
+ String path = script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script.");
+ value = path;
+ }
+ } else {
+ value = obj->get(E.name);
+ }
+
int len;
- Error err = encode_variant(obj->get(E.name), buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(value, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
r_len += len;
@@ -1594,24 +1710,41 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
} break;
case Variant::ARRAY: {
- Array v = p_variant;
+ Array array = p_variant;
+
+ if (array.is_typed()) {
+ Variant variant = array.get_typed_script();
+ Ref<Script> script = variant;
+ if (script.is_valid()) {
+ String path = script->get_path();
+ ERR_FAIL_COND_V_MSG(path.is_empty() || !path.begins_with("res://"), ERR_UNAVAILABLE, "Failed to encode a path to a custom script for an array type.");
+ _encode_string(path, buf, r_len);
+ } else if (array.get_typed_class_name() != StringName()) {
+ _encode_string(array.get_typed_class_name(), buf, r_len);
+ } else {
+ if (buf) {
+ encode_uint32(array.get_typed_builtin(), buf);
+ buf += 4;
+ }
+ r_len += 4;
+ }
+ }
if (buf) {
- encode_uint32(uint32_t(v.size()), buf);
+ encode_uint32(uint32_t(array.size()), buf);
buf += 4;
}
-
r_len += 4;
- for (int i = 0; i < v.size(); i++) {
+ for (const Variant &var : array) {
int len;
- Error err = encode_variant(v.get(i), buf, len, p_full_objects, p_depth + 1);
+ Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
ERR_FAIL_COND_V(err, err);
ERR_FAIL_COND_V(len % 4, ERR_BUG);
- r_len += len;
if (buf) {
buf += len;
}
+ r_len += len;
}
} break;
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index dc974a545a..74f18ceee1 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -424,8 +424,7 @@ RID Resource::get_rid() const {
}
}
if (_get_extension() && _get_extension()->get_rid) {
- RID ret;
- ret.from_uint64(_get_extension()->get_rid(_get_extension_instance()));
+ RID ret = RID::from_uint64(_get_extension()->get_rid(_get_extension_instance()));
if (ret.is_valid()) {
return ret;
}
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 17cffb878e..d0a8200546 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1844,8 +1844,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
f->store_32(VARIANT_ARRAY);
Array a = p_property;
f->store_32(uint32_t(a.size()));
- for (int i = 0; i < a.size(); i++) {
- write_variant(f, a[i], resource_map, external_resources, string_map);
+ for (const Variant &var : a) {
+ write_variant(f, var, resource_map, external_resources, string_map);
}
} break;
@@ -2016,9 +2016,8 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
case Variant::ARRAY: {
Array varray = p_variant;
- int len = varray.size();
- for (int i = 0; i < len; i++) {
- const Variant &v = varray.get(i);
+ _find_resources(varray.get_typed_script());
+ for (const Variant &v : varray) {
_find_resources(v);
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index ff563a35b2..eea6357084 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -402,7 +402,7 @@ static String _validate_local_path(const String &p_path) {
if (uid != ResourceUID::INVALID_ID) {
return ResourceUID::get_singleton()->get_id_path(uid);
} else if (p_path.is_relative_path()) {
- return "res://" + p_path;
+ return ("res://" + p_path).simplify_path();
} else {
return ProjectSettings::get_singleton()->localize_path(p_path);
}
@@ -1035,8 +1035,9 @@ void ResourceLoader::load_translation_remaps() {
Array langs = remaps[E];
Vector<String> lang_remaps;
lang_remaps.resize(langs.size());
- for (int i = 0; i < langs.size(); i++) {
- lang_remaps.write[i] = langs[i];
+ String *lang_remaps_ptrw = lang_remaps.ptrw();
+ for (const Variant &lang : langs) {
+ *lang_remaps_ptrw++ = lang;
}
translation_remaps[String(E)] = lang_remaps;
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index 2b9487b9e1..90a8f49a75 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -51,6 +51,7 @@ Error StreamPeerTCP::poll() {
status = STATUS_ERROR;
return err;
}
+ return OK;
} else if (status != STATUS_CONNECTING) {
return OK;
}
diff --git a/core/math/a_star.compat.inc b/core/math/a_star.compat.inc
new file mode 100644
index 0000000000..664d7ffd5e
--- /dev/null
+++ b/core/math/a_star.compat.inc
@@ -0,0 +1,59 @@
+/**************************************************************************/
+/* a_star.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+Vector<int64_t> AStar3D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector3> AStar3D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStar3D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::_get_point_path_bind_compat_88047);
+}
+
+Vector<int64_t> AStar2D::_get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector2> AStar2D::_get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStar2D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::_get_point_path_bind_compat_88047);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index fb54058bd9..4497604947 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "a_star.h"
+#include "a_star.compat.inc"
#include "core/math/geometry_3d.h"
#include "core/object/script_language.h"
@@ -319,6 +320,7 @@ Vector3 AStar3D::get_closest_position_in_segment(const Vector3 &p_point) const {
}
bool AStar3D::_solve(Point *begin_point, Point *end_point) {
+ last_closest_point = nullptr;
pass++;
if (!end_point->enabled) {
@@ -332,11 +334,18 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
+ begin_point->abs_g_score = 0;
+ begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
+ last_closest_point = p;
+ }
+
if (p == end_point) {
found_route = true;
break;
@@ -368,6 +377,8 @@ bool AStar3D::_solve(Point *begin_point, Point *end_point) {
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@@ -414,7 +425,7 @@ real_t AStar3D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos);
}
-Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@@ -434,7 +445,12 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector3>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<Vector3>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -463,7 +479,7 @@ Vector<Vector3> AStar3D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path;
}
-Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@@ -483,7 +499,12 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<int64_t>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<int64_t>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -555,8 +576,8 @@ void AStar3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar3D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar3D::get_closest_position_in_segment);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar3D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar3D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar3D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
@@ -688,7 +709,7 @@ real_t AStar2D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return from_point->pos.distance_to(to_point->pos);
}
-Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
@@ -707,7 +728,12 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector2>();
+ if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
+ return Vector<Vector2>();
+ }
+
+ // Use closest point instead.
+ end_point = astar.last_closest_point;
}
AStar3D::Point *p = end_point;
@@ -736,7 +762,7 @@ Vector<Vector2> AStar2D::get_point_path(int64_t p_from_id, int64_t p_to_id) {
return path;
}
-Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
+Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path) {
AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
@@ -756,7 +782,12 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<int64_t>();
+ if (!p_allow_partial_path || astar.last_closest_point == nullptr) {
+ return Vector<int64_t>();
+ }
+
+ // Use closest point instead.
+ end_point = astar.last_closest_point;
}
AStar3D::Point *p = end_point;
@@ -786,6 +817,7 @@ Vector<int64_t> AStar2D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
}
bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
+ astar.last_closest_point = nullptr;
astar.pass++;
if (!end_point->enabled) {
@@ -799,11 +831,18 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
begin_point->g_score = 0;
begin_point->f_score = _estimate_cost(begin_point->id, end_point->id);
+ begin_point->abs_g_score = 0;
+ begin_point->abs_f_score = _estimate_cost(begin_point->id, end_point->id);
open_list.push_back(begin_point);
while (!open_list.is_empty()) {
AStar3D::Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (astar.last_closest_point == nullptr || astar.last_closest_point->abs_f_score > p->abs_f_score || (astar.last_closest_point->abs_f_score >= p->abs_f_score && astar.last_closest_point->abs_g_score > p->abs_g_score)) {
+ astar.last_closest_point = p;
+ }
+
if (p == end_point) {
found_route = true;
break;
@@ -835,6 +874,8 @@ bool AStar2D::_solve(AStar3D::Point *begin_point, AStar3D::Point *end_point) {
e->prev_point = p;
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
@@ -874,8 +915,8 @@ void AStar2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point", "to_position", "include_disabled"), &AStar2D::get_closest_point, DEFVAL(false));
ClassDB::bind_method(D_METHOD("get_closest_position_in_segment", "to_position"), &AStar2D::get_closest_position_in_segment);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStar2D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStar2D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStar2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
diff --git a/core/math/a_star.h b/core/math/a_star.h
index 0758500c8a..8e054c4789 100644
--- a/core/math/a_star.h
+++ b/core/math/a_star.h
@@ -60,6 +60,10 @@ class AStar3D : public RefCounted {
real_t f_score = 0;
uint64_t open_pass = 0;
uint64_t closed_pass = 0;
+
+ // Used for getting closest_point_of_last_pathing_call.
+ real_t abs_g_score = 0;
+ real_t abs_f_score = 0;
};
struct SortPoints {
@@ -109,6 +113,7 @@ class AStar3D : public RefCounted {
OAHashMap<int64_t, Point *> points;
HashSet<Segment, Segment> segments;
+ Point *last_closest_point = nullptr;
bool _solve(Point *begin_point, Point *end_point);
@@ -121,6 +126,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+#ifndef DISABLE_DEPRECATED
+ Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector3> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ static void _bind_compatibility_methods();
+#endif
+
public:
int64_t get_available_point_id() const;
@@ -149,8 +160,8 @@ public:
int64_t get_closest_point(const Vector3 &p_point, bool p_include_disabled = false) const;
Vector3 get_closest_position_in_segment(const Vector3 &p_point) const;
- Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id);
- Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector3> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
+ Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar3D() {}
~AStar3D();
@@ -171,6 +182,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, int64_t, int64_t)
GDVIRTUAL2RC(real_t, _compute_cost, int64_t, int64_t)
+#ifndef DISABLE_DEPRECATED
+ Vector<int64_t> _get_id_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector2> _get_point_path_bind_compat_88047(int64_t p_from_id, int64_t p_to_id);
+ static void _bind_compatibility_methods();
+#endif
+
public:
int64_t get_available_point_id() const;
@@ -199,8 +216,8 @@ public:
int64_t get_closest_point(const Vector2 &p_point, bool p_include_disabled = false) const;
Vector2 get_closest_position_in_segment(const Vector2 &p_point) const;
- Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id);
- Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id);
+ Vector<Vector2> get_point_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
+ Vector<int64_t> get_id_path(int64_t p_from_id, int64_t p_to_id, bool p_allow_partial_path = false);
AStar2D() {}
~AStar2D() {}
diff --git a/core/math/a_star_grid_2d.compat.inc b/core/math/a_star_grid_2d.compat.inc
new file mode 100644
index 0000000000..e7124c2477
--- /dev/null
+++ b/core/math/a_star_grid_2d.compat.inc
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/* a_star_grid_2d.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+#include "core/variant/typed_array.h"
+
+TypedArray<Vector2i> AStarGrid2D::_get_id_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+ return get_id_path(p_from_id, p_to_id, false);
+}
+
+Vector<Vector2> AStarGrid2D::_get_point_path_bind_compat_88047(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+ return get_point_path(p_from_id, p_to_id, false);
+}
+
+void AStarGrid2D::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::_get_id_path_bind_compat_88047);
+ ClassDB::bind_compatibility_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::_get_point_path_bind_compat_88047);
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index d17f465ab8..f272407869 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "a_star_grid_2d.h"
+#include "a_star_grid_2d.compat.inc"
#include "core/variant/typed_array.h"
@@ -446,6 +447,7 @@ void AStarGrid2D::_get_nbors(Point *p_point, LocalVector<Point *> &r_nbors) {
}
bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
+ last_closest_point = nullptr;
pass++;
if (p_end_point->solid) {
@@ -459,12 +461,19 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
p_begin_point->g_score = 0;
p_begin_point->f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
+ p_begin_point->abs_g_score = 0;
+ p_begin_point->abs_f_score = _estimate_cost(p_begin_point->id, p_end_point->id);
open_list.push_back(p_begin_point);
end = p_end_point;
while (!open_list.is_empty()) {
Point *p = open_list[0]; // The currently processed point.
+ // Find point closer to end_point, or same distance to end_point but closer to begin_point.
+ if (last_closest_point == nullptr || last_closest_point->abs_f_score > p->abs_f_score || (last_closest_point->abs_f_score >= p->abs_f_score && last_closest_point->abs_g_score > p->abs_g_score)) {
+ last_closest_point = p;
+ }
+
if (p == p_end_point) {
found_route = true;
break;
@@ -508,6 +517,9 @@ bool AStarGrid2D::_solve(Point *p_begin_point, Point *p_end_point) {
e->g_score = tentative_g_score;
e->f_score = e->g_score + _estimate_cost(e->id, p_end_point->id);
+ e->abs_g_score = tentative_g_score;
+ e->abs_f_score = e->f_score - e->g_score;
+
if (new_point) { // The position of the new points is already known.
sorter.push_heap(0, open_list.size() - 1, 0, e, open_list.ptr());
} else {
@@ -546,7 +558,7 @@ Vector2 AStarGrid2D::get_point_position(const Vector2i &p_id) const {
return _get_point_unchecked(p_id)->pos;
}
-Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, Vector<Vector2>(), "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), Vector<Vector2>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
@@ -565,7 +577,12 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return Vector<Vector2>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return Vector<Vector2>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -594,7 +611,7 @@ Vector<Vector2> AStarGrid2D::get_point_path(const Vector2i &p_from_id, const Vec
return path;
}
-TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id) {
+TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const Vector2i &p_to_id, bool p_allow_partial_path) {
ERR_FAIL_COND_V_MSG(dirty, TypedArray<Vector2i>(), "Grid is not initialized. Call the update method.");
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_from_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_from_id, region));
ERR_FAIL_COND_V_MSG(!is_in_boundsv(p_to_id), TypedArray<Vector2i>(), vformat("Can't get id path. Point %s out of bounds %s.", p_to_id, region));
@@ -613,7 +630,12 @@ TypedArray<Vector2i> AStarGrid2D::get_id_path(const Vector2i &p_from_id, const V
bool found_route = _solve(begin_point, end_point);
if (!found_route) {
- return TypedArray<Vector2i>();
+ if (!p_allow_partial_path || last_closest_point == nullptr) {
+ return TypedArray<Vector2i>();
+ }
+
+ // Use closest point instead.
+ end_point = last_closest_point;
}
Point *p = end_point;
@@ -672,8 +694,8 @@ void AStarGrid2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &AStarGrid2D::clear);
ClassDB::bind_method(D_METHOD("get_point_position", "id"), &AStarGrid2D::get_point_position);
- ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id"), &AStarGrid2D::get_point_path);
- ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id"), &AStarGrid2D::get_id_path);
+ ClassDB::bind_method(D_METHOD("get_point_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_point_path, DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("get_id_path", "from_id", "to_id", "allow_partial_path"), &AStarGrid2D::get_id_path, DEFVAL(false));
GDVIRTUAL_BIND(_estimate_cost, "from_id", "to_id")
GDVIRTUAL_BIND(_compute_cost, "from_id", "to_id")
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index 69cb77dd3e..1a9f6dcc11 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -89,6 +89,10 @@ private:
uint64_t open_pass = 0;
uint64_t closed_pass = 0;
+ // Used for getting last_closest_point.
+ real_t abs_g_score = 0;
+ real_t abs_f_score = 0;
+
Point() {}
Point(const Vector2i &p_id, const Vector2 &p_pos) :
@@ -109,6 +113,7 @@ private:
LocalVector<LocalVector<Point>> points;
Point *end = nullptr;
+ Point *last_closest_point = nullptr;
uint64_t pass = 1;
@@ -152,6 +157,12 @@ protected:
GDVIRTUAL2RC(real_t, _estimate_cost, Vector2i, Vector2i)
GDVIRTUAL2RC(real_t, _compute_cost, Vector2i, Vector2i)
+#ifndef DISABLE_DEPRECATED
+ TypedArray<Vector2i> _get_id_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
+ Vector<Vector2> _get_point_path_bind_compat_88047(const Vector2i &p_from, const Vector2i &p_to);
+ static void _bind_compatibility_methods();
+#endif
+
public:
void set_region(const Rect2i &p_region);
Rect2i get_region() const;
@@ -198,8 +209,8 @@ public:
void clear();
Vector2 get_point_position(const Vector2i &p_id) const;
- Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to);
- TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to);
+ Vector<Vector2> get_point_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
+ TypedArray<Vector2i> get_id_path(const Vector2i &p_from, const Vector2i &p_to, bool p_allow_partial_path = false);
};
VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode);
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 3ebd13b9fe..84ac878172 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -278,7 +278,7 @@ Basis Basis::scaled_orthogonal(const Vector3 &p_scale) const {
return m;
}
-float Basis::get_uniform_scale() const {
+real_t Basis::get_uniform_scale() const {
return (rows[0].length() + rows[1].length() + rows[2].length()) / 3.0f;
}
diff --git a/core/math/basis.h b/core/math/basis.h
index 1fc08e95e1..79f3bda8f8 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -99,7 +99,7 @@ struct _NO_DISCARD_ Basis {
void scale_orthogonal(const Vector3 &p_scale);
Basis scaled_orthogonal(const Vector3 &p_scale) const;
- float get_uniform_scale() const;
+ real_t get_uniform_scale() const;
Vector3 get_scale() const;
Vector3 get_scale_abs() const;
diff --git a/core/math/bvh_abb.h b/core/math/bvh_abb.h
index cec3dc90db..3d32c250c9 100644
--- a/core/math/bvh_abb.h
+++ b/core/math/bvh_abb.h
@@ -258,7 +258,7 @@ struct BVH_ABB {
}
// Actually surface area metric.
- float get_area() const {
+ real_t get_area() const {
POINT d = calculate_size();
return 2.0f * (d.x * d.y + d.y * d.z + d.z * d.x);
}
diff --git a/core/math/bvh_debug.inc b/core/math/bvh_debug.inc
index 1964f2fa83..a1fdf78c98 100644
--- a/core/math/bvh_debug.inc
+++ b/core/math/bvh_debug.inc
@@ -10,7 +10,7 @@ String _debug_aabb_to_string(const BVHABB_CLASS &aabb) const {
POINT size = aabb.calculate_size();
String sz;
- float vol = 0.0;
+ real_t vol = 0.0;
for (int i = 0; i < POINT::AXIS_COUNT; ++i) {
sz += "(";
diff --git a/core/math/bvh_split.inc b/core/math/bvh_split.inc
index 2c85a63575..6da89bd027 100644
--- a/core/math/bvh_split.inc
+++ b/core/math/bvh_split.inc
@@ -150,7 +150,7 @@ void _split_leaf_sort_groups(int &num_a, int &num_b, uint16_t *group_a, uint16_t
BVHABB_CLASS rest_aabb;
- float best_size = FLT_MAX;
+ real_t best_size = FLT_MAX;
int best_candidate = -1;
// find most likely from a to move into b
@@ -171,7 +171,7 @@ void _split_leaf_sort_groups(int &num_a, int &num_b, uint16_t *group_a, uint16_t
groupb_aabb_new.merge(temp_bounds[group_a[check]]);
// now compare the sizes
- float size = groupb_aabb_new.get_area() + rest_aabb.get_area();
+ real_t size = groupb_aabb_new.get_area() + rest_aabb.get_area();
if (size < best_size) {
best_size = size;
best_candidate = check;
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 478fde3a64..80662c1b07 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -77,15 +77,17 @@ subject to the following restrictions:
#ifdef DEBUG_ENABLED
#define CHULL_ASSERT(m_cond) \
- do { \
+ if constexpr (true) { \
if (unlikely(!(m_cond))) { \
ERR_PRINT("Assertion \"" _STR(m_cond) "\" failed."); \
} \
- } while (0)
+ } else \
+ ((void)0)
#else
#define CHULL_ASSERT(m_cond) \
- do { \
- } while (0)
+ if constexpr (true) { \
+ } else \
+ ((void)0)
#endif
#if defined(DEBUG_CONVEX_HULL) || defined(SHOW_ITERATIONS)
diff --git a/core/math/delaunay_3d.h b/core/math/delaunay_3d.h
index 846acdecc3..45571fb570 100644
--- a/core/math/delaunay_3d.h
+++ b/core/math/delaunay_3d.h
@@ -233,7 +233,7 @@ public:
points[i] = (points[i] - rect.position) / rect.size;
}
- float delta_max = Math::sqrt(2.0) * 20.0;
+ const real_t delta_max = Math::sqrt(2.0) * 20.0;
Vector3 center = Vector3(0.5, 0.5, 0.5);
// any simplex that contains everything is good
@@ -332,8 +332,7 @@ public:
center.y = double(new_simplex->circum_center_y);
center.z = double(new_simplex->circum_center_z);
- float radius2 = Math::sqrt(double(new_simplex->circum_r2));
- radius2 += 0.0001; //
+ const real_t radius2 = Math::sqrt(double(new_simplex->circum_r2)) + 0.0001;
Vector3 extents = Vector3(radius2, radius2, radius2);
Vector3i from = Vector3i((center - extents) * ACCEL_GRID_SIZE);
Vector3i to = Vector3i((center + extents) * ACCEL_GRID_SIZE);
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index d9788d036f..ff39d82595 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -594,7 +594,7 @@ public:
max = x2; \
}
- _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, float d, Vector3 maxbox) {
+ _FORCE_INLINE_ static bool planeBoxOverlap(Vector3 normal, real_t d, Vector3 maxbox) {
int q;
Vector3 vmin, vmax;
for (q = 0; q <= 2; q++) {
@@ -678,8 +678,7 @@ public:
return false; \
}
- /*======================== Z-tests ========================*/
-
+/*======================== Z-tests ========================*/
#define AXISTEST_Z12(a, b, fa, fb) \
p1 = a * v1.x - b * v1.y; \
p2 = a * v2.x - b * v2.y; \
@@ -718,21 +717,19 @@ public:
/* 2) normal of the triangle */
/* 3) crossproduct(edge from tri, {x,y,z}-directin) */
/* this gives 3x3=9 more tests */
- Vector3 v0, v1, v2;
- float min, max, d, p0, p1, p2, rad, fex, fey, fez;
- Vector3 normal, e0, e1, e2;
+ real_t min, max, p0, p1, p2, rad, fex, fey, fez;
/* This is the fastest branch on Sun */
/* move everything so that the boxcenter is in (0,0,0) */
- v0 = triverts[0] - boxcenter;
- v1 = triverts[1] - boxcenter;
- v2 = triverts[2] - boxcenter;
+ const Vector3 v0 = triverts[0] - boxcenter;
+ const Vector3 v1 = triverts[1] - boxcenter;
+ const Vector3 v2 = triverts[2] - boxcenter;
/* compute triangle edges */
- e0 = v1 - v0; /* tri edge 0 */
- e1 = v2 - v1; /* tri edge 1 */
- e2 = v0 - v2; /* tri edge 2 */
+ const Vector3 e0 = v1 - v0; /* tri edge 0 */
+ const Vector3 e1 = v2 - v1; /* tri edge 1 */
+ const Vector3 e2 = v0 - v2; /* tri edge 2 */
/* Bullet 3: */
/* test the 9 tests first (this was faster) */
@@ -784,8 +781,8 @@ public:
/* Bullet 2: */
/* test if the box intersects the plane of the triangle */
/* compute plane equation of triangle: normal*x+d=0 */
- normal = e0.cross(e1);
- d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
+ const Vector3 normal = e0.cross(e1);
+ const real_t d = -normal.dot(v0); /* plane eq: normal.x+d=0 */
return planeBoxOverlap(normal, d, boxhalfsize); /* if true, box and triangle overlaps */
}
@@ -793,51 +790,51 @@ public:
static Vector<int8_t> generate_sdf8(const Vector<uint32_t> &p_positive, const Vector<uint32_t> &p_negative);
static Vector3 triangle_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_pos) {
- Vector3 v0 = p_b - p_a;
- Vector3 v1 = p_c - p_a;
- Vector3 v2 = p_pos - p_a;
-
- float d00 = v0.dot(v0);
- float d01 = v0.dot(v1);
- float d11 = v1.dot(v1);
- float d20 = v2.dot(v0);
- float d21 = v2.dot(v1);
- float denom = (d00 * d11 - d01 * d01);
+ const Vector3 v0 = p_b - p_a;
+ const Vector3 v1 = p_c - p_a;
+ const Vector3 v2 = p_pos - p_a;
+
+ const real_t d00 = v0.dot(v0);
+ const real_t d01 = v0.dot(v1);
+ const real_t d11 = v1.dot(v1);
+ const real_t d20 = v2.dot(v0);
+ const real_t d21 = v2.dot(v1);
+ const real_t denom = (d00 * d11 - d01 * d01);
if (denom == 0) {
return Vector3(); //invalid triangle, return empty
}
- float v = (d11 * d20 - d01 * d21) / denom;
- float w = (d00 * d21 - d01 * d20) / denom;
- float u = 1.0f - v - w;
+ const real_t v = (d11 * d20 - d01 * d21) / denom;
+ const real_t w = (d00 * d21 - d01 * d20) / denom;
+ const real_t u = 1.0f - v - w;
return Vector3(u, v, w);
}
static Color tetrahedron_get_barycentric_coords(const Vector3 &p_a, const Vector3 &p_b, const Vector3 &p_c, const Vector3 &p_d, const Vector3 &p_pos) {
- Vector3 vap = p_pos - p_a;
- Vector3 vbp = p_pos - p_b;
+ const Vector3 vap = p_pos - p_a;
+ const Vector3 vbp = p_pos - p_b;
- Vector3 vab = p_b - p_a;
- Vector3 vac = p_c - p_a;
- Vector3 vad = p_d - p_a;
+ const Vector3 vab = p_b - p_a;
+ const Vector3 vac = p_c - p_a;
+ const Vector3 vad = p_d - p_a;
- Vector3 vbc = p_c - p_b;
- Vector3 vbd = p_d - p_b;
+ const Vector3 vbc = p_c - p_b;
+ const Vector3 vbd = p_d - p_b;
// ScTP computes the scalar triple product
#define STP(m_a, m_b, m_c) ((m_a).dot((m_b).cross((m_c))))
- float va6 = STP(vbp, vbd, vbc);
- float vb6 = STP(vap, vac, vad);
- float vc6 = STP(vap, vad, vab);
- float vd6 = STP(vap, vab, vac);
- float v6 = 1 / STP(vab, vac, vad);
+ const real_t va6 = STP(vbp, vbd, vbc);
+ const real_t vb6 = STP(vap, vac, vad);
+ const real_t vc6 = STP(vap, vad, vab);
+ const real_t vd6 = STP(vap, vab, vac);
+ const real_t v6 = 1 / STP(vab, vac, vad);
return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6);
#undef STP
}
_FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) {
// https://twitter.com/Stubbesaurus/status/937994790553227264
- Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f);
+ const Vector2 f = p_uv * 2.0f - Vector2(1.0f, 1.0f);
Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y));
- float t = CLAMP(-n.z, 0.0f, 1.0f);
+ const real_t t = CLAMP(-n.z, 0.0f, 1.0f);
n.x += n.x >= 0 ? -t : t;
n.y += n.y >= 0 ? -t : t;
return n.normalized();
diff --git a/core/math/projection.cpp b/core/math/projection.cpp
index 9d5dc8b4d6..d0ca7c5684 100644
--- a/core/math/projection.cpp
+++ b/core/math/projection.cpp
@@ -37,7 +37,7 @@
#include "core/math/transform_3d.h"
#include "core/string/ustring.h"
-float Projection::determinant() const {
+real_t Projection::determinant() const {
return columns[0][3] * columns[1][2] * columns[2][1] * columns[3][0] - columns[0][2] * columns[1][3] * columns[2][1] * columns[3][0] -
columns[0][3] * columns[1][1] * columns[2][2] * columns[3][0] + columns[0][1] * columns[1][3] * columns[2][2] * columns[3][0] +
columns[0][2] * columns[1][1] * columns[2][3] * columns[3][0] - columns[0][1] * columns[1][2] * columns[2][3] * columns[3][0] -
@@ -719,7 +719,8 @@ Projection Projection::operator*(const Projection &p_matrix) const {
return new_matrix;
}
-void Projection::set_depth_correction(bool p_flip_y) {
+void Projection::set_depth_correction(bool p_flip_y, bool p_reverse_z, bool p_remap_z) {
+ // p_remap_z is used to convert from OpenGL-style clip space (-1 - 1) to Vulkan style (0 - 1).
real_t *m = &columns[0][0];
m[0] = 1;
@@ -732,11 +733,11 @@ void Projection::set_depth_correction(bool p_flip_y) {
m[7] = 0.0;
m[8] = 0.0;
m[9] = 0.0;
- m[10] = 0.5;
+ m[10] = p_remap_z ? (p_reverse_z ? -0.5 : 0.5) : (p_reverse_z ? -1.0 : 1.0);
m[11] = 0.0;
m[12] = 0.0;
m[13] = 0.0;
- m[14] = 0.5;
+ m[14] = p_remap_z ? 0.5 : 0.0;
m[15] = 1.0;
}
@@ -831,13 +832,13 @@ real_t Projection::get_fov() const {
}
}
-float Projection::get_lod_multiplier() const {
+real_t Projection::get_lod_multiplier() const {
if (is_orthogonal()) {
return get_viewport_half_extents().x;
} else {
- float zn = get_z_near();
- float width = get_viewport_half_extents().x * 2.0;
- return 1.0 / (zn / width);
+ const real_t zn = get_z_near();
+ const real_t width = get_viewport_half_extents().x * 2.0f;
+ return 1.0f / (zn / width);
}
// Usage is lod_size / (lod_distance * multiplier) < threshold
diff --git a/core/math/projection.h b/core/math/projection.h
index b98f636344..f3ed9d7b1c 100644
--- a/core/math/projection.h
+++ b/core/math/projection.h
@@ -65,11 +65,11 @@ struct _NO_DISCARD_ Projection {
return columns[p_axis];
}
- float determinant() const;
+ real_t determinant() const;
void set_identity();
void set_zero();
void set_light_bias();
- void set_depth_correction(bool p_flip_y = true);
+ void set_depth_correction(bool p_flip_y = true, bool p_reverse_z = true, bool p_remap_z = true);
void set_light_atlas_rect(const Rect2 &p_rect);
void set_perspective(real_t p_fovy_degrees, real_t p_aspect, real_t p_z_near, real_t p_z_far, bool p_flip_fov = false);
@@ -148,7 +148,7 @@ struct _NO_DISCARD_ Projection {
return !(*this == p_cam);
}
- float get_lod_multiplier() const;
+ real_t get_lod_multiplier() const;
Projection();
Projection(const Vector4 &p_x, const Vector4 &p_y, const Vector4 &p_z, const Vector4 &p_w);
diff --git a/core/math/rect2.h b/core/math/rect2.h
index 497ed8cf04..7f410feb1c 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -307,14 +307,14 @@ struct _NO_DISCARD_ Rect2 {
i_f = i;
Vector2 r = (b - a);
- float l = r.length();
+ const real_t l = r.length();
if (l == 0.0f) {
continue;
}
// Check inside.
Vector2 tg = r.orthogonal();
- float s = tg.dot(center) - tg.dot(a);
+ const real_t s = tg.dot(center) - tg.dot(a);
if (s < 0.0f) {
side_plus++;
} else {
@@ -330,8 +330,8 @@ struct _NO_DISCARD_ Rect2 {
Vector2 t13 = (position - a) * ir;
Vector2 t24 = (end - a) * ir;
- float tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y));
- float tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y));
+ const real_t tmin = MAX(MIN(t13.x, t24.x), MIN(t13.y, t24.y));
+ const real_t tmax = MIN(MAX(t13.x, t24.x), MAX(t13.y, t24.y));
// if tmax < 0, ray (line) is intersecting AABB, but the whole AABB is behind us
if (tmax < 0 || tmin > tmax || tmin >= l) {
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index be494705ff..fad5f2c0fb 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -101,14 +101,14 @@ Vector2 Vector3::octahedron_encode() const {
Vector3 Vector3::octahedron_decode(const Vector2 &p_oct) {
Vector2 f(p_oct.x * 2.0f - 1.0f, p_oct.y * 2.0f - 1.0f);
Vector3 n(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y));
- float t = CLAMP(-n.z, 0.0f, 1.0f);
+ const real_t t = CLAMP(-n.z, 0.0f, 1.0f);
n.x += n.x >= 0 ? -t : t;
n.y += n.y >= 0 ? -t : t;
return n.normalized();
}
Vector2 Vector3::octahedron_tangent_encode(float p_sign) const {
- const float bias = 1.0f / 32767.0f;
+ const real_t bias = 1.0f / (real_t)32767.0f;
Vector2 res = octahedron_encode();
res.y = MAX(res.y, bias);
res.y = res.y * 0.5f + 0.5f;
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index 80a2703c2f..7ef1ce74ed 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -182,8 +182,20 @@ public:
// Construct a placeholder.
Object *obj = native_parent->creation_func();
+
+ // ClassDB::set_object_extension_instance() won't be called for placeholders.
+ // We need need to make sure that all the things it would have done (even if
+ // done in a different way to support placeholders) will also be done here.
+
obj->_extension = ClassDB::get_placeholder_extension(ti->name);
obj->_extension_instance = memnew(PlaceholderExtensionInstance(ti->name));
+
+#ifdef TOOLS_ENABLED
+ if (obj->_extension->track_instance) {
+ obj->_extension->track_instance(obj->_extension->tracking_userdata, obj);
+ }
+#endif
+
return obj;
}
@@ -506,14 +518,7 @@ Object *ClassDB::_instantiate_internal(const StringName &p_class, bool p_require
extension = get_placeholder_extension(ti->name);
}
#endif
- Object *obj = (Object *)extension->create_instance(extension->class_userdata);
-
-#ifdef TOOLS_ENABLED
- if (extension->track_instance) {
- extension->track_instance(extension->tracking_userdata, obj);
- }
-#endif
- return obj;
+ return (Object *)extension->create_instance(extension->class_userdata);
} else {
#ifdef TOOLS_ENABLED
if (!p_require_real_class && ti->is_runtime && Engine::get_singleton()->is_editor_hint()) {
@@ -638,6 +643,12 @@ void ClassDB::set_object_extension_instance(Object *p_object, const StringName &
p_object->_extension = ti->gdextension;
p_object->_extension_instance = p_instance;
+
+#ifdef TOOLS_ENABLED
+ if (p_object->_extension->track_instance) {
+ p_object->_extension->track_instance(p_object->_extension->tracking_userdata, p_object);
+ }
+#endif
}
bool ClassDB::can_instantiate(const StringName &p_class) {
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 8b6fd587e0..f8d2feb5a8 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -142,16 +142,16 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
args = p_dict["args"];
}
- for (int i = 0; i < args.size(); i++) {
- Dictionary d = args[i];
+ for (const Variant &arg : args) {
+ Dictionary d = arg;
mi.arguments.push_back(PropertyInfo::from_dict(d));
}
Array defargs;
if (p_dict.has("default_args")) {
defargs = p_dict["default_args"];
}
- for (int i = 0; i < defargs.size(); i++) {
- mi.default_arguments.push_back(defargs[i]);
+ for (const Variant &defarg : defargs) {
+ mi.default_arguments.push_back(defarg);
}
if (p_dict.has("return")) {
@@ -1100,6 +1100,20 @@ bool Object::_has_user_signal(const StringName &p_name) const {
return signal_map[p_name].user.name.length() > 0;
}
+void Object::_remove_user_signal(const StringName &p_name) {
+ SignalData *s = signal_map.getptr(p_name);
+ ERR_FAIL_NULL_MSG(s, "Provided signal does not exist.");
+ ERR_FAIL_COND_MSG(!s->removable, "Signal is not removable (not added with add_user_signal).");
+ for (const KeyValue<Callable, SignalData::Slot> &slot_kv : s->slot_map) {
+ Object *target = slot_kv.key.get_object();
+ if (likely(target)) {
+ target->connections.erase(slot_kv.value.cE);
+ }
+ }
+
+ signal_map.erase(p_name);
+}
+
Error Object::_emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
if (unlikely(p_argcount < 1)) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
@@ -1233,8 +1247,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
MethodInfo mi;
mi.name = p_name;
- for (int i = 0; i < p_args.size(); i++) {
- Dictionary d = p_args[i];
+ for (const Variant &arg : p_args) {
+ Dictionary d = arg;
PropertyInfo param;
if (d.has("name")) {
@@ -1248,6 +1262,10 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
}
add_user_signal(mi);
+
+ if (signal_map.has(p_name)) {
+ signal_map.getptr(p_name)->removable = true;
+ }
}
TypedArray<Dictionary> Object::_get_signal_list() const {
@@ -1585,8 +1603,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {
} break;
case Variant::ARRAY: {
Array a = p_var;
- for (int i = 0; i < a.size(); i++) {
- _clear_internal_resource_paths(a[i]);
+ for (const Variant &var : a) {
+ _clear_internal_resource_paths(var);
}
} break;
@@ -1661,6 +1679,7 @@ void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_user_signal", "signal", "arguments"), &Object::_add_user_signal, DEFVAL(Array()));
ClassDB::bind_method(D_METHOD("has_user_signal", "signal"), &Object::_has_user_signal);
+ ClassDB::bind_method(D_METHOD("remove_user_signal", "signal"), &Object::_remove_user_signal);
{
MethodInfo mi;
diff --git a/core/object/object.h b/core/object/object.h
index d9551ecd01..915c3a8c25 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -619,6 +619,7 @@ private:
MethodInfo user;
HashMap<Callable, Slot, HashableHasher<Callable>> slot_map;
+ bool removable = false;
};
HashMap<StringName, SignalData> signal_map;
@@ -646,6 +647,7 @@ private:
void _add_user_signal(const String &p_name, const Array &p_args = Array());
bool _has_user_signal(const StringName &p_name) const;
+ void _remove_user_signal(const StringName &p_name);
Error _emit_signal(const Variant **p_args, int p_argcount, Callable::CallError &r_error);
TypedArray<Dictionary> _get_signal_list() const;
TypedArray<Dictionary> _get_signal_connection_list(const StringName &p_signal) const;
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 42e9c421ae..bd3199ca0a 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -257,8 +257,8 @@ void ScriptServer::init_languages() {
if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
Array script_classes = GLOBAL_GET("_global_script_classes");
- for (int i = 0; i < script_classes.size(); i++) {
- Dictionary c = script_classes[i];
+ for (const Variant &script_class : script_classes) {
+ Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
continue;
}
@@ -269,8 +269,8 @@ void ScriptServer::init_languages() {
#endif
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
- for (int i = 0; i < script_classes.size(); i++) {
- Dictionary c = script_classes[i];
+ for (const Variant &script_class : script_classes) {
+ Dictionary c = script_class;
if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
continue;
}
@@ -469,8 +469,8 @@ void ScriptServer::save_global_classes() {
Dictionary class_icons;
Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
- for (int i = 0; i < script_classes.size(); i++) {
- Dictionary d = script_classes[i];
+ for (const Variant &script_class : script_classes) {
+ Dictionary d = script_class;
if (!d.has("name") || !d.has("icon")) {
continue;
}
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 1db322526d..cc6b729ae8 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -319,8 +319,8 @@ public:
}
if (r_errors != nullptr && ret.has("errors")) {
Array errors = ret["errors"];
- for (int i = 0; i < errors.size(); i++) {
- Dictionary err = errors[i];
+ for (const Variant &error : errors) {
+ Dictionary err = error;
ERR_CONTINUE(!err.has("line"));
ERR_CONTINUE(!err.has("column"));
ERR_CONTINUE(!err.has("message"));
@@ -339,8 +339,8 @@ public:
if (r_warnings != nullptr && ret.has("warnings")) {
ERR_FAIL_COND_V(!ret.has("warnings"), false);
Array warnings = ret["warnings"];
- for (int i = 0; i < warnings.size(); i++) {
- Dictionary warn = warnings[i];
+ for (const Variant &warning : warnings) {
+ Dictionary warn = warning;
ERR_CONTINUE(!warn.has("start_line"));
ERR_CONTINUE(!warn.has("end_line"));
ERR_CONTINUE(!warn.has("leftmost_column"));
@@ -402,8 +402,8 @@ public:
if (r_options != nullptr && ret.has("options")) {
Array options = ret["options"];
- for (int i = 0; i < options.size(); i++) {
- Dictionary op = options[i];
+ for (const Variant &var : options) {
+ Dictionary op = var;
CodeCompletionOption option;
ERR_CONTINUE(!op.has("kind"));
option.kind = CodeCompletionKind(int(op["kind"]));
@@ -502,8 +502,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
- for (int i = 0; i < values.size(); i++) {
- p_values->push_back(values[i]);
+ for (const Variant &value : values) {
+ p_values->push_back(value);
}
}
}
@@ -522,8 +522,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
- for (int i = 0; i < values.size(); i++) {
- p_values->push_back(values[i]);
+ for (const Variant &value : values) {
+ p_values->push_back(value);
}
}
}
@@ -549,8 +549,8 @@ public:
}
if (p_values != nullptr && ret.has("values")) {
Array values = ret["values"];
- for (int i = 0; i < values.size(); i++) {
- p_values->push_back(values[i]);
+ for (const Variant &value : values) {
+ p_values->push_back(value);
}
}
}
@@ -562,9 +562,9 @@ public:
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
Vector<StackInfo> sret;
- for (int i = 0; i < ret.size(); i++) {
+ for (const Variant &var : ret) {
StackInfo si;
- Dictionary d = ret[i];
+ Dictionary d = var;
ERR_CONTINUE(!d.has("file"));
ERR_CONTINUE(!d.has("func"));
ERR_CONTINUE(!d.has("line"));
@@ -595,8 +595,8 @@ public:
virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
- for (int i = 0; i < ret.size(); i++) {
- MethodInfo mi = MethodInfo::from_dict(ret[i]);
+ for (const Variant &var : ret) {
+ MethodInfo mi = MethodInfo::from_dict(var);
p_functions->push_back(mi);
}
}
@@ -615,8 +615,8 @@ public:
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
- for (int i = 0; i < ret.size(); i++) {
- MethodInfo mi = MethodInfo::from_dict(ret[i]);
+ for (const Variant &var : ret) {
+ MethodInfo mi = MethodInfo::from_dict(var);
p_annotations->push_back(mi);
}
}
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index ef3d315e4b..9c9e0fa899 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -35,6 +35,8 @@
#include "core/os/thread_safe.h"
#include "core/templates/command_queue_mt.h"
+WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1;
+
void WorkerThreadPool::Task::free_template_userdata() {
ERR_FAIL_NULL(template_userdata);
ERR_FAIL_NULL(native_func_userdata);
@@ -60,14 +62,19 @@ void WorkerThreadPool::_process_task(Task *p_task) {
// its pre-created threads can't have ScriptServer::thread_enter() called on them early.
// Therefore, we do it late at the first opportunity, so in case the task
// about to be run uses scripting, guarantees are held.
+ task_mutex.lock();
if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) {
+ task_mutex.unlock();
ScriptServer::thread_enter();
+ task_mutex.lock();
curr_thread.ready_for_scripting = true;
}
- task_mutex.lock();
p_task->pool_thread_index = pool_thread_index;
prev_task = curr_thread.current_task;
curr_thread.current_task = p_task;
+ if (p_task->pending_notify_yield_over) {
+ curr_thread.yield_is_over = true;
+ }
task_mutex.unlock();
}
#endif
@@ -389,83 +396,119 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
task_mutex.unlock();
if (caller_pool_thread) {
- while (true) {
- Task *task_to_process = nullptr;
- {
- MutexLock lock(task_mutex);
- bool was_signaled = caller_pool_thread->signaled;
- caller_pool_thread->signaled = false;
-
- if (task->completed) {
- // This thread was awaken also for some reason, but it's about to exit.
- // Let's find out what may be pending and forward the requests.
- if (!exit_threads && was_signaled) {
- uint32_t to_process = task_queue.first() ? 1 : 0;
- uint32_t to_promote = caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
- if (to_process || to_promote) {
- // This thread must be left alone since it won't loop again.
- caller_pool_thread->signaled = true;
- _notify_threads(caller_pool_thread, to_process, to_promote);
- }
- }
+ _wait_collaboratively(caller_pool_thread, task);
+ task->waiting_pool--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ } else {
+ task->done_semaphore.wait();
+ task_mutex.lock();
+ task->waiting_user--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
+ }
+ task_mutex.unlock();
+ }
- task->waiting_pool--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
- }
+ return OK;
+}
- break;
- }
+void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task) {
+ // Keep processing tasks until the condition to stop waiting is met.
- if (!exit_threads) {
- // This is a thread from the pool. It shouldn't just idle.
- // Let's try to process other tasks while we wait.
+#define IS_WAIT_OVER (unlikely(p_task == ThreadData::YIELDING) ? p_caller_pool_thread->yield_is_over : p_task->completed)
- if (caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
- if (_try_promote_low_priority_task()) {
- _notify_threads(caller_pool_thread, 1, 0);
- }
+ while (true) {
+ Task *task_to_process = nullptr;
+ {
+ MutexLock lock(task_mutex);
+ bool was_signaled = p_caller_pool_thread->signaled;
+ p_caller_pool_thread->signaled = false;
+
+ if (IS_WAIT_OVER) {
+ p_caller_pool_thread->yield_is_over = false;
+ if (!exit_threads && was_signaled) {
+ // This thread was awaken for some additional reason, but it's about to exit.
+ // Let's find out what may be pending and forward the requests.
+ uint32_t to_process = task_queue.first() ? 1 : 0;
+ uint32_t to_promote = p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first() ? 1 : 0;
+ if (to_process || to_promote) {
+ // This thread must be left alone since it won't loop again.
+ p_caller_pool_thread->signaled = true;
+ _notify_threads(p_caller_pool_thread, to_process, to_promote);
}
+ }
- if (singleton->task_queue.first()) {
- task_to_process = task_queue.first()->self();
- task_queue.remove(task_queue.first());
+ break;
+ }
+
+ if (!exit_threads) {
+ if (p_caller_pool_thread->current_task->low_priority && low_priority_task_queue.first()) {
+ if (_try_promote_low_priority_task()) {
+ _notify_threads(p_caller_pool_thread, 1, 0);
}
+ }
- if (!task_to_process) {
- caller_pool_thread->awaited_task = task;
+ if (singleton->task_queue.first()) {
+ task_to_process = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ }
- if (flushing_cmd_queue) {
- flushing_cmd_queue->unlock();
- }
- caller_pool_thread->cond_var.wait(lock);
- if (flushing_cmd_queue) {
- flushing_cmd_queue->lock();
- }
+ if (!task_to_process) {
+ p_caller_pool_thread->awaited_task = p_task;
- DEV_ASSERT(exit_threads || caller_pool_thread->signaled || task->completed);
- caller_pool_thread->awaited_task = nullptr;
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->unlock();
+ }
+ p_caller_pool_thread->cond_var.wait(lock);
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->lock();
}
+
+ DEV_ASSERT(exit_threads || p_caller_pool_thread->signaled || IS_WAIT_OVER);
+ p_caller_pool_thread->awaited_task = nullptr;
}
}
+ }
- if (task_to_process) {
- _process_task(task_to_process);
- }
+ if (task_to_process) {
+ _process_task(task_to_process);
}
- } else {
- task->done_semaphore.wait();
- task_mutex.lock();
- task->waiting_user--;
- if (task->waiting_pool == 0 && task->waiting_user == 0) {
- tasks.erase(p_task_id);
- task_allocator.free(task);
+ }
+}
+
+void WorkerThreadPool::yield() {
+ int th_index = get_thread_index();
+ ERR_FAIL_COND_MSG(th_index == -1, "This function can only be called from a worker thread.");
+ _wait_collaboratively(&threads[th_index], ThreadData::YIELDING);
+}
+
+void WorkerThreadPool::notify_yield_over(TaskID p_task_id) {
+ task_mutex.lock();
+ Task **taskp = tasks.getptr(p_task_id);
+ if (!taskp) {
+ task_mutex.unlock();
+ ERR_FAIL_MSG("Invalid Task ID.");
+ }
+ Task *task = *taskp;
+ if (task->pool_thread_index == -1) { // Completed or not started yet.
+ if (!task->completed) {
+ // This avoids a race condition where a task is created and yield-over called before it's processed.
+ task->pending_notify_yield_over = true;
}
task_mutex.unlock();
+ return;
}
- return OK;
+ ThreadData &td = threads[task->pool_thread_index];
+ td.yield_is_over = true;
+ td.signaled = true;
+ td.cond_var.notify_one();
+
+ task_mutex.unlock();
}
WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index fdddc9a647..a9cf260a0f 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -81,7 +81,8 @@ private:
void *native_func_userdata = nullptr;
String description;
Semaphore done_semaphore; // For user threads awaiting.
- bool completed = false;
+ bool completed : 1;
+ bool pending_notify_yield_over : 1;
Group *group = nullptr;
SelfList<Task> task_elem;
uint32_t waiting_pool = 0;
@@ -92,6 +93,8 @@ private:
void free_template_userdata();
Task() :
+ completed(false),
+ pending_notify_yield_over(false),
task_elem(this) {}
};
@@ -107,13 +110,21 @@ private:
BinaryMutex task_mutex;
struct ThreadData {
+ static Task *const YIELDING; // Too bad constexpr doesn't work here.
+
uint32_t index = 0;
Thread thread;
- bool ready_for_scripting = false;
- bool signaled = false;
+ bool ready_for_scripting : 1;
+ bool signaled : 1;
+ bool yield_is_over : 1;
Task *current_task = nullptr;
- Task *awaited_task = nullptr; // Null if not awaiting the condition variable. Special value for idle-waiting.
+ Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
ConditionVariable cond_var;
+
+ ThreadData() :
+ ready_for_scripting(false),
+ signaled(false),
+ yield_is_over(false) {}
};
TightLocalVector<ThreadData> threads;
@@ -177,6 +188,8 @@ private:
}
};
+ void _wait_collaboratively(ThreadData *p_caller_pool_thread, Task *p_task);
+
protected:
static void _bind_methods();
@@ -196,6 +209,9 @@ public:
bool is_task_completed(TaskID p_task_id) const;
Error wait_for_task_completion(TaskID p_task_id);
+ void yield();
+ void notify_yield_over(TaskID p_task_id);
+
template <typename C, typename M, typename U>
GroupID add_template_group_task(C *p_instance, M p_method, U p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String()) {
typedef GroupUserData<C, M, U> GroupUD;
diff --git a/core/os/os.h b/core/os/os.h
index 370e724c53..06be0e2b41 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -153,7 +153,7 @@ public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!");
- virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr) { return ERR_UNAVAILABLE; }
+ virtual Error open_dynamic_library(const String &p_path, void *&p_library_handle, bool p_also_set_library_path = false, String *r_resolved_path = nullptr, bool p_generate_temp_files = 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; }
@@ -176,6 +176,7 @@ public:
virtual Error kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const;
virtual bool is_process_running(const ProcessID &p_pid) const = 0;
+ virtual int get_process_exit_code(const ProcessID &p_pid) const = 0;
virtual void vibrate_handheld(int p_duration_ms = 500) {}
virtual Error shell_open(const String &p_uri);
diff --git a/core/os/pool_allocator.cpp b/core/os/pool_allocator.cpp
index acbaed4ce8..9a993cd14f 100644
--- a/core/os/pool_allocator.cpp
+++ b/core/os/pool_allocator.cpp
@@ -36,12 +36,13 @@
#include "core/string/print_string.h"
#define COMPACT_CHUNK(m_entry, m_to_pos) \
- do { \
+ if constexpr (true) { \
void *_dst = &((unsigned char *)pool)[m_to_pos]; \
void *_src = &((unsigned char *)pool)[(m_entry).pos]; \
memmove(_dst, _src, aligned((m_entry).len)); \
(m_entry).pos = m_to_pos; \
- } while (0);
+ } else \
+ ((void)0)
void PoolAllocator::mt_lock() const {
}
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 0a0052d6cb..613edd11cd 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -776,9 +776,9 @@ void TranslationServer::set_property_translation(const Ref<Translation> &p_trans
property_translation = p_translation;
}
-StringName TranslationServer::property_translate(const StringName &p_message) const {
+StringName TranslationServer::property_translate(const StringName &p_message, const StringName &p_context) const {
if (property_translation.is_valid()) {
- StringName r = property_translation->get_message(p_message);
+ StringName r = property_translation->get_message(p_message, p_context);
if (r) {
return r;
}
diff --git a/core/string/translation.h b/core/string/translation.h
index 470ba88232..78d6721347 100644
--- a/core/string/translation.h
+++ b/core/string/translation.h
@@ -183,7 +183,7 @@ public:
StringName tool_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
void set_property_translation(const Ref<Translation> &p_translation);
- StringName property_translate(const StringName &p_message) const;
+ StringName property_translate(const StringName &p_message, const StringName &p_context = "") const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message, const StringName &p_context = "") const;
StringName doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context = "") const;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index bdef1b9bbe..2b62b72a51 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -2110,12 +2110,12 @@ CharString String::utf8() const {
String String::utf16(const char16_t *p_utf16, int p_len) {
String ret;
- ret.parse_utf16(p_utf16, p_len);
+ ret.parse_utf16(p_utf16, p_len, true);
return ret;
}
-Error String::parse_utf16(const char16_t *p_utf16, int p_len) {
+Error String::parse_utf16(const char16_t *p_utf16, int p_len, bool p_default_little_endian) {
if (!p_utf16) {
return ERR_INVALID_DATA;
}
@@ -2125,8 +2125,12 @@ Error String::parse_utf16(const char16_t *p_utf16, int p_len) {
int cstr_size = 0;
int str_size = 0;
+#ifdef BIG_ENDIAN_ENABLED
+ bool byteswap = p_default_little_endian;
+#else
+ bool byteswap = !p_default_little_endian;
+#endif
/* HANDLE BOM (Byte Order Mark) */
- bool byteswap = false; // assume correct endianness if no BOM found
if (p_len < 0 || p_len >= 1) {
bool has_bom = false;
if (uint16_t(p_utf16[0]) == 0xfeff) { // correct BOM, read as is
diff --git a/core/string/ustring.h b/core/string/ustring.h
index fa904c8200..693df6dcba 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -393,7 +393,7 @@ public:
static String utf8(const char *p_utf8, int p_len = -1);
Char16String utf16() const;
- Error parse_utf16(const char16_t *p_utf16, int p_len = -1);
+ Error parse_utf16(const char16_t *p_utf16, int p_len = -1, bool p_default_little_endian = true);
static String utf16(const char16_t *p_utf16, int p_len = -1);
static uint32_t hash(const char32_t *p_cstr, int p_len); /* hash the string */
diff --git a/core/templates/command_queue_mt.cpp b/core/templates/command_queue_mt.cpp
index 6ecd75ebc1..0c5c6394a1 100644
--- a/core/templates/command_queue_mt.cpp
+++ b/core/templates/command_queue_mt.cpp
@@ -70,14 +70,8 @@ CommandQueueMT::SyncSemaphore *CommandQueueMT::_alloc_sync_sem() {
return &sync_sems[idx];
}
-CommandQueueMT::CommandQueueMT(bool p_sync) {
- if (p_sync) {
- sync = memnew(Semaphore);
- }
+CommandQueueMT::CommandQueueMT() {
}
CommandQueueMT::~CommandQueueMT() {
- if (sync) {
- memdelete(sync);
- }
}
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index 4056119851..a4ac338bed 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -248,16 +248,17 @@
#define CMD_TYPE(N) Command##N<T, M COMMA(N) COMMA_SEP_LIST(TYPE_ARG, N)>
#define CMD_ASSIGN_PARAM(N) cmd->p##N = p##N
-#define DECL_PUSH(N) \
- template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
- void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
- CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
- cmd->instance = p_instance; \
- cmd->method = p_method; \
- SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
- unlock(); \
- if (sync) \
- sync->post(); \
+#define DECL_PUSH(N) \
+ template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \
+ void push(T *p_instance, M p_method COMMA(N) COMMA_SEP_LIST(PARAM, N)) { \
+ CMD_TYPE(N) *cmd = allocate_and_lock<CMD_TYPE(N)>(); \
+ cmd->instance = p_instance; \
+ cmd->method = p_method; \
+ SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
+ unlock(); \
}
#define CMD_RET_TYPE(N) CommandRet##N<T, M, COMMA_SEP_LIST(TYPE_ARG, N) COMMA(N) R>
@@ -272,9 +273,10 @@
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->ret = r_ret; \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -290,9 +292,10 @@
cmd->method = p_method; \
SEMIC_SEP_LIST(CMD_ASSIGN_PARAM, N); \
cmd->sync_sem = ss; \
+ if (pump_task_id != WorkerThreadPool::INVALID_TASK_ID) { \
+ WorkerThreadPool::get_singleton()->notify_yield_over(pump_task_id); \
+ } \
unlock(); \
- if (sync) \
- sync->post(); \
ss->sem.wait(); \
ss->in_use = false; \
}
@@ -340,7 +343,7 @@ class CommandQueueMT {
LocalVector<uint8_t> command_mem;
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
- Semaphore *sync = nullptr;
+ WorkerThreadPool::TaskID pump_task_id = WorkerThreadPool::INVALID_TASK_ID;
uint64_t flush_read_ptr = 0;
template <typename T>
@@ -364,6 +367,12 @@ class CommandQueueMT {
void _flush() {
lock();
+ if (unlikely(flush_read_ptr)) {
+ // Re-entrant call.
+ unlock();
+ return;
+ }
+
WorkerThreadPool::thread_enter_command_queue_mt_flush(this);
while (flush_read_ptr < command_mem.size()) {
uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr];
@@ -376,13 +385,6 @@ class CommandQueueMT {
sync_sem->sem.post(); // Release in case it needs sync/ret.
}
- if (unlikely(flush_read_ptr == 0)) {
- // A reentrant call flushed.
- DEV_ASSERT(command_mem.is_empty());
- unlock();
- return;
- }
-
flush_read_ptr += size;
}
WorkerThreadPool::thread_exit_command_queue_mt_flush();
@@ -421,12 +423,16 @@ public:
}
void wait_and_flush() {
- ERR_FAIL_NULL(sync);
- sync->wait();
+ ERR_FAIL_COND(pump_task_id == WorkerThreadPool::INVALID_TASK_ID);
+ WorkerThreadPool::get_singleton()->wait_for_task_completion(pump_task_id);
_flush();
}
- CommandQueueMT(bool p_sync);
+ void set_pump_task_id(WorkerThreadPool::TaskID p_task_id) {
+ pump_task_id = p_task_id;
+ }
+
+ CommandQueueMT();
~CommandQueueMT();
};
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index 5d6fbb8bed..3685515db5 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -81,6 +81,22 @@ void Array::_unref() const {
_p = nullptr;
}
+Array::Iterator Array::begin() {
+ return Iterator(_p->array.ptrw(), _p->read_only);
+}
+
+Array::Iterator Array::end() {
+ return Iterator(_p->array.ptrw() + _p->array.size(), _p->read_only);
+}
+
+Array::ConstIterator Array::begin() const {
+ return ConstIterator(_p->array.ptr(), _p->read_only);
+}
+
+Array::ConstIterator Array::end() const {
+ return ConstIterator(_p->array.ptr() + _p->array.size(), _p->read_only);
+}
+
Variant &Array::operator[](int p_idx) {
if (unlikely(_p->read_only)) {
*_p->read_only = _p->array[p_idx];
diff --git a/core/variant/array.h b/core/variant/array.h
index 8b1f8c0678..3aa957b312 100644
--- a/core/variant/array.h
+++ b/core/variant/array.h
@@ -46,6 +46,70 @@ class Array {
void _unref() const;
public:
+ struct ConstIterator {
+ _FORCE_INLINE_ const Variant &operator*() const;
+ _FORCE_INLINE_ const Variant *operator->() const;
+
+ _FORCE_INLINE_ ConstIterator &operator++();
+ _FORCE_INLINE_ ConstIterator &operator--();
+
+ _FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr != p_other.element_ptr; }
+
+ _FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) :
+ element_ptr(p_element_ptr), read_only(p_read_only) {}
+ _FORCE_INLINE_ ConstIterator() {}
+ _FORCE_INLINE_ ConstIterator(const ConstIterator &p_other) :
+ element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
+
+ _FORCE_INLINE_ ConstIterator &operator=(const ConstIterator &p_other) {
+ element_ptr = p_other.element_ptr;
+ read_only = p_other.read_only;
+ return *this;
+ }
+
+ private:
+ const Variant *element_ptr = nullptr;
+ Variant *read_only = nullptr;
+ };
+
+ struct Iterator {
+ _FORCE_INLINE_ Variant &operator*() const;
+ _FORCE_INLINE_ Variant *operator->() const;
+
+ _FORCE_INLINE_ Iterator &operator++();
+ _FORCE_INLINE_ Iterator &operator--();
+
+ _FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return element_ptr == p_other.element_ptr; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return element_ptr != p_other.element_ptr; }
+
+ _FORCE_INLINE_ Iterator(Variant *p_element_ptr, Variant *p_read_only = nullptr) :
+ element_ptr(p_element_ptr), read_only(p_read_only) {}
+ _FORCE_INLINE_ Iterator() {}
+ _FORCE_INLINE_ Iterator(const Iterator &p_other) :
+ element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
+
+ _FORCE_INLINE_ Iterator &operator=(const Iterator &p_other) {
+ element_ptr = p_other.element_ptr;
+ read_only = p_other.read_only;
+ return *this;
+ }
+
+ operator ConstIterator() const {
+ return ConstIterator(element_ptr, read_only);
+ }
+
+ private:
+ Variant *element_ptr = nullptr;
+ Variant *read_only = nullptr;
+ };
+
+ Iterator begin();
+ Iterator end();
+
+ ConstIterator begin() const;
+ ConstIterator end() const;
+
void _ref(const Array &p_from) const;
Variant &operator[](int p_idx);
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 89c22c05dd..155a5b2781 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -1417,7 +1417,7 @@ void Variant::_clear_internal() {
}
}
-Variant::operator signed int() const {
+Variant::operator int64_t() const {
switch (type) {
case NIL:
return 0;
@@ -1435,7 +1435,7 @@ Variant::operator signed int() const {
}
}
-Variant::operator unsigned int() const {
+Variant::operator int32_t() const {
switch (type) {
case NIL:
return 0;
@@ -1453,7 +1453,7 @@ Variant::operator unsigned int() const {
}
}
-Variant::operator int64_t() const {
+Variant::operator int16_t() const {
switch (type) {
case NIL:
return 0;
@@ -1471,7 +1471,7 @@ Variant::operator int64_t() const {
}
}
-Variant::operator uint64_t() const {
+Variant::operator int8_t() const {
switch (type) {
case NIL:
return 0;
@@ -1489,18 +1489,7 @@ Variant::operator uint64_t() const {
}
}
-Variant::operator ObjectID() const {
- if (type == INT) {
- return ObjectID(_data._int);
- } else if (type == OBJECT) {
- return _get_obj().id;
- } else {
- return ObjectID();
- }
-}
-
-#ifdef NEED_LONG_INT
-Variant::operator signed long() const {
+Variant::operator uint64_t() const {
switch (type) {
case NIL:
return 0;
@@ -1516,11 +1505,9 @@ Variant::operator signed long() const {
return 0;
}
}
-
- return 0;
}
-Variant::operator unsigned long() const {
+Variant::operator uint32_t() const {
switch (type) {
case NIL:
return 0;
@@ -1536,12 +1523,9 @@ Variant::operator unsigned long() const {
return 0;
}
}
-
- return 0;
}
-#endif
-Variant::operator signed short() const {
+Variant::operator uint16_t() const {
switch (type) {
case NIL:
return 0;
@@ -1559,7 +1543,7 @@ Variant::operator signed short() const {
}
}
-Variant::operator unsigned short() const {
+Variant::operator uint8_t() const {
switch (type) {
case NIL:
return 0;
@@ -1577,44 +1561,18 @@ Variant::operator unsigned short() const {
}
}
-Variant::operator signed char() const {
- switch (type) {
- case NIL:
- return 0;
- case BOOL:
- return _data._bool ? 1 : 0;
- case INT:
- return _data._int;
- case FLOAT:
- return _data._float;
- case STRING:
- return operator String().to_int();
- default: {
- return 0;
- }
- }
-}
-
-Variant::operator unsigned char() const {
- switch (type) {
- case NIL:
- return 0;
- case BOOL:
- return _data._bool ? 1 : 0;
- case INT:
- return _data._int;
- case FLOAT:
- return _data._float;
- case STRING:
- return operator String().to_int();
- default: {
- return 0;
- }
+Variant::operator ObjectID() const {
+ if (type == INT) {
+ return ObjectID(_data._int);
+ } else if (type == OBJECT) {
+ return _get_obj().id;
+ } else {
+ return ObjectID();
}
}
Variant::operator char32_t() const {
- return operator unsigned int();
+ return operator uint32_t();
}
Variant::operator float() const {
@@ -2420,57 +2378,44 @@ Variant::Variant(bool p_bool) {
_data._bool = p_bool;
}
-Variant::Variant(signed int p_int) {
+Variant::Variant(int64_t p_int64) {
type = INT;
- _data._int = p_int;
+ _data._int = p_int64;
}
-Variant::Variant(unsigned int p_int) {
+Variant::Variant(int32_t p_int32) {
type = INT;
- _data._int = p_int;
+ _data._int = p_int32;
}
-#ifdef NEED_LONG_INT
-
-Variant::Variant(signed long p_int) {
- type = INT;
- _data._int = p_int;
-}
-
-Variant::Variant(unsigned long p_int) {
- type = INT;
- _data._int = p_int;
-}
-#endif
-
-Variant::Variant(int64_t p_int) {
+Variant::Variant(int16_t p_int16) {
type = INT;
- _data._int = p_int;
+ _data._int = p_int16;
}
-Variant::Variant(uint64_t p_int) {
+Variant::Variant(int8_t p_int8) {
type = INT;
- _data._int = p_int;
+ _data._int = p_int8;
}
-Variant::Variant(signed short p_short) {
+Variant::Variant(uint64_t p_uint64) {
type = INT;
- _data._int = p_short;
+ _data._int = p_uint64;
}
-Variant::Variant(unsigned short p_short) {
+Variant::Variant(uint32_t p_uint32) {
type = INT;
- _data._int = p_short;
+ _data._int = p_uint32;
}
-Variant::Variant(signed char p_char) {
+Variant::Variant(uint16_t p_uint16) {
type = INT;
- _data._int = p_char;
+ _data._int = p_uint16;
}
-Variant::Variant(unsigned char p_char) {
+Variant::Variant(uint8_t p_uint8) {
type = INT;
- _data._int = p_char;
+ _data._int = p_uint8;
}
Variant::Variant(float p_float) {
diff --git a/core/variant/variant.h b/core/variant/variant.h
index e8eec8171b..10f8dc3c7f 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -355,19 +355,14 @@ public:
const Variant &operator[](const Variant &p_key) const = delete;
operator bool() const;
- operator signed int() const;
- operator unsigned int() const; // this is the real one
- operator signed short() const;
- operator unsigned short() const;
- operator signed char() const;
- operator unsigned char() const;
- //operator long unsigned int() const;
operator int64_t() const;
+ operator int32_t() const;
+ operator int16_t() const;
+ operator int8_t() const;
operator uint64_t() const;
-#ifdef NEED_LONG_INT
- operator signed long() const;
- operator unsigned long() const;
-#endif
+ operator uint32_t() const;
+ operator uint16_t() const;
+ operator uint8_t() const;
operator ObjectID() const;
@@ -430,18 +425,14 @@ public:
Object *get_validated_object_with_check(bool &r_previously_freed) const;
Variant(bool p_bool);
- Variant(signed int p_int); // real one
- Variant(unsigned int p_int);
-#ifdef NEED_LONG_INT
- Variant(signed long p_long); // real one
- Variant(unsigned long p_long);
-#endif
- Variant(signed short p_short); // real one
- Variant(unsigned short p_short);
- Variant(signed char p_char); // real one
- Variant(unsigned char p_char);
- Variant(int64_t p_int); // real one
- Variant(uint64_t p_int);
+ Variant(int64_t p_int64);
+ Variant(int32_t p_int32);
+ Variant(int16_t p_int16);
+ Variant(int8_t p_int8);
+ Variant(uint64_t p_uint64);
+ Variant(uint32_t p_uint32);
+ Variant(uint16_t p_uint16);
+ Variant(uint8_t p_uint8);
Variant(float p_float);
Variant(double p_double);
Variant(const ObjectID &p_id);
@@ -874,4 +865,56 @@ Callable Callable::bind(VarArgs... p_args) const {
return bindp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
}
+Variant &Array::Iterator::operator*() const {
+ if (unlikely(read_only)) {
+ *read_only = *element_ptr;
+ return *read_only;
+ }
+ return *element_ptr;
+}
+
+Variant *Array::Iterator::operator->() const {
+ if (unlikely(read_only)) {
+ *read_only = *element_ptr;
+ return read_only;
+ }
+ return element_ptr;
+}
+
+Array::Iterator &Array::Iterator::operator++() {
+ element_ptr++;
+ return *this;
+}
+
+Array::Iterator &Array::Iterator::operator--() {
+ element_ptr--;
+ return *this;
+}
+
+const Variant &Array::ConstIterator::operator*() const {
+ if (unlikely(read_only)) {
+ *read_only = *element_ptr;
+ return *read_only;
+ }
+ return *element_ptr;
+}
+
+const Variant *Array::ConstIterator::operator->() const {
+ if (unlikely(read_only)) {
+ *read_only = *element_ptr;
+ return read_only;
+ }
+ return element_ptr;
+}
+
+Array::ConstIterator &Array::ConstIterator::operator++() {
+ element_ptr++;
+ return *this;
+}
+
+Array::ConstIterator &Array::ConstIterator::operator--() {
+ element_ptr--;
+ return *this;
+}
+
#endif // VARIANT_H
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ba7c44e405..d0d940c47d 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -1799,7 +1799,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2, dot, sarray("with"), varray());
bind_method(Vector2, slide, sarray("n"), varray());
bind_method(Vector2, bounce, sarray("n"), varray());
- bind_method(Vector2, reflect, sarray("n"), varray());
+ bind_method(Vector2, reflect, sarray("line"), varray());
bind_method(Vector2, cross, sarray("with"), varray());
bind_method(Vector2, abs, sarray(), varray());
bind_method(Vector2, sign, sarray(), varray());
@@ -1896,7 +1896,7 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3, project, sarray("b"), varray());
bind_method(Vector3, slide, sarray("n"), varray());
bind_method(Vector3, bounce, sarray("n"), varray());
- bind_method(Vector3, reflect, sarray("n"), varray());
+ bind_method(Vector3, reflect, sarray("direction"), varray());
bind_method(Vector3, sign, sarray(), varray());
bind_method(Vector3, octahedron_encode, sarray(), varray());
bind_static_method(Vector3, octahedron_decode, sarray("uv"), varray());
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index 60ae09c6f1..dcf4b287d1 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -230,18 +230,20 @@ public:
};
#define register_string_op(m_op_type, m_op_code) \
- do { \
+ if constexpr (true) { \
register_op<m_op_type<String, String>>(m_op_code, Variant::STRING, Variant::STRING); \
register_op<m_op_type<String, StringName>>(m_op_code, Variant::STRING, Variant::STRING_NAME); \
register_op<m_op_type<StringName, String>>(m_op_code, Variant::STRING_NAME, Variant::STRING); \
register_op<m_op_type<StringName, StringName>>(m_op_code, Variant::STRING_NAME, Variant::STRING_NAME); \
- } while (false)
+ } else \
+ ((void)0)
#define register_string_modulo_op(m_class, m_type) \
- do { \
+ if constexpr (true) { \
register_op<OperatorEvaluatorStringFormat<String, m_class>>(Variant::OP_MODULE, Variant::STRING, m_type); \
register_op<OperatorEvaluatorStringFormat<StringName, m_class>>(Variant::OP_MODULE, Variant::STRING_NAME, m_type); \
- } while (false)
+ } else \
+ ((void)0)
void Variant::_register_variant_operators() {
memset(operator_return_type_table, 0, sizeof(operator_return_type_table));
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 46c450d9f8..dcb94b16b1 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -30,6 +30,7 @@
#include "variant_parser.h"
+#include "core/crypto/crypto_core.h"
#include "core/input/input_event.h"
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
@@ -595,6 +596,82 @@ Error VariantParser::_parse_construct(Stream *p_stream, Vector<T> &r_construct,
return OK;
}
+Error VariantParser::_parse_byte_array(Stream *p_stream, Vector<uint8_t> &r_construct, int &line, String &r_err_str) {
+ Token token;
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_OPEN) {
+ r_err_str = "Expected '(' in constructor";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type == TK_STRING) {
+ // Base64 encoded array.
+ String base64_encoded_string = token.value;
+ int strlen = base64_encoded_string.length();
+ CharString cstr = base64_encoded_string.ascii();
+
+ size_t arr_len = 0;
+ r_construct.resize(strlen / 4 * 3 + 1);
+ uint8_t *w = r_construct.ptrw();
+ Error err = CryptoCore::b64_decode(&w[0], r_construct.size(), &arr_len, (unsigned char *)cstr.get_data(), strlen);
+ if (err) {
+ r_err_str = "Invalid base64-encoded string";
+ return ERR_PARSE_ERROR;
+ }
+ r_construct.resize(arr_len);
+
+ get_token(p_stream, token, line, r_err_str);
+ if (token.type != TK_PARENTHESIS_CLOSE) {
+ r_err_str = "Expected ')' in constructor";
+ return ERR_PARSE_ERROR;
+ }
+
+ } else if (token.type == TK_NUMBER || token.type == TK_IDENTIFIER) {
+ // Individual elements.
+ while (true) {
+ if (token.type != TK_NUMBER) {
+ bool valid = false;
+ if (token.type == TK_IDENTIFIER) {
+ double real = stor_fix(token.value);
+ if (real != -1) {
+ token.type = TK_NUMBER;
+ token.value = real;
+ valid = true;
+ }
+ }
+ if (!valid) {
+ r_err_str = "Expected number in constructor";
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ r_construct.push_back(token.value);
+
+ get_token(p_stream, token, line, r_err_str);
+
+ if (token.type == TK_COMMA) {
+ //do none
+ } else if (token.type == TK_PARENTHESIS_CLOSE) {
+ break;
+ } else {
+ r_err_str = "Expected ',' or ')' in constructor";
+ return ERR_PARSE_ERROR;
+ }
+
+ get_token(p_stream, token, line, r_err_str);
+ }
+ } else if (token.type == TK_PARENTHESIS_CLOSE) {
+ // Empty array.
+ return OK;
+ } else {
+ r_err_str = "Expected base64 string, or list of numbers in constructor";
+ return ERR_PARSE_ERROR;
+ }
+
+ return OK;
+}
+
Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser) {
if (token.type == TK_CURLY_BRACKET_OPEN) {
Dictionary d;
@@ -1027,7 +1104,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
Error err = p_res_parser->ext_func(p_res_parser->userdata, p_stream, res, line, r_err_str);
if (err) {
// If the file is missing, the error can be ignored.
- if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN) {
+ if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN && err != ERR_FILE_CANT_OPEN) {
return err;
}
}
@@ -1148,7 +1225,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
value = array;
} else if (id == "PackedByteArray" || id == "PoolByteArray" || id == "ByteArray") {
Vector<uint8_t> args;
- Error err = _parse_construct<uint8_t>(p_stream, args, line, r_err_str);
+ Error err = _parse_byte_array(p_stream, args, line, r_err_str);
if (err) {
return err;
}
@@ -2012,12 +2089,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
p_recursion_count++;
p_store_string_func(p_store_string_ud, "[");
- int len = array.size();
- for (int i = 0; i < len; i++) {
- if (i > 0) {
+ bool first = true;
+ for (const Variant &var : array) {
+ if (first) {
+ first = false;
+ } else {
p_store_string_func(p_store_string_ud, ", ");
}
- write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
+ write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
}
p_store_string_func(p_store_string_ud, "]");
@@ -2031,17 +2110,11 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
case Variant::PACKED_BYTE_ARRAY: {
p_store_string_func(p_store_string_ud, "PackedByteArray(");
Vector<uint8_t> data = p_variant;
- int len = data.size();
- const uint8_t *ptr = data.ptr();
-
- for (int i = 0; i < len; i++) {
- if (i > 0) {
- p_store_string_func(p_store_string_ud, ", ");
- }
-
- p_store_string_func(p_store_string_ud, itos(ptr[i]));
+ if (data.size() > 0) {
+ p_store_string_func(p_store_string_ud, "\"");
+ p_store_string_func(p_store_string_ud, CryptoCore::b64_encode_str(data.ptr(), data.size()));
+ p_store_string_func(p_store_string_ud, "\"");
}
-
p_store_string_func(p_store_string_ud, ")");
} break;
case Variant::PACKED_INT32_ARRAY: {
diff --git a/core/variant/variant_parser.h b/core/variant/variant_parser.h
index 18448100e0..2f8974849f 100644
--- a/core/variant/variant_parser.h
+++ b/core/variant/variant_parser.h
@@ -141,6 +141,7 @@ private:
template <typename T>
static Error _parse_construct(Stream *p_stream, Vector<T> &r_construct, int &line, String &r_err_str);
+ static Error _parse_byte_array(Stream *p_stream, Vector<uint8_t> &r_construct, int &line, String &r_err_str);
static Error _parse_enginecfg(Stream *p_stream, Vector<String> &strings, int &line, String &r_err_str);
static Error _parse_dictionary(Dictionary &object, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser = nullptr);
static Error _parse_array(Array &array, Stream *p_stream, int &line, String &r_err_str, ResourceParser *p_res_parser = nullptr);