summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp1
-rw-r--r--core/config/project_settings.cpp120
-rw-r--r--core/config/project_settings.h14
-rw-r--r--core/core_bind.cpp30
-rw-r--r--core/core_bind.h18
-rw-r--r--core/core_constants.cpp6
-rw-r--r--core/core_constants.h2
-rw-r--r--core/debugger/engine_debugger.cpp2
-rw-r--r--core/debugger/remote_debugger.cpp41
-rw-r--r--core/debugger/remote_debugger.h1
-rw-r--r--core/doc_data.h45
-rw-r--r--core/error/error_macros.h20
-rw-r--r--core/extension/extension_api_dump.cpp11
-rw-r--r--core/extension/gdextension.cpp35
-rw-r--r--core/extension/gdextension.h25
-rw-r--r--core/extension/gdextension_compat_hashes.cpp134
-rw-r--r--core/extension/gdextension_interface.cpp46
-rw-r--r--core/extension/gdextension_interface.h86
-rw-r--r--core/input/gamecontrollerdb.txt38
-rw-r--r--core/input/input.cpp210
-rw-r--r--core/input/input.h45
-rw-r--r--core/input/input_builders.py4
-rw-r--r--core/input/input_event.cpp100
-rw-r--r--core/input/input_event.h21
-rw-r--r--core/input/input_map.cpp4
-rw-r--r--core/io/file_access.cpp2
-rw-r--r--core/io/file_access_memory.cpp2
-rw-r--r--core/io/file_access_pack.cpp4
-rw-r--r--core/io/http_client.cpp2
-rw-r--r--core/io/image.cpp32
-rw-r--r--core/io/image.h7
-rw-r--r--core/io/image_loader.cpp2
-rw-r--r--core/io/marshalls.cpp4
-rw-r--r--core/io/packet_peer.cpp6
-rw-r--r--core/io/plist.cpp868
-rw-r--r--core/io/plist.h128
-rw-r--r--core/io/resource.cpp124
-rw-r--r--core/io/resource.h7
-rw-r--r--core/io/resource_format_binary.cpp6
-rw-r--r--core/io/resource_loader.cpp61
-rw-r--r--core/io/resource_loader.h6
-rw-r--r--core/io/resource_saver.cpp10
-rw-r--r--core/io/xml_parser.cpp2
-rw-r--r--core/math/a_star.cpp56
-rw-r--r--core/math/a_star_grid_2d.cpp39
-rw-r--r--core/math/a_star_grid_2d.h12
-rw-r--r--core/math/aabb.cpp14
-rw-r--r--core/math/aabb.h6
-rw-r--r--core/math/audio_frame.h110
-rw-r--r--core/math/basis.cpp27
-rw-r--r--core/math/basis.h15
-rw-r--r--core/math/convex_hull.cpp16
-rw-r--r--core/math/dynamic_bvh.h18
-rw-r--r--core/math/geometry_2d.cpp2
-rw-r--r--core/math/geometry_2d.h26
-rw-r--r--core/math/geometry_3d.h16
-rw-r--r--core/math/math_funcs.h16
-rw-r--r--core/math/quaternion.cpp22
-rw-r--r--core/math/quaternion.h5
-rw-r--r--core/math/transform_2d.cpp12
-rw-r--r--core/math/transform_2d.h2
-rw-r--r--core/math/transform_3d.cpp11
-rw-r--r--core/math/transform_3d.h2
-rw-r--r--core/math/vector2.cpp8
-rw-r--r--core/math/vector2i.h8
-rw-r--r--core/math/vector3.cpp2
-rw-r--r--core/math/vector3.h10
-rw-r--r--core/math/vector3i.h11
-rw-r--r--core/math/vector4i.h11
-rw-r--r--core/object/callable_method_pointer.h24
-rw-r--r--core/object/class_db.cpp29
-rw-r--r--core/object/class_db.h21
-rw-r--r--core/object/make_virtuals.py236
-rw-r--r--core/object/message_queue.cpp20
-rw-r--r--core/object/object.cpp17
-rw-r--r--core/object/object.h4
-rw-r--r--core/object/ref_counted.h5
-rw-r--r--core/object/script_language.cpp18
-rw-r--r--core/object/script_language.h6
-rw-r--r--core/object/script_language_extension.cpp3
-rw-r--r--core/object/script_language_extension.h4
-rw-r--r--core/object/undo_redo.cpp20
-rw-r--r--core/object/undo_redo.h4
-rw-r--r--core/object/worker_thread_pool.cpp533
-rw-r--r--core/object/worker_thread_pool.h66
-rw-r--r--core/os/condition_variable.h27
-rw-r--r--core/os/keyboard.cpp4
-rw-r--r--core/os/keyboard.h6
-rw-r--r--core/os/memory.cpp24
-rw-r--r--core/os/memory.h43
-rw-r--r--core/os/mutex.cpp12
-rw-r--r--core/os/mutex.h105
-rw-r--r--core/os/os.cpp57
-rw-r--r--core/os/os.h8
-rw-r--r--core/os/rw_lock.h9
-rw-r--r--core/os/safe_binary_mutex.h124
-rw-r--r--core/os/semaphore.h41
-rw-r--r--core/os/spin_lock.h21
-rw-r--r--core/os/thread.cpp15
-rw-r--r--core/os/thread.h73
-rw-r--r--core/os/time.cpp3
-rw-r--r--core/register_core_types.cpp25
-rw-r--r--core/string/char_range.inc4
-rw-r--r--core/string/translation.cpp16
-rw-r--r--core/string/translation_po.cpp4
-rw-r--r--core/string/ustring.cpp133
-rw-r--r--core/string/ustring.h8
-rw-r--r--core/templates/command_queue_mt.h50
-rw-r--r--core/templates/cowdata.h218
-rw-r--r--core/templates/hashfuncs.h6
-rw-r--r--core/templates/list.h39
-rw-r--r--core/templates/local_vector.h10
-rw-r--r--core/templates/paged_allocator.h14
-rw-r--r--core/templates/paged_array.h37
-rw-r--r--core/templates/rb_map.h18
-rw-r--r--core/templates/ring_buffer.h2
-rw-r--r--core/templates/safe_refcount.h2
-rw-r--r--core/templates/self_list.h6
-rw-r--r--core/templates/vector.h69
-rw-r--r--core/typedefs.h6
-rw-r--r--core/variant/array.cpp6
-rw-r--r--core/variant/binder_common.h9
-rw-r--r--core/variant/callable.cpp3
-rw-r--r--core/variant/dictionary.cpp11
-rw-r--r--core/variant/dictionary.h1
-rw-r--r--core/variant/type_info.h23
-rw-r--r--core/variant/typed_array.h44
-rw-r--r--core/variant/variant.cpp214
-rw-r--r--core/variant/variant.h70
-rw-r--r--core/variant/variant_call.cpp112
-rw-r--r--core/variant/variant_callable.cpp81
-rw-r--r--core/variant/variant_callable.h58
-rw-r--r--core/variant/variant_internal.h152
-rw-r--r--core/variant/variant_op.cpp9
-rw-r--r--core/variant/variant_op.h32
-rw-r--r--core/variant/variant_parser.cpp5
-rw-r--r--core/variant/variant_setget.cpp93
-rw-r--r--core/version.h6
138 files changed, 4445 insertions, 1542 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 24080c056a..203f8c3882 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -110,7 +110,6 @@ Dictionary Engine::get_version_info() const {
dict["hex"] = VERSION_HEX;
dict["status"] = VERSION_STATUS;
dict["build"] = VERSION_BUILD;
- dict["year"] = VERSION_YEAR;
String hash = String(VERSION_HASH);
dict["hash"] = hash.is_empty() ? String("unknown") : hash;
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 93934f2320..974fd5283b 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -95,7 +95,7 @@ const PackedStringArray ProjectSettings::_get_supported_features() {
features.append(VERSION_FULL_CONFIG);
features.append(VERSION_FULL_BUILD);
-#ifdef VULKAN_ENABLED
+#ifdef RD_ENABLED
features.append("Forward Plus");
features.append("Mobile");
#endif
@@ -281,6 +281,11 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
if (autoloads.has(node_name)) {
remove_autoload(node_name);
}
+ } else if (p_name.operator String().begins_with("global_group/")) {
+ String group_name = p_name.operator String().get_slice("/", 1);
+ if (global_groups.has(group_name)) {
+ remove_global_group(group_name);
+ }
}
} else {
if (p_name == CoreStringNames::get_singleton()->_custom_features) {
@@ -327,6 +332,9 @@ bool ProjectSettings::_set(const StringName &p_name, const Variant &p_value) {
autoload.path = path;
}
add_autoload(autoload);
+ } else if (p_name.operator String().begins_with("global_group/")) {
+ String group_name = p_name.operator String().get_slice("/", 1);
+ add_global_group(group_name, p_value);
}
}
@@ -674,6 +682,8 @@ Error ProjectSettings::setup(const String &p_path, const String &p_main_pack, bo
Compression::gzip_level = GLOBAL_GET("compression/formats/gzip/compression_level");
+ load_scene_groups_cache();
+
project_loaded = err == OK;
return err;
}
@@ -1241,6 +1251,73 @@ ProjectSettings::AutoloadInfo ProjectSettings::get_autoload(const StringName &p_
return autoloads[p_name];
}
+const HashMap<StringName, String> &ProjectSettings::get_global_groups_list() const {
+ return global_groups;
+}
+
+void ProjectSettings::add_global_group(const StringName &p_name, const String &p_description) {
+ ERR_FAIL_COND_MSG(p_name == StringName(), "Trying to add global group with no name.");
+ global_groups[p_name] = p_description;
+}
+
+void ProjectSettings::remove_global_group(const StringName &p_name) {
+ ERR_FAIL_COND_MSG(!global_groups.has(p_name), "Trying to remove non-existent global group.");
+ global_groups.erase(p_name);
+}
+
+bool ProjectSettings::has_global_group(const StringName &p_name) const {
+ return global_groups.has(p_name);
+}
+
+void ProjectSettings::remove_scene_groups_cache(const StringName &p_path) {
+ scene_groups_cache.erase(p_path);
+}
+
+void ProjectSettings::add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache) {
+ scene_groups_cache[p_path] = p_cache;
+}
+
+void ProjectSettings::save_scene_groups_cache() {
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ for (const KeyValue<StringName, HashSet<StringName>> &E : scene_groups_cache) {
+ if (E.value.is_empty()) {
+ continue;
+ }
+ Array list;
+ for (const StringName &group : E.value) {
+ list.push_back(group);
+ }
+ cf->set_value(E.key, "groups", list);
+ }
+ cf->save(get_scene_groups_cache_path());
+}
+
+String ProjectSettings::get_scene_groups_cache_path() const {
+ return get_project_data_path().path_join("scene_groups_cache.cfg");
+}
+
+void ProjectSettings::load_scene_groups_cache() {
+ Ref<ConfigFile> cf;
+ cf.instantiate();
+ if (cf->load(get_scene_groups_cache_path()) == OK) {
+ List<String> scene_paths;
+ cf->get_sections(&scene_paths);
+ for (const String &E : scene_paths) {
+ Array scene_groups = cf->get_value(E, "groups", Array());
+ HashSet<StringName> cache;
+ for (int i = 0; i < scene_groups.size(); ++i) {
+ cache.insert(scene_groups[i]);
+ }
+ add_scene_groups_cache(E, cache);
+ }
+ }
+}
+
+const HashMap<StringName, HashSet<StringName>> &ProjectSettings::get_scene_groups_cache() const {
+ return scene_groups_cache;
+}
+
void ProjectSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_setting", "name"), &ProjectSettings::has_setting);
ClassDB::bind_method(D_METHOD("set_setting", "name", "value"), &ProjectSettings::set_setting);
@@ -1295,6 +1372,19 @@ ProjectSettings::ProjectSettings() {
CRASH_COND_MSG(singleton != nullptr, "Instantiating a new ProjectSettings singleton is not supported.");
singleton = this;
+#ifdef TOOLS_ENABLED
+ // Available only at runtime in editor builds. Needs to be processed before anything else to work properly.
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ String editor_features = OS::get_singleton()->get_environment("GODOT_EDITOR_CUSTOM_FEATURES");
+ if (!editor_features.is_empty()) {
+ PackedStringArray feature_list = editor_features.split(",");
+ for (const String &s : feature_list) {
+ custom_features.insert(s);
+ }
+ }
+ }
+#endif
+
GLOBAL_DEF_BASIC("application/config/name", "");
GLOBAL_DEF_BASIC(PropertyInfo(Variant::DICTIONARY, "application/config/name_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary());
GLOBAL_DEF_BASIC(PropertyInfo(Variant::STRING, "application/config/description", PROPERTY_HINT_MULTILINE_TEXT), "");
@@ -1316,8 +1406,8 @@ ProjectSettings::ProjectSettings() {
// - Have a 16:9 aspect ratio,
// - Have both dimensions divisible by 8 to better play along with video recording,
// - Be displayable correctly in windowed mode on a 1366×768 display (tested on Windows 10 with default settings).
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_width", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 1152); // 8K resolution
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_height", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 648); // 8K resolution
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_width", PROPERTY_HINT_RANGE, "1,7680,1,or_greater"), 1152); // 8K resolution
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/viewport_height", PROPERTY_HINT_RANGE, "1,4320,1,or_greater"), 648); // 8K resolution
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "display/window/size/mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen,Exclusive Fullscreen"), 0);
@@ -1385,20 +1475,30 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("debug/settings/crash_handler/message.editor",
String("Please include this when reporting the bug on: https://github.com/godotengine/godot/issues"));
GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/occlusion_culling/bvh_build_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), 2);
- GLOBAL_DEF(PropertyInfo(Variant::INT, "memory/limits/multithreaded_server/rid_pool_prealloc", PROPERTY_HINT_RANGE, "0,500,1"), 60); // No negative and limit to 500 due to crashes.
GLOBAL_DEF_RST("internationalization/rendering/force_right_to_left_layout_direction", false);
- GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Locale,Left-to-Right,Right-to-Left"), 0);
+ GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "internationalization/rendering/root_node_layout_direction", PROPERTY_HINT_ENUM, "Based on Application Locale,Left-to-Right,Right-to-Left,Based on System Locale"), 0);
GLOBAL_DEF(PropertyInfo(Variant::INT, "gui/timers/incremental_search_max_interval_msec", PROPERTY_HINT_RANGE, "0,10000,1,or_greater"), 2000);
GLOBAL_DEF_BASIC("gui/common/snap_controls_to_pixels", true);
GLOBAL_DEF_BASIC("gui/fonts/dynamic_fonts/use_oversampling", true);
- GLOBAL_DEF("rendering/rendering_device/staging_buffer/block_size_kb", 256);
- GLOBAL_DEF("rendering/rendering_device/staging_buffer/max_size_mb", 128);
- GLOBAL_DEF("rendering/rendering_device/staging_buffer/texture_upload_region_size_px", 64);
- GLOBAL_DEF("rendering/rendering_device/pipeline_cache/save_chunk_size_mb", 3.0);
- GLOBAL_DEF("rendering/rendering_device/vulkan/max_descriptors_per_pool", 64);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/frame_queue_size", PROPERTY_HINT_RANGE, "2,3,1"), 2);
+ GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/vsync/swapchain_image_count", PROPERTY_HINT_RANGE, "2,4,1"), 3);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/block_size_kb", PROPERTY_HINT_RANGE, "4,2048,1,or_greater"), 256);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/max_size_mb", PROPERTY_HINT_RANGE, "1,1024,1,or_greater"), 128);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/staging_buffer/texture_upload_region_size_px", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
+ GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/rendering_device/pipeline_cache/save_chunk_size_mb", PROPERTY_HINT_RANGE, "0.000001,64.0,0.001,or_greater"), 3.0);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/rendering_device/vulkan/max_descriptors_per_pool", PROPERTY_HINT_RANGE, "1,256,1,or_greater"), 64);
+
+ GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_resource_descriptors_per_frame", 16384);
+ custom_prop_info["rendering/rendering_device/d3d12/max_resource_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_resource_descriptors_per_frame", PROPERTY_HINT_RANGE, "512,262144");
+ GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", 1024);
+ custom_prop_info["rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_sampler_descriptors_per_frame", PROPERTY_HINT_RANGE, "256,2048");
+ GLOBAL_DEF_RST("rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", 512);
+ custom_prop_info["rendering/rendering_device/d3d12/max_misc_descriptors_per_frame"] = PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/max_misc_descriptors_per_frame", PROPERTY_HINT_RANGE, "32,4096");
+
+ GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/rendering_device/d3d12/agility_sdk_version"), 610);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), 1);
GLOBAL_DEF_BASIC(PropertyInfo(Variant::INT, "rendering/textures/canvas_textures/default_texture_repeat", PROPERTY_HINT_ENUM, "Disable,Enable,Mirror"), 0);
diff --git a/core/config/project_settings.h b/core/config/project_settings.h
index 302df7e8d0..55d5957ad1 100644
--- a/core/config/project_settings.h
+++ b/core/config/project_settings.h
@@ -106,6 +106,8 @@ protected:
LocalVector<String> hidden_prefixes;
HashMap<StringName, AutoloadInfo> autoloads;
+ HashMap<StringName, String> global_groups;
+ HashMap<StringName, HashSet<StringName>> scene_groups_cache;
Array global_class_list;
bool is_global_class_list_loaded = false;
@@ -208,6 +210,18 @@ public:
bool has_autoload(const StringName &p_autoload) const;
AutoloadInfo get_autoload(const StringName &p_name) const;
+ const HashMap<StringName, String> &get_global_groups_list() const;
+ void add_global_group(const StringName &p_name, const String &p_description);
+ void remove_global_group(const StringName &p_name);
+ bool has_global_group(const StringName &p_name) const;
+
+ const HashMap<StringName, HashSet<StringName>> &get_scene_groups_cache() const;
+ void add_scene_groups_cache(const StringName &p_path, const HashSet<StringName> &p_cache);
+ void remove_scene_groups_cache(const StringName &p_path);
+ void save_scene_groups_cache();
+ String get_scene_groups_cache_path() const;
+ void load_scene_groups_cache();
+
ProjectSettings();
~ProjectSettings();
};
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 981d9b0025..e5363f9acc 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -662,6 +662,7 @@ void OS::_bind_methods() {
BIND_ENUM_CONSTANT(RENDERING_DRIVER_VULKAN);
BIND_ENUM_CONSTANT(RENDERING_DRIVER_OPENGL3);
+ BIND_ENUM_CONSTANT(RENDERING_DRIVER_D3D12);
BIND_ENUM_CONSTANT(SYSTEM_DIR_DESKTOP);
BIND_ENUM_CONSTANT(SYSTEM_DIR_DCIM);
@@ -1039,6 +1040,10 @@ Vector<Vector3> Geometry3D::clip_polygon(const Vector<Vector3> &p_points, const
return ::Geometry3D::clip_polygon(p_points, p_plane);
}
+Vector<int32_t> Geometry3D::tetrahedralize_delaunay(const Vector<Vector3> &p_points) {
+ return ::Geometry3D::tetrahedralize_delaunay(p_points);
+}
+
void Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("compute_convex_mesh_points", "planes"), &Geometry3D::compute_convex_mesh_points);
ClassDB::bind_method(D_METHOD("build_box_planes", "extents"), &Geometry3D::build_box_planes);
@@ -1060,6 +1065,7 @@ void Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("segment_intersects_convex", "from", "to", "planes"), &Geometry3D::segment_intersects_convex);
ClassDB::bind_method(D_METHOD("clip_polygon", "points", "plane"), &Geometry3D::clip_polygon);
+ ClassDB::bind_method(D_METHOD("tetrahedralize_delaunay", "points"), &Geometry3D::tetrahedralize_delaunay);
}
////// Marshalls //////
@@ -1369,11 +1375,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const {
}
}
-bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const {
+bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const {
return ::ClassDB::has_signal(p_class, p_signal);
}
-Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const {
+Dictionary ClassDB::class_get_signal(const StringName &p_class, const StringName &p_signal) const {
MethodInfo signal;
if (::ClassDB::get_signal(p_class, p_signal, &signal)) {
return signal.operator Dictionary();
@@ -1382,7 +1388,7 @@ Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) co
}
}
-TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_signal_list(const StringName &p_class, bool p_no_inheritance) const {
List<MethodInfo> signals;
::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1394,7 +1400,7 @@ TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p
return ret;
}
-TypedArray<Dictionary> ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_class, bool p_no_inheritance) const {
List<PropertyInfo> plist;
::ClassDB::get_property_list(p_class, &plist, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1422,11 +1428,11 @@ Error ClassDB::class_set_property(Object *p_object, const StringName &p_property
return OK;
}
-bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const {
+bool ClassDB::class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) const {
return ::ClassDB::has_method(p_class, p_method, p_no_inheritance);
}
-TypedArray<Dictionary> ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const {
+TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class, bool p_no_inheritance) const {
List<MethodInfo> methods;
::ClassDB::get_method_list(p_class, &methods, p_no_inheritance);
TypedArray<Dictionary> ret;
@@ -1507,7 +1513,7 @@ StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, c
return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance);
}
-bool ClassDB::is_class_enabled(StringName p_class) const {
+bool ClassDB::is_class_enabled(const StringName &p_class) const {
return ::ClassDB::is_class_enabled(p_class);
}
@@ -1717,6 +1723,16 @@ bool Engine::is_printing_error_messages() const {
return ::Engine::get_singleton()->is_printing_error_messages();
}
+void Engine::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const {
+ String pf = p_function;
+ if (p_idx == 0 && (pf == "has_singleton" || pf == "get_singleton" || pf == "unregister_singleton")) {
+ for (const String &E : get_singleton_list()) {
+ r_options->push_back(E.quote());
+ }
+ }
+ Object::get_argument_options(p_function, p_idx, r_options);
+}
+
void Engine::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_physics_ticks_per_second", "physics_ticks_per_second"), &Engine::set_physics_ticks_per_second);
ClassDB::bind_method(D_METHOD("get_physics_ticks_per_second"), &Engine::get_physics_ticks_per_second);
diff --git a/core/core_bind.h b/core/core_bind.h
index 5f51b64eb7..94d95f2ce9 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -129,6 +129,7 @@ public:
enum RenderingDriver {
RENDERING_DRIVER_VULKAN,
RENDERING_DRIVER_OPENGL3,
+ RENDERING_DRIVER_D3D12,
};
virtual PackedStringArray get_connected_midi_inputs();
@@ -336,6 +337,7 @@ public:
Vector<Vector3> segment_intersects_convex(const Vector3 &p_from, const Vector3 &p_to, const TypedArray<Plane> &p_planes);
Vector<Vector3> clip_polygon(const Vector<Vector3> &p_points, const Plane &p_plane);
+ Vector<int32_t> tetrahedralize_delaunay(const Vector<Vector3> &p_points);
Geometry3D() { singleton = this; }
};
@@ -433,17 +435,17 @@ public:
bool can_instantiate(const StringName &p_class) const;
Variant instantiate(const StringName &p_class) const;
- bool class_has_signal(StringName p_class, StringName p_signal) const;
- Dictionary class_get_signal(StringName p_class, StringName p_signal) const;
- TypedArray<Dictionary> class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const;
+ bool class_has_signal(const StringName &p_class, const StringName &p_signal) const;
+ Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const;
+ TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const;
- TypedArray<Dictionary> class_get_property_list(StringName p_class, bool p_no_inheritance = false) const;
+ TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const;
Variant class_get_property(Object *p_object, const StringName &p_property) const;
Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const;
- bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const;
+ bool class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const;
- TypedArray<Dictionary> class_get_method_list(StringName p_class, bool p_no_inheritance = false) const;
+ TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const;
PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const;
bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const;
@@ -454,7 +456,7 @@ public:
PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const;
StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const;
- bool is_class_enabled(StringName p_class) const;
+ bool is_class_enabled(const StringName &p_class) const;
ClassDB() {}
~ClassDB() {}
@@ -526,6 +528,8 @@ public:
void set_print_error_messages(bool p_enabled);
bool is_printing_error_messages() const;
+ virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override;
+
Engine() { singleton = this; }
};
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 2f70fdf219..aaabbabfd9 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -507,6 +507,10 @@ void register_global_constants() {
BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, KPAD);
BIND_CORE_BITFIELD_CLASS_FLAG(KeyModifierMask, KEY_MASK, GROUP_SWITCH);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, UNSPECIFIED);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, LEFT);
+ BIND_CORE_ENUM_CLASS_CONSTANT(KeyLocation, KEY_LOCATION, RIGHT);
+
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, NONE);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, LEFT);
BIND_CORE_ENUM_CLASS_CONSTANT(MouseButton, MOUSE_BUTTON, RIGHT);
@@ -845,7 +849,7 @@ bool CoreConstants::is_global_enum(const StringName &p_enum) {
return _global_enums.has(p_enum);
}
-void CoreConstants::get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values) {
+void CoreConstants::get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values) {
ERR_FAIL_NULL_MSG(p_values, "Trying to get enum values with null map.");
ERR_FAIL_COND_MSG(!_global_enums.has(p_enum), "Trying to get values of non-existing enum.");
for (const _CoreConstant &constant : _global_enums[p_enum]) {
diff --git a/core/core_constants.h b/core/core_constants.h
index 51842490c8..82d626c749 100644
--- a/core/core_constants.h
+++ b/core/core_constants.h
@@ -45,7 +45,7 @@ public:
static bool is_global_constant(const StringName &p_name);
static int get_global_constant_index(const StringName &p_name);
static bool is_global_enum(const StringName &p_enum);
- static void get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values);
+ static void get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values);
};
#endif // CORE_CONSTANTS_H
diff --git a/core/debugger/engine_debugger.cpp b/core/debugger/engine_debugger.cpp
index 32dc060aa2..0cce44d02f 100644
--- a/core/debugger/engine_debugger.cpp
+++ b/core/debugger/engine_debugger.cpp
@@ -162,7 +162,7 @@ void EngineDebugger::initialize(const String &p_uri, bool p_skip_breakpoints, Ve
singleton_script_debugger->set_skip_breakpoints(p_skip_breakpoints);
for (int i = 0; i < p_breakpoints.size(); i++) {
- String bp = p_breakpoints[i];
+ const String &bp = p_breakpoints[i];
int sp = bp.rfind(":");
ERR_CONTINUE_MSG(sp == -1, "Invalid breakpoint: '" + bp + "', expected file:line format.");
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index a817ea871d..6f2036705d 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -36,6 +36,7 @@
#include "core/debugger/engine_profiler.h"
#include "core/debugger/script_debugger.h"
#include "core/input/input.h"
+#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
@@ -435,9 +436,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
messages.insert(Thread::get_caller_id(), List<Message>());
}
- mutex.lock();
while (is_peer_connected()) {
- mutex.unlock();
flush_output();
_poll_messages();
@@ -515,8 +514,9 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
_send_stack_vars(globals, globals_vals, 2);
} else if (command == "reload_scripts") {
+ script_paths_to_reload = data;
+ } else if (command == "reload_all_scripts") {
reload_all_scripts = true;
-
} else if (command == "breakpoint") {
ERR_FAIL_COND(data.size() < 3);
bool set = data[2];
@@ -527,7 +527,7 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
}
} else if (command == "set_skip_breakpoints") {
- ERR_FAIL_COND(data.size() < 1);
+ ERR_FAIL_COND(data.is_empty());
script_debugger->set_skip_breakpoints(data[0]);
} else {
bool captured = false;
@@ -591,19 +591,36 @@ void RemoteDebugger::poll_events(bool p_is_idle) {
}
// Reload scripts during idle poll only.
- if (p_is_idle && reload_all_scripts) {
- for (int i = 0; i < ScriptServer::get_language_count(); i++) {
- ScriptServer::get_language(i)->reload_all_scripts();
+ if (p_is_idle) {
+ if (reload_all_scripts) {
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_all_scripts();
+ }
+ reload_all_scripts = false;
+ } else if (!script_paths_to_reload.is_empty()) {
+ Array scripts_to_reload;
+ for (int i = 0; i < script_paths_to_reload.size(); ++i) {
+ String path = script_paths_to_reload[i];
+ Error err = OK;
+ Ref<Script> script = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &err);
+ ERR_CONTINUE_MSG(err != OK, vformat("Could not reload script '%s': %s", path, error_names[err]));
+ ERR_CONTINUE_MSG(script.is_null(), vformat("Could not reload script '%s': Not a script!", path, error_names[err]));
+ scripts_to_reload.push_back(script);
+ }
+ for (int i = 0; i < ScriptServer::get_language_count(); i++) {
+ ScriptServer::get_language(i)->reload_scripts(scripts_to_reload, true);
+ }
}
- reload_all_scripts = false;
+ script_paths_to_reload.clear();
}
}
Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
r_captured = true;
if (p_cmd == "reload_scripts") {
+ script_paths_to_reload = p_data;
+ } else if (p_cmd == "reload_all_scripts") {
reload_all_scripts = true;
-
} else if (p_cmd == "breakpoint") {
ERR_FAIL_COND_V(p_data.size() < 3, ERR_INVALID_DATA);
bool set = p_data[2];
@@ -614,7 +631,7 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo
}
} else if (p_cmd == "set_skip_breakpoints") {
- ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA);
script_debugger->set_skip_breakpoints(p_data[0]);
} else if (p_cmd == "break") {
script_debugger->debug(script_debugger->get_break_language());
@@ -626,7 +643,7 @@ Error RemoteDebugger::_core_capture(const String &p_cmd, const Array &p_data, bo
Error RemoteDebugger::_profiler_capture(const String &p_cmd, const Array &p_data, bool &r_captured) {
r_captured = false;
- ERR_FAIL_COND_V(p_data.size() < 1, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(p_data.is_empty(), ERR_INVALID_DATA);
ERR_FAIL_COND_V(p_data[0].get_type() != Variant::BOOL, ERR_INVALID_DATA);
ERR_FAIL_COND_V(!has_profiler(p_cmd), ERR_UNAVAILABLE);
Array opts;
@@ -648,7 +665,7 @@ RemoteDebugger::RemoteDebugger(Ref<RemoteDebuggerPeer> p_peer) {
// Performance Profiler
Object *perf = Engine::get_singleton()->get_singleton_object("Performance");
if (perf) {
- performance_profiler = Ref<PerformanceProfiler>(memnew(PerformanceProfiler(perf)));
+ performance_profiler.instantiate(perf);
performance_profiler->bind("performance");
profiler_enable("performance", true);
}
diff --git a/core/debugger/remote_debugger.h b/core/debugger/remote_debugger.h
index 7c399178c6..519a90e7cc 100644
--- a/core/debugger/remote_debugger.h
+++ b/core/debugger/remote_debugger.h
@@ -74,6 +74,7 @@ private:
int warn_count = 0;
int last_reset = 0;
bool reload_all_scripts = false;
+ Array script_paths_to_reload;
// Make handlers and send_message thread safe.
Mutex mutex;
diff --git a/core/doc_data.h b/core/doc_data.h
index b8c92a4b67..45463f5931 100644
--- a/core/doc_data.h
+++ b/core/doc_data.h
@@ -117,6 +117,7 @@ public:
bool is_experimental = false;
Vector<ArgumentDoc> arguments;
Vector<int> errors_returned;
+ String keywords;
bool operator<(const MethodDoc &p_method) const {
if (name == p_method.name) {
// Must be an operator or a constructor since there is no other overloading
@@ -195,6 +196,10 @@ public:
doc.errors_returned.push_back(errors_returned[i]);
}
+ if (p_dict.has("keywords")) {
+ doc.keywords = p_dict["keywords"];
+ }
+
return doc;
}
static Dictionary to_dict(const MethodDoc &p_doc) {
@@ -225,6 +230,10 @@ public:
dict["is_experimental"] = p_doc.is_experimental;
+ if (!p_doc.keywords.is_empty()) {
+ dict["keywords"] = p_doc.keywords;
+ }
+
if (!p_doc.arguments.is_empty()) {
Array arguments;
for (int i = 0; i < p_doc.arguments.size(); i++) {
@@ -254,6 +263,7 @@ public:
String description;
bool is_deprecated = false;
bool is_experimental = false;
+ String keywords;
bool operator<(const ConstantDoc &p_const) const {
return name < p_const.name;
}
@@ -291,6 +301,10 @@ public:
doc.is_experimental = p_dict["is_experimental"];
}
+ if (p_dict.has("keywords")) {
+ doc.keywords = p_dict["keywords"];
+ }
+
return doc;
}
static Dictionary to_dict(const ConstantDoc &p_doc) {
@@ -319,6 +333,10 @@ public:
dict["is_experimental"] = p_doc.is_experimental;
+ if (!p_doc.keywords.is_empty()) {
+ dict["keywords"] = p_doc.keywords;
+ }
+
return dict;
}
};
@@ -335,6 +353,7 @@ public:
String overrides;
bool is_deprecated = false;
bool is_experimental = false;
+ String keywords;
bool operator<(const PropertyDoc &p_prop) const {
return name.naturalcasecmp_to(p_prop.name) < 0;
}
@@ -388,6 +407,10 @@ public:
doc.is_experimental = p_dict["is_experimental"];
}
+ if (p_dict.has("keywords")) {
+ doc.keywords = p_dict["keywords"];
+ }
+
return doc;
}
static Dictionary to_dict(const PropertyDoc &p_doc) {
@@ -432,6 +455,10 @@ public:
dict["is_experimental"] = p_doc.is_experimental;
+ if (!p_doc.keywords.is_empty()) {
+ dict["keywords"] = p_doc.keywords;
+ }
+
return dict;
}
};
@@ -442,6 +469,7 @@ public:
String data_type;
String description;
String default_value;
+ String keywords;
bool operator<(const ThemeItemDoc &p_theme_item) const {
// First sort by the data type, then by name.
if (data_type == p_theme_item.data_type) {
@@ -472,6 +500,10 @@ public:
doc.default_value = p_dict["default_value"];
}
+ if (p_dict.has("keywords")) {
+ doc.keywords = p_dict["keywords"];
+ }
+
return doc;
}
static Dictionary to_dict(const ThemeItemDoc &p_doc) {
@@ -497,6 +529,10 @@ public:
dict["default_value"] = p_doc.default_value;
}
+ if (!p_doc.keywords.is_empty()) {
+ dict["keywords"] = p_doc.keywords;
+ }
+
return dict;
}
};
@@ -573,6 +609,7 @@ public:
String inherits;
String brief_description;
String description;
+ String keywords;
Vector<TutorialDoc> tutorials;
Vector<MethodDoc> constructors;
Vector<MethodDoc> methods;
@@ -609,6 +646,10 @@ public:
doc.description = p_dict["description"];
}
+ if (p_dict.has("keywords")) {
+ doc.keywords = p_dict["keywords"];
+ }
+
Array tutorials;
if (p_dict.has("tutorials")) {
tutorials = p_dict["tutorials"];
@@ -816,6 +857,10 @@ public:
dict["script_path"] = p_doc.script_path;
}
+ if (!p_doc.keywords.is_empty()) {
+ dict["keywords"] = p_doc.keywords;
+ }
+
return dict;
}
};
diff --git a/core/error/error_macros.h b/core/error/error_macros.h
index c8182975d5..ab7dbcbd44 100644
--- a/core/error/error_macros.h
+++ b/core/error/error_macros.h
@@ -730,6 +730,16 @@ void _err_flush_stdout();
} else \
((void)0)
+/**
+ * Warns about `m_msg` only when verbose mode is enabled.
+ */
+#define WARN_VERBOSE(m_msg) \
+ { \
+ if (is_print_verbose_enabled()) { \
+ WARN_PRINT(m_msg); \
+ } \
+ }
+
// Print deprecated warning message macros.
/**
@@ -812,4 +822,14 @@ void _err_flush_stdout();
#define DEV_ASSERT(m_cond)
#endif
+#ifdef DEV_ENABLED
+#define DEV_CHECK_ONCE(m_cond) \
+ if (unlikely(!(m_cond))) { \
+ ERR_PRINT_ONCE("DEV_CHECK_ONCE failed \"" _STR(m_cond) "\" is false."); \
+ } else \
+ ((void)0)
+#else
+#define DEV_CHECK_ONCE(m_cond)
+#endif
+
#endif // ERROR_MACROS_H
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index f3e988633c..543dabfb16 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -742,14 +742,19 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
Dictionary d2;
String operator_name = Variant::get_operator_name(Variant::Operator(k));
d2["name"] = operator_name;
- if (k != Variant::OP_NEGATE && k != Variant::OP_POSITIVE && k != Variant::OP_NOT && k != Variant::OP_BIT_NEGATE) {
- d2["right_type"] = get_builtin_or_variant_type_name(Variant::Type(j));
+
+ String right_type_name = get_builtin_or_variant_type_name(Variant::Type(j));
+ bool is_unary = k == Variant::OP_NEGATE || k == Variant::OP_POSITIVE || k == Variant::OP_NOT || k == Variant::OP_BIT_NEGATE;
+ if (!is_unary) {
+ d2["right_type"] = right_type_name;
}
+
d2["return_type"] = get_builtin_or_variant_type_name(Variant::get_operator_return_type(Variant::Operator(k), type, Variant::Type(j)));
if (p_include_docs && builtin_doc != nullptr) {
for (const DocData::MethodDoc &operator_doc : builtin_doc->operators) {
- if (operator_doc.name == "operator " + operator_name) {
+ if (operator_doc.name == "operator " + operator_name &&
+ (is_unary || operator_doc.arguments[0].type == right_type_name)) {
d2["description"] = fix_doc_description(operator_doc.description);
break;
}
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 2bac1f6592..53007bf5bf 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -518,6 +518,12 @@ void GDExtension::_register_extension_class_method(GDExtensionClassLibraryPtr p_
ClassDB::bind_method_custom(class_name, method);
}
+
+void GDExtension::_register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info) {
+ StringName class_name = *reinterpret_cast<const StringName *>(p_class_name);
+ ClassDB::add_extension_class_virtual_method(class_name, p_method_info);
+}
+
void GDExtension::_register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield) {
GDExtension *self = reinterpret_cast<GDExtension *>(p_library);
@@ -653,6 +659,8 @@ void GDExtension::_unregister_extension_class(GDExtensionClassLibraryPtr p_libra
if (!ext->is_reloading) {
self->extension_classes.erase(class_name);
}
+
+ GDExtensionEditorHelp::remove_class(class_name);
#else
self->extension_classes.erase(class_name);
#endif
@@ -666,12 +674,12 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte
HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
-void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
+void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
}
-GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
+GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) {
GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
return *function;
@@ -715,10 +723,8 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
#endif
Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &library_path);
- if (err != OK) {
- ERR_PRINT("GDExtension dynamic library not found: " + abs_path);
- return err;
- }
+ ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
// If we copied the file, let's change the library path to point at the original,
@@ -794,6 +800,9 @@ void GDExtension::deinitialize_library(InitializationLevel p_level) {
ERR_FAIL_COND(p_level > int32_t(level_initialized));
level_initialized = int32_t(p_level) - 1;
+
+ ERR_FAIL_NULL(initialization.deinitialize);
+
initialization.deinitialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
@@ -834,6 +843,7 @@ void GDExtension::initialize_gdextensions() {
#endif // DISABLE_DEPRECATED
register_interface_function("classdb_register_extension_class2", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class2);
register_interface_function("classdb_register_extension_class_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_method);
+ register_interface_function("classdb_register_extension_class_virtual_method", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_virtual_method);
register_interface_function("classdb_register_extension_class_integer_constant", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_integer_constant);
register_interface_function("classdb_register_extension_class_property", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property);
register_interface_function("classdb_register_extension_class_property_indexed", (GDExtensionInterfaceFunctionPtr)&GDExtension::_register_extension_class_property_indexed);
@@ -1198,4 +1208,17 @@ void GDExtensionEditorPlugins::remove_extension_class(const StringName &p_class_
extension_classes.erase(p_class_name);
}
}
+
+GDExtensionEditorHelp::EditorHelpLoadXmlBufferFunc GDExtensionEditorHelp::editor_help_load_xml_buffer = nullptr;
+GDExtensionEditorHelp::EditorHelpRemoveClassFunc GDExtensionEditorHelp::editor_help_remove_class = nullptr;
+
+void GDExtensionEditorHelp::load_xml_buffer(const uint8_t *p_buffer, int p_size) {
+ ERR_FAIL_NULL(editor_help_load_xml_buffer);
+ editor_help_load_xml_buffer(p_buffer, p_size);
+}
+
+void GDExtensionEditorHelp::remove_class(const String &p_class) {
+ ERR_FAIL_NULL(editor_help_remove_class);
+ editor_help_remove_class(p_class);
+}
#endif // TOOLS_ENABLED
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 0d20b8e50c..dbb39acb2e 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -77,6 +77,7 @@ class GDExtension : public Resource {
static void _register_extension_class2(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs);
static void _register_extension_class_internal(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_parent_class_name, const GDExtensionClassCreationInfo2 *p_extension_funcs, const ClassCreationDeprecatedInfo *p_deprecated_funcs = nullptr);
static void _register_extension_class_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
+ static void _register_extension_class_virtual_method(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
static void _register_extension_class_integer_constant(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, GDExtensionConstStringNamePtr p_enum_name, GDExtensionConstStringNamePtr p_constant_name, GDExtensionInt p_constant_value, GDExtensionBool p_is_bitfield);
static void _register_extension_class_property(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter);
static void _register_extension_class_property_indexed(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionPropertyInfo *p_info, GDExtensionConstStringNamePtr p_setter, GDExtensionConstStringNamePtr p_getter, GDExtensionInt p_index);
@@ -154,8 +155,8 @@ public:
void initialize_library(InitializationLevel p_level);
void deinitialize_library(InitializationLevel p_level);
- static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer);
- static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name);
+ static void register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer);
+ static GDExtensionInterfaceFunctionPtr get_interface_function(const StringName &p_function_name);
static void initialize_gdextensions();
static void finalize_gdextensions();
@@ -197,6 +198,26 @@ public:
return extension_classes;
}
};
+
+class GDExtensionEditorHelp {
+protected:
+ friend class EditorHelp;
+
+ // Similarly to EditorNode above, we need to be able to ask EditorHelp to parse
+ // new documentation data. Note though that, differently from EditorHelp, this
+ // is initialized even _before_ it gets instantiated, as we need to rely on
+ // this method while initializing the engine.
+ typedef void (*EditorHelpLoadXmlBufferFunc)(const uint8_t *p_buffer, int p_size);
+ static EditorHelpLoadXmlBufferFunc editor_help_load_xml_buffer;
+
+ typedef void (*EditorHelpRemoveClassFunc)(const String &p_class);
+ static EditorHelpRemoveClassFunc editor_help_remove_class;
+
+public:
+ static void load_xml_buffer(const uint8_t *p_buffer, int p_size);
+ static void remove_class(const String &p_class);
+};
+
#endif // TOOLS_ENABLED
#endif // GDEXTENSION_H
diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp
index dd4cd20d09..ebbf795070 100644
--- a/core/extension/gdextension_compat_hashes.cpp
+++ b/core/extension/gdextension_compat_hashes.cpp
@@ -65,7 +65,7 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
if (p_check_valid) {
MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash);
if (!mb) {
- WARN_PRINT(vformat("Compatibility hash %d mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, mapping.current_hash));
+ WARN_PRINT(vformat("Compatibility hash %d for %s::%s() mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, p_class, p_method, mapping.current_hash));
continue;
}
}
@@ -107,7 +107,11 @@ void GDExtensionCompatHashes::initialize() {
{ "add_track", 2393815928, 3843682357 },
{ "track_insert_key", 1985425300, 808952278 },
{ "track_find_key", 3898229885, 3245197284 },
+#ifdef REAL_T_IS_DOUBLE
+ { "bezier_track_insert_key", 1057544502, 3767441357 },
+#else
{ "bezier_track_insert_key", 1057544502, 3656773645 },
+#endif
{ "bezier_track_set_key_in_handle", 1028302688, 1719223284 },
{ "bezier_track_set_key_out_handle", 1028302688, 1719223284 },
{ "audio_track_insert_key", 3489962123, 4021027286 },
@@ -125,10 +129,18 @@ void GDExtensionCompatHashes::initialize() {
{ "add_triangle", 642454959, 753017335 },
});
mappings.insert("AnimationNodeBlendTree", {
+#ifdef REAL_T_IS_DOUBLE
+ { "add_node", 2055804584, 1407702499 },
+#else
{ "add_node", 2055804584, 1980270704 },
+#endif
});
mappings.insert("AnimationNodeStateMachine", {
+#ifdef REAL_T_IS_DOUBLE
+ { "add_node", 2055804584, 1407702499 },
+#else
{ "add_node", 2055804584, 1980270704 },
+#endif
});
mappings.insert("AnimationNodeStateMachinePlayback", {
{ "travel", 3683006648, 3823612587 },
@@ -177,8 +189,13 @@ void GDExtensionCompatHashes::initialize() {
{ "draw_multiline_string_outline", 3717870722, 1912318525 },
{ "draw_char", 2329089032, 3339793283 },
{ "draw_char_outline", 419453826, 3302344391 },
+#ifdef REAL_T_IS_DOUBLE
+ { "draw_mesh", 1634855856, 4036154158 },
+ { "draw_set_transform", 3283884939, 156553079 },
+#else
{ "draw_mesh", 1634855856, 153818295 },
{ "draw_set_transform", 3283884939, 288975085 },
+#endif
{ "draw_animation_slice", 2295343543, 3112831842 },
});
mappings.insert("CodeEdit", {
@@ -210,10 +227,18 @@ void GDExtensionCompatHashes::initialize() {
{ "add_point", 2766148617, 434072736 },
});
mappings.insert("Curve2D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "add_point", 529706502, 3343370600 },
+#else
{ "add_point", 2437345566, 4175465202 },
+#endif
});
mappings.insert("Curve3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "add_point", 3544159631, 917388502 },
+#else
{ "add_point", 3836314258, 2931053748 },
+#endif
});
mappings.insert("DirAccess", {
{ "list_dir_begin", 2018049411, 2610976713 },
@@ -253,8 +278,13 @@ void GDExtensionCompatHashes::initialize() {
{ "window_set_ime_active", 450484987, 1661950165 },
{ "window_set_ime_position", 3614040015, 2019273902 },
{ "window_set_vsync_mode", 1708924624, 2179333492 },
- { "virtual_keyboard_show", 860410478, 3042891259 },
+#ifdef REAL_T_IS_DOUBLE
+ { "cursor_set_custom_image", 1358907026, 4163678968 },
+ { "virtual_keyboard_show", 384539973, 1323934605 },
+#else
{ "cursor_set_custom_image", 1358907026, 1816663697 },
+ { "virtual_keyboard_show", 860410478, 3042891259 },
+#endif
});
mappings.insert("ENetConnection", {
{ "create_host_bound", 866250949, 1515002313 },
@@ -289,7 +319,11 @@ void GDExtensionCompatHashes::initialize() {
});
mappings.insert("EditorNode3DGizmo", {
{ "add_lines", 302451090, 2910971437 },
+ #ifdef REAL_T_IS_DOUBLE
+ { "add_mesh", 3332776472, 2161761131 },
+ #else
{ "add_mesh", 1868867708, 1579955111 },
+ #endif
{ "add_unscaled_billboard", 3719733075, 520007164 },
});
mappings.insert("EditorNode3DGizmoPlugin", {
@@ -316,7 +350,6 @@ void GDExtensionCompatHashes::initialize() {
{ "add_filter", 233059325, 3388804757 },
});
mappings.insert("Font", {
- { "find_variation", 1222433716, 3344325384 },
{ "get_string_size", 3678918099, 1868866121 },
{ "get_multiline_string_size", 2427690650, 519636710 },
{ "draw_string", 2565402639, 1983721962 },
@@ -325,8 +358,13 @@ void GDExtensionCompatHashes::initialize() {
{ "draw_multiline_string_outline", 1649790182, 3206388178 },
{ "draw_char", 1462476057, 3815617597 },
{ "draw_char_outline", 4161008124, 209525354 },
+ #ifdef REAL_T_IS_DOUBLE
+ { "find_variation", 625117670, 2196349508 },
+ #else
+ { "find_variation", 1222433716, 3344325384 },
// Pre-existing compatibility hash.
{ "find_variation", 1149405976, 1851767612 },
+ #endif
});
mappings.insert("GLTFDocument", {
{ "append_from_file", 1862991421, 866380864 },
@@ -380,11 +418,19 @@ void GDExtensionCompatHashes::initialize() {
{ "get_vector", 1517139831, 2479607902 },
{ "start_joy_vibration", 1890603622, 2576575033 },
{ "action_press", 573731101, 1713091165 },
+#ifdef REAL_T_IS_DOUBLE
+ { "set_custom_mouse_cursor", 3489634142, 1277868338 },
+#else
{ "set_custom_mouse_cursor", 3489634142, 703945977 },
+#endif
});
mappings.insert("InputEvent", {
{ "is_match", 3392494811, 1754951977 },
+#ifdef REAL_T_IS_DOUBLE
+ { "xformed_by", 2747409789, 3242949850 },
+#else
{ "xformed_by", 2747409789, 1282766827 },
+#endif
});
mappings.insert("InputMap", {
{ "add_action", 573731101, 4100757082 },
@@ -429,8 +475,13 @@ void GDExtensionCompatHashes::initialize() {
{ "set_multiplayer_authority", 4023243586, 972357352 },
});
mappings.insert("Node3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "look_at", 136915519, 819337406 },
+ { "look_at_from_position", 4067663783, 1809580162 },
+#else
{ "look_at", 3123400617, 2882425029 },
{ "look_at_from_position", 4067663783, 2086826090 },
+#endif
});
mappings.insert("Noise", {
{ "get_image", 2569233413, 3180683109 },
@@ -470,7 +521,11 @@ void GDExtensionCompatHashes::initialize() {
{ "add_custom_monitor", 2865980031, 4099036814 },
});
mappings.insert("PhysicalBone3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "apply_impulse", 1002852006, 2485728502 },
+#else
{ "apply_impulse", 1002852006, 2754756483 },
+#endif
});
mappings.insert("PhysicsBody2D", {
{ "move_and_collide", 1529961754, 3681923724 },
@@ -481,14 +536,26 @@ void GDExtensionCompatHashes::initialize() {
{ "test_move", 680299713, 2481691619 },
});
mappings.insert("PhysicsDirectBodyState2D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "apply_impulse", 496058220, 1271588277 },
+ { "apply_force", 496058220, 1271588277 },
+ { "add_constant_force", 496058220, 1271588277 },
+#else
{ "apply_impulse", 496058220, 4288681949 },
{ "apply_force", 496058220, 4288681949 },
{ "add_constant_force", 496058220, 4288681949 },
+#endif
});
mappings.insert("PhysicsDirectBodyState3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "apply_impulse", 1002852006, 2485728502 },
+ { "apply_force", 1002852006, 2485728502 },
+ { "add_constant_force", 1002852006, 2485728502 },
+#else
{ "apply_impulse", 1002852006, 2754756483 },
{ "apply_force", 1002852006, 2754756483 },
{ "add_constant_force", 1002852006, 2754756483 },
+#endif
});
mappings.insert("PhysicsDirectSpaceState2D", {
{ "intersect_point", 3278207904, 2118456068 },
@@ -507,21 +574,37 @@ void GDExtensionCompatHashes::initialize() {
{ "create", 680321959, 3110599579 },
});
mappings.insert("PhysicsServer2D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "area_add_shape", 754862190, 3597527023 },
+ { "body_add_shape", 754862190, 3597527023 },
+ { "body_apply_impulse", 34330743, 1124035137 },
+ { "body_apply_force", 34330743, 1124035137 },
+ { "body_add_constant_force", 34330743, 1124035137 },
+#else
{ "area_add_shape", 754862190, 339056240 },
{ "body_add_shape", 754862190, 339056240 },
{ "body_apply_impulse", 34330743, 205485391 },
{ "body_apply_force", 34330743, 205485391 },
{ "body_add_constant_force", 34330743, 205485391 },
+#endif
{ "joint_make_pin", 2288600450, 1612646186 },
{ "joint_make_groove", 3573265764, 481430435 },
{ "joint_make_damped_spring", 206603952, 1994657646 },
});
mappings.insert("PhysicsServer3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "area_add_shape", 4040559639, 183938777 },
+ { "body_add_shape", 4040559639, 183938777 },
+ { "body_apply_impulse", 110375048, 2238283471 },
+ { "body_apply_force", 110375048, 2238283471 },
+ { "body_add_constant_force", 110375048, 2238283471 },
+#else
{ "area_add_shape", 4040559639, 3711419014 },
{ "body_add_shape", 4040559639, 3711419014 },
{ "body_apply_impulse", 110375048, 390416203 },
{ "body_apply_force", 110375048, 390416203 },
{ "body_add_constant_force", 110375048, 390416203 },
+#endif
});
mappings.insert("PopupMenu", {
{ "add_item", 3224536192, 3674230041 },
@@ -581,10 +664,16 @@ void GDExtensionCompatHashes::initialize() {
{ "buffer_get_data", 125363422, 3101830688 },
{ "render_pipeline_create", 2911419500, 2385451958 },
{ "compute_pipeline_create", 403593840, 1448838280 },
+ { "draw_list_draw", 3710874499, 4230067973 },
+#ifdef REAL_T_IS_DOUBLE
+ { "draw_list_begin", 4252992020, 848735039 },
+ { "draw_list_begin_split", 832527510, 2228306807 },
+ { "draw_list_enable_scissor", 338791288, 730833978 },
+#else
{ "draw_list_begin", 4252992020, 2468082605 },
{ "draw_list_begin_split", 832527510, 2406300660 },
- { "draw_list_draw", 3710874499, 4230067973 },
{ "draw_list_enable_scissor", 338791288, 244650101 },
+#endif
});
mappings.insert("RenderingServer", {
{ "texture_rd_create", 3291180269, 1434128712 },
@@ -592,12 +681,10 @@ void GDExtensionCompatHashes::initialize() {
{ "shader_get_default_texture_parameter", 2523186822, 1464608890 },
{ "mesh_create_from_surfaces", 4007581507, 4291747531 },
{ "mesh_add_surface_from_arrays", 1247008646, 2342446560 },
- { "viewport_attach_to_screen", 1278520651, 1062245816 },
{ "environment_set_ambient_light", 491659071, 1214961493 },
{ "instances_cull_aabb", 2031554939, 2570105777 },
{ "instances_cull_ray", 3388524336, 2208759584 },
{ "instances_cull_convex", 3690700105, 2488539944 },
- { "canvas_item_set_custom_rect", 2180266943, 1333997032 },
{ "canvas_item_add_line", 2843922985, 1819681853 },
{ "canvas_item_add_polyline", 3438017257, 3098767073 },
{ "canvas_item_add_multiline", 3176074788, 2088642721 },
@@ -607,11 +694,19 @@ void GDExtensionCompatHashes::initialize() {
{ "canvas_item_add_nine_patch", 904428547, 389957886 },
{ "canvas_item_add_polygon", 2907936855, 3580000528 },
{ "canvas_item_add_triangle_array", 749685193, 660261329 },
- { "canvas_item_add_mesh", 3548053052, 316450961 },
{ "canvas_item_add_multimesh", 1541595251, 2131855138 },
{ "canvas_item_add_animation_slice", 4107531031, 2646834499 },
{ "canvas_item_set_canvas_group_mode", 41973386, 3973586316 },
{ "set_boot_image", 2244367877, 3759744527 },
+#ifdef REAL_T_IS_DOUBLE
+ { "viewport_attach_to_screen", 1410474027, 2248302004 },
+ { "canvas_item_set_custom_rect", 2180266943, 1134449082 },
+ { "canvas_item_add_mesh", 3877492181, 3024949314 },
+#else
+ { "viewport_attach_to_screen", 1278520651, 1062245816 },
+ { "canvas_item_set_custom_rect", 2180266943, 1333997032 },
+ { "canvas_item_add_mesh", 3548053052, 316450961 },
+#endif
});
mappings.insert("ResourceLoader", {
{ "load_threaded_request", 1939848623, 3614384323 },
@@ -623,23 +718,40 @@ void GDExtensionCompatHashes::initialize() {
{ "save", 2303056517, 2983274697 },
});
mappings.insert("RichTextLabel", {
- { "add_image", 3346058748, 3580801207 },
{ "push_font", 814287596, 2347424842 },
{ "push_paragraph", 3218895358, 3089306873 },
{ "push_list", 4036303897, 3017143144 },
{ "push_table", 1125058220, 2623499273 },
- { "push_dropcap", 311501835, 4061635501 },
{ "set_table_column_expand", 4132157579, 2185176273 },
+#ifdef REAL_T_IS_DOUBLE
+ { "add_image", 3346058748, 1507062345 },
+ { "push_dropcap", 981432822, 763534173 },
+#else
+ { "add_image", 3346058748, 3580801207 },
+ { "push_dropcap", 311501835, 4061635501 },
+#endif
});
mappings.insert("RigidBody2D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "apply_impulse", 496058220, 1271588277 },
+ { "apply_force", 496058220, 1271588277 },
+ { "add_constant_force", 496058220, 1271588277 },
+#else
{ "apply_impulse", 496058220, 4288681949 },
{ "apply_force", 496058220, 4288681949 },
{ "add_constant_force", 496058220, 4288681949 },
+#endif
});
mappings.insert("RigidBody3D", {
+#ifdef REAL_T_IS_DOUBLE
+ { "apply_impulse", 1002852006, 2485728502 },
+ { "apply_force", 1002852006, 2485728502 },
+ { "add_constant_force", 1002852006, 2485728502 },
+#else
{ "apply_impulse", 1002852006, 2754756483 },
{ "apply_force", 1002852006, 2754756483 },
{ "add_constant_force", 1002852006, 2754756483 },
+#endif
});
mappings.insert("SceneMultiplayer", {
{ "send_bytes", 2742700601, 1307428718 },
@@ -699,7 +811,11 @@ void GDExtensionCompatHashes::initialize() {
{ "draw_outline", 1364491366, 1343401456 },
});
mappings.insert("TextParagraph", {
+#ifdef REAL_T_IS_DOUBLE
+ { "set_dropcap", 2613124475, 2897844600 },
+#else
{ "set_dropcap", 2613124475, 2498990330 },
+#endif
{ "add_string", 867188035, 621426851 },
{ "add_object", 735420116, 1316529304 },
{ "resize_object", 960819067, 2095776372 },
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index e02e7aa701..c6d7779473 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -42,6 +42,8 @@
#include "core/variant/variant.h"
#include "core/version.h"
+#include <string.h>
+
class CallableCustomExtension : public CallableCustom {
void *userdata;
void *token;
@@ -1192,6 +1194,33 @@ static GDObjectInstanceID gdextension_object_get_instance_id(GDExtensionConstObj
return (GDObjectInstanceID)o->get_instance_id();
}
+static GDExtensionBool gdextension_object_has_script_method(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method) {
+ Object *o = (Object *)p_object;
+ const StringName method = *reinterpret_cast<const StringName *>(p_method);
+
+ ScriptInstance *script_instance = o->get_script_instance();
+ if (script_instance) {
+ return script_instance->has_method(method);
+ }
+ return false;
+}
+
+static void gdextension_object_call_script_method(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error) {
+ Object *o = (Object *)p_object;
+ const StringName method = *reinterpret_cast<const StringName *>(p_method);
+ const Variant **args = (const Variant **)p_args;
+
+ Callable::CallError error;
+ memnew_placement(r_return, Variant);
+ *(Variant *)r_return = o->callp(method, args, p_argument_count, error);
+
+ if (r_error) {
+ r_error->error = (GDExtensionCallErrorType)(error.error);
+ r_error->argument = error.argument;
+ r_error->expected = error.expected;
+ }
+}
+
static GDExtensionObjectPtr gdextension_ref_get_object(GDExtensionConstRefPtr p_ref) {
const Ref<RefCounted> *ref = (const Ref<RefCounted> *)p_ref;
if (ref == nullptr || ref->is_null()) {
@@ -1373,6 +1402,19 @@ static void gdextension_editor_remove_plugin(GDExtensionConstStringNamePtr p_cla
#endif
}
+static void gdextension_editor_help_load_xml_from_utf8_chars_and_len(const char *p_data, GDExtensionInt p_size) {
+#ifdef TOOLS_ENABLED
+ GDExtensionEditorHelp::load_xml_buffer((const uint8_t *)p_data, p_size);
+#endif
+}
+
+static void gdextension_editor_help_load_xml_from_utf8_chars(const char *p_data) {
+#ifdef TOOLS_ENABLED
+ size_t len = strlen(p_data);
+ gdextension_editor_help_load_xml_from_utf8_chars_and_len(p_data, len);
+#endif
+}
+
#define REGISTER_INTERFACE_FUNC(m_name) GDExtension::register_interface_function(#m_name, (GDExtensionInterfaceFunctionPtr)&gdextension_##m_name)
void gdextension_setup_interface() {
@@ -1500,6 +1542,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(object_cast_to);
REGISTER_INTERFACE_FUNC(object_get_instance_from_id);
REGISTER_INTERFACE_FUNC(object_get_instance_id);
+ REGISTER_INTERFACE_FUNC(object_has_script_method);
+ REGISTER_INTERFACE_FUNC(object_call_script_method);
REGISTER_INTERFACE_FUNC(ref_get_object);
REGISTER_INTERFACE_FUNC(ref_set_object);
#ifndef DISABLE_DEPRECATED
@@ -1516,6 +1560,8 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(classdb_get_class_tag);
REGISTER_INTERFACE_FUNC(editor_add_plugin);
REGISTER_INTERFACE_FUNC(editor_remove_plugin);
+ REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars);
+ REGISTER_INTERFACE_FUNC(editor_help_load_xml_from_utf8_chars_and_len);
}
#undef REGISTER_INTERFACE_FUNCTION
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index d58f0226d8..48a66c9fae 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -364,13 +364,18 @@ typedef struct {
GDExtensionClassMethodPtrCall ptrcall_func;
uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`.
- /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored. */
+ /* If `has_return_value` is false, `return_value_info` and `return_value_metadata` are ignored.
+ *
+ * @todo Consider dropping `has_return_value` and making the other two properties match `GDExtensionMethodInfo` and `GDExtensionClassVirtualMethod` for consistency in future version of this struct.
+ */
GDExtensionBool has_return_value;
GDExtensionPropertyInfo *return_value_info;
GDExtensionClassMethodArgumentMetadata return_value_metadata;
/* Arguments: `arguments_info` and `arguments_metadata` are array of size `argument_count`.
* Name and hint information for the argument can be omitted in release builds. Class name should always be present if it applies.
+ *
+ * @todo Consider renaming `arguments_info` to `arguments` for consistency in future version of this struct.
*/
uint32_t argument_count;
GDExtensionPropertyInfo *arguments_info;
@@ -381,6 +386,18 @@ typedef struct {
GDExtensionVariantPtr *default_arguments;
} GDExtensionClassMethodInfo;
+typedef struct {
+ GDExtensionStringNamePtr name;
+ uint32_t method_flags; // Bitfield of `GDExtensionClassMethodFlags`.
+
+ GDExtensionPropertyInfo return_value;
+ GDExtensionClassMethodArgumentMetadata return_value_metadata;
+
+ uint32_t argument_count;
+ GDExtensionPropertyInfo *arguments;
+ GDExtensionClassMethodArgumentMetadata *arguments_metadata;
+} GDExtensionClassVirtualMethodInfo;
+
typedef void (*GDExtensionCallableCustomCall)(void *callable_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error);
typedef GDExtensionBool (*GDExtensionCallableCustomIsValid)(void *callable_userdata);
typedef void (*GDExtensionCallableCustomFree)(void *callable_userdata);
@@ -2268,6 +2285,34 @@ typedef GDExtensionObjectPtr (*GDExtensionInterfaceObjectGetInstanceFromId)(GDOb
*/
typedef GDObjectInstanceID (*GDExtensionInterfaceObjectGetInstanceId)(GDExtensionConstObjectPtr p_object);
+/**
+ * @name object_has_script_method
+ * @since 4.3
+ *
+ * Checks if this object has a script with the given method.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_method A pointer to a StringName identifying the method.
+ *
+ * @returns true if the object has a script and that script has a method with the given name. Returns false if the object has no script.
+ */
+typedef GDExtensionBool (*GDExtensionInterfaceObjectHasScriptMethod)(GDExtensionConstObjectPtr p_object, GDExtensionConstStringNamePtr p_method);
+
+/**
+ * @name object_call_script_method
+ * @since 4.3
+ *
+ * Call the given script method on this object.
+ *
+ * @param p_object A pointer to the Object.
+ * @param p_method A pointer to a StringName identifying the method.
+ * @param p_args A pointer to a C array of Variant.
+ * @param p_argument_count The number of arguments.
+ * @param r_return A pointer a Variant which will be assigned the return value.
+ * @param r_error A pointer the structure which will hold error information.
+ */
+typedef void (*GDExtensionInterfaceObjectCallScriptMethod)(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_method, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionUninitializedVariantPtr r_return, GDExtensionCallError *r_error);
+
/* INTERFACE: Reference */
/**
@@ -2484,6 +2529,20 @@ typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClass2)(GDExtensionCl
typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassMethodInfo *p_method_info);
/**
+ * @name classdb_register_extension_class_virtual_method
+ * @since 4.3
+ *
+ * Registers a virtual method on an extension class in ClassDB, that can be implemented by scripts or other extensions.
+ *
+ * Provided struct can be safely freed once the function returns.
+ *
+ * @param p_library A pointer the library received by the GDExtension's entry point function.
+ * @param p_class_name A pointer to a StringName with the class name.
+ * @param p_method_info A pointer to a GDExtensionClassMethodInfo struct.
+ */
+typedef void (*GDExtensionInterfaceClassdbRegisterExtensionClassVirtualMethod)(GDExtensionClassLibraryPtr p_library, GDExtensionConstStringNamePtr p_class_name, const GDExtensionClassVirtualMethodInfo *p_method_info);
+
+/**
* @name classdb_register_extension_class_integer_constant
* @since 4.1
*
@@ -2617,6 +2676,31 @@ typedef void (*GDExtensionInterfaceEditorAddPlugin)(GDExtensionConstStringNamePt
*/
typedef void (*GDExtensionInterfaceEditorRemovePlugin)(GDExtensionConstStringNamePtr p_class_name);
+/**
+ * @name editor_help_load_xml_from_utf8_chars
+ * @since 4.3
+ *
+ * Loads new XML-formatted documentation data in the editor.
+ *
+ * The provided pointer can be immediately freed once the function returns.
+ *
+ * @param p_data A pointer to an UTF-8 encoded C string (null terminated).
+ */
+typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8Chars)(const char *p_data);
+
+/**
+ * @name editor_help_load_xml_from_utf8_chars_and_len
+ * @since 4.3
+ *
+ * Loads new XML-formatted documentation data in the editor.
+ *
+ * The provided pointer can be immediately freed once the function returns.
+ *
+ * @param p_data A pointer to an UTF-8 encoded C string.
+ * @param p_size The number of bytes (not code units).
+ */
+typedef void (*GDExtensionsInterfaceEditorHelpLoadXmlFromUtf8CharsAndLen)(const char *p_data, GDExtensionInt p_size);
+
#ifdef __cplusplus
}
#endif
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 77655e9b6a..420de7f9b7 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -314,6 +314,7 @@
03000000242e00000b20000000000000,Hyperkin Admiral N64 Controller,+rightx:b11,+righty:b13,-rightx:b8,-righty:b12,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b14,leftx:a0,lefty:a1,rightshoulder:b5,start:b9,platform:Windows,
03000000242e0000ff0b000000000000,Hyperkin N64 Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a2,righty:a3,start:b9,platform:Windows,
03000000790000004e95000000000000,Hyperkin N64 Controller Adapter,a:b1,b:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b7,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b5,rightx:a5,righty:a2,start:b9,platform:Windows,
+03000000242e00006a48000000000000,Hyperkin RetroN Sq,a:b3,b:b7,back:b5,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b0,rightshoulder:b1,start:b4,x:b2,y:b6,platform:Windows,
03000000242e00006a38000000000000,Hyperkin Trooper 2,a:b0,b:b1,back:b4,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b3,start:b5,platform:Windows,
03000000d81d00000e00000000000000,iBuffalo AC02 Arcade Joystick,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b11,righttrigger:b3,rightx:a2,righty:a5,start:b8,x:b4,y:b5,platform:Windows,
03000000d81d00000f00000000000000,iBuffalo BSGP1204 Series,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -352,7 +353,7 @@
030000006d040000d2ca000000000000,Logitech Cordless Precision,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006d04000011c2000000000000,Logitech Cordless Wingman,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b5,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b2,righttrigger:b7,rightx:a3,righty:a4,x:b4,platform:Windows,
030000006d04000016c2000000000000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
-030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
+030000006d0400001dc2000000000000,Logitech F310,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000006d04000018c2000000000000,Logitech F510,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006d0400001ec2000000000000,Logitech F510,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
030000006d04000019c2000000000000,Logitech F710,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
@@ -429,6 +430,8 @@
03000000f70600000100000000000000,N64 Adaptoid,+rightx:b2,+righty:b1,-rightx:b4,-righty:b5,a:b0,b:b3,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,platform:Windows,
030000006b140000010c000000000000,Nacon GC 400ES,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000006b1400001106000000000000,Nacon Revolution 3 PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+0300000085320000170d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
+0300000085320000190d000000000000,Nacon Revolution 5 Pro,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
030000006b140000100d000000000000,Nacon Revolution Infinity PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006b140000080d000000000000,Nacon Revolution Unlimited Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000bd12000001c0000000000000,Nebular,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
@@ -450,7 +453,7 @@
03000000550900001072000000000000,NVIDIA Shield,a:b9,b:b8,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b3,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b4,rightstick:b2,righttrigger:a4,rightx:a2,righty:a5,start:b0,x:b7,y:b6,platform:Windows,
030000005509000000b4000000000000,NVIDIA Virtual,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:-a2,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000120c00000288000000000000,Nyko Air Flo Xbox Controller,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b11,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
-030000004b120000014d000000000000,Nyko Airflo,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a3,leftstick:a0,lefttrigger:b6,rightshoulder:b5,rightstick:a2,righttrigger:b7,start:b9,x:b2,y:b3,platform:Windows,
+030000004b120000014d000000000000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
03000000d62000001d57000000000000,Nyko Airflo PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000791d00000900000000000000,Nyko Playpad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000782300000a10000000000000,Onlive Controller,a:b15,b:b14,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b11,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b13,y:b12,platform:Windows,
@@ -459,7 +462,7 @@
030000008916000000fd000000000000,Onza TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000d62000006d57000000000000,OPP PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000006b14000001a1000000000000,Orange Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b6,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,platform:Windows,
-03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:b13,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,
+03000000362800000100000000000000,OUYA Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,platform:Windows,
03000000120c0000f60e000000000000,P4 Gamepad,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b7,rightshoulder:b4,righttrigger:b6,start:b9,x:b0,y:b3,platform:Windows,
03000000790000002201000000000000,PC Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
030000006f0e00008501000000000000,PDP Fightpad Pro GameCube Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
@@ -532,6 +535,7 @@
030000004c050000a00b000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
030000004c050000cc09000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+030000004c0500005f0e000000000000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
030000004c050000e60c000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
030000004c050000f20d000000000000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Windows,
03000000830500005020000000000000,PSX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b2,y:b3,platform:Windows,
@@ -556,6 +560,7 @@
030000009b2800003200000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800006000000000000000,Raphnet GC and N64 Adapter,a:b0,b:b7,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,lefttrigger:+a5,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:+a2,rightx:a3,righty:a4,start:b3,x:b1,y:b8,platform:Windows,
030000009b2800001800000000000000,Raphnet Jaguar Adapter,a:b2,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b10,start:b3,x:b11,y:b12,platform:Windows,
+030000009b2800006100000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800006300000000000000,Raphnet N64 Adapter,+rightx:b9,+righty:b7,-rightx:b8,-righty:b6,a:b0,b:b1,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,leftshoulder:b4,lefttrigger:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Windows,
030000009b2800000200000000000000,Raphnet NES Adapter,a:b7,b:b6,back:b5,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftx:a0,lefty:a1,start:b4,platform:Windows,
030000009b2800004400000000000000,Raphnet PS1 and PS2 Adapter,a:b1,b:b2,back:b5,dpdown:b13,dpleft:b14,dpright:b15,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b9,rightx:a3,righty:a4,start:b4,x:b0,y:b3,platform:Windows,
@@ -608,6 +613,7 @@
03000000050b00001a1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
03000000050b00001c1a000000000000,ROG Chakram X,a:b1,b:b0,leftx:a0,lefty:a1,x:b2,y:b3,platform:Windows,
030000004f04000001d0000000000000,Rumble Force,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,platform:Windows,
+030000000d0f0000ad00000000000000,RX Gamepad,a:b0,b:b4,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,rightshoulder:b6,start:b9,x:b2,y:b1,platform:Windows,
030000008916000000fe000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000c6240000045d000000000000,Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Windows,
03000000a30600001af5000000000000,Saitek Cyborg,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,platform:Windows,
@@ -674,6 +680,7 @@
03000000457500002211000000000000,Szmy Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
030000004f0400000ab1000000000000,T16000M,a:b0,b:b1,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b11,leftshoulder:b4,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:b7,start:b10,x:b2,y:b3,platform:Windows,
030000000d0f00007b00000000000000,TAC GEAR,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
+03000000e40a00000307000000000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,
03000000e40a00000207000000000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Windows,
03000000d814000001a0000000000000,TE Kitty,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
03000000fa1900000706000000000000,Team 5,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Windows,
@@ -961,6 +968,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000004c050000c405000000000000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
+0300004b4c0500005f0e000000010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
030000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
030000004c050000f20d000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
050000004c050000e60c000000010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Mac OS X,
@@ -1013,6 +1021,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000110100001714000020010000,SteelSeries Stratus XL,a:b0,b:b1,dpdown:b9,dpleft:b11,dpright:b10,dpup:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1~,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3~,start:b12,x:b2,y:b3,platform:Mac OS X,
030000000d0f0000f600000000010000,Switch Hori Pad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000457500002211000000010000,SZMY Power PC Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Mac OS X,
+03000000e40a00000307000001000000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,
+03000000e40a00000207000001000000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Mac OS X,
03000000790000001c18000003100000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000591c00002400000021000000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a4,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Mac OS X,
03000000591c00002600000021000000,THEGamepad,a:b2,b:b1,back:b6,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b0,platform:Mac OS X,
@@ -1164,14 +1174,15 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000c21100000791000011010000,Be1 GC101 Controller 1.03,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
03000000c31100000791000011010000,Be1 GC101 Controller 1.03,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000005e0400008e02000003030000,Be1 GC101 Xbox 360,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
-03000000bc2000004d50000011010000,BEITONG A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
-05000000bc2000000055000001000000,BETOP AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000bc2000004d50000011010000,Beitong A1T2 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+05000000bc2000000055000001000000,Betop AX1 BFM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000bc2000006412000011010000,Betop Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b30,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000006b1400000209000011010000,Bigben,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000120c0000200e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000120c0000210e000011010000,Brook Mars PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000120c0000f70e000011010000,Brook Universal Fighting Board,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,rightshoulder:b5,rightstick:b11,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+03000000af1e00002400000010010000,Clockwork Pi DevTerm,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b9,x:b3,y:b0,platform:Linux,
030000000b0400003365000000010000,Competition Pro,a:b0,b:b1,back:b2,leftx:a0,lefty:a1,start:b3,platform:Linux,
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,platform:Linux,
03000000a306000022f6000011010000,Cyborg V3 Rumble,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:+a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:-a3,rightx:a2,righty:a4,start:b9,x:b0,y:b3,platform:Linux,
@@ -1218,7 +1229,6 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
06000000adde0000efbe000002010000,Hidromancer Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000d81400000862000011010000,HitBox PS3 PC Analog Mode,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,guide:b9,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,platform:Linux,
03000000c9110000f055000011010000,HJC Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
-03000000632500002605000010010000,HJDX,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
030000000d0f00000d00000000010000,Hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,rightshoulder:b7,start:b9,x:b1,y:b2,platform:Linux,
030000000d0f00006d00000020010000,Hori EDGE 301,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:+a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:+a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000000d0f00008400000011010000,Hori Fighting Commander,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
@@ -1256,6 +1266,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000242e00008816000001010000,Hyperkin X91,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000f00300008d03000011010000,HyperX Clutch,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000830500006020000010010000,iBuffalo Super Famicom Controller,a:b1,b:b0,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
+030000008f0e00001330000001010000,iCode Retro Adapter,b:b3,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b9,lefttrigger:b10,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b1,start:b7,x:b2,y:b0,platform:Linux,
050000006964726f69643a636f6e0000,idroidcon Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000b50700001503000010010000,Impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,platform:Linux,
03000000d80400008200000003000000,IMS PCU0,a:b1,b:b0,back:b4,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,start:b5,x:b3,y:b2,platform:Linux,
@@ -1389,6 +1400,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,platform:Linux,
03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,platform:Linux,
+030000004b120000014d000000010000,NYKO Airflo EX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,platform:Linux,
03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
19000000010000000100000001010000,ODROID Go 2,a:b1,b:b0,dpdown:b7,dpleft:b8,dpright:b9,dpup:b6,guide:b10,leftshoulder:b4,leftstick:b12,lefttrigger:b11,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b13,righttrigger:b14,start:b15,x:b2,y:b3,platform:Linux,
19000000010000000200000011000000,ODROID Go 2,a:b1,b:b0,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b12,leftshoulder:b4,leftstick:b14,lefttrigger:b13,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b15,righttrigger:b16,start:b17,x:b2,y:b3,platform:Linux,
@@ -1407,6 +1419,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000006f0e0000c802000012010000,PDP Kingdom Hearts Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e00002801000011010000,PDP PS3 Rock Candy Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
030000006f0e00000901000011010000,PDP PS3 Versus Fighting,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,platform:Linux,
+030000006f0e00002f01000011010000,PDP Wired PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
03000000ad1b000004f9000000010000,PDP Xbox 360 Versus Fighting,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,platform:Linux,
030000006f0e0000a802000023020000,PDP Xbox One Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
030000006f0e0000a702000023020000,PDP Xbox One Raven Black,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
@@ -1463,6 +1476,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
+0300004b4c0500005f0e000011010000,PS5 Access Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000e60c000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
030000004c050000e60c000011810000,PS5 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,platform:Linux,
030000004c050000f20d000011010000,PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b14,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
@@ -1528,9 +1542,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000b40400000a01000000010000,Sega Saturn,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Linux,
030000001f08000001e4000010010000,SFC Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Linux,
03000000632500002305000010010000,ShanWan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+03000000632500002605000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+03000000632500007505000010010000,Shanwan Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
+03000000bc2000000055000010010000,Shanwan Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000f025000021c1000010010000,Shanwan Gioteck PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
-03000000632500007505000010010000,Shanwan PS3 PC,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
-03000000bc2000000055000010010000,Shanwan PS3 PC ,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000341a00000908000010010000,SL6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
050000004c050000cc09000001000000,Sony DualShock 4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000ff000000cb01000010010000,Sony PlayStation Portable,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
@@ -1564,6 +1579,8 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000457500000401000011010000,SZMY Power DS4 Wired Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Linux,
03000000457500002211000010010000,SZMY Power Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,platform:Linux,
030000008f0e00001431000010010000,SZMY Power PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Linux,
+03000000e40a00000307000011010000,Taito Egret II Mini Control Panel,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,
+03000000e40a00000207000011010000,Taito Egret II Mini Controller,a:b4,b:b2,back:b6,guide:b9,leftx:a0,lefty:a1,rightshoulder:b0,righttrigger:b1,start:b7,x:b8,y:b3,platform:Linux,
03000000ba2200000701000001010000,Technology Innovation PS2 Adapter,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a5,righty:a2,start:b9,x:b3,y:b2,platform:Linux,
03000000790000001c18000011010000,TGZ Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000591c00002400000010010000,THEC64 Joystick,a:b0,b:b1,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b2,y:b3,platform:Linux,
@@ -1871,9 +1888,9 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
36313938306539326233393732613361,Retro Bit SNES Controller,a:b0,b:b1,back:b15,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b9,rightshoulder:b10,start:b6,x:b2,y:b3,platform:Android,
526574726f466c616720576972656420,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,rightshoulder:b18,start:b10,x:b2,y:b3,platform:Android,
61343739353764363165343237303336,Retro Controller,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b17,lefttrigger:b18,leftx:a0,lefty:a1,start:b10,x:b2,y:b3,platform:Android,
+526574726f696420506f636b65742043,Retroid Pocket,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
+582d426f7820436f6e74726f6c6c6572,Retroid Pocket,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
38653130373365613538333235303036,Retroid Pocket 2,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
-526574726f696420506f636b65742043,Retroid Pocket Flip,a:b1,b:b0,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b3,y:b2,platform:Android,
-582d426f7820436f6e74726f6c6c6572,Retroid Pocket Flip,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,paddle1:b17,paddle2:b18,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
64363363336633363736393038313463,Retrolink,a:b1,b:b0,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,start:b6,platform:Android,
37393234373533633333323633646531,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android,
5365616c6965436f6d707574696e6720,RetroUSB N64 RetroPort,+rightx:b17,+righty:b15,-rightx:b18,-righty:b6,a:b10,b:b9,dpdown:b19,dpleft:b1,dpright:b0,dpup:b2,leftshoulder:b7,lefttrigger:b20,leftx:a0,lefty:a1,rightshoulder:b5,start:b3,platform:Android,
@@ -1902,6 +1919,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,platform:Android,
0500000011010000201400000f7e0f00,SteelSeries Nimbus,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b3,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b20,righttrigger:b10,rightx:a2,righty:a3,x:b19,y:b2,platform:Android,
35306436396437373135383665646464,SteelSeries Nimbus Plus,a:b0,b:b1,leftshoulder:b3,leftstick:b17,lefttrigger:b9,leftx:a0,rightshoulder:b20,rightstick:b18,righttrigger:b10,rightx:a2,x:b19,y:b2,platform:Android,
+33313930373536613937326534303931,Taito Egret II Mini Control Panel,a:b25,b:b23,back:b27,guide:b30,leftx:a0,lefty:a1,rightshoulder:b21,righttrigger:b22,start:b28,x:b29,y:b24,platform:Android,
54475a20436f6e74726f6c6c65720000,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
62363434353532386238336663643836,TGZ Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
37323236633763666465316365313236,THEC64 Joystick,a:b21,b:b22,back:b27,leftshoulder:b25,leftx:a0,lefty:a1,rightshoulder:b26,start:b27,x:b23,y:b24,platform:Android,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 257452b3d8..d87a8824f7 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -130,6 +130,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_magnetometer", "value"), &Input::set_magnetometer);
ClassDB::bind_method(D_METHOD("set_gyroscope", "value"), &Input::set_gyroscope);
ClassDB::bind_method(D_METHOD("get_last_mouse_velocity"), &Input::get_last_mouse_velocity);
+ ClassDB::bind_method(D_METHOD("get_last_mouse_screen_velocity"), &Input::get_last_mouse_screen_velocity);
ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask);
ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode);
ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode);
@@ -143,9 +144,15 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input);
ClassDB::bind_method(D_METHOD("is_using_accumulated_input"), &Input::is_using_accumulated_input);
ClassDB::bind_method(D_METHOD("flush_buffered_events"), &Input::flush_buffered_events);
+ ClassDB::bind_method(D_METHOD("set_emulate_mouse_from_touch", "enable"), &Input::set_emulate_mouse_from_touch);
+ ClassDB::bind_method(D_METHOD("is_emulating_mouse_from_touch"), &Input::is_emulating_mouse_from_touch);
+ ClassDB::bind_method(D_METHOD("set_emulate_touch_from_mouse", "enable"), &Input::set_emulate_touch_from_mouse);
+ ClassDB::bind_method(D_METHOD("is_emulating_touch_from_mouse"), &Input::is_emulating_touch_from_mouse);
ADD_PROPERTY(PropertyInfo(Variant::INT, "mouse_mode"), "set_mouse_mode", "get_mouse_mode");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_accumulated_input"), "set_use_accumulated_input", "is_using_accumulated_input");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_mouse_from_touch"), "set_emulate_mouse_from_touch", "is_emulating_mouse_from_touch");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emulate_touch_from_mouse"), "set_emulate_touch_from_mouse", "is_emulating_touch_from_mouse");
BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE);
BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN);
@@ -192,9 +199,10 @@ void Input::get_argument_options(const StringName &p_function, int p_idx, List<S
r_options->push_back(name.quote());
}
}
+ Object::get_argument_options(p_function, p_idx, r_options);
}
-void Input::VelocityTrack::update(const Vector2 &p_delta_p) {
+void Input::VelocityTrack::update(const Vector2 &p_delta_p, const Vector2 &p_screen_delta_p) {
uint64_t tick = OS::get_singleton()->get_ticks_usec();
uint32_t tdiff = tick - last_tick;
float delta_t = tdiff / 1000000.0;
@@ -203,12 +211,15 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) {
if (delta_t > max_ref_frame) {
// First movement in a long time, reset and start again.
velocity = Vector2();
+ screen_velocity = Vector2();
accum = p_delta_p;
+ screen_accum = p_screen_delta_p;
accum_t = 0;
return;
}
accum += p_delta_p;
+ screen_accum += p_screen_delta_p;
accum_t += delta_t;
if (accum_t < min_ref_frame) {
@@ -217,6 +228,7 @@ void Input::VelocityTrack::update(const Vector2 &p_delta_p) {
}
velocity = accum / accum_t;
+ screen_velocity = screen_accum / accum_t;
accum = Vector2();
accum_t = 0;
}
@@ -241,8 +253,8 @@ bool Input::is_anything_pressed() const {
return true;
}
- for (const KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed) {
+ for (const KeyValue<StringName, Input::ActionState> &E : action_states) {
+ if (E.value.cache.pressed) {
return true;
}
}
@@ -285,12 +297,17 @@ bool Input::is_joy_button_pressed(int p_device, JoyButton p_button) const {
bool Input::is_action_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- return action_state.has(p_action) && action_state[p_action].pressed > 0 && (p_exact ? action_state[p_action].exact : true);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
+ if (!E) {
+ return false;
+ }
+
+ return E->value.cache.pressed && (p_exact ? E->value.exact : true);
}
bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return false;
}
@@ -300,7 +317,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
}
// Backward compatibility for legacy behavior, only return true if currently pressed.
- bool pressed_requirement = legacy_just_pressed_behavior ? E->value.pressed : true;
+ bool pressed_requirement = legacy_just_pressed_behavior ? E->value.cache.pressed : true;
if (Engine::get_singleton()->is_in_physics_frame()) {
return pressed_requirement && E->value.pressed_physics_frame == Engine::get_singleton()->get_physics_frames();
@@ -311,7 +328,7 @@ bool Input::is_action_just_pressed(const StringName &p_action, bool p_exact) con
bool Input::is_action_just_released(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), false, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return false;
}
@@ -321,7 +338,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
}
// Backward compatibility for legacy behavior, only return true if currently released.
- bool released_requirement = legacy_just_pressed_behavior ? !E->value.pressed : true;
+ bool released_requirement = legacy_just_pressed_behavior ? !E->value.cache.pressed : true;
if (Engine::get_singleton()->is_in_physics_frame()) {
return released_requirement && E->value.released_physics_frame == Engine::get_singleton()->get_physics_frames();
@@ -332,7 +349,7 @@ bool Input::is_action_just_released(const StringName &p_action, bool p_exact) co
float Input::get_action_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return 0.0f;
}
@@ -341,12 +358,12 @@ float Input::get_action_strength(const StringName &p_action, bool p_exact) const
return 0.0f;
}
- return E->value.strength;
+ return E->value.cache.strength;
}
float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) const {
ERR_FAIL_COND_V_MSG(!InputMap::get_singleton()->has_action(p_action), 0.0, InputMap::get_singleton()->suggest_actions(p_action));
- HashMap<StringName, Action>::ConstIterator E = action_state.find(p_action);
+ HashMap<StringName, ActionState>::ConstIterator E = action_states.find(p_action);
if (!E) {
return 0.0f;
}
@@ -355,7 +372,7 @@ float Input::get_action_raw_strength(const StringName &p_action, bool p_exact) c
return 0.0f;
}
- return E->value.raw_strength;
+ return E->value.cache.raw_strength;
}
float Input::get_axis(const StringName &p_negative_action, const StringName &p_positive_action) const {
@@ -440,6 +457,18 @@ static String _hex_str(uint8_t p_byte) {
void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) {
_THREAD_SAFE_METHOD_
+
+ // Clear the pressed status if a Joypad gets disconnected.
+ if (!p_connected) {
+ for (KeyValue<StringName, ActionState> &E : action_states) {
+ HashMap<int, ActionState::DeviceState>::Iterator it = E.value.device_states.find(p_idx);
+ if (it) {
+ E.value.device_states.remove(it);
+ _update_action_cache(E.key, E.value);
+ }
+ }
+ }
+
Joypad js;
js.name = p_connected ? p_name : "";
js.uid = p_connected ? p_guid : "";
@@ -570,7 +599,8 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
set_mouse_position(position);
}
Vector2 relative = mm->get_relative();
- mouse_velocity_track.update(relative);
+ Vector2 screen_relative = mm->get_relative_screen_position();
+ mouse_velocity_track.update(relative, screen_relative);
if (event_dispatch_function && emulate_touch_from_mouse && !p_is_emulated && mm->get_button_mask().has_flag(MouseButtonMask::LEFT)) {
Ref<InputEventScreenDrag> drag_event;
@@ -578,10 +608,12 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
drag_event->set_position(position);
drag_event->set_relative(relative);
+ drag_event->set_relative_screen_position(screen_relative);
drag_event->set_tilt(mm->get_tilt());
drag_event->set_pen_inverted(mm->get_pen_inverted());
drag_event->set_pressure(mm->get_pressure());
drag_event->set_velocity(get_last_mouse_velocity());
+ drag_event->set_screen_velocity(get_last_mouse_screen_velocity());
drag_event->set_device(InputEvent::DEVICE_ID_EMULATION);
_THREAD_SAFE_UNLOCK_
@@ -645,8 +677,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (sd.is_valid()) {
VelocityTrack &track = touch_velocity_track[sd->get_index()];
- track.update(sd->get_relative());
+ track.update(sd->get_relative(), sd->get_relative_screen_position());
sd->set_velocity(track.velocity);
+ sd->set_screen_velocity(track.screen_velocity);
if (emulate_mouse_from_touch && sd->get_index() == mouse_from_touch_index) {
Ref<InputEventMouseMotion> motion_event;
@@ -659,7 +692,9 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
motion_event->set_position(sd->get_position());
motion_event->set_global_position(sd->get_position());
motion_event->set_relative(sd->get_relative());
+ motion_event->set_relative_screen_position(sd->get_relative_screen_position());
motion_event->set_velocity(sd->get_velocity());
+ motion_event->set_screen_velocity(sd->get_screen_velocity());
motion_event->set_button_mask(mouse_button_mask);
_parse_input_event_impl(motion_event, true);
@@ -699,30 +734,35 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
if (event_index == -1) {
continue;
}
+ ERR_FAIL_COND_MSG(event_index >= (int)MAX_EVENT, vformat("Input singleton does not support more than %d events assigned to an action.", MAX_EVENT));
+
+ int device_id = p_event->get_device();
+ bool is_pressed = p_event->is_action_pressed(E.key, true);
+ ActionState &action_state = action_states[E.key];
+
+ // Update the action's per-device state.
+ ActionState::DeviceState &device_state = action_state.device_states[device_id];
+ device_state.pressed[event_index] = is_pressed;
+ device_state.strength[event_index] = p_event->get_action_strength(E.key);
+ device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key);
+
+ // Update the action's global state and cache.
+ if (!is_pressed) {
+ action_state.api_pressed = false; // Always release the event from action_press() method.
+ action_state.api_strength = 0.0;
+ }
+ action_state.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- Action &action = action_state[E.key];
- if (!p_event->is_echo()) {
- if (p_event->is_action_pressed(E.key)) {
- if (!action.pressed) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- }
- action.pressed |= ((uint64_t)1 << event_index);
- } else {
- action.pressed &= ~((uint64_t)1 << event_index);
- action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method.
-
- if (!action.pressed) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- }
- _update_action_strength(action, MAX_EVENT, 0.0);
- _update_action_raw_strength(action, MAX_EVENT, 0.0);
- }
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
+ bool was_pressed = action_state.cache.pressed;
+ _update_action_cache(E.key, action_state);
+ if (action_state.cache.pressed && !was_pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ if (!action_state.cache.pressed && was_pressed) {
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
}
- _update_action_strength(action, event_index, p_event->get_action_strength(E.key));
- _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key));
}
if (event_dispatch_function) {
@@ -798,10 +838,15 @@ Point2 Input::get_mouse_position() const {
}
Point2 Input::get_last_mouse_velocity() {
- mouse_velocity_track.update(Vector2());
+ mouse_velocity_track.update(Vector2(), Vector2());
return mouse_velocity_track.velocity;
}
+Point2 Input::get_last_mouse_screen_velocity() {
+ mouse_velocity_track.update(Vector2(), Vector2());
+ return mouse_velocity_track.screen_velocity;
+}
+
BitField<MouseButtonMask> Input::get_mouse_button_mask() const {
return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state();
}
@@ -836,33 +881,35 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con
}
void Input::action_press(const StringName &p_action, float p_strength) {
+ ERR_FAIL_COND_MSG(!InputMap::get_singleton()->has_action(p_action), InputMap::get_singleton()->suggest_actions(p_action));
+
// Create or retrieve existing action.
- Action &action = action_state[p_action];
+ ActionState &action_state = action_states[p_action];
- if (!action.pressed) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ if (!action_state.cache.pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.pressed |= 1 << MAX_EVENT;
- _update_action_strength(action, MAX_EVENT, p_strength);
- _update_action_raw_strength(action, MAX_EVENT, p_strength);
- action.exact = true;
+ action_state.exact = true;
+ action_state.api_pressed = true;
+ action_state.api_strength = p_strength;
+ _update_action_cache(p_action, action_state);
}
void Input::action_release(const StringName &p_action) {
- // Create or retrieve existing action.
- Action &action = action_state[p_action];
+ ERR_FAIL_COND_MSG(!InputMap::get_singleton()->has_action(p_action), InputMap::get_singleton()->suggest_actions(p_action));
- action.pressed = 0;
- action.strength = 0.0;
- action.raw_strength = 0.0;
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- action.strengths[i] = 0.0;
- action.raw_strengths[i] = 0.0;
- }
- action.exact = true;
+ // Create or retrieve existing action.
+ ActionState &action_state = action_states[p_action];
+ action_state.cache.pressed = 0;
+ action_state.cache.strength = 0.0;
+ action_state.cache.raw_strength = 0.0;
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
+ action_state.device_states.clear();
+ action_state.exact = true;
+ action_state.api_pressed = false;
+ action_state.api_strength = 0.0;
}
void Input::set_emulate_touch_from_mouse(bool p_emulate) {
@@ -1020,10 +1067,8 @@ void Input::release_pressed_events() {
joy_buttons_pressed.clear();
_joy_axis.clear();
- for (KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed > 0) {
- // Make sure the action is really released.
- E.value.pressed = 1;
+ for (KeyValue<StringName, Input::ActionState> &E : action_states) {
+ if (E.value.cache.pressed) {
action_release(E.key);
}
}
@@ -1190,35 +1235,26 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
parse_input_event(ievent);
}
-void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) {
- ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
-
- float old_strength = p_action.strengths[p_event_index];
- p_action.strengths[p_event_index] = p_strength;
+void Input::_update_action_cache(const StringName &p_action_name, ActionState &r_action_state) {
+ // Update the action cache, computed from the per-device and per-event states.
+ r_action_state.cache.pressed = false;
+ r_action_state.cache.strength = 0.0;
+ r_action_state.cache.raw_strength = 0.0;
- if (p_strength > p_action.strength) {
- p_action.strength = p_strength;
- } else if (Math::is_equal_approx(old_strength, p_action.strength)) {
- p_action.strength = p_strength;
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- p_action.strength = MAX(p_action.strength, p_action.strengths[i]);
+ int max_event = InputMap::get_singleton()->action_get_events(p_action_name)->size();
+ for (const KeyValue<int, ActionState::DeviceState> &kv : r_action_state.device_states) {
+ const ActionState::DeviceState &device_state = kv.value;
+ for (int i = 0; i < max_event; i++) {
+ r_action_state.cache.pressed = r_action_state.cache.pressed || device_state.pressed[i];
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, device_state.strength[i]);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, device_state.raw_strength[i]);
}
}
-}
-void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) {
- ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
-
- float old_strength = p_action.raw_strengths[p_event_index];
- p_action.raw_strengths[p_event_index] = p_strength;
-
- if (p_strength > p_action.raw_strength) {
- p_action.raw_strength = p_strength;
- } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) {
- p_action.raw_strength = p_strength;
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]);
- }
+ if (r_action_state.api_pressed) {
+ r_action_state.cache.pressed = true;
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, r_action_state.api_strength);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, r_action_state.api_strength); // Use the strength as raw_strength for API-pressed states.
}
}
@@ -1503,7 +1539,7 @@ void Input::add_joy_mapping(String p_mapping, bool p_update_existing) {
parse_mapping(p_mapping);
if (p_update_existing) {
Vector<String> entry = p_mapping.split(",");
- String uid = entry[0];
+ const String &uid = entry[0];
for (KeyValue<int, Joypad> &E : joy_names) {
Joypad &joy = E.value;
if (joy.uid == uid) {
diff --git a/core/input/input.h b/core/input/input.h
index dd613c4877..a7ae3349b2 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -44,7 +44,7 @@ class Input : public Object {
static Input *singleton;
- static constexpr uint64_t MAX_EVENT = 31;
+ static constexpr uint64_t MAX_EVENT = 32;
public:
enum MouseMode {
@@ -100,30 +100,31 @@ private:
int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false;
- struct Action {
+ struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX;
uint64_t pressed_process_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_process_frame = UINT64_MAX;
- uint64_t pressed = 0;
bool exact = true;
- float strength = 0.0f;
- float raw_strength = 0.0f;
- LocalVector<float> strengths;
- LocalVector<float> raw_strengths;
-
- Action() {
- strengths.resize(MAX_EVENT + 1);
- raw_strengths.resize(MAX_EVENT + 1);
-
- for (uint64_t i = 0; i <= MAX_EVENT; i++) {
- strengths[i] = 0.0;
- raw_strengths[i] = 0.0;
- }
- }
+
+ struct DeviceState {
+ bool pressed[MAX_EVENT] = { false };
+ float strength[MAX_EVENT] = { 0.0 };
+ float raw_strength[MAX_EVENT] = { 0.0 };
+ };
+ bool api_pressed = false;
+ float api_strength = 0.0;
+ HashMap<int, DeviceState> device_states;
+
+ // Cache.
+ struct ActionStateCache {
+ bool pressed = false;
+ float strength = false;
+ float raw_strength = false;
+ } cache;
};
- HashMap<StringName, Action> action_state;
+ HashMap<StringName, ActionState> action_states;
bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
@@ -144,12 +145,14 @@ private:
struct VelocityTrack {
uint64_t last_tick = 0;
Vector2 velocity;
+ Vector2 screen_velocity;
Vector2 accum;
+ Vector2 screen_accum;
float accum_t = 0.0f;
float min_ref_frame;
float max_ref_frame;
- void update(const Vector2 &p_delta_p);
+ void update(const Vector2 &p_delta_p, const Vector2 &p_screen_delta_p);
void reset();
VelocityTrack();
};
@@ -240,8 +243,7 @@ private:
JoyAxis _get_output_axis(String output);
void _button_event(int p_device, JoyButton p_index, bool p_pressed);
void _axis_event(int p_device, JoyAxis p_axis, float p_value);
- void _update_action_strength(Action &p_action, int p_event_index, float p_strength);
- void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength);
+ void _update_action_cache(const StringName &p_action_name, ActionState &r_action_state);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
@@ -302,6 +304,7 @@ public:
Point2 get_mouse_position() const;
Vector2 get_last_mouse_velocity();
+ Vector2 get_last_mouse_screen_velocity();
BitField<MouseButtonMask> get_mouse_button_mask() const;
void warp_mouse(const Vector2 &p_position);
diff --git a/core/input/input_builders.py b/core/input/input_builders.py
index e98e2441e2..94c566493e 100644
--- a/core/input/input_builders.py
+++ b/core/input/input_builders.py
@@ -45,10 +45,10 @@ def make_default_controller_mappings(target, source, env):
platform_mappings[current_platform][guid] = line
platform_variables = {
- "Linux": "#if LINUXBSD_ENABLED",
+ "Linux": "#ifdef LINUXBSD_ENABLED",
"Windows": "#ifdef WINDOWS_ENABLED",
"Mac OS X": "#ifdef MACOS_ENABLED",
- "Android": "#if defined(__ANDROID__)",
+ "Android": "#ifdef ANDROID_ENABLED",
"iOS": "#ifdef IOS_ENABLED",
"Web": "#ifdef WEB_ENABLED",
}
diff --git a/core/input/input_event.cpp b/core/input/input_event.cpp
index e99dd04599..bd1fde5a85 100644
--- a/core/input/input_event.cpp
+++ b/core/input/input_event.cpp
@@ -364,6 +364,15 @@ char32_t InputEventKey::get_unicode() const {
return unicode;
}
+void InputEventKey::set_location(KeyLocation p_key_location) {
+ location = p_key_location;
+ emit_changed();
+}
+
+KeyLocation InputEventKey::get_location() const {
+ return location;
+}
+
void InputEventKey::set_echo(bool p_enable) {
echo = p_enable;
emit_changed();
@@ -436,6 +445,23 @@ String InputEventKey::as_text_key_label() const {
return mods_text.is_empty() ? kc : mods_text + "+" + kc;
}
+String InputEventKey::as_text_location() const {
+ String loc;
+
+ switch (location) {
+ case KeyLocation::LEFT:
+ loc = "left";
+ break;
+ case KeyLocation::RIGHT:
+ loc = "right";
+ break;
+ default:
+ break;
+ }
+
+ return loc;
+}
+
String InputEventKey::as_text() const {
String kc;
@@ -464,6 +490,11 @@ String InputEventKey::to_string() {
String kc = "";
String physical = "false";
+ String loc = as_text_location();
+ if (loc.is_empty()) {
+ loc = "unspecified";
+ }
+
if (keycode == Key::NONE && physical_keycode == Key::NONE && unicode != 0) {
kc = "U+" + String::num_uint64(unicode, 16) + " (" + String::chr(unicode) + ")";
} else if (keycode != Key::NONE) {
@@ -478,7 +509,7 @@ String InputEventKey::to_string() {
String mods = InputEventWithModifiers::as_text();
mods = mods.is_empty() ? "none" : mods;
- return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, pressed=%s, echo=%s", kc, mods, physical, p, e);
+ return vformat("InputEventKey: keycode=%s, mods=%s, physical=%s, location=%s, pressed=%s, echo=%s", kc, mods, physical, loc, p, e);
}
Ref<InputEventKey> InputEventKey::create_reference(Key p_keycode, bool p_physical) {
@@ -531,6 +562,9 @@ bool InputEventKey::action_match(const Ref<InputEvent> &p_event, bool p_exact_ma
match = keycode == key->keycode;
} else if (physical_keycode != Key::NONE) {
match = physical_keycode == key->physical_keycode;
+ if (location != KeyLocation::UNSPECIFIED) {
+ match &= location == key->location;
+ }
} else {
match = false;
}
@@ -572,6 +606,9 @@ bool InputEventKey::is_match(const Ref<InputEvent> &p_event, bool p_exact_match)
return (keycode == key->keycode) &&
(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask());
} else if (physical_keycode != Key::NONE) {
+ if (location != KeyLocation::UNSPECIFIED && location != key->location) {
+ return false;
+ }
return (physical_keycode == key->physical_keycode) &&
(!p_exact_match || get_modifiers_mask() == key->get_modifiers_mask());
} else {
@@ -594,6 +631,9 @@ void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_unicode", "unicode"), &InputEventKey::set_unicode);
ClassDB::bind_method(D_METHOD("get_unicode"), &InputEventKey::get_unicode);
+ ClassDB::bind_method(D_METHOD("set_location", "location"), &InputEventKey::set_location);
+ ClassDB::bind_method(D_METHOD("get_location"), &InputEventKey::get_location);
+
ClassDB::bind_method(D_METHOD("set_echo", "echo"), &InputEventKey::set_echo);
ClassDB::bind_method(D_METHOD("get_keycode_with_modifiers"), &InputEventKey::get_keycode_with_modifiers);
@@ -603,12 +643,14 @@ void InputEventKey::_bind_methods() {
ClassDB::bind_method(D_METHOD("as_text_keycode"), &InputEventKey::as_text_keycode);
ClassDB::bind_method(D_METHOD("as_text_physical_keycode"), &InputEventKey::as_text_physical_keycode);
ClassDB::bind_method(D_METHOD("as_text_key_label"), &InputEventKey::as_text_key_label);
+ ClassDB::bind_method(D_METHOD("as_text_location"), &InputEventKey::as_text_location);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pressed"), "set_pressed", "is_pressed");
ADD_PROPERTY(PropertyInfo(Variant::INT, "keycode"), "set_keycode", "get_keycode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "physical_keycode"), "set_physical_keycode", "get_physical_keycode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "key_label"), "set_key_label", "get_key_label");
ADD_PROPERTY(PropertyInfo(Variant::INT, "unicode"), "set_unicode", "get_unicode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "location", PROPERTY_HINT_ENUM, "Unspecified,Left,Right"), "set_location", "get_location");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "echo"), "set_echo", "is_echo");
}
@@ -885,6 +927,14 @@ Vector2 InputEventMouseMotion::get_relative() const {
return relative;
}
+void InputEventMouseMotion::set_relative_screen_position(const Vector2 &p_relative) {
+ screen_relative = p_relative;
+}
+
+Vector2 InputEventMouseMotion::get_relative_screen_position() const {
+ return screen_relative;
+}
+
void InputEventMouseMotion::set_velocity(const Vector2 &p_velocity) {
velocity = p_velocity;
}
@@ -893,6 +943,14 @@ Vector2 InputEventMouseMotion::get_velocity() const {
return velocity;
}
+void InputEventMouseMotion::set_screen_velocity(const Vector2 &p_velocity) {
+ screen_velocity = p_velocity;
+}
+
+Vector2 InputEventMouseMotion::get_screen_velocity() const {
+ return screen_velocity;
+}
+
Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventMouseMotion> mm;
mm.instantiate();
@@ -910,7 +968,9 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co
mm->set_button_mask(get_button_mask());
mm->set_relative(p_xform.basis_xform(get_relative()));
+ mm->set_relative_screen_position(get_relative_screen_position());
mm->set_velocity(p_xform.basis_xform(get_velocity()));
+ mm->set_screen_velocity(get_screen_velocity());
return mm;
}
@@ -985,7 +1045,9 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) {
set_position(motion->get_position());
set_global_position(motion->get_global_position());
set_velocity(motion->get_velocity());
+ set_screen_velocity(motion->get_screen_velocity());
relative += motion->get_relative();
+ screen_relative += motion->get_relative_screen_position();
return true;
}
@@ -1003,14 +1065,22 @@ void InputEventMouseMotion::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventMouseMotion::set_relative);
ClassDB::bind_method(D_METHOD("get_relative"), &InputEventMouseMotion::get_relative);
+ ClassDB::bind_method(D_METHOD("set_screen_relative", "relative"), &InputEventMouseMotion::set_relative_screen_position);
+ ClassDB::bind_method(D_METHOD("get_screen_relative"), &InputEventMouseMotion::get_relative_screen_position);
+
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventMouseMotion::set_velocity);
ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventMouseMotion::get_velocity);
+ ClassDB::bind_method(D_METHOD("set_screen_velocity", "velocity"), &InputEventMouseMotion::set_screen_velocity);
+ ClassDB::bind_method(D_METHOD("get_screen_velocity"), &InputEventMouseMotion::get_screen_velocity);
+
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pen_inverted"), "set_pen_inverted", "get_pen_inverted");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative", PROPERTY_HINT_NONE, "suffix:px"), "set_relative", "get_relative");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_relative", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_relative", "get_screen_relative");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_screen_velocity", "get_screen_velocity");
}
///////////////////////////////////
@@ -1380,6 +1450,14 @@ Vector2 InputEventScreenDrag::get_relative() const {
return relative;
}
+void InputEventScreenDrag::set_relative_screen_position(const Vector2 &p_relative) {
+ screen_relative = p_relative;
+}
+
+Vector2 InputEventScreenDrag::get_relative_screen_position() const {
+ return screen_relative;
+}
+
void InputEventScreenDrag::set_velocity(const Vector2 &p_velocity) {
velocity = p_velocity;
}
@@ -1388,6 +1466,14 @@ Vector2 InputEventScreenDrag::get_velocity() const {
return velocity;
}
+void InputEventScreenDrag::set_screen_velocity(const Vector2 &p_velocity) {
+ screen_velocity = p_velocity;
+}
+
+Vector2 InputEventScreenDrag::get_screen_velocity() const {
+ return screen_velocity;
+}
+
Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs) const {
Ref<InputEventScreenDrag> sd;
@@ -1402,7 +1488,9 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con
sd->set_tilt(get_tilt());
sd->set_position(p_xform.xform(pos + p_local_ofs));
sd->set_relative(p_xform.basis_xform(relative));
+ sd->set_relative_screen_position(get_relative_screen_position());
sd->set_velocity(p_xform.basis_xform(velocity));
+ sd->set_screen_velocity(get_screen_velocity());
return sd;
}
@@ -1427,7 +1515,9 @@ bool InputEventScreenDrag::accumulate(const Ref<InputEvent> &p_event) {
set_position(drag->get_position());
set_velocity(drag->get_velocity());
+ set_screen_velocity(drag->get_screen_velocity());
relative += drag->get_relative();
+ screen_relative += drag->get_relative_screen_position();
return true;
}
@@ -1451,16 +1541,24 @@ void InputEventScreenDrag::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_relative", "relative"), &InputEventScreenDrag::set_relative);
ClassDB::bind_method(D_METHOD("get_relative"), &InputEventScreenDrag::get_relative);
+ ClassDB::bind_method(D_METHOD("set_screen_relative", "relative"), &InputEventScreenDrag::set_relative_screen_position);
+ ClassDB::bind_method(D_METHOD("get_screen_relative"), &InputEventScreenDrag::get_relative_screen_position);
+
ClassDB::bind_method(D_METHOD("set_velocity", "velocity"), &InputEventScreenDrag::set_velocity);
ClassDB::bind_method(D_METHOD("get_velocity"), &InputEventScreenDrag::get_velocity);
+ ClassDB::bind_method(D_METHOD("set_screen_velocity", "velocity"), &InputEventScreenDrag::set_screen_velocity);
+ ClassDB::bind_method(D_METHOD("get_screen_velocity"), &InputEventScreenDrag::get_screen_velocity);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "index"), "set_index", "get_index");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "tilt"), "set_tilt", "get_tilt");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure"), "set_pressure", "get_pressure");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "pen_inverted"), "set_pen_inverted", "get_pen_inverted");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "position", PROPERTY_HINT_NONE, "suffix:px"), "set_position", "get_position");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "relative", PROPERTY_HINT_NONE, "suffix:px"), "set_relative", "get_relative");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_relative", PROPERTY_HINT_NONE, "suffix:px"), "set_screen_relative", "get_screen_relative");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_velocity", "get_velocity");
+ ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "screen_velocity", PROPERTY_HINT_NONE, "suffix:px/s"), "set_screen_velocity", "get_screen_velocity");
}
///////////////////////////////////
diff --git a/core/input/input_event.h b/core/input/input_event.h
index ed7ccf0a9f..21b61f3bc2 100644
--- a/core/input/input_event.h
+++ b/core/input/input_event.h
@@ -157,6 +157,7 @@ class InputEventKey : public InputEventWithModifiers {
Key physical_keycode = Key::NONE;
Key key_label = Key::NONE;
uint32_t unicode = 0; ///unicode
+ KeyLocation location = KeyLocation::UNSPECIFIED;
bool echo = false; /// true if this is an echo key
@@ -178,6 +179,9 @@ public:
void set_unicode(char32_t p_unicode);
char32_t get_unicode() const;
+ void set_location(KeyLocation p_key_location);
+ KeyLocation get_location() const;
+
void set_echo(bool p_enable);
virtual bool is_echo() const override;
@@ -193,6 +197,7 @@ public:
virtual String as_text_physical_keycode() const;
virtual String as_text_keycode() const;
virtual String as_text_key_label() const;
+ virtual String as_text_location() const;
virtual String as_text() const override;
virtual String to_string() override;
@@ -266,7 +271,9 @@ class InputEventMouseMotion : public InputEventMouse {
Vector2 tilt;
float pressure = 0;
Vector2 relative;
+ Vector2 screen_relative;
Vector2 velocity;
+ Vector2 screen_velocity;
bool pen_inverted = false;
protected:
@@ -285,9 +292,15 @@ public:
void set_relative(const Vector2 &p_relative);
Vector2 get_relative() const;
+ void set_relative_screen_position(const Vector2 &p_relative);
+ Vector2 get_relative_screen_position() const;
+
void set_velocity(const Vector2 &p_velocity);
Vector2 get_velocity() const;
+ void set_screen_velocity(const Vector2 &p_velocity);
+ Vector2 get_screen_velocity() const;
+
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
@@ -388,7 +401,9 @@ class InputEventScreenDrag : public InputEventFromWindow {
int index = 0;
Vector2 pos;
Vector2 relative;
+ Vector2 screen_relative;
Vector2 velocity;
+ Vector2 screen_velocity;
Vector2 tilt;
float pressure = 0;
bool pen_inverted = false;
@@ -415,9 +430,15 @@ public:
void set_relative(const Vector2 &p_relative);
Vector2 get_relative() const;
+ void set_relative_screen_position(const Vector2 &p_relative);
+ Vector2 get_relative_screen_position() const;
+
void set_velocity(const Vector2 &p_velocity);
Vector2 get_velocity() const;
+ void set_screen_velocity(const Vector2 &p_velocity);
+ Vector2 get_screen_velocity() const;
+
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 78b9ada884..70041ecfd6 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -754,7 +754,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur
String fullname = E.key;
Vector<String> split = fullname.split(".");
- String name = split[0];
+ const String &name = split[0];
String override_for = split.size() > 1 ? split[1] : String();
if (!override_for.is_empty() && OS::get_singleton()->has_feature(override_for)) {
@@ -766,7 +766,7 @@ const HashMap<String, List<Ref<InputEvent>>> &InputMap::get_builtins_with_featur
String fullname = E.key;
Vector<String> split = fullname.split(".");
- String name = split[0];
+ const String &name = split[0];
String override_for = split.size() > 1 ? split[1] : String();
if (builtins_with_overrides.has(name) && override_for.is_empty()) {
diff --git a/core/io/file_access.cpp b/core/io/file_access.cpp
index 6026dbf896..55286277fa 100644
--- a/core/io/file_access.cpp
+++ b/core/io/file_access.cpp
@@ -38,7 +38,7 @@
#include "core/io/marshalls.h"
#include "core/os/os.h"
-FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = { nullptr, nullptr };
+FileAccess::CreateFunc FileAccess::create_func[ACCESS_MAX] = {};
FileAccess::FileCloseFailNotify FileAccess::close_fail_notify = nullptr;
diff --git a/core/io/file_access_memory.cpp b/core/io/file_access_memory.cpp
index 0983920b94..da02c883e8 100644
--- a/core/io/file_access_memory.cpp
+++ b/core/io/file_access_memory.cpp
@@ -119,7 +119,7 @@ uint64_t FileAccessMemory::get_length() const {
}
bool FileAccessMemory::eof_reached() const {
- return pos > length;
+ return pos >= length;
}
uint8_t FileAccessMemory::get_8() const {
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 74c5c1c191..5a4d6dd099 100644
--- a/core/io/file_access_pack.cpp
+++ b/core/io/file_access_pack.cpp
@@ -327,7 +327,7 @@ uint64_t FileAccessPack::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
to_read = (int64_t)pf.size - (int64_t)pos;
}
- pos += p_length;
+ pos += to_read;
if (to_read <= 0) {
return 0;
@@ -491,7 +491,7 @@ PackedData::PackedDir *DirAccessPack::_find_dir(String p_dir) {
}
for (int i = 0; i < paths.size(); i++) {
- String p = paths[i];
+ const String &p = paths[i];
if (p == ".") {
continue;
} else if (p == "..") {
diff --git a/core/io/http_client.cpp b/core/io/http_client.cpp
index 09505ea05d..833fd1adc3 100644
--- a/core/io/http_client.cpp
+++ b/core/io/http_client.cpp
@@ -73,7 +73,7 @@ String HTTPClient::query_string_from_dict(const Dictionary &p_dict) {
Array keys = p_dict.keys();
for (int i = 0; i < keys.size(); ++i) {
String encoded_key = String(keys[i]).uri_encode();
- Variant value = p_dict[keys[i]];
+ const Variant &value = p_dict[keys[i]];
switch (value.get_type()) {
case Variant::ARRAY: {
// Repeat the key with every values
diff --git a/core/io/image.cpp b/core/io/image.cpp
index ce08b417a8..24caccca5f 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -509,6 +509,7 @@ static void _convert(int p_width, int p_height, const uint8_t *p_src, uint8_t *p
}
void Image::convert(Format p_new_format) {
+ ERR_FAIL_INDEX_MSG(p_new_format, FORMAT_MAX, "The Image format specified (" + itos(p_new_format) + ") is out of range. See Image's Format enum.");
if (data.size() == 0) {
return;
}
@@ -1028,7 +1029,7 @@ void Image::resize_to_po2(bool p_square, Interpolation p_interpolation) {
}
void Image::resize(int p_width, int p_height, Interpolation p_interpolation) {
- ERR_FAIL_COND_MSG(data.size() == 0, "Cannot resize image before creating it, use set_data() first.");
+ ERR_FAIL_COND_MSG(data.is_empty(), "Cannot resize image before creating it, use set_data() first.");
ERR_FAIL_COND_MSG(!_can_modify(format), "Cannot resize in compressed or custom image formats.");
bool mipmap_aware = p_interpolation == INTERPOLATE_TRILINEAR /* || p_interpolation == INTERPOLATE_TRICUBIC */;
@@ -1699,7 +1700,7 @@ static void _generate_po2_mipmap(const Component *p_src, Component *p_dst, uint3
}
void Image::shrink_x2() {
- ERR_FAIL_COND(data.size() == 0);
+ ERR_FAIL_COND(data.is_empty());
if (mipmaps) {
//just use the lower mipmap as base and copy all
@@ -1709,7 +1710,7 @@ void Image::shrink_x2() {
int new_size = data.size() - ofs;
new_img.resize(new_size);
- ERR_FAIL_COND(new_img.size() == 0);
+ ERR_FAIL_COND(new_img.is_empty());
{
uint8_t *w = new_img.ptrw();
@@ -1728,8 +1729,8 @@ void Image::shrink_x2() {
ERR_FAIL_COND(!_can_modify(format));
int ps = get_format_pixel_size(format);
new_img.resize((width / 2) * (height / 2) * ps);
- ERR_FAIL_COND(new_img.size() == 0);
- ERR_FAIL_COND(data.size() == 0);
+ ERR_FAIL_COND(new_img.is_empty());
+ ERR_FAIL_COND(data.is_empty());
{
uint8_t *w = new_img.ptrw();
@@ -2780,7 +2781,7 @@ void Image::_get_clipped_src_and_dest_rects(const Ref<Image> &p_src, const Rect2
}
void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) {
- ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blit_rect an image: invalid source Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
ERR_FAIL_COND(dsize == 0);
@@ -2822,8 +2823,8 @@ void Image::blit_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const P
}
void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) {
- ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
- ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blit_rect_mask an image: invalid source Image object.");
+ ERR_FAIL_COND_MSG(p_mask.is_null(), "Cannot blit_rect_mask an image: invalid mask Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
int maskdsize = p_mask->data.size();
@@ -2872,7 +2873,7 @@ void Image::blit_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, co
}
void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const Point2i &p_dest) {
- ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blend_rect an image: invalid source Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
ERR_FAIL_COND(dsize == 0);
@@ -2907,8 +2908,8 @@ void Image::blend_rect(const Ref<Image> &p_src, const Rect2i &p_src_rect, const
}
void Image::blend_rect_mask(const Ref<Image> &p_src, const Ref<Image> &p_mask, const Rect2i &p_src_rect, const Point2i &p_dest) {
- ERR_FAIL_COND_MSG(p_src.is_null(), "It's not a reference to a valid Image object.");
- ERR_FAIL_COND_MSG(p_mask.is_null(), "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_MSG(p_src.is_null(), "Cannot blend_rect_mask an image: invalid source Image object.");
+ ERR_FAIL_COND_MSG(p_mask.is_null(), "Cannot blend_rect_mask an image: invalid mask Image object.");
int dsize = data.size();
int srcdsize = p_src->data.size();
int maskdsize = p_mask->data.size();
@@ -3012,6 +3013,7 @@ void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) {
}
ImageMemLoadFunc Image::_png_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr;
ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr;
ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
@@ -3321,7 +3323,7 @@ void Image::adjust_bcs(float p_brightness, float p_contrast, float p_saturation)
}
Image::UsedChannels Image::detect_used_channels(CompressSource p_source) const {
- ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA);
+ ERR_FAIL_COND_V(data.is_empty(), USED_CHANNELS_RGBA);
ERR_FAIL_COND_V(is_compressed(), USED_CHANNELS_RGBA);
bool r = false, g = false, b = false, a = false, c = false;
@@ -3876,7 +3878,7 @@ Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) {
void Image::convert_rg_to_ra_rgba8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
- ERR_FAIL_COND(!data.size());
+ ERR_FAIL_COND(data.is_empty());
int s = data.size();
uint8_t *w = data.ptrw();
@@ -3889,7 +3891,7 @@ void Image::convert_rg_to_ra_rgba8() {
void Image::convert_ra_rgba8_to_rg() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
- ERR_FAIL_COND(!data.size());
+ ERR_FAIL_COND(data.is_empty());
int s = data.size();
uint8_t *w = data.ptrw();
@@ -3902,7 +3904,7 @@ void Image::convert_ra_rgba8_to_rg() {
void Image::convert_rgba8_to_bgra8() {
ERR_FAIL_COND(format != FORMAT_RGBA8);
- ERR_FAIL_COND(!data.size());
+ ERR_FAIL_COND(data.is_empty());
int s = data.size();
uint8_t *w = data.ptrw();
diff --git a/core/io/image.h b/core/io/image.h
index a21d05187b..e35b359a79 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -145,6 +145,7 @@ public:
};
static ImageMemLoadFunc _png_mem_loader_func;
+ static ImageMemLoadFunc _png_mem_unpacker_func;
static ImageMemLoadFunc _jpg_mem_loader_func;
static ImageMemLoadFunc _webp_mem_loader_func;
static ImageMemLoadFunc _tga_mem_loader_func;
@@ -312,8 +313,8 @@ public:
Error save_jpg(const String &p_path, float p_quality = 0.75) const;
Vector<uint8_t> save_png_to_buffer() const;
Vector<uint8_t> save_jpg_to_buffer(float p_quality = 0.75) const;
- Vector<uint8_t> save_exr_to_buffer(bool p_grayscale) const;
- Error save_exr(const String &p_path, bool p_grayscale) const;
+ Vector<uint8_t> save_exr_to_buffer(bool p_grayscale = false) const;
+ Error save_exr(const String &p_path, bool p_grayscale = false) const;
Error save_webp(const String &p_path, const bool p_lossy = false, const float p_quality = 0.75f) const;
Vector<uint8_t> save_webp_to_buffer(const bool p_lossy = false, const float p_quality = 0.75f) const;
@@ -430,7 +431,7 @@ public:
void set_as_black();
void copy_internals_from(const Ref<Image> &p_image) {
- ERR_FAIL_COND_MSG(p_image.is_null(), "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_MSG(p_image.is_null(), "Cannot copy image internals: invalid Image object.");
format = p_image->format;
width = p_image->width;
height = p_image->height;
diff --git a/core/io/image_loader.cpp b/core/io/image_loader.cpp
index c6452f1033..bef515f259 100644
--- a/core/io/image_loader.cpp
+++ b/core/io/image_loader.cpp
@@ -81,7 +81,7 @@ void ImageFormatLoaderExtension::_bind_methods() {
}
Error ImageLoader::load_image(String p_file, Ref<Image> p_image, Ref<FileAccess> p_custom, BitField<ImageFormatLoader::LoaderFlags> p_flags, float p_scale) {
- ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "It's not a reference to a valid Image object.");
+ ERR_FAIL_COND_V_MSG(p_image.is_null(), ERR_INVALID_PARAMETER, "Can't load an image: invalid Image object.");
Ref<FileAccess> f = p_custom;
if (f.is_null()) {
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index 3d384d9345..bc2493d360 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -638,6 +638,8 @@ Error decode_variant(Variant &r_variant, const uint8_t *p_buffer, int p_len, int
if (str.is_empty()) {
r_variant = (Object *)nullptr;
} else {
+ ERR_FAIL_COND_V(!ClassDB::can_instantiate(str), ERR_INVALID_DATA);
+
Object *obj = ClassDB::instantiate(str);
ERR_FAIL_NULL_V(obj, ERR_UNAVAILABLE);
@@ -1492,6 +1494,8 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
r_len += 4;
} else {
+ ERR_FAIL_COND_V(!ClassDB::can_instantiate(obj->get_class()), ERR_INVALID_PARAMETER);
+
_encode_string(obj->get_class(), buf, r_len);
List<PropertyInfo> props;
diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp
index 48806fba73..0329ace313 100644
--- a/core/io/packet_peer.cpp
+++ b/core/io/packet_peer.cpp
@@ -318,9 +318,9 @@ int PacketPeerStream::get_output_buffer_max_size() const {
}
PacketPeerStream::PacketPeerStream() {
- int rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2");
+ int64_t rbsize = GLOBAL_GET("network/limits/packet_peer_stream/max_buffer_po2");
ring_buffer.resize(rbsize);
- input_buffer.resize(1 << rbsize);
- output_buffer.resize(1 << rbsize);
+ input_buffer.resize(int64_t(1) << rbsize);
+ output_buffer.resize(int64_t(1) << rbsize);
}
diff --git a/core/io/plist.cpp b/core/io/plist.cpp
new file mode 100644
index 0000000000..86737609bf
--- /dev/null
+++ b/core/io/plist.cpp
@@ -0,0 +1,868 @@
+/**************************************************************************/
+/* plist.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "plist.h"
+
+PList::PLNodeType PListNode::get_type() const {
+ return data_type;
+}
+
+Variant PListNode::get_value() const {
+ switch (data_type) {
+ case PList::PL_NODE_TYPE_NIL: {
+ return Variant();
+ } break;
+ case PList::PL_NODE_TYPE_STRING: {
+ return String::utf8(data_string.get_data());
+ } break;
+ case PList::PL_NODE_TYPE_ARRAY: {
+ Array arr;
+ for (const Ref<PListNode> &E : data_array) {
+ arr.push_back(E);
+ }
+ return arr;
+ } break;
+ case PList::PL_NODE_TYPE_DICT: {
+ Dictionary dict;
+ for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
+ dict[E.key] = E.value;
+ }
+ return dict;
+ } break;
+ case PList::PL_NODE_TYPE_BOOLEAN: {
+ return data_bool;
+ } break;
+ case PList::PL_NODE_TYPE_INTEGER: {
+ return data_int;
+ } break;
+ case PList::PL_NODE_TYPE_REAL: {
+ return data_real;
+ } break;
+ case PList::PL_NODE_TYPE_DATA: {
+ int strlen = data_string.length();
+
+ size_t arr_len = 0;
+ Vector<uint8_t> buf;
+ {
+ buf.resize(strlen / 4 * 3 + 1);
+ uint8_t *w = buf.ptrw();
+
+ ERR_FAIL_COND_V(CryptoCore::b64_decode(&w[0], buf.size(), &arr_len, (unsigned char *)data_string.get_data(), strlen) != OK, Vector<uint8_t>());
+ }
+ buf.resize(arr_len);
+ return buf;
+ } break;
+ case PList::PL_NODE_TYPE_DATE: {
+ return String(data_string.get_data());
+ } break;
+ }
+ return Variant();
+}
+
+Ref<PListNode> PListNode::new_node(const Variant &p_value) {
+ Ref<PListNode> node;
+ node.instantiate();
+
+ switch (p_value.get_type()) {
+ case Variant::NIL: {
+ node->data_type = PList::PL_NODE_TYPE_NIL;
+ } break;
+ case Variant::BOOL: {
+ node->data_type = PList::PL_NODE_TYPE_BOOLEAN;
+ node->data_bool = p_value;
+ } break;
+ case Variant::INT: {
+ node->data_type = PList::PL_NODE_TYPE_INTEGER;
+ node->data_int = p_value;
+ } break;
+ case Variant::FLOAT: {
+ node->data_type = PList::PL_NODE_TYPE_REAL;
+ node->data_real = p_value;
+ } break;
+ case Variant::STRING_NAME:
+ case Variant::STRING: {
+ node->data_type = PList::PL_NODE_TYPE_STRING;
+ node->data_string = p_value.operator String().utf8();
+ } break;
+ case Variant::DICTIONARY: {
+ node->data_type = PList::PL_NODE_TYPE_DICT;
+ Dictionary dict = p_value;
+ const Variant *next = dict.next(nullptr);
+ while (next) {
+ Ref<PListNode> sub_node = dict[*next];
+ ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid dictionary element, should be PListNode.");
+ node->data_dict[*next] = sub_node;
+ next = dict.next(next);
+ }
+ } break;
+ case Variant::ARRAY: {
+ node->data_type = PList::PL_NODE_TYPE_ARRAY;
+ Array ar = p_value;
+ for (int i = 0; i < ar.size(); i++) {
+ Ref<PListNode> sub_node = ar[i];
+ ERR_FAIL_COND_V_MSG(sub_node.is_null(), Ref<PListNode>(), "Invalid array element, should be PListNode.");
+ node->data_array.push_back(sub_node);
+ }
+ } break;
+ case Variant::PACKED_BYTE_ARRAY: {
+ node->data_type = PList::PL_NODE_TYPE_DATA;
+ PackedByteArray buf = p_value;
+ node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8();
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(Ref<PListNode>(), "Unsupported data type.");
+ } break;
+ }
+ return node;
+}
+
+Ref<PListNode> PListNode::new_array() {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_ARRAY;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_dict() {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_string(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_STRING;
+ node->data_string = p_string.utf8();
+ return node;
+}
+
+Ref<PListNode> PListNode::new_data(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATA;
+ node->data_string = p_string.utf8();
+ return node;
+}
+
+Ref<PListNode> PListNode::new_date(const String &p_string) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_DATE;
+ node->data_string = p_string.utf8();
+ node->data_real = (double)Time::get_singleton()->get_unix_time_from_datetime_string(p_string) - 978307200.0;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_bool(bool p_bool) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_BOOLEAN;
+ node->data_bool = p_bool;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_int(int64_t p_int) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_INTEGER;
+ node->data_int = p_int;
+ return node;
+}
+
+Ref<PListNode> PListNode::new_real(double p_real) {
+ Ref<PListNode> node = memnew(PListNode());
+ ERR_FAIL_COND_V(node.is_null(), Ref<PListNode>());
+ node->data_type = PList::PLNodeType::PL_NODE_TYPE_REAL;
+ node->data_real = p_real;
+ return node;
+}
+
+bool PListNode::push_subnode(const Ref<PListNode> &p_node, const String &p_key) {
+ ERR_FAIL_COND_V(p_node.is_null(), false);
+ if (data_type == PList::PLNodeType::PL_NODE_TYPE_DICT) {
+ ERR_FAIL_COND_V(p_key.is_empty(), false);
+ ERR_FAIL_COND_V(data_dict.has(p_key), false);
+ data_dict[p_key] = p_node;
+ return true;
+ } else if (data_type == PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
+ data_array.push_back(p_node);
+ return true;
+ } else {
+ ERR_FAIL_V_MSG(false, "PList: Invalid parent node type, should be DICT or ARRAY.");
+ }
+}
+
+size_t PListNode::get_asn1_size(uint8_t p_len_octets) const {
+ // Get size of all data, excluding type and size information.
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ return 0;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATA:
+ case PList::PLNodeType::PL_NODE_TYPE_DATE: {
+ ERR_FAIL_V_MSG(0, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ return data_string.length();
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ return 1;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER:
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ return 4;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ size_t size = 0;
+ for (int i = 0; i < data_array.size(); i++) {
+ size += 1 + _asn1_size_len(p_len_octets) + data_array[i]->get_asn1_size(p_len_octets);
+ }
+ return size;
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ size_t size = 0;
+
+ for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
+ size += 1 + _asn1_size_len(p_len_octets); // Sequence.
+ size += 1 + _asn1_size_len(p_len_octets) + E.key.utf8().length(); //Key.
+ size += 1 + _asn1_size_len(p_len_octets) + E.value->get_asn1_size(p_len_octets); // Value.
+ }
+ return size;
+ } break;
+ default: {
+ return 0;
+ } break;
+ }
+}
+
+int PListNode::_asn1_size_len(uint8_t p_len_octets) {
+ if (p_len_octets > 1) {
+ return p_len_octets + 1;
+ } else {
+ return 1;
+ }
+}
+
+void PListNode::store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const {
+ uint32_t size = get_asn1_size(p_len_octets);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+}
+
+bool PListNode::store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const {
+ // Convert to binary ASN1 stream.
+ bool valid = true;
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ // Nothing to store.
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATE:
+ case PList::PLNodeType::PL_NODE_TYPE_DATA: {
+ ERR_FAIL_V_MSG(false, "PList: DATE and DATA nodes are not supported by ASN.1 serialization.");
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ p_stream.push_back(0x0C);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 0; i < data_string.size(); i++) {
+ p_stream.push_back(data_string[i]);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ p_stream.push_back(0x01);
+ store_asn1_size(p_stream, p_len_octets);
+ if (data_bool) {
+ p_stream.push_back(0x01);
+ } else {
+ p_stream.push_back(0x00);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
+ p_stream.push_back(0x02);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 4; i >= 0; i--) {
+ uint8_t x = (data_int >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ p_stream.push_back(0x03);
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 4; i >= 0; i--) {
+ uint8_t x = (data_int >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ p_stream.push_back(0x30); // Sequence.
+ store_asn1_size(p_stream, p_len_octets);
+ for (int i = 0; i < data_array.size(); i++) {
+ valid = valid && data_array[i]->store_asn1(p_stream, p_len_octets);
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ p_stream.push_back(0x31); // Set.
+ store_asn1_size(p_stream, p_len_octets);
+ for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
+ CharString cs = E.key.utf8();
+ uint32_t size = cs.length();
+
+ // Sequence.
+ p_stream.push_back(0x30);
+ uint32_t seq_size = 2 * (1 + _asn1_size_len(p_len_octets)) + size + E.value->get_asn1_size(p_len_octets);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (seq_size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ // Key.
+ p_stream.push_back(0x0C);
+ if (p_len_octets > 1) {
+ p_stream.push_back(0x80 + p_len_octets);
+ }
+ for (int i = p_len_octets - 1; i >= 0; i--) {
+ uint8_t x = (size >> i * 8) & 0xFF;
+ p_stream.push_back(x);
+ }
+ for (uint32_t i = 0; i < size; i++) {
+ p_stream.push_back(cs[i]);
+ }
+ // Value.
+ valid = valid && E.value->store_asn1(p_stream, p_len_octets);
+ }
+ } break;
+ }
+ return valid;
+}
+
+void PListNode::store_text(String &p_stream, uint8_t p_indent) const {
+ // Convert to text XML stream.
+ switch (data_type) {
+ case PList::PLNodeType::PL_NODE_TYPE_NIL: {
+ // Nothing to store.
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATA: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<data>\n";
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += data_string + "\n";
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</data>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DATE: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<date>";
+ p_stream += data_string;
+ p_stream += "</date>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_STRING: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<string>";
+ p_stream += String::utf8(data_string);
+ p_stream += "</string>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_BOOLEAN: {
+ p_stream += String("\t").repeat(p_indent);
+ if (data_bool) {
+ p_stream += "<true/>\n";
+ } else {
+ p_stream += "<false/>\n";
+ }
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_INTEGER: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<integer>";
+ p_stream += itos(data_int);
+ p_stream += "</integer>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_REAL: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<real>";
+ p_stream += rtos(data_real);
+ p_stream += "</real>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_ARRAY: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<array>\n";
+ for (int i = 0; i < data_array.size(); i++) {
+ data_array[i]->store_text(p_stream, p_indent + 1);
+ }
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</array>\n";
+ } break;
+ case PList::PLNodeType::PL_NODE_TYPE_DICT: {
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "<dict>\n";
+ for (const KeyValue<String, Ref<PListNode>> &E : data_dict) {
+ p_stream += String("\t").repeat(p_indent + 1);
+ p_stream += "<key>";
+ p_stream += E.key;
+ p_stream += "</key>\n";
+ E.value->store_text(p_stream, p_indent + 1);
+ }
+ p_stream += String("\t").repeat(p_indent);
+ p_stream += "</dict>\n";
+ } break;
+ }
+}
+
+/*************************************************************************/
+
+PList::PList() {
+ root = PListNode::new_dict();
+}
+
+PList::PList(const String &p_string) {
+ String err_str;
+ bool ok = load_string(p_string, err_str);
+ ERR_FAIL_COND_MSG(!ok, "PList: " + err_str);
+}
+
+uint64_t PList::read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size) {
+ uint64_t pos = p_file->get_position();
+ uint64_t ret = 0;
+ switch (p_size) {
+ case 1: {
+ ret = p_file->get_8();
+ } break;
+ case 2: {
+ ret = BSWAP16(p_file->get_16());
+ } break;
+ case 3: {
+ ret = BSWAP32(p_file->get_32() & 0x00FFFFFF);
+ } break;
+ case 4: {
+ ret = BSWAP32(p_file->get_32());
+ } break;
+ case 5: {
+ ret = BSWAP64(p_file->get_64() & 0x000000FFFFFFFFFF);
+ } break;
+ case 6: {
+ ret = BSWAP64(p_file->get_64() & 0x0000FFFFFFFFFFFF);
+ } break;
+ case 7: {
+ ret = BSWAP64(p_file->get_64() & 0x00FFFFFFFFFFFFFF);
+ } break;
+ case 8: {
+ ret = BSWAP64(p_file->get_64());
+ } break;
+ default: {
+ ret = 0;
+ }
+ }
+ p_file->seek(pos + p_size);
+
+ return ret;
+}
+
+Ref<PListNode> PList::read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx) {
+ Ref<PListNode> node;
+ node.instantiate();
+
+ uint64_t ot_off = trailer.offset_table_start + p_offset_idx * trailer.offset_size;
+ p_file->seek(ot_off);
+ uint64_t marker_off = read_bplist_var_size_int(p_file, trailer.offset_size);
+ ERR_FAIL_COND_V_MSG(marker_off == 0, Ref<PListNode>(), "Invalid marker size.");
+
+ p_file->seek(marker_off);
+ uint8_t marker = p_file->get_8();
+ uint8_t marker_type = marker & 0xF0;
+ uint64_t marker_size = marker & 0x0F;
+
+ switch (marker_type) {
+ case 0x00: {
+ if (marker_size == 0x00) {
+ node->data_type = PL_NODE_TYPE_NIL;
+ } else if (marker_size == 0x08) {
+ node->data_type = PL_NODE_TYPE_BOOLEAN;
+ node->data_bool = false;
+ } else if (marker_size == 0x09) {
+ node->data_type = PL_NODE_TYPE_BOOLEAN;
+ node->data_bool = true;
+ } else {
+ ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid nil/bool marker value.");
+ }
+ } break;
+ case 0x10: {
+ node->data_type = PL_NODE_TYPE_INTEGER;
+ node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size)));
+ } break;
+ case 0x20: {
+ node->data_type = PL_NODE_TYPE_REAL;
+ node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, pow(2, marker_size)));
+ } break;
+ case 0x30: {
+ node->data_type = PL_NODE_TYPE_DATE;
+ node->data_int = BSWAP64(p_file->get_64());
+ node->data_string = Time::get_singleton()->get_datetime_string_from_unix_time(node->data_real + 978307200.0).utf8();
+ } break;
+ case 0x40: {
+ if (marker_size == 0x0F) {
+ uint8_t ext = p_file->get_8() & 0xF;
+ marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
+ }
+ node->data_type = PL_NODE_TYPE_DATA;
+ PackedByteArray buf;
+ buf.resize(marker_size + 1);
+ p_file->get_buffer(reinterpret_cast<uint8_t *>(buf.ptrw()), marker_size);
+ node->data_string = CryptoCore::b64_encode_str(buf.ptr(), buf.size()).utf8();
+ } break;
+ case 0x50: {
+ if (marker_size == 0x0F) {
+ uint8_t ext = p_file->get_8() & 0xF;
+ marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
+ }
+ node->data_type = PL_NODE_TYPE_STRING;
+ node->data_string.resize(marker_size + 1);
+ p_file->get_buffer(reinterpret_cast<uint8_t *>(node->data_string.ptrw()), marker_size);
+ } break;
+ case 0x60: {
+ if (marker_size == 0x0F) {
+ uint8_t ext = p_file->get_8() & 0xF;
+ marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
+ }
+ Char16String cs16;
+ cs16.resize(marker_size + 1);
+ for (uint64_t i = 0; i < marker_size; i++) {
+ cs16[i] = BSWAP16(p_file->get_16());
+ }
+ node->data_type = PL_NODE_TYPE_STRING;
+ node->data_string = String::utf16(cs16.ptr(), cs16.length()).utf8();
+ } break;
+ case 0x80: {
+ node->data_type = PL_NODE_TYPE_INTEGER;
+ node->data_int = static_cast<int64_t>(read_bplist_var_size_int(p_file, marker_size + 1));
+ } break;
+ case 0xA0:
+ case 0xC0: {
+ if (marker_size == 0x0F) {
+ uint8_t ext = p_file->get_8() & 0xF;
+ marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
+ }
+ uint64_t pos = p_file->get_position();
+
+ node->data_type = PL_NODE_TYPE_ARRAY;
+ for (uint64_t i = 0; i < marker_size; i++) {
+ p_file->seek(pos + trailer.ref_size * i);
+ uint64_t ref = read_bplist_var_size_int(p_file, trailer.ref_size);
+
+ Ref<PListNode> element = read_bplist_obj(p_file, ref);
+ ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>());
+ node->data_array.push_back(element);
+ }
+ } break;
+ case 0xD0: {
+ if (marker_size == 0x0F) {
+ uint8_t ext = p_file->get_8() & 0xF;
+ marker_size = read_bplist_var_size_int(p_file, pow(2, ext));
+ }
+ uint64_t pos = p_file->get_position();
+
+ node->data_type = PL_NODE_TYPE_DICT;
+ for (uint64_t i = 0; i < marker_size; i++) {
+ p_file->seek(pos + trailer.ref_size * i);
+ uint64_t key_ref = read_bplist_var_size_int(p_file, trailer.ref_size);
+
+ p_file->seek(pos + trailer.ref_size * (i + marker_size));
+ uint64_t obj_ref = read_bplist_var_size_int(p_file, trailer.ref_size);
+
+ Ref<PListNode> element_key = read_bplist_obj(p_file, key_ref);
+ ERR_FAIL_COND_V(element_key.is_null() || element_key->data_type != PL_NODE_TYPE_STRING, Ref<PListNode>());
+ Ref<PListNode> element = read_bplist_obj(p_file, obj_ref);
+ ERR_FAIL_COND_V(element.is_null(), Ref<PListNode>());
+ node->data_dict[String::utf8(element_key->data_string.ptr(), element_key->data_string.length())] = element;
+ }
+ } break;
+ default: {
+ ERR_FAIL_V_MSG(Ref<PListNode>(), "Invalid marker type.");
+ }
+ }
+ return node;
+}
+
+bool PList::load_file(const String &p_filename) {
+ root = Ref<PListNode>();
+
+ Ref<FileAccess> fb = FileAccess::open(p_filename, FileAccess::READ);
+ if (fb.is_null()) {
+ return false;
+ }
+
+ unsigned char magic[8];
+ fb->get_buffer(magic, 8);
+
+ if (String((const char *)magic, 8) == "bplist00") {
+ fb->seek_end(-26);
+ trailer.offset_size = fb->get_8();
+ trailer.ref_size = fb->get_8();
+ trailer.object_num = BSWAP64(fb->get_64());
+ trailer.root_offset_idx = BSWAP64(fb->get_64());
+ trailer.offset_table_start = BSWAP64(fb->get_64());
+ root = read_bplist_obj(fb, trailer.root_offset_idx);
+
+ return root.is_valid();
+ } else {
+ // Load text plist.
+ Error err;
+ Vector<uint8_t> array = FileAccess::get_file_as_bytes(p_filename, &err);
+ ERR_FAIL_COND_V(err != OK, false);
+
+ String ret;
+ ret.parse_utf8((const char *)array.ptr(), array.size());
+ String err_str;
+ bool ok = load_string(ret, err_str);
+ ERR_FAIL_COND_V_MSG(!ok, false, "PList: " + err_str);
+
+ return true;
+ }
+}
+
+bool PList::load_string(const String &p_string, String &r_err_out) {
+ root = Ref<PListNode>();
+
+ int pos = 0;
+ bool in_plist = false;
+ bool done_plist = false;
+ List<Ref<PListNode>> stack;
+ String key;
+ while (pos >= 0) {
+ int open_token_s = p_string.find("<", pos);
+ if (open_token_s == -1) {
+ r_err_out = "Unexpected end of data. No tags found.";
+ return false;
+ }
+ int open_token_e = p_string.find(">", open_token_s);
+ pos = open_token_e;
+
+ String token = p_string.substr(open_token_s + 1, open_token_e - open_token_s - 1);
+ if (token.is_empty()) {
+ r_err_out = "Invalid token name.";
+ return false;
+ }
+ String value;
+ if (token[0] == '?' || token[0] == '!') { // Skip <?xml ... ?> and <!DOCTYPE ... >
+ int end_token_e = p_string.find(">", open_token_s);
+ pos = end_token_e;
+ continue;
+ }
+
+ if (token.find("plist", 0) == 0) {
+ in_plist = true;
+ continue;
+ }
+
+ if (token == "/plist") {
+ done_plist = true;
+ break;
+ }
+
+ if (!in_plist) {
+ r_err_out = "Node outside of <plist> tag.";
+ return false;
+ }
+
+ if (token == "dict") {
+ if (!stack.is_empty()) {
+ // Add subnode end enter it.
+ Ref<PListNode> dict = PListNode::new_dict();
+ dict->data_type = PList::PLNodeType::PL_NODE_TYPE_DICT;
+ if (!stack.back()->get()->push_subnode(dict, key)) {
+ r_err_out = "Can't push subnode, invalid parent type.";
+ return false;
+ }
+ stack.push_back(dict);
+ } else {
+ // Add root node.
+ if (!root.is_null()) {
+ r_err_out = "Root node already set.";
+ return false;
+ }
+ Ref<PListNode> dict = PListNode::new_dict();
+ stack.push_back(dict);
+ root = dict;
+ }
+ continue;
+ }
+
+ if (token == "/dict") {
+ // Exit current dict.
+ if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_DICT) {
+ r_err_out = "Mismatched </dict> tag.";
+ return false;
+ }
+ stack.pop_back();
+ continue;
+ }
+
+ if (token == "array") {
+ if (!stack.is_empty()) {
+ // Add subnode end enter it.
+ Ref<PListNode> arr = PListNode::new_array();
+ if (!stack.back()->get()->push_subnode(arr, key)) {
+ r_err_out = "Can't push subnode, invalid parent type.";
+ return false;
+ }
+ stack.push_back(arr);
+ } else {
+ // Add root node.
+ if (!root.is_null()) {
+ r_err_out = "Root node already set.";
+ return false;
+ }
+ Ref<PListNode> arr = PListNode::new_array();
+ stack.push_back(arr);
+ root = arr;
+ }
+ continue;
+ }
+
+ if (token == "/array") {
+ // Exit current array.
+ if (stack.is_empty() || stack.back()->get()->data_type != PList::PLNodeType::PL_NODE_TYPE_ARRAY) {
+ r_err_out = "Mismatched </array> tag.";
+ return false;
+ }
+ stack.pop_back();
+ continue;
+ }
+
+ if (token[token.length() - 1] == '/') {
+ token = token.substr(0, token.length() - 1);
+ } else {
+ int end_token_s = p_string.find("</", pos);
+ if (end_token_s == -1) {
+ r_err_out = vformat("Mismatched <%s> tag.", token);
+ return false;
+ }
+ int end_token_e = p_string.find(">", end_token_s);
+ pos = end_token_e;
+ String end_token = p_string.substr(end_token_s + 2, end_token_e - end_token_s - 2);
+ if (end_token != token) {
+ r_err_out = vformat("Mismatched <%s> and <%s> token pair.", token, end_token);
+ return false;
+ }
+ value = p_string.substr(open_token_e + 1, end_token_s - open_token_e - 1);
+ }
+ if (token == "key") {
+ key = value;
+ } else {
+ Ref<PListNode> var = nullptr;
+ if (token == "true") {
+ var = PListNode::new_bool(true);
+ } else if (token == "false") {
+ var = PListNode::new_bool(false);
+ } else if (token == "integer") {
+ var = PListNode::new_int(value.to_int());
+ } else if (token == "real") {
+ var = PListNode::new_real(value.to_float());
+ } else if (token == "string") {
+ var = PListNode::new_string(value);
+ } else if (token == "data") {
+ var = PListNode::new_data(value);
+ } else if (token == "date") {
+ var = PListNode::new_date(value);
+ } else {
+ r_err_out = vformat("Invalid value type: %s.", token);
+ return false;
+ }
+ if (stack.is_empty() || !stack.back()->get()->push_subnode(var, key)) {
+ r_err_out = "Can't push subnode, invalid parent type.";
+ return false;
+ }
+ }
+ }
+ if (!stack.is_empty() || !done_plist) {
+ r_err_out = "Unexpected end of data. Root node is not closed.";
+ return false;
+ }
+ return true;
+}
+
+PackedByteArray PList::save_asn1() const {
+ if (root == nullptr) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: Invalid PList, no root node.");
+ }
+ size_t size = root->get_asn1_size(1);
+ uint8_t len_octets = 0;
+ if (size < 0x80) {
+ len_octets = 1;
+ } else {
+ size = root->get_asn1_size(2);
+ if (size < 0xFFFF) {
+ len_octets = 2;
+ } else {
+ size = root->get_asn1_size(3);
+ if (size < 0xFFFFFF) {
+ len_octets = 3;
+ } else {
+ size = root->get_asn1_size(4);
+ if (size < 0xFFFFFFFF) {
+ len_octets = 4;
+ } else {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: Data is too big for ASN.1 serializer, should be < 4 GiB.");
+ }
+ }
+ }
+ }
+
+ PackedByteArray ret;
+ if (!root->store_asn1(ret, len_octets)) {
+ ERR_FAIL_V_MSG(PackedByteArray(), "PList: ASN.1 serializer error.");
+ }
+ return ret;
+}
+
+String PList::save_text() const {
+ if (root == nullptr) {
+ ERR_FAIL_V_MSG(String(), "PList: Invalid PList, no root node.");
+ }
+
+ String ret;
+ ret += "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+ ret += "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n";
+ ret += "<plist version=\"1.0\">\n";
+
+ root->store_text(ret, 0);
+
+ ret += "</plist>\n\n";
+ return ret;
+}
+
+Ref<PListNode> PList::get_root() {
+ return root;
+}
diff --git a/core/io/plist.h b/core/io/plist.h
new file mode 100644
index 0000000000..7d8b8ef0b4
--- /dev/null
+++ b/core/io/plist.h
@@ -0,0 +1,128 @@
+/**************************************************************************/
+/* plist.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef PLIST_H
+#define PLIST_H
+
+// Property list file format (application/x-plist) parser, property list ASN-1 serialization.
+
+#include "core/crypto/crypto_core.h"
+#include "core/io/file_access.h"
+#include "core/os/time.h"
+
+class PListNode;
+
+class PList : public RefCounted {
+ friend class PListNode;
+
+public:
+ enum PLNodeType {
+ PL_NODE_TYPE_NIL,
+ PL_NODE_TYPE_STRING,
+ PL_NODE_TYPE_ARRAY,
+ PL_NODE_TYPE_DICT,
+ PL_NODE_TYPE_BOOLEAN,
+ PL_NODE_TYPE_INTEGER,
+ PL_NODE_TYPE_REAL,
+ PL_NODE_TYPE_DATA,
+ PL_NODE_TYPE_DATE,
+ };
+
+private:
+ struct PListTrailer {
+ uint8_t offset_size;
+ uint8_t ref_size;
+ uint64_t object_num;
+ uint64_t root_offset_idx;
+ uint64_t offset_table_start;
+ };
+
+ PListTrailer trailer;
+ Ref<PListNode> root;
+
+ uint64_t read_bplist_var_size_int(Ref<FileAccess> p_file, uint8_t p_size);
+ Ref<PListNode> read_bplist_obj(Ref<FileAccess> p_file, uint64_t p_offset_idx);
+
+public:
+ PList();
+ PList(const String &p_string);
+
+ bool load_file(const String &p_filename);
+ bool load_string(const String &p_string, String &r_err_out);
+
+ PackedByteArray save_asn1() const;
+ String save_text() const;
+
+ Ref<PListNode> get_root();
+};
+
+/*************************************************************************/
+
+class PListNode : public RefCounted {
+ static int _asn1_size_len(uint8_t p_len_octets);
+
+public:
+ PList::PLNodeType data_type = PList::PLNodeType::PL_NODE_TYPE_NIL;
+
+ CharString data_string;
+ Vector<Ref<PListNode>> data_array;
+ HashMap<String, Ref<PListNode>> data_dict;
+ union {
+ int64_t data_int;
+ bool data_bool;
+ double data_real;
+ };
+
+ PList::PLNodeType get_type() const;
+ Variant get_value() const;
+
+ static Ref<PListNode> new_node(const Variant &p_value);
+ static Ref<PListNode> new_array();
+ static Ref<PListNode> new_dict();
+ static Ref<PListNode> new_string(const String &p_string);
+ static Ref<PListNode> new_data(const String &p_string);
+ static Ref<PListNode> new_date(const String &p_string);
+ static Ref<PListNode> new_bool(bool p_bool);
+ static Ref<PListNode> new_int(int64_t p_int);
+ static Ref<PListNode> new_real(double p_real);
+
+ bool push_subnode(const Ref<PListNode> &p_node, const String &p_key = "");
+
+ size_t get_asn1_size(uint8_t p_len_octets) const;
+
+ void store_asn1_size(PackedByteArray &p_stream, uint8_t p_len_octets) const;
+ bool store_asn1(PackedByteArray &p_stream, uint8_t p_len_octets) const;
+ void store_text(String &p_stream, uint8_t p_indent) const;
+
+ PListNode() {}
+ ~PListNode() {}
+};
+
+#endif // PLIST_H
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 64fa597a67..daa57be76f 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -204,7 +204,58 @@ void Resource::reload_from_file() {
copy_from(s);
}
-Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) {
+void Resource::_dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
+ switch (r_variant.get_type()) {
+ case Variant::ARRAY: {
+ Array a = r_variant;
+ for (int i = 0; i < a.size(); i++) {
+ _dupe_sub_resources(a[i], p_for_scene, p_remap_cache);
+ }
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = r_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for (Variant &k : keys) {
+ if (k.get_type() == Variant::OBJECT) {
+ // Replace in dictionary key.
+ Ref<Resource> sr = k;
+ if (sr.is_valid() && sr->is_local_to_scene()) {
+ if (p_remap_cache.has(sr)) {
+ d[p_remap_cache[sr]] = d[k];
+ d.erase(k);
+ } else {
+ Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
+ d[dupe] = d[k];
+ d.erase(k);
+ p_remap_cache[sr] = dupe;
+ }
+ }
+ } else {
+ _dupe_sub_resources(k, p_for_scene, p_remap_cache);
+ }
+
+ _dupe_sub_resources(d[k], p_for_scene, p_remap_cache);
+ }
+ } break;
+ case Variant::OBJECT: {
+ Ref<Resource> sr = r_variant;
+ if (sr.is_valid() && sr->is_local_to_scene()) {
+ if (p_remap_cache.has(sr)) {
+ r_variant = p_remap_cache[sr];
+ } else {
+ Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
+ r_variant = dupe;
+ p_remap_cache[sr] = dupe;
+ }
+ }
+ } break;
+ default: {
+ }
+ }
+}
+
+Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
List<PropertyInfo> plist;
get_property_list(&plist);
@@ -217,21 +268,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue;
}
- Variant p = get(E.name);
- if (p.get_type() == Variant::OBJECT) {
- Ref<Resource> sr = p;
- if (sr.is_valid()) {
- if (sr->is_local_to_scene()) {
- if (remap_cache.has(sr)) {
- p = remap_cache[sr];
- } else {
- Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache);
- p = dupe;
- remap_cache[sr] = dupe;
- }
- }
- }
- }
+ Variant p = get(E.name).duplicate(true);
+
+ _dupe_sub_resources(p, p_for_scene, p_remap_cache);
r->set(E.name, p);
}
@@ -239,7 +278,35 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
return r;
}
-void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) {
+void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found) {
+ switch (p_variant.get_type()) {
+ case Variant::ARRAY: {
+ Array a = p_variant;
+ for (int i = 0; i < a.size(); i++) {
+ _find_sub_resources(a[i], p_resources_found);
+ }
+ } break;
+ case Variant::DICTIONARY: {
+ Dictionary d = p_variant;
+ List<Variant> keys;
+ d.get_key_list(&keys);
+ for (const Variant &k : keys) {
+ _find_sub_resources(k, p_resources_found);
+ _find_sub_resources(d[k], p_resources_found);
+ }
+ } break;
+ case Variant::OBJECT: {
+ Ref<Resource> r = p_variant;
+ if (r.is_valid()) {
+ p_resources_found.insert(r);
+ }
+ } break;
+ default: {
+ }
+ }
+}
+
+void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
List<PropertyInfo> plist;
get_property_list(&plist);
@@ -251,14 +318,15 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource
continue;
}
Variant p = get(E.name);
- if (p.get_type() == Variant::OBJECT) {
- Ref<Resource> sr = p;
- if (sr.is_valid()) {
- if (sr->is_local_to_scene()) {
- if (!remap_cache.has(sr)) {
- sr->configure_for_local_scene(p_for_scene, remap_cache);
- remap_cache[sr] = sr;
- }
+
+ HashSet<Ref<Resource>> sub_resources;
+ _find_sub_resources(p, sub_resources);
+
+ for (Ref<Resource> sr : sub_resources) {
+ if (sr->is_local_to_scene()) {
+ if (!p_remap_cache.has(sr)) {
+ sr->configure_for_local_scene(p_for_scene, p_remap_cache);
+ p_remap_cache[sr] = sr;
}
}
}
@@ -489,12 +557,14 @@ RWLock ResourceCache::path_cache_lock;
#endif
void ResourceCache::clear() {
- if (resources.size()) {
- ERR_PRINT("Resources still in use at exit (run with --verbose for details).");
+ if (!resources.is_empty()) {
if (OS::get_singleton()->is_stdout_verbose()) {
+ ERR_PRINT(vformat("%d resources still in use at exit.", resources.size()));
for (const KeyValue<String, Resource *> &E : resources) {
print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class()));
}
+ } else {
+ ERR_PRINT(vformat("%d resources still in use at exit (run with --verbose for details).", resources.size()));
}
}
diff --git a/core/io/resource.h b/core/io/resource.h
index 610c2150db..b885b773ac 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -74,6 +74,9 @@ private:
SelfList<Resource> remapped_list;
+ void _dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
+ void _find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found);
+
protected:
virtual void _resource_path_changed();
static void _bind_methods();
@@ -111,8 +114,8 @@ public:
String get_scene_unique_id() const;
virtual Ref<Resource> duplicate(bool p_subresources = false) const;
- Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache);
- void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache);
+ Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
+ void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
void set_local_to_scene(bool p_enable);
bool is_local_to_scene() const;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index 2a33f723dc..20c494516b 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -1454,8 +1454,10 @@ Error ResourceFormatLoaderBinary::rename_dependencies(const String &p_path, cons
fw.unref();
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_RESOURCES);
- da->remove(p_path);
- da->rename(p_path + ".depren", p_path);
+ if (da->exists(p_path + ".depren")) {
+ da->remove(p_path);
+ da->rename(p_path + ".depren", p_path);
+ }
return OK;
}
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index 529128b9a2..64b47ad19d 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -36,6 +36,7 @@
#include "core/object/script_language.h"
#include "core/os/condition_variable.h"
#include "core/os/os.h"
+#include "core/os/safe_binary_mutex.h"
#include "core/string/print_string.h"
#include "core/string/translation.h"
#include "core/variant/variant_parser.h"
@@ -244,11 +245,11 @@ Ref<Resource> ResourceLoader::_load(const String &p_path, const String &p_origin
thread_load_mutex.lock();
HashMap<String, ThreadLoadTask>::Iterator E = thread_load_tasks.find(load_paths_stack->get(load_paths_stack->size() - 1));
if (E) {
- E->value.sub_tasks.insert(p_path);
+ E->value.sub_tasks.insert(p_original_path);
}
thread_load_mutex.unlock();
}
- load_paths_stack->push_back(p_path);
+ load_paths_stack->push_back(p_original_path);
// Try all loaders and pick the first match for the type hint
bool found = false;
@@ -340,7 +341,15 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (load_task.resource.is_valid()) {
if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
- load_task.resource->set_path(load_task.local_path);
+ if (load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE) {
+ Ref<Resource> old_res = ResourceCache::get_ref(load_task.local_path);
+ if (old_res.is_valid() && old_res != load_task.resource) {
+ // If resource is already loaded, only replace its data, to avoid existing invalidating instances.
+ old_res->copy_from(load_task.resource);
+ load_task.resource = old_res;
+ }
+ }
+ load_task.resource->set_path(load_task.local_path, load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE);
} else if (!load_task.local_path.is_resource_file()) {
load_task.resource->set_path_cache(load_task.local_path);
}
@@ -361,6 +370,17 @@ void ResourceLoader::_thread_load_function(void *p_userdata) {
if (_loaded_callback) {
_loaded_callback(load_task.resource, load_task.local_path);
}
+ } else if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path);
+ if (existing.is_valid()) {
+ load_task.resource = existing;
+ load_task.status = THREAD_LOAD_LOADED;
+ load_task.progress = 1.0;
+
+ if (_loaded_callback) {
+ _loaded_callback(load_task.resource, load_task.local_path);
+ }
+ }
}
thread_load_mutex.unlock();
@@ -463,7 +483,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
load_task.type_hint = p_type_hint;
load_task.cache_mode = p_cache_mode;
load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE;
- if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
+ if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) {
Ref<Resource> existing = ResourceCache::get_ref(local_path);
if (existing.is_valid()) {
//referencing is fine
@@ -509,20 +529,20 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path,
float ResourceLoader::_dependency_get_progress(const String &p_path) {
if (thread_load_tasks.has(p_path)) {
ThreadLoadTask &load_task = thread_load_tasks[p_path];
+ float current_progress = 0.0;
int dep_count = load_task.sub_tasks.size();
if (dep_count > 0) {
- float dep_progress = 0;
for (const String &E : load_task.sub_tasks) {
- dep_progress += _dependency_get_progress(E);
+ current_progress += _dependency_get_progress(E);
}
- dep_progress /= float(dep_count);
- dep_progress *= 0.5;
- dep_progress += load_task.progress * 0.5;
- return dep_progress;
+ current_progress /= float(dep_count);
+ current_progress *= 0.5;
+ current_progress += load_task.progress * 0.5;
} else {
- return load_task.progress;
+ current_progress = load_task.progress;
}
-
+ load_task.max_reported_progress = MAX(load_task.max_reported_progress, current_progress);
+ return load_task.max_reported_progress;
} else {
return 1.0; //assume finished loading it so it no longer exists
}
@@ -630,15 +650,16 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
if (load_task.task_id != 0) {
// Loading thread is in the worker pool.
- load_task.awaited = true;
thread_load_mutex.unlock();
Error err = WorkerThreadPool::get_singleton()->wait_for_task_completion(load_task.task_id);
if (err == ERR_BUSY) {
- // The WorkerThreadPool has scheduled tasks in a way that the current load depends on
- // another one in a lower stack frame. Restart such load here. When the stack is eventually
- // unrolled, the original load will have been notified to go on.
+ // The WorkerThreadPool has reported that the current task wants to await on an older one.
+ // That't not allowed for safety, to avoid deadlocks. Fortunately, though, in the context of
+ // resource loading that means that the task to wait for can be restarted here to break the
+ // cycle, with as much recursion into this process as needed.
+ // When the stack is eventually unrolled, the original load will have been notified to go on.
#ifdef DEV_ENABLED
- print_verbose("ResourceLoader: Load task happened to wait on another one deep in the call stack. Attempting to avoid deadlock by re-issuing the load now.");
+ print_verbose("ResourceLoader: Potential for deadlock detected in task dependency. Attempting to avoid it by re-issuing the load now.");
#endif
// CACHE_MODE_IGNORE is needed because, otherwise, the new request would just see there's
// an ongoing load for that resource and wait for it again. This value forces a new load.
@@ -652,6 +673,7 @@ Ref<Resource> ResourceLoader::_load_complete_inner(LoadToken &p_load_token, Erro
} else {
DEV_ASSERT(err == OK);
thread_load_mutex.lock();
+ load_task.awaited = true;
}
} else {
// Loading thread is main or user thread.
@@ -1113,11 +1135,10 @@ bool ResourceLoader::add_custom_resource_format_loader(String script_path) {
Ref<Script> s = res;
StringName ibt = s->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatLoader");
- ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceLoader: " + script_path + ".");
+ ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource loader, script '%s' does not inherit 'ResourceFormatLoader'.", script_path));
Object *obj = ClassDB::instantiate(ibt);
-
- ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource loader, expected 'ResourceFormatLoader' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource loader, cannot instantiate '%s'.", ibt));
Ref<ResourceFormatLoader> crl = Object::cast_to<ResourceFormatLoader>(obj);
crl->set_script(s);
diff --git a/core/io/resource_loader.h b/core/io/resource_loader.h
index 3c32a19066..3099d9aab3 100644
--- a/core/io/resource_loader.h
+++ b/core/io/resource_loader.h
@@ -39,6 +39,9 @@
class ConditionVariable;
+template <int Tag>
+class SafeBinaryMutex;
+
class ResourceFormatLoader : public RefCounted {
GDCLASS(ResourceFormatLoader, RefCounted);
@@ -167,7 +170,8 @@ private:
String remapped_path;
String dependent_path;
String type_hint;
- float progress = 0.0;
+ float progress = 0.0f;
+ float max_reported_progress = 0.0f;
ThreadLoadStatus status = THREAD_LOAD_IN_PROGRESS;
ResourceFormatLoader::CacheMode cache_mode = ResourceFormatLoader::CACHE_MODE_REUSE;
Error error = OK;
diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp
index 564a54b913..51ebea7f2c 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -120,9 +120,8 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path,
String local_path = ProjectSettings::get_singleton()->localize_path(path);
- Ref<Resource> rwcopy = p_resource;
if (p_flags & FLAG_CHANGE_PATH) {
- rwcopy->set_path(local_path);
+ p_resource->set_path(local_path);
}
err = saver[i]->save(p_resource, path, p_flags);
@@ -139,7 +138,7 @@ Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path,
#endif
if (p_flags & FLAG_CHANGE_PATH) {
- rwcopy->set_path(old_path);
+ p_resource->set_path(old_path);
}
if (save_callback && path.begins_with("res://")) {
@@ -237,11 +236,10 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
Ref<Script> s = res;
StringName ibt = s->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver");
- ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + ".");
+ ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource saver, script '%s' does not inherit 'ResourceFormatSaver'.", script_path));
Object *obj = ClassDB::instantiate(ibt);
-
- ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource saver, cannot instantiate '%s'.", ibt));
Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj);
crl->set_script(s);
diff --git a/core/io/xml_parser.cpp b/core/io/xml_parser.cpp
index faf7d75172..06888c7cda 100644
--- a/core/io/xml_parser.cpp
+++ b/core/io/xml_parser.cpp
@@ -454,7 +454,7 @@ bool XMLParser::is_empty() const {
}
Error XMLParser::open_buffer(const Vector<uint8_t> &p_buffer) {
- ERR_FAIL_COND_V(p_buffer.size() == 0, ERR_INVALID_DATA);
+ ERR_FAIL_COND_V(p_buffer.is_empty(), ERR_INVALID_DATA);
if (data_copy) {
memdelete_arr(data_copy);
diff --git a/core/math/a_star.cpp b/core/math/a_star.cpp
index f0f160940d..fb54058bd9 100644
--- a/core/math/a_star.cpp
+++ b/core/math/a_star.cpp
@@ -69,7 +69,7 @@ void AStar3D::add_point(int64_t p_id, const Vector3 &p_pos, real_t p_weight_scal
}
Vector3 AStar3D::get_point_position(int64_t p_id) const {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_V_MSG(!p_exists, Vector3(), vformat("Can't get point's position. Point with id: %d doesn't exist.", p_id));
@@ -77,7 +77,7 @@ Vector3 AStar3D::get_point_position(int64_t p_id) const {
}
void AStar3D::set_point_position(int64_t p_id, const Vector3 &p_pos) {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's position. Point with id: %d doesn't exist.", p_id));
@@ -85,7 +85,7 @@ void AStar3D::set_point_position(int64_t p_id, const Vector3 &p_pos) {
}
real_t AStar3D::get_point_weight_scale(int64_t p_id) const {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_V_MSG(!p_exists, 0, vformat("Can't get point's weight scale. Point with id: %d doesn't exist.", p_id));
@@ -93,7 +93,7 @@ real_t AStar3D::get_point_weight_scale(int64_t p_id) const {
}
void AStar3D::set_point_weight_scale(int64_t p_id, real_t p_weight_scale) {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set point's weight scale. Point with id: %d doesn't exist.", p_id));
ERR_FAIL_COND_MSG(p_weight_scale < 0.0, vformat("Can't set point's weight scale less than 0.0: %f.", p_weight_scale));
@@ -102,7 +102,7 @@ void AStar3D::set_point_weight_scale(int64_t p_id, real_t p_weight_scale) {
}
void AStar3D::remove_point(int64_t p_id) {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_MSG(!p_exists, vformat("Can't remove point. Point with id: %d doesn't exist.", p_id));
@@ -130,11 +130,11 @@ void AStar3D::remove_point(int64_t p_id) {
void AStar3D::connect_points(int64_t p_id, int64_t p_with_id, bool bidirectional) {
ERR_FAIL_COND_MSG(p_id == p_with_id, vformat("Can't connect point with id: %d to itself.", p_id));
- Point *a;
+ Point *a = nullptr;
bool from_exists = points.lookup(p_id, a);
ERR_FAIL_COND_MSG(!from_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_id));
- Point *b;
+ Point *b = nullptr;
bool to_exists = points.lookup(p_with_id, b);
ERR_FAIL_COND_MSG(!to_exists, vformat("Can't connect points. Point with id: %d doesn't exist.", p_with_id));
@@ -166,11 +166,11 @@ void AStar3D::connect_points(int64_t p_id, int64_t p_with_id, bool bidirectional
}
void AStar3D::disconnect_points(int64_t p_id, int64_t p_with_id, bool bidirectional) {
- Point *a;
+ Point *a = nullptr;
bool a_exists = points.lookup(p_id, a);
ERR_FAIL_COND_MSG(!a_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_id));
- Point *b;
+ Point *b = nullptr;
bool b_exists = points.lookup(p_with_id, b);
ERR_FAIL_COND_MSG(!b_exists, vformat("Can't disconnect points. Point with id: %d doesn't exist.", p_with_id));
@@ -220,7 +220,7 @@ PackedInt64Array AStar3D::get_point_ids() {
}
Vector<int64_t> AStar3D::get_point_connections(int64_t p_id) {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_V_MSG(!p_exists, Vector<int64_t>(), vformat("Can't get point's connections. Point with id: %d doesn't exist.", p_id));
@@ -386,11 +386,11 @@ real_t AStar3D::_estimate_cost(int64_t p_from_id, int64_t p_to_id) {
return scost;
}
- Point *from_point;
+ Point *from_point = nullptr;
bool from_exists = points.lookup(p_from_id, from_point);
ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));
- Point *to_point;
+ Point *to_point = nullptr;
bool to_exists = points.lookup(p_to_id, to_point);
ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));
@@ -403,11 +403,11 @@ real_t AStar3D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return scost;
}
- Point *from_point;
+ Point *from_point = nullptr;
bool from_exists = points.lookup(p_from_id, from_point);
ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));
- Point *to_point;
+ Point *to_point = nullptr;
bool to_exists = points.lookup(p_to_id, to_point);
ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));
@@ -415,11 +415,11 @@ real_t AStar3D::_compute_cost(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) {
- Point *a;
+ Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
- Point *b;
+ Point *b = nullptr;
bool to_exists = points.lookup(p_to_id, b);
ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector3>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
@@ -464,11 +464,11 @@ Vector<Vector3> AStar3D::get_point_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) {
- Point *a;
+ Point *a = nullptr;
bool from_exists = points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
- Point *b;
+ Point *b = nullptr;
bool to_exists = points.lookup(p_to_id, b);
ERR_FAIL_COND_V_MSG(!to_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
@@ -513,7 +513,7 @@ Vector<int64_t> AStar3D::get_id_path(int64_t p_from_id, int64_t p_to_id) {
}
void AStar3D::set_point_disabled(int64_t p_id, bool p_disabled) {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_MSG(!p_exists, vformat("Can't set if point is disabled. Point with id: %d doesn't exist.", p_id));
@@ -521,7 +521,7 @@ void AStar3D::set_point_disabled(int64_t p_id, bool p_disabled) {
}
bool AStar3D::is_point_disabled(int64_t p_id) const {
- Point *p;
+ Point *p = nullptr;
bool p_exists = points.lookup(p_id, p);
ERR_FAIL_COND_V_MSG(!p_exists, false, vformat("Can't get if point is disabled. Point with id: %d doesn't exist.", p_id));
@@ -660,11 +660,11 @@ real_t AStar2D::_estimate_cost(int64_t p_from_id, int64_t p_to_id) {
return scost;
}
- AStar3D::Point *from_point;
+ AStar3D::Point *from_point = nullptr;
bool from_exists = astar.points.lookup(p_from_id, from_point);
ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_from_id));
- AStar3D::Point *to_point;
+ AStar3D::Point *to_point = nullptr;
bool to_exists = astar.points.lookup(p_to_id, to_point);
ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't estimate cost. Point with id: %d doesn't exist.", p_to_id));
@@ -677,11 +677,11 @@ real_t AStar2D::_compute_cost(int64_t p_from_id, int64_t p_to_id) {
return scost;
}
- AStar3D::Point *from_point;
+ AStar3D::Point *from_point = nullptr;
bool from_exists = astar.points.lookup(p_from_id, from_point);
ERR_FAIL_COND_V_MSG(!from_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_from_id));
- AStar3D::Point *to_point;
+ AStar3D::Point *to_point = nullptr;
bool to_exists = astar.points.lookup(p_to_id, to_point);
ERR_FAIL_COND_V_MSG(!to_exists, 0, vformat("Can't compute cost. Point with id: %d doesn't exist.", p_to_id));
@@ -689,11 +689,11 @@ real_t AStar2D::_compute_cost(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) {
- AStar3D::Point *a;
+ AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_from_id));
- AStar3D::Point *b;
+ AStar3D::Point *b = nullptr;
bool to_exists = astar.points.lookup(p_to_id, b);
ERR_FAIL_COND_V_MSG(!to_exists, Vector<Vector2>(), vformat("Can't get point path. Point with id: %d doesn't exist.", p_to_id));
@@ -737,11 +737,11 @@ Vector<Vector2> AStar2D::get_point_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) {
- AStar3D::Point *a;
+ AStar3D::Point *a = nullptr;
bool from_exists = astar.points.lookup(p_from_id, a);
ERR_FAIL_COND_V_MSG(!from_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_from_id));
- AStar3D::Point *b;
+ AStar3D::Point *b = nullptr;
bool to_exists = astar.points.lookup(p_to_id, b);
ERR_FAIL_COND_V_MSG(!to_exists, Vector<int64_t>(), vformat("Can't get id path. Point with id: %d doesn't exist.", p_to_id));
diff --git a/core/math/a_star_grid_2d.cpp b/core/math/a_star_grid_2d.cpp
index 379d34aa2a..d17f465ab8 100644
--- a/core/math/a_star_grid_2d.cpp
+++ b/core/math/a_star_grid_2d.cpp
@@ -106,16 +106,45 @@ Size2 AStarGrid2D::get_cell_size() const {
return cell_size;
}
+void AStarGrid2D::set_cell_shape(CellShape p_cell_shape) {
+ if (cell_shape == p_cell_shape) {
+ return;
+ }
+
+ ERR_FAIL_INDEX(p_cell_shape, CellShape::CELL_SHAPE_MAX);
+ cell_shape = p_cell_shape;
+ dirty = true;
+}
+
+AStarGrid2D::CellShape AStarGrid2D::get_cell_shape() const {
+ return cell_shape;
+}
+
void AStarGrid2D::update() {
points.clear();
const int32_t end_x = region.get_end().x;
const int32_t end_y = region.get_end().y;
+ const Vector2 half_cell_size = cell_size / 2;
for (int32_t y = region.position.y; y < end_y; y++) {
LocalVector<Point> line;
for (int32_t x = region.position.x; x < end_x; x++) {
- line.push_back(Point(Vector2i(x, y), offset + Vector2(x, y) * cell_size));
+ Vector2 v = offset;
+ switch (cell_shape) {
+ case CELL_SHAPE_ISOMETRIC_RIGHT:
+ v += half_cell_size + Vector2(x + y, y - x) * half_cell_size;
+ break;
+ case CELL_SHAPE_ISOMETRIC_DOWN:
+ v += half_cell_size + Vector2(x - y, x + y) * half_cell_size;
+ break;
+ case CELL_SHAPE_SQUARE:
+ v += Vector2(x, y) * cell_size;
+ break;
+ default:
+ break;
+ }
+ line.push_back(Point(Vector2i(x, y), v));
}
points.push_back(line);
}
@@ -620,6 +649,8 @@ void AStarGrid2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_offset"), &AStarGrid2D::get_offset);
ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &AStarGrid2D::set_cell_size);
ClassDB::bind_method(D_METHOD("get_cell_size"), &AStarGrid2D::get_cell_size);
+ ClassDB::bind_method(D_METHOD("set_cell_shape", "cell_shape"), &AStarGrid2D::set_cell_shape);
+ ClassDB::bind_method(D_METHOD("get_cell_shape"), &AStarGrid2D::get_cell_shape);
ClassDB::bind_method(D_METHOD("is_in_bounds", "x", "y"), &AStarGrid2D::is_in_bounds);
ClassDB::bind_method(D_METHOD("is_in_boundsv", "id"), &AStarGrid2D::is_in_boundsv);
ClassDB::bind_method(D_METHOD("is_dirty"), &AStarGrid2D::is_dirty);
@@ -651,6 +682,7 @@ void AStarGrid2D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cell_size"), "set_cell_size", "get_cell_size");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "cell_shape", PROPERTY_HINT_ENUM, "Square,IsometricRight,IsometricDown"), "set_cell_shape", "get_cell_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "jumping_enabled"), "set_jumping_enabled", "is_jumping_enabled");
ADD_PROPERTY(PropertyInfo(Variant::INT, "default_compute_heuristic", PROPERTY_HINT_ENUM, "Euclidean,Manhattan,Octile,Chebyshev"), "set_default_compute_heuristic", "get_default_compute_heuristic");
@@ -668,4 +700,9 @@ void AStarGrid2D::_bind_methods() {
BIND_ENUM_CONSTANT(DIAGONAL_MODE_AT_LEAST_ONE_WALKABLE);
BIND_ENUM_CONSTANT(DIAGONAL_MODE_ONLY_IF_NO_OBSTACLES);
BIND_ENUM_CONSTANT(DIAGONAL_MODE_MAX);
+
+ BIND_ENUM_CONSTANT(CELL_SHAPE_SQUARE);
+ BIND_ENUM_CONSTANT(CELL_SHAPE_ISOMETRIC_RIGHT);
+ BIND_ENUM_CONSTANT(CELL_SHAPE_ISOMETRIC_DOWN);
+ BIND_ENUM_CONSTANT(CELL_SHAPE_MAX);
}
diff --git a/core/math/a_star_grid_2d.h b/core/math/a_star_grid_2d.h
index 619551b754..69cb77dd3e 100644
--- a/core/math/a_star_grid_2d.h
+++ b/core/math/a_star_grid_2d.h
@@ -56,11 +56,19 @@ public:
HEURISTIC_MAX,
};
+ enum CellShape {
+ CELL_SHAPE_SQUARE,
+ CELL_SHAPE_ISOMETRIC_RIGHT,
+ CELL_SHAPE_ISOMETRIC_DOWN,
+ CELL_SHAPE_MAX,
+ };
+
private:
Rect2i region;
Vector2 offset;
Size2 cell_size = Size2(1, 1);
bool dirty = false;
+ CellShape cell_shape = CELL_SHAPE_SQUARE;
bool jumping_enabled = false;
DiagonalMode diagonal_mode = DIAGONAL_MODE_ALWAYS;
@@ -157,6 +165,9 @@ public:
void set_cell_size(const Size2 &p_cell_size);
Size2 get_cell_size() const;
+ void set_cell_shape(CellShape p_cell_shape);
+ CellShape get_cell_shape() const;
+
void update();
bool is_in_bounds(int32_t p_x, int32_t p_y) const;
@@ -193,5 +204,6 @@ public:
VARIANT_ENUM_CAST(AStarGrid2D::DiagonalMode);
VARIANT_ENUM_CAST(AStarGrid2D::Heuristic);
+VARIANT_ENUM_CAST(AStarGrid2D::CellShape)
#endif // A_STAR_GRID_2D_H
diff --git a/core/math/aabb.cpp b/core/math/aabb.cpp
index 1071df0979..76e9e74dea 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -125,8 +125,8 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *
#endif
Vector3 c1, c2;
Vector3 end = position + size;
- real_t near = -1e20;
- real_t far = 1e20;
+ real_t depth_near = -1e20;
+ real_t depth_far = 1e20;
int axis = 0;
for (int i = 0; i < 3; i++) {
@@ -141,14 +141,14 @@ bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *
if (c1[i] > c2[i]) {
SWAP(c1, c2);
}
- if (c1[i] > near) {
- near = c1[i];
+ if (c1[i] > depth_near) {
+ depth_near = c1[i];
axis = i;
}
- if (c2[i] < far) {
- far = c2[i];
+ if (c2[i] < depth_far) {
+ depth_far = c2[i];
}
- if ((near > far) || (far < 0)) {
+ if ((depth_near > depth_far) || (depth_far < 0)) {
return false;
}
}
diff --git a/core/math/aabb.h b/core/math/aabb.h
index 859810df37..cea845bf7c 100644
--- a/core/math/aabb.h
+++ b/core/math/aabb.h
@@ -200,11 +200,11 @@ inline bool AABB::encloses(const AABB &p_aabb) const {
return (
(src_min.x <= dst_min.x) &&
- (src_max.x > dst_max.x) &&
+ (src_max.x >= dst_max.x) &&
(src_min.y <= dst_min.y) &&
- (src_max.y > dst_max.y) &&
+ (src_max.y >= dst_max.y) &&
(src_min.z <= dst_min.z) &&
- (src_max.z > dst_max.z));
+ (src_max.z >= dst_max.z));
}
Vector3 AABB::get_support(const Vector3 &p_normal) const {
diff --git a/core/math/audio_frame.h b/core/math/audio_frame.h
index d26336e9a2..e205126cdf 100644
--- a/core/math/audio_frame.h
+++ b/core/math/audio_frame.h
@@ -51,105 +51,123 @@ static const float AUDIO_PEAK_OFFSET = 0.0000000001f;
static const float AUDIO_MIN_PEAK_DB = -200.0f; // linear_to_db(AUDIO_PEAK_OFFSET)
struct AudioFrame {
- //left and right samples
- float l = 0.f, r = 0.f;
-
- _ALWAYS_INLINE_ const float &operator[](int idx) const { return idx == 0 ? l : r; }
- _ALWAYS_INLINE_ float &operator[](int idx) { return idx == 0 ? l : r; }
+ // Left and right samples.
+ union {
+ struct {
+ float left;
+ float right;
+ };
+#ifndef DISABLE_DEPRECATED
+ struct {
+ float l;
+ float r;
+ };
+#endif
+ float levels[2] = { 0.0 };
+ };
+
+ _ALWAYS_INLINE_ const float &operator[](int p_idx) const {
+ DEV_ASSERT((unsigned int)p_idx < 2);
+ return levels[p_idx];
+ }
+ _ALWAYS_INLINE_ float &operator[](int p_idx) {
+ DEV_ASSERT((unsigned int)p_idx < 2);
+ return levels[p_idx];
+ }
- _ALWAYS_INLINE_ AudioFrame operator+(const AudioFrame &p_frame) const { return AudioFrame(l + p_frame.l, r + p_frame.r); }
- _ALWAYS_INLINE_ AudioFrame operator-(const AudioFrame &p_frame) const { return AudioFrame(l - p_frame.l, r - p_frame.r); }
- _ALWAYS_INLINE_ AudioFrame operator*(const AudioFrame &p_frame) const { return AudioFrame(l * p_frame.l, r * p_frame.r); }
- _ALWAYS_INLINE_ AudioFrame operator/(const AudioFrame &p_frame) const { return AudioFrame(l / p_frame.l, r / p_frame.r); }
+ _ALWAYS_INLINE_ AudioFrame operator+(const AudioFrame &p_frame) const { return AudioFrame(left + p_frame.left, right + p_frame.right); }
+ _ALWAYS_INLINE_ AudioFrame operator-(const AudioFrame &p_frame) const { return AudioFrame(left - p_frame.left, right - p_frame.right); }
+ _ALWAYS_INLINE_ AudioFrame operator*(const AudioFrame &p_frame) const { return AudioFrame(left * p_frame.left, right * p_frame.right); }
+ _ALWAYS_INLINE_ AudioFrame operator/(const AudioFrame &p_frame) const { return AudioFrame(left / p_frame.left, right / p_frame.right); }
- _ALWAYS_INLINE_ AudioFrame operator+(float p_sample) const { return AudioFrame(l + p_sample, r + p_sample); }
- _ALWAYS_INLINE_ AudioFrame operator-(float p_sample) const { return AudioFrame(l - p_sample, r - p_sample); }
- _ALWAYS_INLINE_ AudioFrame operator*(float p_sample) const { return AudioFrame(l * p_sample, r * p_sample); }
- _ALWAYS_INLINE_ AudioFrame operator/(float p_sample) const { return AudioFrame(l / p_sample, r / p_sample); }
+ _ALWAYS_INLINE_ AudioFrame operator+(float p_sample) const { return AudioFrame(left + p_sample, right + p_sample); }
+ _ALWAYS_INLINE_ AudioFrame operator-(float p_sample) const { return AudioFrame(left - p_sample, right - p_sample); }
+ _ALWAYS_INLINE_ AudioFrame operator*(float p_sample) const { return AudioFrame(left * p_sample, right * p_sample); }
+ _ALWAYS_INLINE_ AudioFrame operator/(float p_sample) const { return AudioFrame(left / p_sample, right / p_sample); }
_ALWAYS_INLINE_ void operator+=(const AudioFrame &p_frame) {
- l += p_frame.l;
- r += p_frame.r;
+ left += p_frame.left;
+ right += p_frame.right;
}
_ALWAYS_INLINE_ void operator-=(const AudioFrame &p_frame) {
- l -= p_frame.l;
- r -= p_frame.r;
+ left -= p_frame.left;
+ right -= p_frame.right;
}
_ALWAYS_INLINE_ void operator*=(const AudioFrame &p_frame) {
- l *= p_frame.l;
- r *= p_frame.r;
+ left *= p_frame.left;
+ right *= p_frame.right;
}
_ALWAYS_INLINE_ void operator/=(const AudioFrame &p_frame) {
- l /= p_frame.l;
- r /= p_frame.r;
+ left /= p_frame.left;
+ right /= p_frame.right;
}
_ALWAYS_INLINE_ void operator+=(float p_sample) {
- l += p_sample;
- r += p_sample;
+ left += p_sample;
+ right += p_sample;
}
_ALWAYS_INLINE_ void operator-=(float p_sample) {
- l -= p_sample;
- r -= p_sample;
+ left -= p_sample;
+ right -= p_sample;
}
_ALWAYS_INLINE_ void operator*=(float p_sample) {
- l *= p_sample;
- r *= p_sample;
+ left *= p_sample;
+ right *= p_sample;
}
_ALWAYS_INLINE_ void operator/=(float p_sample) {
- l /= p_sample;
- r /= p_sample;
+ left /= p_sample;
+ right /= p_sample;
}
_ALWAYS_INLINE_ void undenormalize() {
- l = ::undenormalize(l);
- r = ::undenormalize(r);
+ left = ::undenormalize(left);
+ right = ::undenormalize(right);
}
_FORCE_INLINE_ AudioFrame lerp(const AudioFrame &p_b, float p_t) const {
AudioFrame res = *this;
- res.l += (p_t * (p_b.l - l));
- res.r += (p_t * (p_b.r - r));
+ res.left += (p_t * (p_b.left - left));
+ res.right += (p_t * (p_b.right - right));
return res;
}
- _ALWAYS_INLINE_ AudioFrame(float p_l, float p_r) {
- l = p_l;
- r = p_r;
+ _ALWAYS_INLINE_ AudioFrame(float p_left, float p_right) {
+ left = p_left;
+ right = p_right;
}
_ALWAYS_INLINE_ AudioFrame(const AudioFrame &p_frame) {
- l = p_frame.l;
- r = p_frame.r;
+ left = p_frame.left;
+ right = p_frame.right;
}
_ALWAYS_INLINE_ void operator=(const AudioFrame &p_frame) {
- l = p_frame.l;
- r = p_frame.r;
+ left = p_frame.left;
+ right = p_frame.right;
}
_ALWAYS_INLINE_ operator Vector2() const {
- return Vector2(l, r);
+ return Vector2(left, right);
}
_ALWAYS_INLINE_ AudioFrame(const Vector2 &p_v2) {
- l = p_v2.x;
- r = p_v2.y;
+ left = p_v2.x;
+ right = p_v2.y;
}
_ALWAYS_INLINE_ AudioFrame() {}
};
_ALWAYS_INLINE_ AudioFrame operator*(float p_scalar, const AudioFrame &p_frame) {
- return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+ return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar);
}
_ALWAYS_INLINE_ AudioFrame operator*(int32_t p_scalar, const AudioFrame &p_frame) {
- return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+ return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar);
}
_ALWAYS_INLINE_ AudioFrame operator*(int64_t p_scalar, const AudioFrame &p_frame) {
- return AudioFrame(p_frame.l * p_scalar, p_frame.r * p_scalar);
+ return AudioFrame(p_frame.left * p_scalar, p_frame.right * p_scalar);
}
#endif // AUDIO_FRAME_H
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 9796ac59c2..1ff6cdd588 100644
--- a/core/math/basis.cpp
+++ b/core/math/basis.cpp
@@ -89,13 +89,26 @@ Basis Basis::orthogonalized() const {
return c;
}
+// Returns true if the basis vectors are orthogonal (perpendicular), so it has no skew or shear, and can be decomposed into rotation and scale.
+// See https://en.wikipedia.org/wiki/Orthogonal_basis
bool Basis::is_orthogonal() const {
- Basis identity;
- Basis m = (*this) * transposed();
+ const Vector3 x = get_column(0);
+ const Vector3 y = get_column(1);
+ const Vector3 z = get_column(2);
+ return Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
+}
- return m.is_equal_approx(identity);
+// Returns true if the basis vectors are orthonormal (orthogonal and normalized), so it has no scale, skew, or shear.
+// See https://en.wikipedia.org/wiki/Orthonormal_basis
+bool Basis::is_orthonormal() const {
+ const Vector3 x = get_column(0);
+ const Vector3 y = get_column(1);
+ const Vector3 z = get_column(2);
+ return Math::is_equal_approx(x.length_squared(), 1) && Math::is_equal_approx(y.length_squared(), 1) && Math::is_equal_approx(z.length_squared(), 1) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
}
+// Returns true if the basis is conformal (orthogonal, uniform scale, preserves angles and distance ratios).
+// See https://en.wikipedia.org/wiki/Conformal_linear_transformation
bool Basis::is_conformal() const {
const Vector3 x = get_column(0);
const Vector3 y = get_column(1);
@@ -104,6 +117,7 @@ bool Basis::is_conformal() const {
return Math::is_equal_approx(x_len_sq, y.length_squared()) && Math::is_equal_approx(x_len_sq, z.length_squared()) && Math::is_zero_approx(x.dot(y)) && Math::is_zero_approx(x.dot(z)) && Math::is_zero_approx(y.dot(z));
}
+// Returns true if the basis only has diagonal elements, so it may only have scale or flip, but no rotation, skew, or shear.
bool Basis::is_diagonal() const {
return (
Math::is_zero_approx(rows[0][1]) && Math::is_zero_approx(rows[0][2]) &&
@@ -111,8 +125,9 @@ bool Basis::is_diagonal() const {
Math::is_zero_approx(rows[2][0]) && Math::is_zero_approx(rows[2][1]));
}
+// Returns true if the basis is a pure rotation matrix, so it has no scale, skew, shear, or flip.
bool Basis::is_rotation() const {
- return Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON) && is_orthogonal();
+ return is_conformal() && Math::is_equal_approx(determinant(), 1, (real_t)UNIT_EPSILON);
}
#ifdef MATH_CHECKS
@@ -707,7 +722,7 @@ Basis::operator String() const {
Quaternion Basis::get_quaternion() const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors.");
+ ERR_FAIL_COND_V_MSG(!is_rotation(), Quaternion(), "Basis " + operator String() + " must be normalized in order to be casted to a Quaternion. Use get_rotation_quaternion() or call orthonormalized() if the Basis contains linearly independent vectors.");
#endif
/* Allow getting a quaternion from an unnormalized transform */
Basis m = *this;
@@ -834,7 +849,7 @@ void Basis::set_quaternion(const Quaternion &p_quaternion) {
void Basis::set_axis_angle(const Vector3 &p_axis, real_t p_angle) {
// Rotation matrix from axis and angle, see https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_angle
#ifdef MATH_CHECKS
- ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
+ ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized.");
#endif
Vector3 axis_sq(p_axis.x * p_axis.x, p_axis.y * p_axis.y, p_axis.z * p_axis.z);
real_t cosine = Math::cos(p_angle);
diff --git a/core/math/basis.h b/core/math/basis.h
index adacd1c216..e3094114e8 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -136,8 +136,11 @@ struct _NO_DISCARD_ Basis {
_FORCE_INLINE_ Basis operator-(const Basis &p_matrix) const;
_FORCE_INLINE_ void operator*=(const real_t p_val);
_FORCE_INLINE_ Basis operator*(const real_t p_val) const;
+ _FORCE_INLINE_ void operator/=(const real_t p_val);
+ _FORCE_INLINE_ Basis operator/(const real_t p_val) const;
bool is_orthogonal() const;
+ bool is_orthonormal() const;
bool is_conformal() const;
bool is_diagonal() const;
bool is_rotation() const;
@@ -288,6 +291,18 @@ _FORCE_INLINE_ Basis Basis::operator*(const real_t p_val) const {
return ret;
}
+_FORCE_INLINE_ void Basis::operator/=(const real_t p_val) {
+ rows[0] /= p_val;
+ rows[1] /= p_val;
+ rows[2] /= p_val;
+}
+
+_FORCE_INLINE_ Basis Basis::operator/(const real_t p_val) const {
+ Basis ret(*this);
+ ret /= p_val;
+ return ret;
+}
+
Vector3 Basis::xform(const Vector3 &p_vector) const {
return Vector3(
rows[0].dot(p_vector),
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index 68d995fe67..478fde3a64 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -344,31 +344,31 @@ public:
Rational128(int64_t p_value) {
if (p_value > 0) {
sign = 1;
- this->numerator = p_value;
+ numerator = p_value;
} else if (p_value < 0) {
sign = -1;
- this->numerator = -p_value;
+ numerator = -p_value;
} else {
sign = 0;
- this->numerator = (uint64_t)0;
+ numerator = (uint64_t)0;
}
- this->denominator = (uint64_t)1;
+ denominator = (uint64_t)1;
is_int_64 = true;
}
Rational128(const Int128 &p_numerator, const Int128 &p_denominator) {
sign = p_numerator.get_sign();
if (sign >= 0) {
- this->numerator = p_numerator;
+ numerator = p_numerator;
} else {
- this->numerator = -p_numerator;
+ numerator = -p_numerator;
}
int32_t dsign = p_denominator.get_sign();
if (dsign >= 0) {
- this->denominator = p_denominator;
+ denominator = p_denominator;
} else {
sign = -sign;
- this->denominator = -p_denominator;
+ denominator = -p_denominator;
}
is_int_64 = false;
}
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h
index dbc1cb31de..9b49fcc3c8 100644
--- a/core/math/dynamic_bvh.h
+++ b/core/math/dynamic_bvh.h
@@ -328,7 +328,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) {
volume.min = p_box.position;
volume.max = p_box.position + p_box.size;
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -343,7 +344,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) {
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
@@ -384,7 +386,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve
}
}
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -399,7 +402,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
@@ -436,7 +440,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
Vector3 bounds[2];
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -456,7 +461,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp
index 74cb92539a..1e5f12bebf 100644
--- a/core/math/geometry_2d.cpp
+++ b/core/math/geometry_2d.cpp
@@ -93,7 +93,7 @@ void Geometry2D::make_atlas(const Vector<Size2i> &p_rects, Vector<Point2i> &r_re
// For example, it will prioritize a 1024x1024 atlas (works everywhere) instead of a
// 256x8192 atlas (won't work anywhere).
- ERR_FAIL_COND(p_rects.size() == 0);
+ ERR_FAIL_COND(p_rects.is_empty());
for (int i = 0; i < p_rects.size(); i++) {
ERR_FAIL_COND(p_rects[i].width <= 0);
ERR_FAIL_COND(p_rects[i].height <= 0);
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index b37fce9e9c..9907d579a5 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -119,6 +119,10 @@ public:
}
}
+ static real_t get_distance_to_segment(const Vector2 &p_point, const Vector2 *p_segment) {
+ return p_point.distance_to(get_closest_point_to_segment(p_point, p_segment));
+ }
+
static bool is_point_in_triangle(const Vector2 &s, const Vector2 &a, const Vector2 &b, const Vector2 &c) {
Vector2 an = a - s;
Vector2 bn = b - s;
@@ -249,6 +253,28 @@ public:
return -1;
}
+ static bool segment_intersects_rect(const Vector2 &p_from, const Vector2 &p_to, const Rect2 &p_rect) {
+ if (p_rect.has_point(p_from) || p_rect.has_point(p_to)) {
+ return true;
+ }
+
+ const Vector2 rect_points[4] = {
+ p_rect.position,
+ p_rect.position + Vector2(p_rect.size.x, 0),
+ p_rect.position + p_rect.size,
+ p_rect.position + Vector2(0, p_rect.size.y)
+ };
+
+ // Check if any of the rect's edges intersect the segment.
+ for (int i = 0; i < 4; i++) {
+ if (segment_intersects_segment(p_from, p_to, rect_points[i], rect_points[(i + 1) % 4], nullptr)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
enum PolyBooleanOperation {
OPERATION_UNION,
OPERATION_DIFFERENCE,
diff --git a/core/math/geometry_3d.h b/core/math/geometry_3d.h
index 99c554fe05..305a64e39c 100644
--- a/core/math/geometry_3d.h
+++ b/core/math/geometry_3d.h
@@ -31,6 +31,7 @@
#ifndef GEOMETRY_3D_H
#define GEOMETRY_3D_H
+#include "core/math/delaunay_3d.h"
#include "core/math/face3.h"
#include "core/object/object.h"
#include "core/templates/local_vector.h"
@@ -532,6 +533,21 @@ public:
return clipped;
}
+ static Vector<int32_t> tetrahedralize_delaunay(const Vector<Vector3> &p_points) {
+ Vector<Delaunay3D::OutputSimplex> tetr = Delaunay3D::tetrahedralize(p_points);
+ Vector<int32_t> tetrahedrons;
+
+ tetrahedrons.resize(4 * tetr.size());
+ int32_t *ptr = tetrahedrons.ptrw();
+ for (int i = 0; i < tetr.size(); i++) {
+ *ptr++ = tetr[i].points[0];
+ *ptr++ = tetr[i].points[1];
+ *ptr++ = tetr[i].points[2];
+ *ptr++ = tetr[i].points[3];
+ }
+ return tetrahedrons;
+ }
+
// Create a "wrap" that encloses the given geometry.
static Vector<Face3> wrap_geometry(Vector<Face3> p_array, real_t *p_error = nullptr);
diff --git a/core/math/math_funcs.h b/core/math/math_funcs.h
index 366ccca4cb..3060f31970 100644
--- a/core/math/math_funcs.h
+++ b/core/math/math_funcs.h
@@ -198,6 +198,22 @@ public:
#endif
}
+ // These methods assume (p_num + p_den) doesn't overflow.
+ static _ALWAYS_INLINE_ int32_t division_round_up(int32_t p_num, int32_t p_den) {
+ int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1;
+ return (p_num + p_den + offset) / p_den;
+ }
+ static _ALWAYS_INLINE_ uint32_t division_round_up(uint32_t p_num, uint32_t p_den) {
+ return (p_num + p_den - 1) / p_den;
+ }
+ static _ALWAYS_INLINE_ int64_t division_round_up(int64_t p_num, int64_t p_den) {
+ int32_t offset = (p_num < 0 && p_den < 0) ? 1 : -1;
+ return (p_num + p_den + offset) / p_den;
+ }
+ static _ALWAYS_INLINE_ uint64_t division_round_up(uint64_t p_num, uint64_t p_den) {
+ return (p_num + p_den - 1) / p_den;
+ }
+
static _ALWAYS_INLINE_ bool is_finite(double p_val) { return isfinite(p_val); }
static _ALWAYS_INLINE_ bool is_finite(float p_val) { return isfinite(p_val); }
diff --git a/core/math/quaternion.cpp b/core/math/quaternion.cpp
index e4ad17c8ef..cbaaa1371a 100644
--- a/core/math/quaternion.cpp
+++ b/core/math/quaternion.cpp
@@ -41,7 +41,7 @@ real_t Quaternion::angle_to(const Quaternion &p_to) const {
Vector3 Quaternion::get_euler(EulerOrder p_order) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Vector3(0, 0, 0), "The quaternion " + operator String() + " must be normalized.");
#endif
return Basis(*this).get_euler(p_order);
}
@@ -88,7 +88,7 @@ bool Quaternion::is_normalized() const {
Quaternion Quaternion::inverse() const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The quaternion " + operator String() + " must be normalized.");
#endif
return Quaternion(-x, -y, -z, w);
}
@@ -112,8 +112,8 @@ Quaternion Quaternion::exp() const {
Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
#endif
Quaternion to1;
real_t omega, cosom, sinom, scale0, scale1;
@@ -153,8 +153,8 @@ Quaternion Quaternion::slerp(const Quaternion &p_to, const real_t &p_weight) con
Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_to.is_normalized(), Quaternion(), "The end quaternion " + p_to.operator String() + " must be normalized.");
#endif
const Quaternion &from = *this;
@@ -177,8 +177,8 @@ Quaternion Quaternion::slerpni(const Quaternion &p_to, const real_t &p_weight) c
Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
#endif
Quaternion from_q = *this;
Quaternion pre_q = p_pre_a;
@@ -228,8 +228,8 @@ Quaternion Quaternion::spherical_cubic_interpolate(const Quaternion &p_b, const
Quaternion Quaternion::spherical_cubic_interpolate_in_time(const Quaternion &p_b, const Quaternion &p_pre_a, const Quaternion &p_post_b, const real_t &p_weight,
const real_t &p_b_t, const real_t &p_pre_a_t, const real_t &p_post_b_t) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion must be normalized.");
- ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), Quaternion(), "The start quaternion " + operator String() + " must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_b.is_normalized(), Quaternion(), "The end quaternion " + p_b.operator String() + " must be normalized.");
#endif
Quaternion from_q = *this;
Quaternion pre_q = p_pre_a;
@@ -294,7 +294,7 @@ real_t Quaternion::get_angle() const {
Quaternion::Quaternion(const Vector3 &p_axis, real_t p_angle) {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 must be normalized.");
+ ERR_FAIL_COND_MSG(!p_axis.is_normalized(), "The axis Vector3 " + p_axis.operator String() + " must be normalized.");
#endif
real_t d = p_axis.length();
if (d == 0) {
diff --git a/core/math/quaternion.h b/core/math/quaternion.h
index ea952304a5..f8133df559 100644
--- a/core/math/quaternion.h
+++ b/core/math/quaternion.h
@@ -33,8 +33,7 @@
#include "core/math/math_funcs.h"
#include "core/math/vector3.h"
-
-class String;
+#include "core/string/ustring.h"
struct _NO_DISCARD_ Quaternion {
union {
@@ -90,7 +89,7 @@ struct _NO_DISCARD_ Quaternion {
_FORCE_INLINE_ Vector3 xform(const Vector3 &v) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion must be normalized.");
+ ERR_FAIL_COND_V_MSG(!is_normalized(), v, "The quaternion " + operator String() + " must be normalized.");
#endif
Vector3 u(x, y, z);
Vector3 uv = u.cross(v);
diff --git a/core/math/transform_2d.cpp b/core/math/transform_2d.cpp
index bc4682fd90..a22d075b64 100644
--- a/core/math/transform_2d.cpp
+++ b/core/math/transform_2d.cpp
@@ -295,6 +295,18 @@ Transform2D Transform2D::operator*(const real_t p_val) const {
return ret;
}
+void Transform2D::operator/=(const real_t p_val) {
+ columns[0] /= p_val;
+ columns[1] /= p_val;
+ columns[2] /= p_val;
+}
+
+Transform2D Transform2D::operator/(const real_t p_val) const {
+ Transform2D ret(*this);
+ ret /= p_val;
+ return ret;
+}
+
Transform2D::operator String() const {
return "[X: " + columns[0].operator String() +
", Y: " + columns[1].operator String() +
diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h
index dd1a33c5d5..9ff925f66f 100644
--- a/core/math/transform_2d.h
+++ b/core/math/transform_2d.h
@@ -109,6 +109,8 @@ struct _NO_DISCARD_ Transform2D {
Transform2D operator*(const Transform2D &p_transform) const;
void operator*=(const real_t p_val);
Transform2D operator*(const real_t p_val) const;
+ void operator/=(const real_t p_val);
+ Transform2D operator/(const real_t p_val) const;
Transform2D interpolate_with(const Transform2D &p_transform, const real_t p_c) const;
diff --git a/core/math/transform_3d.cpp b/core/math/transform_3d.cpp
index cdc94676c9..20713349d7 100644
--- a/core/math/transform_3d.cpp
+++ b/core/math/transform_3d.cpp
@@ -208,6 +208,17 @@ Transform3D Transform3D::operator*(const real_t p_val) const {
return ret;
}
+void Transform3D::operator/=(const real_t p_val) {
+ basis /= p_val;
+ origin /= p_val;
+}
+
+Transform3D Transform3D::operator/(const real_t p_val) const {
+ Transform3D ret(*this);
+ ret /= p_val;
+ return ret;
+}
+
Transform3D::operator String() const {
return "[X: " + basis.get_column(0).operator String() +
", Y: " + basis.get_column(1).operator String() +
diff --git a/core/math/transform_3d.h b/core/math/transform_3d.h
index 70141a3dbe..d1ec34d53f 100644
--- a/core/math/transform_3d.h
+++ b/core/math/transform_3d.h
@@ -104,6 +104,8 @@ struct _NO_DISCARD_ Transform3D {
Transform3D operator*(const Transform3D &p_transform) const;
void operator*=(const real_t p_val);
Transform3D operator*(const real_t p_val) const;
+ void operator/=(const real_t p_val);
+ Transform3D operator/(const real_t p_val) const;
Transform3D interpolate_with(const Transform3D &p_transform, real_t p_c) const;
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp
index df8c804243..74631d3e29 100644
--- a/core/math/vector2.cpp
+++ b/core/math/vector2.cpp
@@ -162,9 +162,9 @@ Vector2 Vector2::move_toward(const Vector2 &p_to, const real_t p_delta) const {
// slide returns the component of the vector along the given plane, specified by its normal vector.
Vector2 Vector2::slide(const Vector2 &p_normal) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 " + p_normal.operator String() + "must be normalized.");
#endif
- return *this - p_normal * this->dot(p_normal);
+ return *this - p_normal * dot(p_normal);
}
Vector2 Vector2::bounce(const Vector2 &p_normal) const {
@@ -173,9 +173,9 @@ Vector2 Vector2::bounce(const Vector2 &p_normal) const {
Vector2 Vector2::reflect(const Vector2 &p_normal) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector2(), "The normal Vector2 " + p_normal.operator String() + "must be normalized.");
#endif
- return 2.0f * p_normal * this->dot(p_normal) - *this;
+ return 2.0f * p_normal * dot(p_normal) - *this;
}
bool Vector2::is_equal_approx(const Vector2 &p_v) const {
diff --git a/core/math/vector2i.h b/core/math/vector2i.h
index e6850347c3..b2c75beb4d 100644
--- a/core/math/vector2i.h
+++ b/core/math/vector2i.h
@@ -85,6 +85,14 @@ struct _NO_DISCARD_ Vector2i {
return Vector2i(MAX(x, p_vector2i.x), MAX(y, p_vector2i.y));
}
+ double distance_to(const Vector2i &p_to) const {
+ return (p_to - *this).length();
+ }
+
+ int64_t distance_squared_to(const Vector2i &p_to) const {
+ return (p_to - *this).length_squared();
+ }
+
Vector2i operator+(const Vector2i &p_v) const;
void operator+=(const Vector2i &p_v);
Vector2i operator-(const Vector2i &p_v) const;
diff --git a/core/math/vector3.cpp b/core/math/vector3.cpp
index ae009fc4ef..c483d659a3 100644
--- a/core/math/vector3.cpp
+++ b/core/math/vector3.cpp
@@ -109,7 +109,7 @@ Vector3 Vector3::octahedron_decode(const Vector2 &p_oct) {
Vector2 Vector3::octahedron_tangent_encode(const float sign) const {
const float bias = 1.0f / 32767.0f;
- Vector2 res = this->octahedron_encode();
+ Vector2 res = octahedron_encode();
res.y = MAX(res.y, bias);
res.y = res.y * 0.5f + 0.5f;
res.y = sign >= 0.0f ? res.y : 1 - res.y;
diff --git a/core/math/vector3.h b/core/math/vector3.h
index 18943a820f..5d4e2c7d87 100644
--- a/core/math/vector3.h
+++ b/core/math/vector3.h
@@ -33,8 +33,8 @@
#include "core/error/error_macros.h"
#include "core/math/math_funcs.h"
+#include "core/string/ustring.h"
-class String;
struct Basis;
struct Vector2;
struct Vector3i;
@@ -512,9 +512,9 @@ void Vector3::zero() {
// slide returns the component of the vector along the given plane, specified by its normal vector.
Vector3 Vector3::slide(const Vector3 &p_normal) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 " + p_normal.operator String() + " must be normalized.");
#endif
- return *this - p_normal * this->dot(p_normal);
+ return *this - p_normal * dot(p_normal);
}
Vector3 Vector3::bounce(const Vector3 &p_normal) const {
@@ -523,9 +523,9 @@ Vector3 Vector3::bounce(const Vector3 &p_normal) const {
Vector3 Vector3::reflect(const Vector3 &p_normal) const {
#ifdef MATH_CHECKS
- ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 must be normalized.");
+ ERR_FAIL_COND_V_MSG(!p_normal.is_normalized(), Vector3(), "The normal Vector3 " + p_normal.operator String() + " must be normalized.");
#endif
- return 2.0f * p_normal * this->dot(p_normal) - *this;
+ return 2.0f * p_normal * dot(p_normal) - *this;
}
#endif // VECTOR3_H
diff --git a/core/math/vector3i.h b/core/math/vector3i.h
index 53d3829a99..5a5e9deda8 100644
--- a/core/math/vector3i.h
+++ b/core/math/vector3i.h
@@ -87,6 +87,9 @@ struct _NO_DISCARD_ Vector3i {
Vector3i clamp(const Vector3i &p_min, const Vector3i &p_max) const;
Vector3i snapped(const Vector3i &p_step) const;
+ _FORCE_INLINE_ double distance_to(const Vector3i &p_to) const;
+ _FORCE_INLINE_ int64_t distance_squared_to(const Vector3i &p_to) const;
+
/* Operators */
_FORCE_INLINE_ Vector3i &operator+=(const Vector3i &p_v);
@@ -143,6 +146,14 @@ Vector3i Vector3i::sign() const {
return Vector3i(SIGN(x), SIGN(y), SIGN(z));
}
+double Vector3i::distance_to(const Vector3i &p_to) const {
+ return (p_to - *this).length();
+}
+
+int64_t Vector3i::distance_squared_to(const Vector3i &p_to) const {
+ return (p_to - *this).length_squared();
+}
+
/* Operators */
Vector3i &Vector3i::operator+=(const Vector3i &p_v) {
diff --git a/core/math/vector4i.h b/core/math/vector4i.h
index b815aa8e76..7d85d473d9 100644
--- a/core/math/vector4i.h
+++ b/core/math/vector4i.h
@@ -84,6 +84,9 @@ struct _NO_DISCARD_ Vector4i {
_FORCE_INLINE_ void zero();
+ _FORCE_INLINE_ double distance_to(const Vector4i &p_to) const;
+ _FORCE_INLINE_ int64_t distance_squared_to(const Vector4i &p_to) const;
+
_FORCE_INLINE_ Vector4i abs() const;
_FORCE_INLINE_ Vector4i sign() const;
Vector4i clamp(const Vector4i &p_min, const Vector4i &p_max) const;
@@ -139,6 +142,14 @@ double Vector4i::length() const {
return Math::sqrt((double)length_squared());
}
+double Vector4i::distance_to(const Vector4i &p_to) const {
+ return (p_to - *this).length();
+}
+
+int64_t Vector4i::distance_squared_to(const Vector4i &p_to) const {
+ return (p_to - *this).length_squared();
+}
+
Vector4i Vector4i::abs() const {
return Vector4i(Math::abs(x), Math::abs(y), Math::abs(z), Math::abs(w));
}
diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index db78b982e4..f8e8c4d7e9 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -81,35 +81,27 @@ template <class T, class... P>
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
-#ifdef DEBUG_ENABLED
uint64_t object_id;
-#endif
void (T::*method)(P...);
} data;
public:
virtual ObjectID get_object() const {
-#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
-#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
-#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
-#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
-#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
@@ -135,36 +127,28 @@ template <class T, class R, class... P>
class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
-#ifdef DEBUG_ENABLED
uint64_t object_id;
-#endif
R(T::*method)
(P...);
} data;
public:
virtual ObjectID get_object() const {
-#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
-#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
-#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
-#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
-#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
@@ -190,36 +174,28 @@ template <class T, class R, class... P>
class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
struct Data {
T *instance;
-#ifdef DEBUG_ENABLED
uint64_t object_id;
-#endif
R(T::*method)
(P...) const;
} data;
public:
virtual ObjectID get_object() const override {
-#ifdef DEBUG_ENABLED
if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
return ObjectID();
}
-#endif
return data.instance->get_instance_id();
}
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
-#ifdef DEBUG_ENABLED
ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
-#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
-#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index bf1bd0de93..f2a9a68d08 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -31,6 +31,7 @@
#include "class_db.h"
#include "core/config/engine.h"
+#include "core/core_string_names.h"
#include "core/io/resource_loader.h"
#include "core/object/script_language.h"
#include "core/os/mutex.h"
@@ -1299,6 +1300,12 @@ bool ClassDB::get_property(Object *p_object, const StringName &p_property, Varia
check = check->inherits_ptr;
}
+ // The "free()" method is special, so we assume it exists and return a Callable.
+ if (p_property == CoreStringNames::get_singleton()->_free) {
+ r_value = Callable(p_object, p_property);
+ return true;
+ }
+
return false;
}
@@ -1608,6 +1615,28 @@ void ClassDB::get_virtual_methods(const StringName &p_class, List<MethodInfo> *p
#endif
}
+void ClassDB::add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info) {
+ ERR_FAIL_COND_MSG(!classes.has(p_class), "Request for nonexistent class '" + p_class + "'.");
+
+#ifdef DEBUG_METHODS_ENABLED
+ PackedStringArray arg_names;
+
+ MethodInfo mi;
+ mi.name = *reinterpret_cast<StringName *>(p_method_info->name);
+ mi.return_val = PropertyInfo(p_method_info->return_value);
+ mi.return_val_metadata = p_method_info->return_value_metadata;
+ mi.flags = p_method_info->method_flags;
+ for (int i = 0; i < (int)p_method_info->argument_count; i++) {
+ PropertyInfo arg(p_method_info->arguments[i]);
+ mi.arguments.push_back(arg);
+ mi.arguments_metadata.push_back(p_method_info->arguments_metadata[i]);
+ arg_names.push_back(arg.name);
+ }
+
+ add_virtual_method(p_class, mi, true, arg_names);
+#endif
+}
+
void ClassDB::set_class_enabled(const StringName &p_class, bool p_enable) {
OBJTYPE_WLOCK;
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 7a4ee1afa4..c910b30d11 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -188,7 +188,7 @@ public:
template <class T>
static void register_class(bool p_virtual = false) {
GLOBAL_LOCK_FUNCTION;
- static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+ static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@@ -203,7 +203,7 @@ public:
template <class T>
static void register_abstract_class() {
GLOBAL_LOCK_FUNCTION;
- static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+ static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@@ -216,7 +216,7 @@ public:
template <class T>
static void register_internal_class() {
GLOBAL_LOCK_FUNCTION;
- static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+ static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@@ -239,7 +239,7 @@ public:
template <class T>
static void register_custom_instance_class() {
GLOBAL_LOCK_FUNCTION;
- static_assert(TypesAreSame<typename T::self_type, T>::value, "Class not declared properly, please use GDCLASS.");
+ static_assert(types_are_same_v<typename T::self_type, T>, "Class not declared properly, please use GDCLASS.");
T::initialize_class();
ClassInfo *t = classes.getptr(T::get_class_static());
ERR_FAIL_NULL(t);
@@ -296,7 +296,7 @@ public:
argptrs[i] = &args[i];
}
MethodBind *bind = create_method_bind(p_method);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@@ -311,7 +311,7 @@ public:
}
MethodBind *bind = create_static_method_bind(p_method);
bind->set_instance_class(p_class);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, false, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@@ -325,7 +325,7 @@ public:
argptrs[i] = &args[i];
}
MethodBind *bind = create_method_bind(p_method);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@@ -340,7 +340,7 @@ public:
}
MethodBind *bind = create_static_method_bind(p_method);
bind->set_instance_class(p_class);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, true, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
@@ -353,7 +353,7 @@ public:
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_NULL_V(bind, nullptr);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return _bind_vararg_method(bind, p_name, p_default_args, false);
@@ -366,7 +366,7 @@ public:
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
ERR_FAIL_NULL_V(bind, nullptr);
- if constexpr (std::is_same<typename member_function_traits<M>::return_type, Object *>::value) {
+ if constexpr (std::is_same_v<typename member_function_traits<M>::return_type, Object *>) {
bind->set_return_type_is_raw_object_ptr(true);
}
return _bind_vararg_method(bind, p_name, p_default_args, true);
@@ -410,6 +410,7 @@ public:
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, bool p_virtual = true, const Vector<String> &p_arg_names = Vector<String>(), bool p_object_core = false);
static void get_virtual_methods(const StringName &p_class, List<MethodInfo> *p_methods, bool p_no_inheritance = false);
+ static void add_extension_class_virtual_method(const StringName &p_class, const GDExtensionClassVirtualMethodInfo *p_method_info);
static void bind_integer_constant(const StringName &p_class, const StringName &p_enum, const StringName &p_name, int64_t p_constant, bool p_is_bitfield = false);
static void get_integer_constant_list(const StringName &p_class, List<String> *p_constants, bool p_no_inheritance = false);
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 79a8df6c8a..ae70981f72 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -1,78 +1,74 @@
-proto = """
-#define GDVIRTUAL$VER($RET m_name $ARG) \\
-StringName _gdvirtual_##m_name##_sn = #m_name;\\
-mutable bool _gdvirtual_##m_name##_initialized = false;\\
-mutable void* _gdvirtual_##m_name = nullptr;\\
-template<bool required>\\
-_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
- ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
- if (_script_instance) {\\
- Callable::CallError ce; \\
- $CALLSIARGS\\
- $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
- if (ce.error == Callable::CallError::CALL_OK) {\\
- $CALLSIRET\\
+proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
+ StringName _gdvirtual_##m_name##_sn = #m_name;\\
+ mutable bool _gdvirtual_##m_name##_initialized = false;\\
+ mutable void *_gdvirtual_##m_name = nullptr;\\
+ template <bool required>\\
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
+ ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
+ if (_script_instance) {\\
+ Callable::CallError ce;\\
+ $CALLSIARGS\\
+ $CALLSIBEGIN_script_instance->callp(_gdvirtual_##m_name##_sn, $CALLSIARGPASS, ce);\\
+ if (ce.error == Callable::CallError::CALL_OK) {\\
+ $CALLSIRET\\
+ return true;\\
+ }\\
+ }\\
+ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
+ _gdvirtual_##m_name##_initialized = true;\\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ $CALLPTRARGS\\
+ $CALLPTRRETDEF\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS, $CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ } else {\\
+ ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(), $CALLPTRARGPASS, $CALLPTRRETPASS);\\
+ $CALLPTRRET\\
+ }\\
return true;\\
- } \\
+ }\\
+ if (required) {\\
+ ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
+ $RVOID\\
+ }\\
+ return false;\\
}\\
- if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- _gdvirtual_##m_name = nullptr;\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- } else if (_get_extension()->get_virtual) {\\
- _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- }\\
- GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
- _gdvirtual_##m_name##_initialized = true;\\
- }\\
- if (_gdvirtual_##m_name) {\\
- $CALLPTRARGS\\
- $CALLPTRRETDEF\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\
- $CALLPTRRET\\
- } else {\\
- ((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
- $CALLPTRRET\\
- }\\
- return true;\\
- }\\
- \\
- if (required) {\\
- ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
- $RVOID\\
- }\\
-\\
- return false;\\
-}\\
-_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
- ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
- if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\
- return true;\\
- }\\
- if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
- _gdvirtual_##m_name = nullptr;\\
- if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
- _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- } else if (_get_extension()->get_virtual) {\\
- _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
- }\\
- GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized); \\
- _gdvirtual_##m_name##_initialized = true;\\
- }\\
- if (_gdvirtual_##m_name) {\\
- return true;\\
+ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
+ ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
+ if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\
+ return true;\\
+ }\\
+ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
+ _gdvirtual_##m_name = nullptr;\\
+ if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
+ _gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ } else if (_get_extension()->get_virtual) {\\
+ _gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
+ }\\
+ GDVIRTUAL_TRACK(_gdvirtual_##m_name, _gdvirtual_##m_name##_initialized);\\
+ _gdvirtual_##m_name##_initialized = true;\\
+ }\\
+ if (_gdvirtual_##m_name) {\\
+ return true;\\
+ }\\
+ return false;\\
}\\
- return false;\\
-}\\
-\\
-_FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() { \\
- MethodInfo method_info;\\
- method_info.name = #m_name;\\
- method_info.flags = METHOD_FLAG_VIRTUAL;\\
- $FILL_METHOD_INFO\\
- return method_info;\\
-}
+ _FORCE_INLINE_ static MethodInfo _gdvirtual_##m_name##_get_method_info() {\\
+ MethodInfo method_info;\\
+ method_info.name = #m_name;\\
+ method_info.flags = $METHOD_FLAGS;\\
+ $FILL_METHOD_INFO\\
+ return method_info;\\
+ }
"""
@@ -83,22 +79,23 @@ def generate_version(argcount, const=False, returns=False):
method_info = ""
if returns:
sproto += "R"
- s = s.replace("$RET", "m_ret, ")
+ s = s.replace("$RET", "m_ret,")
s = s.replace("$RVOID", "(void)r_ret;") # If required, may lead to uninitialized errors
s = s.replace("$CALLPTRRETDEF", "PtrToArg<m_ret>::EncodeT ret;")
- method_info += "\tmethod_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
- method_info += "\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;\\\n"
+ method_info += "method_info.return_val = GetTypeInfo<m_ret>::get_class_info();\\\n"
+ method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
else:
- s = s.replace("$RET", "")
- s = s.replace("$RVOID", "")
- s = s.replace("$CALLPTRRETDEF", "")
+ s = s.replace("$RET ", "")
+ s = s.replace("\t\t\t$RVOID\\\n", "")
+ s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "")
if const:
sproto += "C"
s = s.replace("$CONST", "const")
- method_info += "\tmethod_info.flags|=METHOD_FLAG_CONST;\\\n"
+ s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST")
else:
- s = s.replace("$CONST", "")
+ s = s.replace("$CONST ", "")
+ s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL")
s = s.replace("$VER", sproto)
argtext = ""
@@ -108,9 +105,9 @@ def generate_version(argcount, const=False, returns=False):
callptrargsptr = ""
if argcount > 0:
argtext += ", "
- callsiargs = "Variant vargs[" + str(argcount) + "]={"
- callsiargptrs = "\t\tconst Variant *vargptrs[" + str(argcount) + "]={"
- callptrargsptr = "\t\tGDExtensionConstTypePtr argptrs[" + str(argcount) + "]={"
+ callsiargs = f"Variant vargs[{argcount}] = {{ "
+ callsiargptrs = f"\t\t\tconst Variant *vargptrs[{argcount}] = {{ "
+ callptrargsptr = f"\t\t\tGDExtensionConstTypePtr argptrs[{argcount}] = {{ "
callptrargs = ""
for i in range(argcount):
if i > 0:
@@ -118,52 +115,55 @@ def generate_version(argcount, const=False, returns=False):
callargtext += ", "
callsiargs += ", "
callsiargptrs += ", "
- callptrargs += "\t\t"
+ callptrargs += "\t\t\t"
callptrargsptr += ", "
- argtext += "m_type" + str(i + 1)
- callargtext += "m_type" + str(i + 1) + " arg" + str(i + 1)
- callsiargs += "Variant(arg" + str(i + 1) + ")"
- callsiargptrs += "&vargs[" + str(i) + "]"
+ argtext += f"m_type{i + 1}"
+ callargtext += f"m_type{i + 1} arg{i + 1}"
+ callsiargs += f"Variant(arg{i + 1})"
+ callsiargptrs += f"&vargs[{i}]"
callptrargs += (
- "PtrToArg<m_type" + str(i + 1) + ">::EncodeT argval" + str(i + 1) + " = arg" + str(i + 1) + ";\\\n"
- )
- callptrargsptr += "&argval" + str(i + 1)
- method_info += "\tmethod_info.arguments.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::get_class_info());\\\n"
- method_info += (
- "\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type" + str(i + 1) + ">::METADATA);\\\n"
+ f"PtrToArg<m_type{i + 1}>::EncodeT argval{i + 1} = (PtrToArg<m_type{i + 1}>::EncodeT)arg{i + 1};\\\n"
)
+ callptrargsptr += f"&argval{i + 1}"
+ if method_info:
+ method_info += "\\\n\t\t"
+ method_info += f"method_info.arguments.push_back(GetTypeInfo<m_type{i + 1}>::get_class_info());\\\n"
+ method_info += f"\t\tmethod_info.arguments_metadata.push_back(GetTypeInfo<m_type{i + 1}>::METADATA);"
if argcount:
- callsiargs += "};\\\n"
- callsiargptrs += "};\\\n"
+ callsiargs += " };\\\n"
+ callsiargptrs += " };"
s = s.replace("$CALLSIARGS", callsiargs + callsiargptrs)
- s = s.replace("$CALLSIARGPASS", "(const Variant **)vargptrs," + str(argcount))
- callptrargsptr += "};\\\n"
+ s = s.replace("$CALLSIARGPASS", f"(const Variant **)vargptrs, {argcount}")
+ callptrargsptr += " };"
s = s.replace("$CALLPTRARGS", callptrargs + callptrargsptr)
- s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr*>(argptrs)")
+ s = s.replace("$CALLPTRARGPASS", "reinterpret_cast<GDExtensionConstTypePtr *>(argptrs)")
else:
- s = s.replace("$CALLSIARGS", "")
+ s = s.replace("\t\t\t$CALLSIARGS\\\n", "")
s = s.replace("$CALLSIARGPASS", "nullptr, 0")
- s = s.replace("$CALLPTRARGS", "")
+ s = s.replace("\t\t\t$CALLPTRARGS\\\n", "")
s = s.replace("$CALLPTRARGPASS", "nullptr")
if returns:
if argcount > 0:
- callargtext += ","
- callargtext += " m_ret& r_ret"
+ callargtext += ", "
+ callargtext += "m_ret &r_ret"
s = s.replace("$CALLSIBEGIN", "Variant ret = ")
s = s.replace("$CALLSIRET", "r_ret = VariantCaster<m_ret>::cast(ret);")
s = s.replace("$CALLPTRRETPASS", "&ret")
s = s.replace("$CALLPTRRET", "r_ret = (m_ret)ret;")
else:
s = s.replace("$CALLSIBEGIN", "")
- s = s.replace("$CALLSIRET", "")
+ s = s.replace("\t\t\t\t$CALLSIRET\\\n", "")
s = s.replace("$CALLPTRRETPASS", "nullptr")
- s = s.replace("$CALLPTRRET", "")
+ s = s.replace("\t\t\t\t$CALLPTRRET\\\n", "")
- s = s.replace("$ARG", argtext)
+ s = s.replace(" $ARG", argtext)
s = s.replace("$CALLARGS", callargtext)
- s = s.replace("$FILL_METHOD_INFO", method_info)
+ if method_info:
+ s = s.replace("$FILL_METHOD_INFO", method_info)
+ else:
+ s = s.replace("\t\t$FILL_METHOD_INFO\\\n", method_info)
return s
@@ -171,21 +171,21 @@ def generate_version(argcount, const=False, returns=False):
def run(target, source, env):
max_versions = 12
- txt = """
+ txt = """/* THIS FILE IS GENERATED DO NOT EDIT */
#ifndef GDVIRTUAL_GEN_H
#define GDVIRTUAL_GEN_H
#include "core/object/script_instance.h"
#ifdef TOOLS_ENABLED
-#define GDVIRTUAL_TRACK(m_virtual, m_initialized) \\
- if (_get_extension()->reloadable) {\\
- VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\
- tracker->method = (void **)&m_virtual;\\
- tracker->initialized = &m_initialized;\\
- tracker->next = virtual_method_list;\\
- virtual_method_list = tracker;\\
- }
+#define GDVIRTUAL_TRACK(m_virtual, m_initialized)\\
+ if (_get_extension()->reloadable) {\\
+ VirtualMethodTracker *tracker = memnew(VirtualMethodTracker);\\
+ tracker->method = (void **)&m_virtual;\\
+ tracker->initialized = &m_initialized;\\
+ tracker->next = virtual_method_list;\\
+ virtual_method_list = tracker;\\
+ }
#else
#define GDVIRTUAL_TRACK(m_virtual, m_initialized)
#endif
@@ -193,13 +193,13 @@ def run(target, source, env):
"""
for i in range(max_versions + 1):
- txt += "/* " + str(i) + " Arguments */\n\n"
+ txt += f"/* {i} Arguments */\n\n"
txt += generate_version(i, False, False)
txt += generate_version(i, False, True)
txt += generate_version(i, True, False)
txt += generate_version(i, True, True)
- txt += "#endif"
+ txt += "#endif // GDVIRTUAL_GEN_H\n"
with open(target[0], "w") as f:
f.write(txt)
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index de71295ee5..83a19554dc 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,15 +35,17 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
+#include <stdio.h>
+
#ifdef DEV_ENABLED
// Includes safety checks to ensure that a queue set as a thread singleton override
// is only ever called from the thread it was set for.
-#define LOCK_MUTEX \
- if (this != MessageQueue::thread_singleton) { \
- DEV_ASSERT(!this->is_current_thread_override); \
- mutex.lock(); \
- } else { \
- DEV_ASSERT(this->is_current_thread_override); \
+#define LOCK_MUTEX \
+ if (this != MessageQueue::thread_singleton) { \
+ DEV_ASSERT(!is_current_thread_override); \
+ mutex.lock(); \
+ } else { \
+ DEV_ASSERT(is_current_thread_override); \
}
#else
#define LOCK_MUTEX \
@@ -93,7 +95,7 @@ Error CallQueue::push_callablep(const Callable &p_callable, const Variant **p_ar
if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
- ERR_PRINT("Failed method: " + p_callable + ". Message queue out of memory. " + error_text);
+ fprintf(stderr, "Failed method: %s. Message queue out of memory. %s\n", String(p_callable).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
@@ -144,7 +146,7 @@ Error CallQueue::push_set(ObjectID p_id, const StringName &p_prop, const Variant
if (ObjectDB::get_instance(p_id)) {
type = ObjectDB::get_instance(p_id)->get_class();
}
- ERR_PRINT("Failed set: " + type + ":" + p_prop + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
+ fprintf(stderr, "Failed set: %s: %s target ID: %s. Message queue out of memory. %s\n", type.utf8().get_data(), String(p_prop).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
@@ -181,7 +183,7 @@ Error CallQueue::push_notification(ObjectID p_id, int p_notification) {
if ((page_bytes[pages_used - 1] + room_needed) > uint32_t(PAGE_SIZE_BYTES)) {
if (pages_used == max_pages) {
- ERR_PRINT("Failed notification: " + itos(p_notification) + " target ID: " + itos(p_id) + ". Message queue out of memory. " + error_text);
+ fprintf(stderr, "Failed notification: %s target ID: %s. Message queue out of memory. %s\n", itos(p_notification).utf8().get_data(), itos(p_id).utf8().get_data(), error_text.utf8().get_data());
statistics();
UNLOCK_MUTEX;
return ERR_OUT_OF_MEMORY;
diff --git a/core/object/object.cpp b/core/object/object.cpp
index 40df13849b..cc33d0ab8a 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -728,7 +728,7 @@ Variant Object::callp(const StringName &p_method, const Variant **p_args, int p_
r_error.expected = 0;
return Variant();
}
- if (Object::cast_to<RefCounted>(this)) {
+ if (is_ref_counted()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_V_MSG(Variant(), "Can't 'free' a reference.");
}
@@ -1347,12 +1347,10 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
s = &signal_map[p_signal];
}
- Callable target = p_callable;
-
//compare with the base callable, so binds can be ignored
- if (s->slot_map.has(*target.get_base_comparator())) {
+ if (s->slot_map.has(*p_callable.get_base_comparator())) {
if (p_flags & CONNECT_REFERENCE_COUNTED) {
- s->slot_map[*target.get_base_comparator()].reference_count++;
+ s->slot_map[*p_callable.get_base_comparator()].reference_count++;
return OK;
} else {
ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Signal '" + p_signal + "' is already connected to given callable '" + p_callable + "' in that object.");
@@ -1364,7 +1362,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
SignalData::Slot slot;
Connection conn;
- conn.callable = target;
+ conn.callable = p_callable;
conn.signal = ::Signal(this, p_signal);
conn.flags = p_flags;
slot.conn = conn;
@@ -1376,7 +1374,7 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
}
//use callable version as key, so binds can be ignored
- s->slot_map[*target.get_base_comparator()] = slot;
+ s->slot_map[*p_callable.get_base_comparator()] = slot;
return OK;
}
@@ -1397,9 +1395,7 @@ bool Object::is_connected(const StringName &p_signal, const Callable &p_callable
ERR_FAIL_V_MSG(false, "Nonexistent signal: " + p_signal + ".");
}
- Callable target = p_callable;
-
- return s->slot_map.has(*target.get_base_comparator());
+ return s->slot_map.has(*p_callable.get_base_comparator());
}
void Object::disconnect(const StringName &p_signal, const Callable &p_callable) {
@@ -1687,6 +1683,7 @@ void Object::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_POSTINITIALIZE);
BIND_CONSTANT(NOTIFICATION_PREDELETE);
+ BIND_CONSTANT(NOTIFICATION_EXTENSION_RELOADED);
BIND_ENUM_CONSTANT(CONNECT_DEFERRED);
BIND_ENUM_CONSTANT(CONNECT_PERSIST);
diff --git a/core/object/object.h b/core/object/object.h
index 7b53fcaa41..d697f14b7e 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -235,7 +235,7 @@ struct MethodInfo {
return arguments_metadata.size() > p_arg ? arguments_metadata[p_arg] : 0;
}
- inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; }
+ inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id && name == p_method.name; }
inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); }
operator Dictionary() const;
@@ -656,7 +656,7 @@ private:
friend class RefCounted;
bool type_is_reference = false;
- std::mutex _instance_binding_mutex;
+ BinaryMutex _instance_binding_mutex;
struct InstanceBinding {
void *binding = nullptr;
void *token = nullptr;
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index 228373d662..10be27b879 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -212,8 +212,9 @@ public:
reference = nullptr;
}
- void instantiate() {
- ref(memnew(T));
+ template <typename... VarArgs>
+ void instantiate(VarArgs... p_params) {
+ ref(memnew(T(p_params...)));
}
Ref() {}
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 086f8a666e..3b9b1f9094 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -138,6 +138,8 @@ void Script::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_base_script"), &Script::get_base_script);
ClassDB::bind_method(D_METHOD("get_instance_base_type"), &Script::get_instance_base_type);
+ ClassDB::bind_method(D_METHOD("get_global_name"), &Script::get_global_name);
+
ClassDB::bind_method(D_METHOD("has_script_signal", "signal_name"), &Script::has_script_signal);
ClassDB::bind_method(D_METHOD("get_script_property_list"), &Script::_get_script_property_list);
@@ -166,6 +168,18 @@ ScriptLanguage *ScriptServer::get_language(int p_idx) {
return _languages[p_idx];
}
+ScriptLanguage *ScriptServer::get_language_for_extension(const String &p_extension) {
+ MutexLock lock(languages_mutex);
+
+ for (int i = 0; i < _language_count; i++) {
+ if (_languages[i] && _languages[i]->get_extension() == p_extension) {
+ return _languages[i];
+ }
+ }
+
+ return nullptr;
+}
+
Error ScriptServer::register_language(ScriptLanguage *p_language) {
MutexLock lock(languages_mutex);
ERR_FAIL_NULL_V(p_language, ERR_INVALID_PARAMETER);
@@ -631,6 +645,10 @@ bool PlaceHolderScriptInstance::has_method(const StringName &p_method) const {
void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties, const HashMap<StringName, Variant> &p_values) {
HashSet<StringName> new_values;
for (const PropertyInfo &E : p_properties) {
+ if (E.usage & (PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP | PROPERTY_USAGE_CATEGORY)) {
+ continue;
+ }
+
StringName n = E.name;
new_values.insert(n);
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 85e64c8d62..294231a3e7 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -75,6 +75,7 @@ public:
static bool is_scripting_enabled();
_FORCE_INLINE_ static int get_language_count() { return _language_count; }
static ScriptLanguage *get_language(int p_idx);
+ static ScriptLanguage *get_language_for_extension(const String &p_extension);
static Error register_language(ScriptLanguage *p_language);
static Error unregister_language(const ScriptLanguage *p_language);
@@ -243,7 +244,7 @@ public:
virtual void get_doc_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); }
- virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); }
+ virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) { return Vector<ScriptTemplate>(); }
virtual bool is_using_templates() { return false; }
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0;
virtual String validate_path(const String &p_path) const { return ""; }
@@ -371,6 +372,7 @@ public:
virtual Vector<StackInfo> debug_get_current_stack_info() { return Vector<StackInfo>(); }
virtual void reload_all_scripts() = 0;
+ virtual void reload_scripts(const Array &p_scripts, bool p_soft_reload) = 0;
virtual void reload_tool_script(const Ref<Script> &p_script, bool p_soft_reload) = 0;
/* LOADER FUNCTIONS */
@@ -384,10 +386,12 @@ public:
uint64_t call_count;
uint64_t total_time;
uint64_t self_time;
+ uint64_t internal_time;
};
virtual void profiling_start() = 0;
virtual void profiling_stop() = 0;
+ virtual void profiling_set_save_native_calls(bool p_enable) = 0;
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) = 0;
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) = 0;
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index e326baf7eb..be62cabe25 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -107,7 +107,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_supports_builtin_mode);
GDVIRTUAL_BIND(_supports_documentation);
GDVIRTUAL_BIND(_can_inherit_from_file);
- GDVIRTUAL_BIND(_find_function, "class_name", "function_name");
+ GDVIRTUAL_BIND(_find_function, "function", "code");
GDVIRTUAL_BIND(_make_function, "class_name", "function_name", "function_args");
GDVIRTUAL_BIND(_open_in_external_editor, "script", "line", "column");
GDVIRTUAL_BIND(_overrides_external_editor);
@@ -145,6 +145,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_profiling_start);
GDVIRTUAL_BIND(_profiling_stop);
+ GDVIRTUAL_BIND(_profiling_set_save_native_calls, "enable");
GDVIRTUAL_BIND(_profiling_get_accumulated_data, "info_array", "info_max");
GDVIRTUAL_BIND(_profiling_get_frame_data, "info_array", "info_max");
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 00ab1cd6c0..5b10739486 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -265,7 +265,7 @@ public:
GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName)
- virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override {
+ virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override {
TypedArray<Dictionary> ret;
GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret);
Vector<ScriptTemplate> stret;
@@ -562,6 +562,7 @@ public:
}
EXBIND0(reload_all_scripts)
+ EXBIND2(reload_scripts, const Array &, bool)
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */
@@ -607,6 +608,7 @@ public:
EXBIND0(profiling_start)
EXBIND0(profiling_stop)
+ EXBIND1(profiling_set_save_native_calls, bool)
GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
diff --git a/core/object/undo_redo.cpp b/core/object/undo_redo.cpp
index 3c1d4ef95e..569e6ae19f 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -316,6 +316,14 @@ void UndoRedo::commit_action(bool p_execute) {
_redo(p_execute); // perform action
committing--;
+ if (max_steps > 0) {
+ // Clear early steps.
+
+ while (actions.size() > max_steps) {
+ _pop_history_tail();
+ }
+ }
+
if (add_message && callback && actions.size() > 0) {
callback(callback_ud, actions[actions.size() - 1].name);
}
@@ -473,6 +481,14 @@ uint64_t UndoRedo::get_version() const {
return version;
}
+void UndoRedo::set_max_steps(int p_max_steps) {
+ max_steps = p_max_steps;
+}
+
+int UndoRedo::get_max_steps() const {
+ return max_steps;
+}
+
void UndoRedo::set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud) {
callback = p_callback;
callback_ud = p_ud;
@@ -517,9 +533,13 @@ void UndoRedo::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_undo"), &UndoRedo::has_undo);
ClassDB::bind_method(D_METHOD("has_redo"), &UndoRedo::has_redo);
ClassDB::bind_method(D_METHOD("get_version"), &UndoRedo::get_version);
+ ClassDB::bind_method(D_METHOD("set_max_steps", "max_steps"), &UndoRedo::set_max_steps);
+ ClassDB::bind_method(D_METHOD("get_max_steps"), &UndoRedo::get_max_steps);
ClassDB::bind_method(D_METHOD("redo"), &UndoRedo::redo);
ClassDB::bind_method(D_METHOD("undo"), &UndoRedo::undo);
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "max_steps", PROPERTY_HINT_RANGE, "0,50,1,or_greater"), "set_max_steps", "get_max_steps");
+
ADD_SIGNAL(MethodInfo("version_changed"));
BIND_ENUM_CONSTANT(MERGE_DISABLE);
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index b3a3322e4b..62aebff877 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -80,6 +80,7 @@ private:
int current_action = -1;
bool force_keep_in_merge_ends = false;
int action_level = 0;
+ int max_steps = 0;
MergeMode merge_mode = MERGE_DISABLE;
bool merging = false;
uint64_t version = 1;
@@ -135,6 +136,9 @@ public:
uint64_t get_version() const;
+ void set_max_steps(int p_max_steps);
+ int get_max_steps() const;
+
void set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud);
void set_method_notify_callback(MethodNotifyCallback p_method_callback, void *p_ud);
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 784acadab4..e2ab473b01 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -33,6 +33,7 @@
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/os/thread_safe.h"
+#include "core/templates/command_queue_mt.h"
void WorkerThreadPool::Task::free_template_userdata() {
ERR_FAIL_NULL(template_userdata);
@@ -43,24 +44,18 @@ void WorkerThreadPool::Task::free_template_userdata() {
WorkerThreadPool *WorkerThreadPool::singleton = nullptr;
-void WorkerThreadPool::_process_task_queue() {
- task_mutex.lock();
- Task *task = task_queue.first()->self();
- task_queue.remove(task_queue.first());
- task_mutex.unlock();
- _process_task(task);
-}
+thread_local CommandQueueMT *WorkerThreadPool::flushing_cmd_queue = nullptr;
void WorkerThreadPool::_process_task(Task *p_task) {
- bool low_priority = p_task->low_priority;
- int pool_thread_index = -1;
- Task *prev_low_prio_task = nullptr; // In case this is recursively called.
+#ifdef THREADS_ENABLED
+ int pool_thread_index = thread_ids[Thread::get_caller_id()];
+ ThreadData &curr_thread = threads[pool_thread_index];
+ Task *prev_task = nullptr; // In case this is recursively called.
+ bool safe_for_nodes_backup = is_current_thread_safe_for_nodes();
- if (!use_native_low_priority_threads) {
+ {
// Tasks must start with this unset. They are free to set-and-forget otherwise.
set_current_thread_safe_for_nodes(false);
- pool_thread_index = thread_ids[Thread::get_caller_id()];
- ThreadData &curr_thread = threads[pool_thread_index];
// Since the WorkerThreadPool is started before the script server,
// its pre-created threads can't have ScriptServer::thread_enter() called on them early.
// Therefore, we do it late at the first opportunity, so in case the task
@@ -71,15 +66,11 @@ void WorkerThreadPool::_process_task(Task *p_task) {
}
task_mutex.lock();
p_task->pool_thread_index = pool_thread_index;
- if (low_priority) {
- low_priority_tasks_running++;
- prev_low_prio_task = curr_thread.current_low_prio_task;
- curr_thread.current_low_prio_task = p_task;
- } else {
- curr_thread.current_low_prio_task = nullptr;
- }
+ prev_task = curr_thread.current_task;
+ curr_thread.current_task = p_task;
task_mutex.unlock();
}
+#endif
if (p_task->group) {
// Handling a group
@@ -111,33 +102,24 @@ void WorkerThreadPool::_process_task(Task *p_task) {
memdelete(p_task->template_userdata); // This is no longer needed at this point, so get rid of it.
}
- if (low_priority && use_native_low_priority_threads) {
- p_task->completed = true;
- p_task->done_semaphore.post();
- if (do_post) {
- p_task->group->completed.set_to(true);
- }
- } else {
- if (do_post) {
- p_task->group->done_semaphore.post();
- p_task->group->completed.set_to(true);
- }
- uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
- uint32_t finished_users = p_task->group->finished.increment();
-
- if (finished_users == max_users) {
- // Get rid of the group, because nobody else is using it.
- task_mutex.lock();
- group_allocator.free(p_task->group);
- task_mutex.unlock();
- }
-
- // For groups, tasks get rid of themselves.
+ if (do_post) {
+ p_task->group->done_semaphore.post();
+ p_task->group->completed.set_to(true);
+ }
+ uint32_t max_users = p_task->group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
+ uint32_t finished_users = p_task->group->finished.increment();
+ if (finished_users == max_users) {
+ // Get rid of the group, because nobody else is using it.
task_mutex.lock();
- task_allocator.free(p_task);
+ group_allocator.free(p_task->group);
task_mutex.unlock();
}
+
+ // For groups, tasks get rid of themselves.
+
+ task_mutex.lock();
+ task_allocator.free(p_task);
} else {
if (p_task->native_func) {
p_task->native_func(p_task->native_func_userdata);
@@ -150,88 +132,164 @@ void WorkerThreadPool::_process_task(Task *p_task) {
task_mutex.lock();
p_task->completed = true;
- for (uint8_t i = 0; i < p_task->waiting; i++) {
- p_task->done_semaphore.post();
+ p_task->pool_thread_index = -1;
+ if (p_task->waiting_user) {
+ p_task->done_semaphore.post(p_task->waiting_user);
}
- if (!use_native_low_priority_threads) {
- p_task->pool_thread_index = -1;
+ // Let awaiters know.
+ for (uint32_t i = 0; i < threads.size(); i++) {
+ if (threads[i].awaited_task == p_task) {
+ threads[i].cond_var.notify_one();
+ threads[i].signaled = true;
+ }
}
- task_mutex.unlock(); // Keep mutex down to here since on unlock the task may be freed.
}
- // Task may have been freed by now (all callers notified).
- p_task = nullptr;
-
- if (!use_native_low_priority_threads) {
- bool post = false;
- task_mutex.lock();
- ThreadData &curr_thread = threads[pool_thread_index];
- curr_thread.current_low_prio_task = prev_low_prio_task;
- if (low_priority) {
+#ifdef THREADS_ENABLED
+ {
+ curr_thread.current_task = prev_task;
+ if (p_task->low_priority) {
low_priority_threads_used--;
- low_priority_tasks_running--;
- // A low prioriry task was freed, so see if we can move a pending one to the high priority queue.
- if (_try_promote_low_priority_task()) {
- post = true;
- }
- if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
- _prevent_low_prio_saturation_deadlock();
+ if (_try_promote_low_priority_task()) {
+ if (prev_task) { // Otherwise, this thread will catch it.
+ _notify_threads(&curr_thread, 1, 0);
+ }
}
}
+
task_mutex.unlock();
- if (post) {
- task_available_semaphore.post();
- }
}
+
+ set_current_thread_safe_for_nodes(safe_for_nodes_backup);
+#endif
}
void WorkerThreadPool::_thread_function(void *p_user) {
+ ThreadData *thread_data = (ThreadData *)p_user;
while (true) {
- singleton->task_available_semaphore.wait();
- if (singleton->exit_threads) {
- break;
+ Task *task_to_process = nullptr;
+ {
+ MutexLock lock(singleton->task_mutex);
+ if (singleton->exit_threads) {
+ return;
+ }
+ thread_data->signaled = false;
+
+ if (singleton->task_queue.first()) {
+ task_to_process = singleton->task_queue.first()->self();
+ singleton->task_queue.remove(singleton->task_queue.first());
+ } else {
+ thread_data->cond_var.wait(lock);
+ DEV_ASSERT(singleton->exit_threads || thread_data->signaled);
+ }
}
- singleton->_process_task_queue();
- }
-}
-void WorkerThreadPool::_native_low_priority_thread_function(void *p_user) {
- Task *task = (Task *)p_user;
- singleton->_process_task(task);
+ if (task_to_process) {
+ singleton->_process_task(task_to_process);
+ }
+ }
}
-void WorkerThreadPool::_post_task(Task *p_task, bool p_high_priority) {
+void WorkerThreadPool::_post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority) {
// Fall back to processing on the calling thread if there are no worker threads.
// Separated into its own variable to make it easier to extend this logic
// in custom builds.
bool process_on_calling_thread = threads.size() == 0;
if (process_on_calling_thread) {
- _process_task(p_task);
+ task_mutex.unlock();
+ for (uint32_t i = 0; i < p_count; i++) {
+ _process_task(p_tasks[i]);
+ }
return;
}
- task_mutex.lock();
- p_task->low_priority = !p_high_priority;
- if (!p_high_priority && use_native_low_priority_threads) {
- p_task->low_priority_thread = native_thread_allocator.alloc();
- task_mutex.unlock();
+ uint32_t to_process = 0;
+ uint32_t to_promote = 0;
- if (p_task->group) {
- p_task->group->low_priority_native_tasks.push_back(p_task);
+ ThreadData *caller_pool_thread = thread_ids.has(Thread::get_caller_id()) ? &threads[thread_ids[Thread::get_caller_id()]] : nullptr;
+
+ for (uint32_t i = 0; i < p_count; i++) {
+ p_tasks[i]->low_priority = !p_high_priority;
+ if (p_high_priority || low_priority_threads_used < max_low_priority_threads) {
+ task_queue.add_last(&p_tasks[i]->task_elem);
+ if (!p_high_priority) {
+ low_priority_threads_used++;
+ }
+ to_process++;
+ } else {
+ // Too many threads using low priority, must go to queue.
+ low_priority_task_queue.add_last(&p_tasks[i]->task_elem);
+ to_promote++;
}
- p_task->low_priority_thread->start(_native_low_priority_thread_function, p_task); // Pask task directly to thread.
- } else if (p_high_priority || low_priority_threads_used < max_low_priority_threads) {
- task_queue.add_last(&p_task->task_elem);
- if (!p_high_priority) {
- low_priority_threads_used++;
+ }
+
+ _notify_threads(caller_pool_thread, to_process, to_promote);
+
+ task_mutex.unlock();
+}
+
+void WorkerThreadPool::_notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count) {
+ uint32_t to_process = p_process_count;
+ uint32_t to_promote = p_promote_count;
+
+ // This is where which threads are awaken is decided according to the workload.
+ // Threads that will anyway have a chance to check the situation and process/promote tasks
+ // are excluded from being notified. Others will be tried anyway to try to distribute load.
+ // The current thread, if is a pool thread, is also excluded depending on the promoting/processing
+ // needs because it will anyway loop again. However, it will contribute to decreasing the count,
+ // which helps reducing sync traffic.
+
+ uint32_t thread_count = threads.size();
+
+ // First round:
+ // 1. For processing: notify threads that are not running tasks, to keep the stacks as shallow as possible.
+ // 2. For promoting: since it's exclusive with processing, we fin threads able to promote low-prio tasks now.
+ for (uint32_t i = 0;
+ i < thread_count && (to_process || to_promote);
+ i++, notify_index = (notify_index + 1) % thread_count) {
+ ThreadData &th = threads[notify_index];
+
+ if (th.signaled) {
+ continue;
+ }
+ if (th.current_task) {
+ // Good thread for promoting low-prio?
+ if (to_promote && th.awaited_task && th.current_task->low_priority) {
+ if (likely(&th != p_current_thread_data)) {
+ th.cond_var.notify_one();
+ }
+ th.signaled = true;
+ to_promote--;
+ }
+ } else {
+ if (to_process) {
+ if (likely(&th != p_current_thread_data)) {
+ th.cond_var.notify_one();
+ }
+ th.signaled = true;
+ to_process--;
+ }
+ }
+ }
+
+ // Second round:
+ // For processing: if the first round wasn't enough, let's try now with threads processing tasks but currently awaiting.
+ for (uint32_t i = 0;
+ i < thread_count && to_process;
+ i++, notify_index = (notify_index + 1) % thread_count) {
+ ThreadData &th = threads[notify_index];
+
+ if (th.signaled) {
+ continue;
+ }
+ if (th.awaited_task) {
+ if (likely(&th != p_current_thread_data)) {
+ th.cond_var.notify_one();
+ }
+ th.signaled = true;
+ to_process--;
}
- task_mutex.unlock();
- task_available_semaphore.post();
- } else {
- // Too many threads using low priority, must go to queue.
- low_priority_task_queue.add_last(&p_task->task_elem);
- task_mutex.unlock();
}
}
@@ -247,23 +305,6 @@ bool WorkerThreadPool::_try_promote_low_priority_task() {
}
}
-void WorkerThreadPool::_prevent_low_prio_saturation_deadlock() {
- if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
-#ifdef DEV_ENABLED
- print_verbose("WorkerThreadPool: Low-prio slots saturated with tasks all waiting for other low-prio tasks. Attempting to avoid deadlock by scheduling one extra task.");
-#endif
- // In order not to create dependency cycles, we can only schedule the next one.
- // We'll keep doing the same until the deadlock is broken,
- SelfList<Task> *to_promote = low_priority_task_queue.first();
- if (to_promote) {
- low_priority_task_queue.remove(to_promote);
- task_queue.add_last(to_promote);
- low_priority_threads_used++;
- task_available_semaphore.post();
- }
- }
-}
-
WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) {
return _add_task(Callable(), p_func, p_userdata, nullptr, p_high_priority, p_description);
}
@@ -273,15 +314,15 @@ WorkerThreadPool::TaskID WorkerThreadPool::_add_task(const Callable &p_callable,
// Get a free task
Task *task = task_allocator.alloc();
TaskID id = last_task++;
+ task->self = id;
task->callable = p_callable;
task->native_func = p_func;
task->native_func_userdata = p_userdata;
task->description = p_description;
task->template_userdata = p_template_userdata;
tasks.insert(id, task);
- task_mutex.unlock();
- _post_task(task, p_high_priority);
+ _post_tasks_and_unlock(&task, 1, p_high_priority);
return id;
}
@@ -313,105 +354,117 @@ Error WorkerThreadPool::wait_for_task_completion(TaskID p_task_id) {
}
Task *task = *taskp;
- if (!task->completed) {
- if (!use_native_low_priority_threads && task->pool_thread_index != -1) { // Otherwise, it's not running yet.
- int caller_pool_th_index = thread_ids.has(Thread::get_caller_id()) ? thread_ids[Thread::get_caller_id()] : -1;
- if (caller_pool_th_index == task->pool_thread_index) {
- // Deadlock prevention.
- // Waiting for a task run on this same thread? That means the task to be awaited started waiting as well
- // and another task was run to make use of the thread in the meantime, with enough bad luck as to
- // the need to wait for the original task arose in turn.
- // In other words, the task we want to wait for is buried in the stack.
- // Let's report the caller about the issue to it handles as it sees fit.
- task_mutex.unlock();
- return ERR_BUSY;
- }
+ if (task->completed) {
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
}
+ task_mutex.unlock();
+ return OK;
+ }
+
+ ThreadData *caller_pool_thread = thread_ids.has(Thread::get_caller_id()) ? &threads[thread_ids[Thread::get_caller_id()]] : nullptr;
+ if (caller_pool_thread && p_task_id <= caller_pool_thread->current_task->self) {
+ // Deadlock prevention:
+ // When a pool thread wants to wait for an older task, the following situations can happen:
+ // 1. Awaited task is deep in the stack of the awaiter.
+ // 2. A group of awaiter threads end up depending on some tasks buried in the stack
+ // of their worker threads in such a way that progress can't be made.
+ // Both would entail a deadlock. Some may be handled here in the WorkerThreadPool
+ // with some extra logic and bookkeeping. However, there would still be unavoidable
+ // cases of deadlock because of the way waiting threads process outstanding tasks.
+ // Taking into account there's no feasible solution for every possible case
+ // with the current design, we just simply reject attempts to await on older tasks,
+ // with a specific error code that signals the situation so the caller can handle it.
+ task_mutex.unlock();
+ return ERR_BUSY;
+ }
+
+ if (caller_pool_thread) {
+ task->waiting_pool++;
+ } else {
+ task->waiting_user++;
+ }
+
+ task_mutex.unlock();
+
+ if (caller_pool_thread) {
+ 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);
+ }
+ }
- task->waiting++;
-
- bool is_low_prio_waiting_for_another = false;
- if (!use_native_low_priority_threads) {
- // Deadlock prevention:
- // If all low-prio tasks are waiting for other low-prio tasks and there are no more free low-prio slots,
- // we have a no progressable situation. We can apply a workaround, consisting in promoting an awaited queued
- // low-prio task to the schedule queue so it can run and break the "impasse".
- // NOTE: A similar reasoning could be made about high priority tasks, but there are usually much more
- // than low-prio. Therefore, a deadlock there would only happen when dealing with a very complex task graph
- // or when there are too few worker threads (limited platforms or exotic settings). If that turns out to be
- // an issue in the real world, a further fix can be applied against that.
- if (task->low_priority) {
- bool awaiter_is_a_low_prio_task = thread_ids.has(Thread::get_caller_id()) && threads[thread_ids[Thread::get_caller_id()]].current_low_prio_task;
- if (awaiter_is_a_low_prio_task) {
- is_low_prio_waiting_for_another = true;
- low_priority_tasks_awaiting_others++;
- if (low_priority_tasks_awaiting_others == low_priority_tasks_running) {
- _prevent_low_prio_saturation_deadlock();
+ task->waiting_pool--;
+ if (task->waiting_pool == 0 && task->waiting_user == 0) {
+ tasks.erase(p_task_id);
+ task_allocator.free(task);
}
+
+ break;
}
- }
- }
- task_mutex.unlock();
+ 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.
- if (use_native_low_priority_threads && task->low_priority) {
- task->done_semaphore.wait();
- } else {
- bool current_is_pool_thread = thread_ids.has(Thread::get_caller_id());
- if (current_is_pool_thread) {
- // We are an actual process thread, we must not be blocked so continue processing stuff if available.
- bool must_exit = false;
- while (true) {
- if (task->done_semaphore.try_wait()) {
- // If done, exit
- break;
+ if (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);
+ }
}
- if (!must_exit) {
- if (task_available_semaphore.try_wait()) {
- if (exit_threads) {
- must_exit = true;
- } else {
- // Solve tasks while they are around.
- bool safe_for_nodes_backup = is_current_thread_safe_for_nodes();
- _process_task_queue();
- set_current_thread_safe_for_nodes(safe_for_nodes_backup);
- continue;
- }
- } else if (!use_native_low_priority_threads && task->low_priority) {
- // A low prioriry task started waiting, so see if we can move a pending one to the high priority queue.
- task_mutex.lock();
- bool post = _try_promote_low_priority_task();
- task_mutex.unlock();
- if (post) {
- task_available_semaphore.post();
- }
+
+ if (singleton->task_queue.first()) {
+ task_to_process = task_queue.first()->self();
+ task_queue.remove(task_queue.first());
+ }
+
+ if (!task_to_process) {
+ caller_pool_thread->awaited_task = task;
+
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->unlock();
+ }
+ caller_pool_thread->cond_var.wait(lock);
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->lock();
}
+
+ DEV_ASSERT(exit_threads || caller_pool_thread->signaled || task->completed);
+ caller_pool_thread->awaited_task = nullptr;
}
- OS::get_singleton()->delay_usec(1); // Microsleep, this could be converted to waiting for multiple objects in supported platforms for a bit more performance.
}
- } else {
- task->done_semaphore.wait();
}
- }
- task_mutex.lock();
- if (is_low_prio_waiting_for_another) {
- low_priority_tasks_awaiting_others--;
+ if (task_to_process) {
+ _process_task(task_to_process);
+ }
}
-
- task->waiting--;
- }
-
- if (task->waiting == 0) {
- if (use_native_low_priority_threads && task->low_priority) {
- task->low_priority_thread->wait_to_finish();
- native_thread_allocator.free(task->low_priority_thread);
+ } 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);
}
- tasks.erase(p_task_id);
- task_allocator.free(task);
+ task_mutex.unlock();
}
- task_mutex.unlock();
return OK;
}
@@ -455,11 +508,8 @@ WorkerThreadPool::GroupID WorkerThreadPool::_add_group_task(const Callable &p_ca
}
groups[id] = group;
- task_mutex.unlock();
- for (int i = 0; i < p_tasks; i++) {
- _post_task(tasks_posted[i], p_high_priority);
- }
+ _post_tasks_and_unlock(tasks_posted, p_tasks, p_high_priority);
return id;
}
@@ -496,28 +546,24 @@ bool WorkerThreadPool::is_group_task_completed(GroupID p_group) const {
}
void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
+#ifdef THREADS_ENABLED
task_mutex.lock();
Group **groupp = groups.getptr(p_group);
task_mutex.unlock();
if (!groupp) {
ERR_FAIL_MSG("Invalid Group ID");
}
- Group *group = *groupp;
- if (group->low_priority_native_tasks.size() > 0) {
- for (Task *task : group->low_priority_native_tasks) {
- task->low_priority_thread->wait_to_finish();
- task_mutex.lock();
- native_thread_allocator.free(task->low_priority_thread);
- task_allocator.free(task);
- task_mutex.unlock();
- }
+ {
+ Group *group = *groupp;
- task_mutex.lock();
- group_allocator.free(group);
- task_mutex.unlock();
- } else {
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->unlock();
+ }
group->done_semaphore.wait();
+ if (flushing_cmd_queue) {
+ flushing_cmd_queue->lock();
+ }
uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later.
@@ -533,21 +579,31 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
task_mutex.lock(); // This mutex is needed when Physics 2D and/or 3D is selected to run on a separate thread.
groups.erase(p_group);
task_mutex.unlock();
+#endif
+}
+
+int WorkerThreadPool::get_thread_index() {
+ Thread::ID tid = Thread::get_caller_id();
+ return singleton->thread_ids.has(tid) ? singleton->thread_ids[tid] : -1;
}
-void WorkerThreadPool::init(int p_thread_count, bool p_use_native_threads_low_priority, float p_low_priority_task_ratio) {
+void WorkerThreadPool::thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue) {
+ ERR_FAIL_COND(flushing_cmd_queue != nullptr);
+ flushing_cmd_queue = p_queue;
+}
+
+void WorkerThreadPool::thread_exit_command_queue_mt_flush() {
+ ERR_FAIL_NULL(flushing_cmd_queue);
+ flushing_cmd_queue = nullptr;
+}
+
+void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio) {
ERR_FAIL_COND(threads.size() > 0);
if (p_thread_count < 0) {
p_thread_count = OS::get_singleton()->get_default_thread_pool_size();
}
- if (p_use_native_threads_low_priority) {
- max_low_priority_threads = 0;
- } else {
- max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1);
- }
-
- use_native_low_priority_threads = p_use_native_threads_low_priority;
+ max_low_priority_threads = CLAMP(p_thread_count * p_low_priority_task_ratio, 1, p_thread_count - 1);
threads.resize(p_thread_count);
@@ -563,24 +619,33 @@ void WorkerThreadPool::finish() {
return;
}
- task_mutex.lock();
- SelfList<Task> *E = low_priority_task_queue.first();
- while (E) {
- print_error("Task waiting was never re-claimed: " + E->self()->description);
- E = E->next();
+ {
+ MutexLock lock(task_mutex);
+ SelfList<Task> *E = low_priority_task_queue.first();
+ while (E) {
+ print_error("Task waiting was never re-claimed: " + E->self()->description);
+ E = E->next();
+ }
}
- task_mutex.unlock();
- exit_threads = true;
-
- for (uint32_t i = 0; i < threads.size(); i++) {
- task_available_semaphore.post();
+ {
+ MutexLock lock(task_mutex);
+ exit_threads = true;
+ }
+ for (ThreadData &data : threads) {
+ data.cond_var.notify_one();
}
-
for (ThreadData &data : threads) {
data.thread.wait_to_finish();
}
+ {
+ MutexLock lock(task_mutex);
+ for (KeyValue<TaskID, Task *> &E : tasks) {
+ task_allocator.free(E.value);
+ }
+ }
+
threads.clear();
}
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index f323a979f7..c9921c808d 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -31,6 +31,7 @@
#ifndef WORKER_THREAD_POOL_H
#define WORKER_THREAD_POOL_H
+#include "core/os/condition_variable.h"
#include "core/os/memory.h"
#include "core/os/os.h"
#include "core/os/semaphore.h"
@@ -40,6 +41,8 @@
#include "core/templates/rid.h"
#include "core/templates/safe_refcount.h"
+class CommandQueueMT;
+
class WorkerThreadPool : public Object {
GDCLASS(WorkerThreadPool, Object)
public:
@@ -60,7 +63,7 @@ private:
};
struct Group {
- GroupID self;
+ GroupID self = -1;
SafeNumeric<uint32_t> index;
SafeNumeric<uint32_t> completed_index;
uint32_t max = 0;
@@ -68,23 +71,23 @@ private:
SafeFlag completed;
SafeNumeric<uint32_t> finished;
uint32_t tasks_used = 0;
- TightLocalVector<Task *> low_priority_native_tasks;
};
struct Task {
+ TaskID self = -1;
Callable callable;
void (*native_func)(void *) = nullptr;
void (*native_group_func)(void *, uint32_t) = nullptr;
void *native_func_userdata = nullptr;
String description;
- Semaphore done_semaphore;
+ Semaphore done_semaphore; // For user threads awaiting.
bool completed = false;
Group *group = nullptr;
SelfList<Task> task_elem;
- uint32_t waiting = 0;
+ uint32_t waiting_pool = 0;
+ uint32_t waiting_user = 0;
bool low_priority = false;
BaseTemplateUserdata *template_userdata = nullptr;
- Thread *low_priority_thread = nullptr;
int pool_thread_index = -1;
void free_template_userdata();
@@ -92,51 +95,65 @@ private:
task_elem(this) {}
};
- PagedAllocator<Task> task_allocator;
- PagedAllocator<Group> group_allocator;
- PagedAllocator<Thread> native_thread_allocator;
+ static const uint32_t TASKS_PAGE_SIZE = 1024;
+ static const uint32_t GROUPS_PAGE_SIZE = 256;
+
+ PagedAllocator<Task, false, TASKS_PAGE_SIZE> task_allocator;
+ PagedAllocator<Group, false, GROUPS_PAGE_SIZE> group_allocator;
SelfList<Task>::List low_priority_task_queue;
SelfList<Task>::List task_queue;
- Mutex task_mutex;
- Semaphore task_available_semaphore;
+ BinaryMutex task_mutex;
struct ThreadData {
- uint32_t index;
+ uint32_t index = 0;
Thread thread;
- Task *current_low_prio_task = nullptr;
bool ready_for_scripting = false;
+ bool signaled = false;
+ Task *current_task = nullptr;
+ Task *awaited_task = nullptr; // Null if not awaiting the condition variable. Special value for idle-waiting.
+ ConditionVariable cond_var;
};
TightLocalVector<ThreadData> threads;
bool exit_threads = false;
HashMap<Thread::ID, int> thread_ids;
- HashMap<TaskID, Task *> tasks;
- HashMap<GroupID, Group *> groups;
+ HashMap<
+ TaskID,
+ Task *,
+ HashMapHasherDefault,
+ HashMapComparatorDefault<TaskID>,
+ PagedAllocator<HashMapElement<TaskID, Task *>, false, TASKS_PAGE_SIZE>>
+ tasks;
+ HashMap<
+ GroupID,
+ Group *,
+ HashMapHasherDefault,
+ HashMapComparatorDefault<GroupID>,
+ PagedAllocator<HashMapElement<GroupID, Group *>, false, GROUPS_PAGE_SIZE>>
+ groups;
- bool use_native_low_priority_threads = false;
uint32_t max_low_priority_threads = 0;
uint32_t low_priority_threads_used = 0;
- uint32_t low_priority_tasks_running = 0;
- uint32_t low_priority_tasks_awaiting_others = 0;
+ uint32_t notify_index = 0; // For rotating across threads, no help distributing load.
uint64_t last_task = 1;
static void _thread_function(void *p_user);
- static void _native_low_priority_thread_function(void *p_user);
- void _process_task_queue();
void _process_task(Task *task);
- void _post_task(Task *p_task, bool p_high_priority);
+ void _post_tasks_and_unlock(Task **p_tasks, uint32_t p_count, bool p_high_priority);
+ void _notify_threads(const ThreadData *p_current_thread_data, uint32_t p_process_count, uint32_t p_promote_count);
bool _try_promote_low_priority_task();
- void _prevent_low_prio_saturation_deadlock();
static WorkerThreadPool *singleton;
+ static thread_local CommandQueueMT *flushing_cmd_queue;
+
TaskID _add_task(const Callable &p_callable, void (*p_func)(void *), void *p_userdata, BaseTemplateUserdata *p_template_userdata, bool p_high_priority, const String &p_description);
GroupID _add_group_task(const Callable &p_callable, void (*p_func)(void *, uint32_t), void *p_userdata, BaseTemplateUserdata *p_template_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description);
@@ -197,7 +214,12 @@ public:
_FORCE_INLINE_ int get_thread_count() const { return threads.size(); }
static WorkerThreadPool *get_singleton() { return singleton; }
- void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3);
+ static int get_thread_index();
+
+ static void thread_enter_command_queue_mt_flush(CommandQueueMT *p_queue);
+ static void thread_exit_command_queue_mt_flush();
+
+ void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3);
void finish();
WorkerThreadPool();
~WorkerThreadPool();
diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h
index 6037ff327d..2b6b272e18 100644
--- a/core/os/condition_variable.h
+++ b/core/os/condition_variable.h
@@ -31,7 +31,18 @@
#ifndef CONDITION_VARIABLE_H
#define CONDITION_VARIABLE_H
+#include "core/os/mutex.h"
+
+#ifdef THREADS_ENABLED
+
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <condition_variable>
+#define THREADING_NAMESPACE std
+#endif
// An object one or multiple threads can wait on a be notified by some other.
// Normally, you want to use a semaphore for such scenarios, but when the
@@ -40,12 +51,12 @@
// own mutex to tie the wait-notify to some other behavior, you need to use this.
class ConditionVariable {
- mutable std::condition_variable condition;
+ mutable THREADING_NAMESPACE::condition_variable condition;
public:
template <class BinaryMutexT>
_ALWAYS_INLINE_ void wait(const MutexLock<BinaryMutexT> &p_lock) const {
- condition.wait(const_cast<std::unique_lock<std::mutex> &>(p_lock.lock));
+ condition.wait(const_cast<THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> &>(p_lock.lock));
}
_ALWAYS_INLINE_ void notify_one() const {
@@ -57,4 +68,16 @@ public:
}
};
+#else // No threads.
+
+class ConditionVariable {
+public:
+ template <class BinaryMutexT>
+ void wait(const MutexLock<BinaryMutexT> &p_lock) const {}
+ void notify_one() const {}
+ void notify_all() const {}
+};
+
+#endif // THREADS_ENABLED
+
#endif // CONDITION_VARIABLE_H
diff --git a/core/os/keyboard.cpp b/core/os/keyboard.cpp
index 6078882839..973c216d15 100644
--- a/core/os/keyboard.cpp
+++ b/core/os/keyboard.cpp
@@ -410,7 +410,7 @@ Key find_keycode(const String &p_codestr) {
return keycode;
}
- String last_part = code_parts[code_parts.size() - 1];
+ const String &last_part = code_parts[code_parts.size() - 1];
const _KeyCodeText *kct = &_keycodes[0];
while (kct->text) {
@@ -422,7 +422,7 @@ Key find_keycode(const String &p_codestr) {
}
for (int part = 0; part < code_parts.size() - 1; part++) {
- String code_part = code_parts[part];
+ const String &code_part = code_parts[part];
if (code_part.nocasecmp_to(find_keycode_name(Key::SHIFT)) == 0) {
keycode |= KeyModifierMask::SHIFT;
} else if (code_part.nocasecmp_to(find_keycode_name(Key::CTRL)) == 0) {
diff --git a/core/os/keyboard.h b/core/os/keyboard.h
index 785972d31d..2051973336 100644
--- a/core/os/keyboard.h
+++ b/core/os/keyboard.h
@@ -260,6 +260,12 @@ enum class KeyModifierMask {
GROUP_SWITCH = (1 << 30)
};
+enum class KeyLocation {
+ UNSPECIFIED,
+ LEFT,
+ RIGHT
+};
+
// To avoid having unnecessary operators, only define the ones that are needed.
constexpr Key operator-(uint32_t a, Key b) {
diff --git a/core/os/memory.cpp b/core/os/memory.cpp
index 5f6216a5f1..32c316e58e 100644
--- a/core/os/memory.cpp
+++ b/core/os/memory.cpp
@@ -72,23 +72,23 @@ void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
bool prepad = p_pad_align;
#endif
- void *mem = malloc(p_bytes + (prepad ? PAD_ALIGN : 0));
+ void *mem = malloc(p_bytes + (prepad ? DATA_OFFSET : 0));
ERR_FAIL_NULL_V(mem, nullptr);
alloc_count.increment();
if (prepad) {
- uint64_t *s = (uint64_t *)mem;
- *s = p_bytes;
-
uint8_t *s8 = (uint8_t *)mem;
+ uint64_t *s = (uint64_t *)(s8 + SIZE_OFFSET);
+ *s = p_bytes;
+
#ifdef DEBUG_ENABLED
uint64_t new_mem_usage = mem_usage.add(p_bytes);
max_usage.exchange_if_greater(new_mem_usage);
#endif
- return s8 + PAD_ALIGN;
+ return s8 + DATA_OFFSET;
} else {
return mem;
}
@@ -108,8 +108,8 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
#endif
if (prepad) {
- mem -= PAD_ALIGN;
- uint64_t *s = (uint64_t *)mem;
+ mem -= DATA_OFFSET;
+ uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
#ifdef DEBUG_ENABLED
if (p_bytes > *s) {
@@ -126,14 +126,14 @@ void *Memory::realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align) {
} else {
*s = p_bytes;
- mem = (uint8_t *)realloc(mem, p_bytes + PAD_ALIGN);
+ mem = (uint8_t *)realloc(mem, p_bytes + DATA_OFFSET);
ERR_FAIL_NULL_V(mem, nullptr);
- s = (uint64_t *)mem;
+ s = (uint64_t *)(mem + SIZE_OFFSET);
*s = p_bytes;
- return mem + PAD_ALIGN;
+ return mem + DATA_OFFSET;
}
} else {
mem = (uint8_t *)realloc(mem, p_bytes);
@@ -158,10 +158,10 @@ void Memory::free_static(void *p_ptr, bool p_pad_align) {
alloc_count.decrement();
if (prepad) {
- mem -= PAD_ALIGN;
+ mem -= DATA_OFFSET;
#ifdef DEBUG_ENABLED
- uint64_t *s = (uint64_t *)mem;
+ uint64_t *s = (uint64_t *)(mem + SIZE_OFFSET);
mem_usage.sub(*s);
#endif
diff --git a/core/os/memory.h b/core/os/memory.h
index a0524b0ea2..6f3f6fed39 100644
--- a/core/os/memory.h
+++ b/core/os/memory.h
@@ -38,10 +38,6 @@
#include <new>
#include <type_traits>
-#ifndef PAD_ALIGN
-#define PAD_ALIGN 16 //must always be greater than this at much
-#endif
-
class Memory {
#ifdef DEBUG_ENABLED
static SafeNumeric<uint64_t> mem_usage;
@@ -51,6 +47,17 @@ class Memory {
static SafeNumeric<uint64_t> alloc_count;
public:
+ // Alignment: ↓ max_align_t ↓ uint64_t ↓ max_align_t
+ // ┌─────────────────┬──┬────────────────┬──┬───────────...
+ // │ uint64_t │░░│ uint64_t │░░│ T[]
+ // │ alloc size │░░│ element count │░░│ data
+ // └─────────────────┴──┴────────────────┴──┴───────────...
+ // Offset: ↑ SIZE_OFFSET ↑ ELEMENT_OFFSET ↑ DATA_OFFSET
+
+ static constexpr size_t SIZE_OFFSET = 0;
+ static constexpr size_t ELEMENT_OFFSET = ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t) == 0) ? (SIZE_OFFSET + sizeof(uint64_t)) : ((SIZE_OFFSET + sizeof(uint64_t)) + alignof(uint64_t) - ((SIZE_OFFSET + sizeof(uint64_t)) % alignof(uint64_t)));
+ static constexpr size_t DATA_OFFSET = ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t) == 0) ? (ELEMENT_OFFSET + sizeof(uint64_t)) : ((ELEMENT_OFFSET + sizeof(uint64_t)) + alignof(max_align_t) - ((ELEMENT_OFFSET + sizeof(uint64_t)) % alignof(max_align_t)));
+
static void *alloc_static(size_t p_bytes, bool p_pad_align = false);
static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
static void free_static(void *p_ptr, bool p_pad_align = false);
@@ -105,7 +112,7 @@ void memdelete(T *p_class) {
if (!predelete_handler(p_class)) {
return; // doesn't want to be deleted
}
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
@@ -117,7 +124,7 @@ void memdelete_allocator(T *p_class) {
if (!predelete_handler(p_class)) {
return; // doesn't want to be deleted
}
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
p_class->~T();
}
@@ -133,6 +140,10 @@ void memdelete_allocator(T *p_class) {
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
+_FORCE_INLINE_ uint64_t *_get_element_count_ptr(uint8_t *p_ptr) {
+ return (uint64_t *)(p_ptr - Memory::DATA_OFFSET + Memory::ELEMENT_OFFSET);
+}
+
template <typename T>
T *memnew_arr_template(size_t p_elements) {
if (p_elements == 0) {
@@ -142,12 +153,14 @@ T *memnew_arr_template(size_t p_elements) {
same strategy used by std::vector, and the Vector class, so it should be safe.*/
size_t len = sizeof(T) * p_elements;
- uint64_t *mem = (uint64_t *)Memory::alloc_static(len, true);
+ uint8_t *mem = (uint8_t *)Memory::alloc_static(len, true);
T *failptr = nullptr; //get rid of a warning
ERR_FAIL_NULL_V(mem, failptr);
- *(mem - 1) = p_elements;
- if (!std::is_trivially_constructible<T>::value) {
+ uint64_t *_elem_count_ptr = _get_element_count_ptr(mem);
+ *(_elem_count_ptr) = p_elements;
+
+ if constexpr (!std::is_trivially_constructible_v<T>) {
T *elems = (T *)mem;
/* call operator new */
@@ -166,16 +179,18 @@ T *memnew_arr_template(size_t p_elements) {
template <typename T>
size_t memarr_len(const T *p_class) {
- uint64_t *ptr = (uint64_t *)p_class;
- return *(ptr - 1);
+ uint8_t *ptr = (uint8_t *)p_class;
+ uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
+ return *(_elem_count_ptr);
}
template <typename T>
void memdelete_arr(T *p_class) {
- uint64_t *ptr = (uint64_t *)p_class;
+ uint8_t *ptr = (uint8_t *)p_class;
- if (!std::is_trivially_destructible<T>::value) {
- uint64_t elem_count = *(ptr - 1);
+ if constexpr (!std::is_trivially_destructible_v<T>) {
+ uint64_t *_elem_count_ptr = _get_element_count_ptr(ptr);
+ uint64_t elem_count = *(_elem_count_ptr);
for (uint64_t i = 0; i < elem_count; i++) {
p_class[i].~T();
diff --git a/core/os/mutex.cpp b/core/os/mutex.cpp
index 7dbb60590b..9a8a2a2961 100644
--- a/core/os/mutex.cpp
+++ b/core/os/mutex.cpp
@@ -40,7 +40,11 @@ void _global_unlock() {
_global_mutex.unlock();
}
-template class MutexImpl<std::recursive_mutex>;
-template class MutexImpl<std::mutex>;
-template class MutexLock<MutexImpl<std::recursive_mutex>>;
-template class MutexLock<MutexImpl<std::mutex>>;
+#ifdef THREADS_ENABLED
+
+template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
+template class MutexImpl<THREADING_NAMESPACE::mutex>;
+template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
+template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
+
+#endif
diff --git a/core/os/mutex.h b/core/os/mutex.h
index cee0f8af74..a4eab0cd86 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -34,7 +34,16 @@
#include "core/error/error_macros.h"
#include "core/typedefs.h"
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.mutex.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <mutex>
+#define THREADING_NAMESPACE std
+#endif
+
+#ifdef THREADS_ENABLED
template <class MutexT>
class MutexLock;
@@ -61,91 +70,45 @@ public:
}
};
-// A very special kind of mutex, used in scenarios where these
-// requirements hold at the same time:
-// - Must be used with a condition variable (only binary mutexes are suitable).
-// - Must have recursive semnantics (or simulate, as this one does).
-// The implementation keeps the lock count in TS. Therefore, only
-// one object of each version of the template can exists; hence the Tag argument.
-// Tags must be unique across the Godot codebase.
-// Also, don't forget to declare the thread_local variable on each use.
-template <int Tag>
-class SafeBinaryMutex {
- friend class MutexLock<SafeBinaryMutex>;
-
- using StdMutexType = std::mutex;
+template <class MutexT>
+class MutexLock {
+ friend class ConditionVariable;
- mutable std::mutex mutex;
- static thread_local uint32_t count;
+ THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
public:
- _ALWAYS_INLINE_ void lock() const {
- if (++count == 1) {
- mutex.lock();
- }
- }
-
- _ALWAYS_INLINE_ void unlock() const {
- DEV_ASSERT(count);
- if (--count == 0) {
- mutex.unlock();
- }
- }
+ explicit MutexLock(const MutexT &p_mutex) :
+ lock(p_mutex.mutex) {}
+};
- _ALWAYS_INLINE_ bool try_lock() const {
- if (count) {
- count++;
- return true;
- } else {
- if (mutex.try_lock()) {
- count++;
- return true;
- } else {
- return false;
- }
- }
- }
+using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
+using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care
- ~SafeBinaryMutex() {
- DEV_ASSERT(!count);
- }
-};
+extern template class MutexImpl<THREADING_NAMESPACE::recursive_mutex>;
+extern template class MutexImpl<THREADING_NAMESPACE::mutex>;
+extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::recursive_mutex>>;
+extern template class MutexLock<MutexImpl<THREADING_NAMESPACE::mutex>>;
-template <class MutexT>
-class MutexLock {
- friend class ConditionVariable;
+#else // No threads.
- std::unique_lock<typename MutexT::StdMutexType> lock;
+class MutexImpl {
+ mutable THREADING_NAMESPACE::mutex mutex;
public:
- _ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) :
- lock(p_mutex.mutex){};
+ void lock() const {}
+ void unlock() const {}
+ bool try_lock() const { return true; }
};
-// This specialization is needed so manual locking and MutexLock can be used
-// at the same time on a SafeBinaryMutex.
-template <int Tag>
-class MutexLock<SafeBinaryMutex<Tag>> {
- friend class ConditionVariable;
-
- std::unique_lock<std::mutex> lock;
-
+template <class MutexT>
+class MutexLock {
public:
- _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
- lock(p_mutex.mutex) {
- SafeBinaryMutex<Tag>::count++;
- };
- _ALWAYS_INLINE_ ~MutexLock() {
- SafeBinaryMutex<Tag>::count--;
- };
+ MutexLock(const MutexT &p_mutex) {}
};
-using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use
-using BinaryMutex = MutexImpl<std::mutex>; // Non-recursive, handle with care
+using Mutex = MutexImpl;
+using BinaryMutex = MutexImpl;
-extern template class MutexImpl<std::recursive_mutex>;
-extern template class MutexImpl<std::mutex>;
-extern template class MutexLock<MutexImpl<std::recursive_mutex>>;
-extern template class MutexLock<MutexImpl<std::mutex>>;
+#endif // THREADS_ENABLED
#endif // MUTEX_H
diff --git a/core/os/os.cpp b/core/os/os.cpp
index c7390f14ff..d5d9988cc1 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -39,7 +39,15 @@
#include "core/version_generated.gen.h"
#include <stdarg.h>
+
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.thread.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <thread>
+#define THREADING_NAMESPACE std
+#endif
OS *OS::singleton = nullptr;
uint64_t OS::target_ticks = 0;
@@ -359,7 +367,7 @@ String OS::get_unique_id() const {
}
int OS::get_processor_count() const {
- return std::thread::hardware_concurrency();
+ return THREADING_NAMESPACE::thread::hardware_concurrency();
}
String OS::get_processor_name() const {
@@ -496,6 +504,12 @@ bool OS::has_feature(const String &p_feature) {
}
#endif
+#ifdef THREADS_ENABLED
+ if (p_feature == "threads") {
+ return true;
+ }
+#endif
+
if (_check_internal_feature_support(p_feature)) {
return true;
}
@@ -618,17 +632,22 @@ String OS::get_benchmark_file() {
return benchmark_file;
}
-void OS::benchmark_begin_measure(const String &p_what) {
+void OS::benchmark_begin_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec();
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what));
+
+ benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec();
#endif
}
-void OS::benchmark_end_measure(const String &p_what) {
+void OS::benchmark_end_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what];
- double total_f = double(total) / double(1000000);
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what));
- startup_benchmark_json[p_what] = total_f;
+ uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key];
+ double total_f = double(total) / double(1000000);
+ benchmark_marks_final[mark_key] = total_f;
#endif
}
@@ -637,19 +656,33 @@ void OS::benchmark_dump() {
if (!use_benchmark) {
return;
}
+
if (!benchmark_file.is_empty()) {
Ref<FileAccess> f = FileAccess::open(benchmark_file, FileAccess::WRITE);
if (f.is_valid()) {
+ Dictionary benchmark_marks;
+ for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
+ const String mark_key = vformat("[%s] %s", E.key.first, E.key.second);
+ benchmark_marks[mark_key] = E.value;
+ }
+
Ref<JSON> json;
json.instantiate();
- f->store_string(json->stringify(startup_benchmark_json, "\t", false, true));
+ f->store_string(json->stringify(benchmark_marks, "\t", false, true));
}
} else {
- List<Variant> keys;
- startup_benchmark_json.get_key_list(&keys);
+ HashMap<String, String> results;
+ for (const KeyValue<Pair<String, String>, double> &E : benchmark_marks_final) {
+ if (E.key.first == "Startup" && !results.has(E.key.first)) {
+ results.insert(E.key.first, "", true); // Hack to make sure "Startup" always comes first.
+ }
+
+ results[E.key.first] += vformat("\t\t- %s: %.3f msec.\n", E.key.second, (E.value * 1000));
+ }
+
print_line("BENCHMARK:");
- for (const Variant &K : keys) {
- print_line("\t-", K, ": ", startup_benchmark_json[K], +" sec.");
+ for (const KeyValue<String, String> &E : results) {
+ print_line(vformat("\t[%s]\n%s", E.key, E.value));
}
}
#endif
diff --git a/core/os/os.h b/core/os/os.h
index cc5ebe1bc8..e22514dce3 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -79,8 +79,8 @@ class OS {
// For tracking benchmark data
bool use_benchmark = false;
String benchmark_file;
- HashMap<String, uint64_t> start_benchmark_from;
- Dictionary startup_benchmark_json;
+ HashMap<Pair<String, String>, uint64_t, PairHash<String, String>> benchmark_marks_from;
+ HashMap<Pair<String, String>, double, PairHash<String, String>> benchmark_marks_final;
protected:
void _set_logger(CompositeLogger *p_logger);
@@ -313,8 +313,8 @@ public:
bool is_use_benchmark_set();
void set_benchmark_file(const String &p_benchmark_file);
String get_benchmark_file();
- virtual void benchmark_begin_measure(const String &p_what);
- virtual void benchmark_end_measure(const String &p_what);
+ virtual void benchmark_begin_measure(const String &p_context, const String &p_what);
+ virtual void benchmark_end_measure(const String &p_context, const String &p_what);
virtual void benchmark_dump();
virtual void process_and_drop_events() {}
diff --git a/core/os/rw_lock.h b/core/os/rw_lock.h
index a232fcb1ce..e5bb6aec2b 100644
--- a/core/os/rw_lock.h
+++ b/core/os/rw_lock.h
@@ -33,10 +33,17 @@
#include "core/typedefs.h"
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.shared_mutex.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <shared_mutex>
+#define THREADING_NAMESPACE std
+#endif
class RWLock {
- mutable std::shared_timed_mutex mutex;
+ mutable THREADING_NAMESPACE::shared_timed_mutex mutex;
public:
// Lock the RWLock, block if locked by someone else.
diff --git a/core/os/safe_binary_mutex.h b/core/os/safe_binary_mutex.h
new file mode 100644
index 0000000000..1e98cc074c
--- /dev/null
+++ b/core/os/safe_binary_mutex.h
@@ -0,0 +1,124 @@
+/**************************************************************************/
+/* safe_binary_mutex.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef SAFE_BINARY_MUTEX_H
+#define SAFE_BINARY_MUTEX_H
+
+#include "core/error/error_macros.h"
+#include "core/os/mutex.h"
+#include "core/typedefs.h"
+
+#ifdef THREADS_ENABLED
+
+// A very special kind of mutex, used in scenarios where these
+// requirements hold at the same time:
+// - Must be used with a condition variable (only binary mutexes are suitable).
+// - Must have recursive semnantics (or simulate, as this one does).
+// The implementation keeps the lock count in TS. Therefore, only
+// one object of each version of the template can exists; hence the Tag argument.
+// Tags must be unique across the Godot codebase.
+// Also, don't forget to declare the thread_local variable on each use.
+template <int Tag>
+class SafeBinaryMutex {
+ friend class MutexLock<SafeBinaryMutex>;
+
+ using StdMutexType = THREADING_NAMESPACE::mutex;
+
+ mutable THREADING_NAMESPACE::mutex mutex;
+ static thread_local uint32_t count;
+
+public:
+ _ALWAYS_INLINE_ void lock() const {
+ if (++count == 1) {
+ mutex.lock();
+ }
+ }
+
+ _ALWAYS_INLINE_ void unlock() const {
+ DEV_ASSERT(count);
+ if (--count == 0) {
+ mutex.unlock();
+ }
+ }
+
+ _ALWAYS_INLINE_ bool try_lock() const {
+ if (count) {
+ count++;
+ return true;
+ } else {
+ if (mutex.try_lock()) {
+ count++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ ~SafeBinaryMutex() {
+ DEV_ASSERT(!count);
+ }
+};
+
+// This specialization is needed so manual locking and MutexLock can be used
+// at the same time on a SafeBinaryMutex.
+template <int Tag>
+class MutexLock<SafeBinaryMutex<Tag>> {
+ friend class ConditionVariable;
+
+ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
+
+public:
+ _ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
+ lock(p_mutex.mutex) {
+ SafeBinaryMutex<Tag>::count++;
+ };
+ _ALWAYS_INLINE_ ~MutexLock() {
+ SafeBinaryMutex<Tag>::count--;
+ };
+};
+
+#else // No threads.
+
+template <int Tag>
+class SafeBinaryMutex : public MutexImpl {
+ static thread_local uint32_t count;
+};
+
+template <int Tag>
+class MutexLock<SafeBinaryMutex<Tag>> {
+public:
+ MutexLock(const SafeBinaryMutex<Tag> &p_mutex) {}
+ ~MutexLock() {}
+};
+
+#endif // THREADS_ENABLED
+
+#endif // SAFE_BINARY_MUTEX_H
diff --git a/core/os/semaphore.h b/core/os/semaphore.h
index 66dfb3ee02..19ef1dedc0 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -31,33 +31,47 @@
#ifndef SEMAPHORE_H
#define SEMAPHORE_H
+#include <cstdint>
+
+#ifdef THREADS_ENABLED
+
#include "core/error/error_list.h"
#include "core/typedefs.h"
#ifdef DEBUG_ENABLED
#include "core/error/error_macros.h"
#endif
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.condition_variable.h"
+#include "thirdparty/mingw-std-threads/mingw.mutex.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <condition_variable>
#include <mutex>
+#define THREADING_NAMESPACE std
+#endif
class Semaphore {
private:
- mutable std::mutex mutex;
- mutable std::condition_variable condition;
+ mutable THREADING_NAMESPACE::mutex mutex;
+ mutable THREADING_NAMESPACE::condition_variable condition;
mutable uint32_t count = 0; // Initialized as locked.
#ifdef DEBUG_ENABLED
mutable uint32_t awaiters = 0;
#endif
public:
- _ALWAYS_INLINE_ void post() const {
+ _ALWAYS_INLINE_ void post(uint32_t p_count = 1) const {
std::lock_guard lock(mutex);
- count++;
- condition.notify_one();
+ count += p_count;
+ for (uint32_t i = 0; i < p_count; ++i) {
+ condition.notify_one();
+ }
}
_ALWAYS_INLINE_ void wait() const {
- std::unique_lock lock(mutex);
+ THREADING_NAMESPACE::unique_lock lock(mutex);
#ifdef DEBUG_ENABLED
++awaiters;
#endif
@@ -116,10 +130,23 @@ public:
"A Semaphore object is being destroyed while one or more threads are still waiting on it.\n"
"Please call post() on it as necessary to prevent such a situation and so ensure correct cleanup.");
// And now, the hacky countermeasure (i.e., leak the condition variable).
- new (&condition) std::condition_variable();
+ new (&condition) THREADING_NAMESPACE::condition_variable();
}
}
#endif
};
+#else // No threads.
+
+class Semaphore {
+public:
+ void post(uint32_t p_count = 1) const {}
+ void wait() const {}
+ bool try_wait() const {
+ return true;
+ }
+};
+
+#endif // THREADS_ENABLED
+
#endif // SEMAPHORE_H
diff --git a/core/os/spin_lock.h b/core/os/spin_lock.h
index 93ea782b60..d386cd5890 100644
--- a/core/os/spin_lock.h
+++ b/core/os/spin_lock.h
@@ -33,6 +33,25 @@
#include "core/typedefs.h"
+#if defined(__APPLE__)
+
+#include <os/lock.h>
+
+class SpinLock {
+ mutable os_unfair_lock _lock = OS_UNFAIR_LOCK_INIT;
+
+public:
+ _ALWAYS_INLINE_ void lock() const {
+ os_unfair_lock_lock(&_lock);
+ }
+
+ _ALWAYS_INLINE_ void unlock() const {
+ os_unfair_lock_unlock(&_lock);
+ }
+};
+
+#else
+
#include <atomic>
class SpinLock {
@@ -49,4 +68,6 @@ public:
}
};
+#endif // __APPLE__
+
#endif // SPIN_LOCK_H
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 03e2c5409d..afc74364f6 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -33,19 +33,22 @@
#include "thread.h"
+#ifdef THREADS_ENABLED
#include "core/object/script_language.h"
#include "core/templates/safe_refcount.h"
-Thread::PlatformFunctions Thread::platform_functions;
-
SafeNumeric<uint64_t> Thread::id_counter(1); // The first value after .increment() is 2, hence by default the main thread ID should be 1.
thread_local Thread::ID Thread::caller_id = Thread::UNASSIGNED_ID;
+#endif
+
+Thread::PlatformFunctions Thread::platform_functions;
void Thread::_set_platform_functions(const PlatformFunctions &p_functions) {
platform_functions = p_functions;
}
+#ifdef THREADS_ENABLED
void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_callback, void *p_userdata) {
Thread::caller_id = p_caller_id;
if (platform_functions.set_priority) {
@@ -69,8 +72,7 @@ void Thread::callback(ID p_caller_id, const Settings &p_settings, Callback p_cal
Thread::ID Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
ERR_FAIL_COND_V_MSG(id != UNASSIGNED_ID, UNASSIGNED_ID, "A Thread object has been re-started without wait_to_finish() having been called on it.");
id = id_counter.increment();
- std::thread new_thread(&Thread::callback, id, p_settings, p_callback, p_user);
- thread.swap(new_thread);
+ thread = THREADING_NAMESPACE::thread(&Thread::callback, id, p_settings, p_callback, p_user);
return id;
}
@@ -82,8 +84,7 @@ void Thread::wait_to_finish() {
ERR_FAIL_COND_MSG(id == UNASSIGNED_ID, "Attempt of waiting to finish on a thread that was never started.");
ERR_FAIL_COND_MSG(id == get_caller_id(), "Threads can't wait to finish on themselves, another thread must wait.");
thread.join();
- std::thread empty_thread;
- thread.swap(empty_thread);
+ thread = THREADING_NAMESPACE::thread();
id = UNASSIGNED_ID;
}
@@ -109,4 +110,6 @@ Thread::~Thread() {
}
}
+#endif // THREADS_ENABLED
+
#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/os/thread.h b/core/os/thread.h
index 3e307adfff..a0ecc24c91 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -42,10 +42,19 @@
#include "core/templates/safe_refcount.h"
#include "core/typedefs.h"
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.thread.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <thread>
+#define THREADING_NAMESPACE std
+#endif
class String;
+#ifdef THREADS_ENABLED
+
class Thread {
public:
typedef void (*Callback)(void *p_userdata);
@@ -79,15 +88,15 @@ public:
private:
friend class Main;
+ static PlatformFunctions platform_functions;
+
ID id = UNASSIGNED_ID;
static SafeNumeric<uint64_t> id_counter;
static thread_local ID caller_id;
- std::thread thread;
+ THREADING_NAMESPACE::thread thread;
static void callback(ID p_caller_id, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
- static PlatformFunctions platform_functions;
-
static void make_main_thread() { caller_id = MAIN_ID; }
static void release_main_thread() { caller_id = UNASSIGNED_ID; }
@@ -118,6 +127,64 @@ public:
~Thread();
};
+#else // No threads.
+
+class Thread {
+public:
+ typedef void (*Callback)(void *p_userdata);
+
+ typedef uint64_t ID;
+
+ enum : ID {
+ UNASSIGNED_ID = 0,
+ MAIN_ID = 1
+ };
+
+ enum Priority {
+ PRIORITY_LOW,
+ PRIORITY_NORMAL,
+ PRIORITY_HIGH
+ };
+
+ struct Settings {
+ Priority priority;
+ Settings() { priority = PRIORITY_NORMAL; }
+ };
+
+ struct PlatformFunctions {
+ Error (*set_name)(const String &) = nullptr;
+ void (*set_priority)(Thread::Priority) = nullptr;
+ void (*init)() = nullptr;
+ void (*wrapper)(Thread::Callback, void *) = nullptr;
+ void (*term)() = nullptr;
+ };
+
+private:
+ friend class Main;
+
+ static PlatformFunctions platform_functions;
+
+ static void make_main_thread() {}
+ static void release_main_thread() {}
+
+public:
+ static void _set_platform_functions(const PlatformFunctions &p_functions);
+
+ _FORCE_INLINE_ ID get_id() const { return 0; }
+ _FORCE_INLINE_ static ID get_caller_id() { return MAIN_ID; }
+ _FORCE_INLINE_ static ID get_main_id() { return MAIN_ID; }
+
+ _FORCE_INLINE_ static bool is_main_thread() { return true; }
+
+ static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }
+
+ void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {}
+ bool is_started() const { return false; }
+ void wait_to_finish() {}
+};
+
+#endif // THREADS_ENABLED
+
#endif // THREAD_H
#endif // PLATFORM_THREAD_OVERRIDE
diff --git a/core/os/time.cpp b/core/os/time.cpp
index bad5cc2e4f..7068935d36 100644
--- a/core/os/time.cpp
+++ b/core/os/time.cpp
@@ -189,9 +189,6 @@ static const uint8_t MONTH_DAYS_TABLE[2][12] = {
Time *Time::singleton = nullptr;
Time *Time::get_singleton() {
- if (!singleton) {
- memnew(Time);
- }
return singleton;
}
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 4ad9dd43c4..4c1ed8a69a 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -102,6 +102,7 @@ static core_bind::Marshalls *_marshalls = nullptr;
static core_bind::EngineDebugger *_engine_debugger = nullptr;
static IP *ip = nullptr;
+static Time *_time = nullptr;
static core_bind::Geometry2D *_geometry_2d = nullptr;
static core_bind::Geometry3D *_geometry_3d = nullptr;
@@ -120,13 +121,15 @@ static ResourceUID *resource_uid = nullptr;
static bool _is_core_extensions_registered = false;
void register_core_types() {
- OS::get_singleton()->benchmark_begin_measure("register_core_types");
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Types");
+
//consistency check
static_assert(sizeof(Callable) <= 16);
ObjectDB::setup();
StringName::setup();
+ _time = memnew(Time);
ResourceLoader::initialize();
register_global_constants();
@@ -296,7 +299,7 @@ void register_core_types() {
worker_thread_pool = memnew(WorkerThreadPool);
- OS::get_singleton()->benchmark_end_measure("register_core_types");
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Types");
}
void register_core_settings() {
@@ -306,11 +309,12 @@ void register_core_settings() {
GLOBAL_DEF(PropertyInfo(Variant::STRING, "network/tls/certificate_bundle_override", PROPERTY_HINT_FILE, "*.crt"), "");
GLOBAL_DEF("threading/worker_pool/max_threads", -1);
- GLOBAL_DEF("threading/worker_pool/use_system_threads_for_low_priority_tasks", true);
GLOBAL_DEF("threading/worker_pool/low_priority_thread_ratio", 0.3);
}
void register_core_singletons() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Singletons");
+
GDREGISTER_CLASS(ProjectSettings);
GDREGISTER_ABSTRACT_CLASS(IP);
GDREGISTER_CLASS(core_bind::Geometry2D);
@@ -346,25 +350,35 @@ void register_core_singletons() {
Engine::get_singleton()->add_singleton(Engine::Singleton("GDExtensionManager", GDExtensionManager::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("ResourceUID", ResourceUID::get_singleton()));
Engine::get_singleton()->add_singleton(Engine::Singleton("WorkerThreadPool", worker_thread_pool));
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Singletons");
}
void register_core_extensions() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Register Extensions");
+
// Hardcoded for now.
GDExtension::initialize_gdextensions();
gdextension_manager->load_extensions();
gdextension_manager->initialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE);
_is_core_extensions_registered = true;
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Register Extensions");
}
void unregister_core_extensions() {
+ OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Extensions");
+
if (_is_core_extensions_registered) {
gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE);
}
GDExtension::finalize_gdextensions();
+
+ OS::get_singleton()->benchmark_end_measure("Core", "Unregister Extensions");
}
void unregister_core_types() {
- OS::get_singleton()->benchmark_begin_measure("unregister_core_types");
+ OS::get_singleton()->benchmark_begin_measure("Core", "Unregister Types");
// Destroy singletons in reverse order to ensure dependencies are not broken.
@@ -424,6 +438,7 @@ void unregister_core_types() {
ResourceLoader::finalize();
ClassDB::cleanup_defaults();
+ memdelete(_time);
ObjectDB::cleanup();
Variant::unregister_types();
@@ -435,5 +450,5 @@ void unregister_core_types() {
CoreStringNames::free();
StringName::cleanup();
- OS::get_singleton()->benchmark_end_measure("unregister_core_types");
+ OS::get_singleton()->benchmark_end_measure("Core", "Unregister Types");
}
diff --git a/core/string/char_range.inc b/core/string/char_range.inc
index be5516e243..5dffe4f20d 100644
--- a/core/string/char_range.inc
+++ b/core/string/char_range.inc
@@ -38,7 +38,7 @@ struct CharRange {
char32_t end;
};
-static CharRange xid_start[] = {
+inline constexpr CharRange xid_start[] = {
{ 0x41, 0x5a },
{ 0x5f, 0x5f },
{ 0x61, 0x7a },
@@ -692,7 +692,7 @@ static CharRange xid_start[] = {
{ 0x0, 0x0 },
};
-static CharRange xid_continue[] = {
+inline constexpr CharRange xid_continue[] = {
{ 0x30, 0x39 },
{ 0x41, 0x5a },
{ 0x5f, 0x5f },
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index a443ed308d..829c9bf777 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -518,8 +518,12 @@ String TranslationServer::get_country_name(const String &p_country) const {
}
void TranslationServer::set_locale(const String &p_locale) {
- locale = standardize_locale(p_locale);
+ String new_locale = standardize_locale(p_locale);
+ if (locale == new_locale) {
+ return;
+ }
+ locale = new_locale;
ResourceLoader::reload_translation_remaps();
if (OS::get_singleton()->get_main_loop()) {
@@ -877,7 +881,7 @@ StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) c
String TranslationServer::get_override_string(String &p_message) const {
String res;
- for (int i = 0; i < p_message.size(); i++) {
+ for (int i = 0; i < p_message.length(); i++) {
if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
res += p_message[i];
res += p_message[i + 1];
@@ -891,7 +895,7 @@ String TranslationServer::get_override_string(String &p_message) const {
String TranslationServer::double_vowels(String &p_message) const {
String res;
- for (int i = 0; i < p_message.size(); i++) {
+ for (int i = 0; i < p_message.length(); i++) {
if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
res += p_message[i];
res += p_message[i + 1];
@@ -909,7 +913,7 @@ String TranslationServer::double_vowels(String &p_message) const {
String TranslationServer::replace_with_accented_string(String &p_message) const {
String res;
- for (int i = 0; i < p_message.size(); i++) {
+ for (int i = 0; i < p_message.length(); i++) {
if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
res += p_message[i];
res += p_message[i + 1];
@@ -932,7 +936,7 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const
char32_t fakebidisuffix = U'\u202c';
res += fakebidiprefix;
// The fake bidi unicode gets popped at every newline so pushing it back at every newline.
- for (int i = 0; i < p_message.size(); i++) {
+ for (int i = 0; i < p_message.length(); i++) {
if (p_message[i] == '\n') {
res += fakebidisuffix;
res += p_message[i];
@@ -974,7 +978,7 @@ const char32_t *TranslationServer::get_accented_version(char32_t p_character) co
}
bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
- return p_index < p_message.size() - 1 && p_message[p_index] == '%' &&
+ return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
(p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
}
diff --git a/core/string/translation_po.cpp b/core/string/translation_po.cpp
index 6b1595174a..06fd4717d7 100644
--- a/core/string/translation_po.cpp
+++ b/core/string/translation_po.cpp
@@ -41,8 +41,8 @@ void TranslationPO::print_translation_map() {
return;
}
- file->store_line("NPlural : " + String::num_int64(this->get_plural_forms()));
- file->store_line("Plural rule : " + this->get_plural_rule());
+ file->store_line("NPlural : " + String::num_int64(get_plural_forms()));
+ file->store_line("Plural rule : " + get_plural_rule());
file->store_line("");
List<StringName> context_l;
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 60e2d539f8..d094184c4b 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -302,10 +302,14 @@ void String::copy_from(const char *p_cstr) {
resize(len + 1); // include 0
- char32_t *dst = this->ptrw();
+ char32_t *dst = ptrw();
for (size_t i = 0; i <= len; i++) {
+#if CHAR_MIN == 0
+ uint8_t c = p_cstr[i];
+#else
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
+#endif
if (c == 0 && i < len) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@@ -335,10 +339,14 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) {
resize(len + 1); // include 0
- char32_t *dst = this->ptrw();
+ char32_t *dst = ptrw();
for (int i = 0; i < len; i++) {
+#if CHAR_MIN == 0
+ uint8_t c = p_cstr[i];
+#else
uint8_t c = p_cstr[i] >= 0 ? p_cstr[i] : uint8_t(256 + p_cstr[i]);
+#endif
if (c == 0) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@@ -544,7 +552,11 @@ String &String::operator+=(const char *p_str) {
char32_t *dst = ptrw() + lhs_len;
for (size_t i = 0; i <= rhs_len; i++) {
+#if CHAR_MIN == 0
+ uint8_t c = p_str[i];
+#else
uint8_t c = p_str[i] >= 0 ? p_str[i] : uint8_t(256 + p_str[i]);
+#endif
if (c == 0 && i < rhs_len) {
print_unicode_error("NUL character", true);
dst[i] = _replacement_char;
@@ -1031,7 +1043,7 @@ String String::_camelcase_to_underscore() const {
String new_string;
int start_index = 0;
- for (int i = 1; i < this->size(); i++) {
+ for (int i = 1; i < size(); i++) {
bool is_prev_upper = is_ascii_upper_case(cstr[i - 1]);
bool is_prev_lower = is_ascii_lower_case(cstr[i - 1]);
bool is_prev_digit = is_digit(cstr[i - 1]);
@@ -1041,7 +1053,7 @@ String String::_camelcase_to_underscore() const {
bool is_curr_digit = is_digit(cstr[i]);
bool is_next_lower = false;
- if (i + 1 < this->size()) {
+ if (i + 1 < size()) {
is_next_lower = is_ascii_lower_case(cstr[i + 1]);
}
@@ -1051,17 +1063,17 @@ String String::_camelcase_to_underscore() const {
const bool cond_d = (is_prev_upper || is_prev_lower) && is_curr_digit; // A2, a2
if (cond_a || cond_b || cond_c || cond_d) {
- new_string += this->substr(start_index, i - start_index) + "_";
+ new_string += substr(start_index, i - start_index) + "_";
start_index = i;
}
}
- new_string += this->substr(start_index, this->size() - start_index);
+ new_string += substr(start_index, size() - start_index);
return new_string.to_lower();
}
String String::capitalize() const {
- String aux = this->_camelcase_to_underscore().replace("_", " ").strip_edges();
+ String aux = _camelcase_to_underscore().replace("_", " ").strip_edges();
String cap;
for (int i = 0; i < aux.get_slice_count(" "); i++) {
String slice = aux.get_slicec(' ', i);
@@ -1078,7 +1090,7 @@ String String::capitalize() const {
}
String String::to_camel_case() const {
- String s = this->to_pascal_case();
+ String s = to_pascal_case();
if (!s.is_empty()) {
s[0] = _find_lower(s[0]);
}
@@ -1086,11 +1098,11 @@ String String::to_camel_case() const {
}
String String::to_pascal_case() const {
- return this->capitalize().replace(" ", "");
+ return capitalize().replace(" ", "");
}
String String::to_snake_case() const {
- return this->_camelcase_to_underscore().replace(" ", "_").strip_edges();
+ return _camelcase_to_underscore().replace(" ", "_").strip_edges();
}
String String::get_with_code_lines() const {
@@ -1105,7 +1117,7 @@ String String::get_with_code_lines() const {
return ret;
}
-int String::get_slice_count(String p_splitter) const {
+int String::get_slice_count(const String &p_splitter) const {
if (is_empty()) {
return 0;
}
@@ -1124,7 +1136,7 @@ int String::get_slice_count(String p_splitter) const {
return slices;
}
-String String::get_slice(String p_splitter, int p_slice) const {
+String String::get_slice(const String &p_splitter, int p_slice) const {
if (is_empty() || p_splitter.is_empty()) {
return "";
}
@@ -1173,7 +1185,7 @@ String String::get_slicec(char32_t p_splitter, int p_slice) const {
return String();
}
- const char32_t *c = this->ptr();
+ const char32_t *c = ptr();
int i = 0;
int prev = 0;
int count = 0;
@@ -1814,7 +1826,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
int skip = 0;
uint8_t c_start = 0;
while (ptrtmp != ptrtmp_limit && *ptrtmp) {
+#if CHAR_MIN == 0
+ uint8_t c = *ptrtmp;
+#else
uint8_t c = *ptrtmp >= 0 ? *ptrtmp : uint8_t(256 + *ptrtmp);
+#endif
if (skip == 0) {
if (p_skip_cr && c == '\r') {
@@ -1882,7 +1898,11 @@ Error String::parse_utf8(const char *p_utf8, int p_len, bool p_skip_cr) {
int skip = 0;
uint32_t unichar = 0;
while (cstr_size) {
+#if CHAR_MIN == 0
+ uint8_t c = *p_utf8;
+#else
uint8_t c = *p_utf8 >= 0 ? *p_utf8 : uint8_t(256 + *p_utf8);
+#endif
if (skip == 0) {
if (p_skip_cr && c == '\r') {
@@ -3495,8 +3515,8 @@ bool String::matchn(const String &p_wildcard) const {
return _wildcard_match(p_wildcard.get_data(), get_data(), false);
}
-String String::format(const Variant &values, String placeholder) const {
- String new_string = String(this->ptr());
+String String::format(const Variant &values, const String &placeholder) const {
+ String new_string = String(ptr());
if (values.get_type() == Variant::ARRAY) {
Array values_arr = values;
@@ -3941,27 +3961,42 @@ static int _humanize_digits(int p_num) {
}
String String::humanize_size(uint64_t p_size) {
+ int magnitude = 0;
uint64_t _div = 1;
- Vector<String> prefixes;
- prefixes.push_back(RTR("B"));
- prefixes.push_back(RTR("KiB"));
- prefixes.push_back(RTR("MiB"));
- prefixes.push_back(RTR("GiB"));
- prefixes.push_back(RTR("TiB"));
- prefixes.push_back(RTR("PiB"));
- prefixes.push_back(RTR("EiB"));
-
- int prefix_idx = 0;
-
- while (prefix_idx < prefixes.size() - 1 && p_size > (_div * 1024)) {
+ while (p_size > _div * 1024 && magnitude < 6) {
_div *= 1024;
- prefix_idx++;
+ magnitude++;
}
- const int digits = prefix_idx > 0 ? _humanize_digits(p_size / _div) : 0;
- const double divisor = prefix_idx > 0 ? _div : 1;
+ if (magnitude == 0) {
+ return String::num(p_size) + " " + RTR("B");
+ } else {
+ String suffix;
+ switch (magnitude) {
+ case 1:
+ suffix = RTR("KiB");
+ break;
+ case 2:
+ suffix = RTR("MiB");
+ break;
+ case 3:
+ suffix = RTR("GiB");
+ break;
+ case 4:
+ suffix = RTR("TiB");
+ break;
+ case 5:
+ suffix = RTR("PiB");
+ break;
+ case 6:
+ suffix = RTR("EiB");
+ break;
+ }
- return String::num(p_size / divisor).pad_decimals(digits) + " " + prefixes[prefix_idx];
+ const double divisor = _div;
+ const int digits = _humanize_digits(p_size / _div);
+ return String::num(p_size / divisor).pad_decimals(digits) + " " + suffix;
+ }
}
bool String::is_absolute_path() const {
@@ -4432,7 +4467,7 @@ bool String::is_valid_float() const {
String String::path_to_file(const String &p_path) const {
// Don't get base dir for src, this is expected to be a dir already.
- String src = this->replace("\\", "/");
+ String src = replace("\\", "/");
String dst = p_path.replace("\\", "/").get_base_dir();
String rel = src.path_to(dst);
if (rel == dst) { // failed
@@ -4443,7 +4478,7 @@ String String::path_to_file(const String &p_path) const {
}
String String::path_to(const String &p_path) const {
- String src = this->replace("\\", "/");
+ String src = replace("\\", "/");
String dst = p_path.replace("\\", "/");
if (!src.ends_with("/")) {
src += "/";
@@ -4549,7 +4584,7 @@ bool String::is_valid_ip_address() const {
if (find(":") >= 0) {
Vector<String> ip = split(":");
for (int i = 0; i < ip.size(); i++) {
- String n = ip[i];
+ const String &n = ip[i];
if (n.is_empty()) {
continue;
}
@@ -4571,7 +4606,7 @@ bool String::is_valid_ip_address() const {
return false;
}
for (int i = 0; i < ip.size(); i++) {
- String n = ip[i];
+ const String &n = ip[i];
if (!n.is_valid_int()) {
return false;
}
@@ -4822,6 +4857,7 @@ String String::sprintf(const Array &values, bool *error) const {
bool pad_with_zeros = false;
bool left_justified = false;
bool show_sign = false;
+ bool as_unsigned = false;
if (error) {
*error = true;
@@ -4862,16 +4898,27 @@ String String::sprintf(const Array &values, bool *error) const {
case 'x':
break;
case 'X':
- base = 16;
capitalize = true;
break;
}
// Get basic number.
- String str = String::num_int64(ABS(value), base, capitalize);
+ String str;
+ if (!as_unsigned) {
+ str = String::num_int64(ABS(value), base, capitalize);
+ } else {
+ uint64_t uvalue = *((uint64_t *)&value);
+ // In unsigned hex, if the value fits in 32 bits, trim it down to that.
+ if (base == 16 && value < 0 && value >= INT32_MIN) {
+ uvalue &= 0xffffffff;
+ }
+ str = String::num_uint64(uvalue, base, capitalize);
+ }
int number_len = str.length();
+ bool negative = value < 0 && !as_unsigned;
+
// Padding.
- int pad_chars_count = (value < 0 || show_sign) ? min_chars - 1 : min_chars;
+ int pad_chars_count = (negative || show_sign) ? min_chars - 1 : min_chars;
String pad_char = pad_with_zeros ? String("0") : String(" ");
if (left_justified) {
str = str.rpad(pad_chars_count, pad_char);
@@ -4880,8 +4927,8 @@ String String::sprintf(const Array &values, bool *error) const {
}
// Sign.
- if (show_sign || value < 0) {
- String sign_char = value < 0 ? "-" : "+";
+ if (show_sign || negative) {
+ String sign_char = negative ? "-" : "+";
if (left_justified) {
str = str.insert(0, sign_char);
} else {
@@ -5074,6 +5121,10 @@ String String::sprintf(const Array &values, bool *error) const {
show_sign = true;
break;
}
+ case 'u': { // Treat as unsigned (for int/hex).
+ as_unsigned = true;
+ break;
+ }
case '0':
case '1':
case '2':
@@ -5172,7 +5223,7 @@ String String::sprintf(const Array &values, bool *error) const {
return formatted;
}
-String String::quote(String quotechar) const {
+String String::quote(const String &quotechar) const {
return quotechar + *this + quotechar;
}
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 897b06fc6d..5ed20396d6 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -299,7 +299,7 @@ public:
bool is_quoted() const;
Vector<String> bigrams() const;
float similarity(const String &p_string) const;
- String format(const Variant &values, String placeholder = "{_}") const;
+ String format(const Variant &values, const String &placeholder = "{_}") const;
String replace_first(const String &p_key, const String &p_with) const;
String replace(const String &p_key, const String &p_with) const;
String replace(const char *p_key, const char *p_with) const;
@@ -315,7 +315,7 @@ public:
String lpad(int min_length, const String &character = " ") const;
String rpad(int min_length, const String &character = " ") const;
String sprintf(const Array &values, bool *error) const;
- String quote(String quotechar = "\"") const;
+ String quote(const String &quotechar = "\"") const;
String unquote() const;
static String num(double p_num, int p_decimals = -1);
static String num_scientific(double p_num);
@@ -349,8 +349,8 @@ public:
String to_snake_case() const;
String get_with_code_lines() const;
- int get_slice_count(String p_splitter) const;
- String get_slice(String p_splitter, int p_slice) const;
+ int get_slice_count(const String &p_splitter) const;
+ String get_slice(const String &p_splitter, int p_slice) const;
String get_slicec(char32_t p_splitter, int p_slice) const;
Vector<String> split(const String &p_splitter = "", bool p_allow_empty = true, int p_maxsplit = 0) const;
diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h
index 7e480653ac..b1010f7f43 100644
--- a/core/templates/command_queue_mt.h
+++ b/core/templates/command_queue_mt.h
@@ -31,6 +31,7 @@
#ifndef COMMAND_QUEUE_MT_H
#define COMMAND_QUEUE_MT_H
+#include "core/object/worker_thread_pool.h"
#include "core/os/memory.h"
#include "core/os/mutex.h"
#include "core/os/semaphore.h"
@@ -306,15 +307,15 @@ class CommandQueueMT {
struct CommandBase {
virtual void call() = 0;
- virtual void post() {}
- virtual ~CommandBase() {}
+ virtual SyncSemaphore *get_sync_semaphore() { return nullptr; }
+ virtual ~CommandBase() = default; // Won't be called.
};
struct SyncCommand : public CommandBase {
SyncSemaphore *sync_sem = nullptr;
- virtual void post() override {
- sync_sem->sem.post();
+ virtual SyncSemaphore *get_sync_semaphore() override {
+ return sync_sem;
}
};
@@ -340,6 +341,7 @@ class CommandQueueMT {
SyncSemaphore sync_sems[SYNC_SEMAPHORES];
Mutex mutex;
Semaphore *sync = nullptr;
+ uint64_t flush_read_ptr = 0;
template <class T>
T *allocate() {
@@ -362,31 +364,41 @@ class CommandQueueMT {
void _flush() {
lock();
- uint64_t read_ptr = 0;
- uint64_t limit = command_mem.size();
-
- while (read_ptr < limit) {
- uint64_t size = *(uint64_t *)&command_mem[read_ptr];
- read_ptr += 8;
- CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[read_ptr]);
-
- cmd->call(); //execute the function
- cmd->post(); //release in case it needs sync/ret
- cmd->~CommandBase(); //should be done, so erase the command
-
- read_ptr += size;
+ WorkerThreadPool::thread_enter_command_queue_mt_flush(this);
+ while (flush_read_ptr < command_mem.size()) {
+ uint64_t size = *(uint64_t *)&command_mem[flush_read_ptr];
+ flush_read_ptr += 8;
+ CommandBase *cmd = reinterpret_cast<CommandBase *>(&command_mem[flush_read_ptr]);
+
+ SyncSemaphore *sync_sem = cmd->get_sync_semaphore();
+ cmd->call();
+ if (sync_sem) {
+ 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();
command_mem.clear();
+ flush_read_ptr = 0;
unlock();
}
- void lock();
- void unlock();
void wait_for_flush();
SyncSemaphore *_alloc_sync_sem();
public:
+ void lock();
+ void unlock();
+
/* NORMAL PUSH COMMANDS */
DECL_PUSH(0)
SPACE_SEP_LIST(DECL_PUSH, 15)
diff --git a/core/templates/cowdata.h b/core/templates/cowdata.h
index 46d9797d6c..466658951e 100644
--- a/core/templates/cowdata.h
+++ b/core/templates/cowdata.h
@@ -46,7 +46,7 @@ class CharString;
template <class T, class V>
class VMap;
-SAFE_NUMERIC_TYPE_PUN_GUARANTEES(uint32_t)
+static_assert(std::is_trivially_destructible_v<std::atomic<uint64_t>>);
// Silence a false positive warning (see GH-52119).
#if defined(__GNUC__) && !defined(__clang__)
@@ -64,45 +64,92 @@ class CowData {
template <class TV, class VV>
friend class VMap;
+public:
+ typedef int64_t Size;
+ typedef uint64_t USize;
+ static constexpr USize MAX_INT = INT64_MAX;
+
private:
+ // Function to find the next power of 2 to an integer.
+ static _FORCE_INLINE_ USize next_po2(USize x) {
+ if (x == 0) {
+ return 0;
+ }
+
+ --x;
+ x |= x >> 1;
+ x |= x >> 2;
+ x |= x >> 4;
+ x |= x >> 8;
+ x |= x >> 16;
+ if (sizeof(USize) == 8) {
+ x |= x >> 32;
+ }
+
+ return ++x;
+ }
+
+ // Alignment: ↓ max_align_t ↓ USize ↓ max_align_t
+ // ┌────────────────────┬──┬─────────────┬──┬───────────...
+ // │ SafeNumeric<USize> │░░│ USize │░░│ T[]
+ // │ ref. count │░░│ data size │░░│ data
+ // └────────────────────┴──┴─────────────┴──┴───────────...
+ // Offset: ↑ REF_COUNT_OFFSET ↑ SIZE_OFFSET ↑ DATA_OFFSET
+
+ static constexpr size_t REF_COUNT_OFFSET = 0;
+ static constexpr size_t SIZE_OFFSET = ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize) == 0) ? (REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) : ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) + alignof(USize) - ((REF_COUNT_OFFSET + sizeof(SafeNumeric<USize>)) % alignof(USize)));
+ static constexpr size_t DATA_OFFSET = ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t) == 0) ? (SIZE_OFFSET + sizeof(USize)) : ((SIZE_OFFSET + sizeof(USize)) + alignof(max_align_t) - ((SIZE_OFFSET + sizeof(USize)) % alignof(max_align_t)));
+
mutable T *_ptr = nullptr;
// internal helpers
- _FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const {
+ static _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount_ptr(uint8_t *p_ptr) {
+ return (SafeNumeric<USize> *)(p_ptr + REF_COUNT_OFFSET);
+ }
+
+ static _FORCE_INLINE_ USize *_get_size_ptr(uint8_t *p_ptr) {
+ return (USize *)(p_ptr + SIZE_OFFSET);
+ }
+
+ static _FORCE_INLINE_ T *_get_data_ptr(uint8_t *p_ptr) {
+ return (T *)(p_ptr + DATA_OFFSET);
+ }
+
+ _FORCE_INLINE_ SafeNumeric<USize> *_get_refcount() const {
if (!_ptr) {
return nullptr;
}
- return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2;
+ return (SafeNumeric<USize> *)((uint8_t *)_ptr - DATA_OFFSET + REF_COUNT_OFFSET);
}
- _FORCE_INLINE_ uint32_t *_get_size() const {
+ _FORCE_INLINE_ USize *_get_size() const {
if (!_ptr) {
return nullptr;
}
- return reinterpret_cast<uint32_t *>(_ptr) - 1;
+ return (USize *)((uint8_t *)_ptr - DATA_OFFSET + SIZE_OFFSET);
}
- _FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const {
- return next_power_of_2(p_elements * sizeof(T));
+ _FORCE_INLINE_ USize _get_alloc_size(USize p_elements) const {
+ return next_po2(p_elements * sizeof(T));
}
- _FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
+ _FORCE_INLINE_ bool _get_alloc_size_checked(USize p_elements, USize *out) const {
if (unlikely(p_elements == 0)) {
*out = 0;
return true;
}
-#if defined(__GNUC__)
- size_t o;
- size_t p;
+#if defined(__GNUC__) && defined(IS_32_BIT)
+ USize o;
+ USize p;
if (__builtin_mul_overflow(p_elements, sizeof(T), &o)) {
*out = 0;
return false;
}
- *out = next_power_of_2(o);
- if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
+ *out = next_po2(o);
+ if (__builtin_add_overflow(o, static_cast<USize>(32), &p)) {
return false; // No longer allocated here.
}
#else
@@ -116,7 +163,7 @@ private:
void _unref(void *p_data);
void _ref(const CowData *p_from);
void _ref(const CowData &p_from);
- uint32_t _copy_on_write();
+ USize _copy_on_write();
public:
void operator=(const CowData<T> &p_from) { _ref(p_from); }
@@ -130,8 +177,8 @@ public:
return _ptr;
}
- _FORCE_INLINE_ int size() const {
- uint32_t *size = (uint32_t *)_get_size();
+ _FORCE_INLINE_ Size size() const {
+ USize *size = (USize *)_get_size();
if (size) {
return *size;
} else {
@@ -142,42 +189,42 @@ public:
_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; }
- _FORCE_INLINE_ void set(int p_index, const T &p_elem) {
+ _FORCE_INLINE_ void set(Size p_index, const T &p_elem) {
ERR_FAIL_INDEX(p_index, size());
_copy_on_write();
_ptr[p_index] = p_elem;
}
- _FORCE_INLINE_ T &get_m(int p_index) {
+ _FORCE_INLINE_ T &get_m(Size p_index) {
CRASH_BAD_INDEX(p_index, size());
_copy_on_write();
return _ptr[p_index];
}
- _FORCE_INLINE_ const T &get(int p_index) const {
+ _FORCE_INLINE_ const T &get(Size p_index) const {
CRASH_BAD_INDEX(p_index, size());
return _ptr[p_index];
}
template <bool p_ensure_zero = false>
- Error resize(int p_size);
+ Error resize(Size p_size);
- _FORCE_INLINE_ void remove_at(int p_index) {
+ _FORCE_INLINE_ void remove_at(Size p_index) {
ERR_FAIL_INDEX(p_index, size());
T *p = ptrw();
- int len = size();
- for (int i = p_index; i < len - 1; i++) {
+ Size len = size();
+ for (Size i = p_index; i < len - 1; i++) {
p[i] = p[i + 1];
}
resize(len - 1);
}
- Error insert(int p_pos, const T &p_val) {
+ Error insert(Size p_pos, const T &p_val) {
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
resize(size() + 1);
- for (int i = (size() - 1); i > p_pos; i--) {
+ for (Size i = (size() - 1); i > p_pos; i--) {
set(i, get(i - 1));
}
set(p_pos, p_val);
@@ -185,9 +232,9 @@ public:
return OK;
}
- int find(const T &p_val, int p_from = 0) const;
- int rfind(const T &p_val, int p_from = -1) const;
- int count(const T &p_val) const;
+ Size find(const T &p_val, Size p_from = 0) const;
+ Size rfind(const T &p_val, Size p_from = -1) const;
+ Size count(const T &p_val) const;
_FORCE_INLINE_ CowData() {}
_FORCE_INLINE_ ~CowData();
@@ -200,59 +247,61 @@ void CowData<T>::_unref(void *p_data) {
return;
}
- SafeNumeric<uint32_t> *refc = _get_refcount();
+ SafeNumeric<USize> *refc = _get_refcount();
if (refc->decrement() > 0) {
return; // still in use
}
// clean up
- if (!std::is_trivially_destructible<T>::value) {
- uint32_t *count = _get_size();
+ if constexpr (!std::is_trivially_destructible_v<T>) {
+ USize *count = _get_size();
T *data = (T *)(count + 1);
- for (uint32_t i = 0; i < *count; ++i) {
+ for (USize i = 0; i < *count; ++i) {
// call destructors
data[i].~T();
}
}
// free mem
- Memory::free_static((uint8_t *)p_data, true);
+ Memory::free_static(((uint8_t *)p_data) - DATA_OFFSET, false);
}
template <class T>
-uint32_t CowData<T>::_copy_on_write() {
+typename CowData<T>::USize CowData<T>::_copy_on_write() {
if (!_ptr) {
return 0;
}
- SafeNumeric<uint32_t> *refc = _get_refcount();
+ SafeNumeric<USize> *refc = _get_refcount();
- uint32_t rc = refc->get();
+ USize rc = refc->get();
if (unlikely(rc > 1)) {
/* in use by more than me */
- uint32_t current_size = *_get_size();
+ USize current_size = *_get_size();
- uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size), true);
+ uint8_t *mem_new = (uint8_t *)Memory::alloc_static(_get_alloc_size(current_size) + DATA_OFFSET, false);
+ ERR_FAIL_NULL_V(mem_new, 0);
- new (mem_new - 2) SafeNumeric<uint32_t>(1); //refcount
- *(mem_new - 1) = current_size; //size
+ SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
+ USize *_size_ptr = _get_size_ptr(mem_new);
+ T *_data_ptr = _get_data_ptr(mem_new);
- T *_data = (T *)(mem_new);
+ new (_refc_ptr) SafeNumeric<USize>(1); //refcount
+ *(_size_ptr) = current_size; //size
// initialize new elements
- if (std::is_trivially_copyable<T>::value) {
- memcpy(mem_new, _ptr, current_size * sizeof(T));
-
+ if constexpr (std::is_trivially_copyable_v<T>) {
+ memcpy((uint8_t *)_data_ptr, _ptr, current_size * sizeof(T));
} else {
- for (uint32_t i = 0; i < current_size; i++) {
- memnew_placement(&_data[i], T(_ptr[i]));
+ for (USize i = 0; i < current_size; i++) {
+ memnew_placement(&_data_ptr[i], T(_ptr[i]));
}
}
_unref(_ptr);
- _ptr = _data;
+ _ptr = _data_ptr;
rc = 1;
}
@@ -261,10 +310,10 @@ uint32_t CowData<T>::_copy_on_write() {
template <class T>
template <bool p_ensure_zero>
-Error CowData<T>::resize(int p_size) {
+Error CowData<T>::resize(Size p_size) {
ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER);
- int current_size = size();
+ Size current_size = size();
if (p_size == current_size) {
return OK;
@@ -278,36 +327,45 @@ Error CowData<T>::resize(int p_size) {
}
// possibly changing size, copy on write
- uint32_t rc = _copy_on_write();
+ USize rc = _copy_on_write();
- size_t current_alloc_size = _get_alloc_size(current_size);
- size_t alloc_size;
+ USize current_alloc_size = _get_alloc_size(current_size);
+ USize alloc_size;
ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY);
if (p_size > current_size) {
if (alloc_size != current_alloc_size) {
if (current_size == 0) {
// alloc from scratch
- uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size, true);
- ERR_FAIL_NULL_V(ptr, ERR_OUT_OF_MEMORY);
- *(ptr - 1) = 0; //size, currently none
- new (ptr - 2) SafeNumeric<uint32_t>(1); //refcount
+ uint8_t *mem_new = (uint8_t *)Memory::alloc_static(alloc_size + DATA_OFFSET, false);
+ ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
- _ptr = (T *)ptr;
+ SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
+ USize *_size_ptr = _get_size_ptr(mem_new);
+ T *_data_ptr = _get_data_ptr(mem_new);
+
+ new (_refc_ptr) SafeNumeric<USize>(1); //refcount
+ *(_size_ptr) = 0; //size, currently none
+
+ _ptr = _data_ptr;
} else {
- uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
- ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
- new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
+ uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
+ ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
+
+ SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
+ T *_data_ptr = _get_data_ptr(mem_new);
- _ptr = (T *)(_ptrnew);
+ new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
+
+ _ptr = _data_ptr;
}
}
// construct the newly created elements
- if (!std::is_trivially_constructible<T>::value) {
- for (int i = *_get_size(); i < p_size; i++) {
+ if constexpr (!std::is_trivially_constructible_v<T>) {
+ for (Size i = *_get_size(); i < p_size; i++) {
memnew_placement(&_ptr[i], T);
}
} else if (p_ensure_zero) {
@@ -317,20 +375,24 @@ Error CowData<T>::resize(int p_size) {
*_get_size() = p_size;
} else if (p_size < current_size) {
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
// deinitialize no longer needed elements
- for (uint32_t i = p_size; i < *_get_size(); i++) {
+ for (USize i = p_size; i < *_get_size(); i++) {
T *t = &_ptr[i];
t->~T();
}
}
if (alloc_size != current_alloc_size) {
- uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size, true);
- ERR_FAIL_NULL_V(_ptrnew, ERR_OUT_OF_MEMORY);
- new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); //refcount
+ uint8_t *mem_new = (uint8_t *)Memory::realloc_static(((uint8_t *)_ptr) - DATA_OFFSET, alloc_size + DATA_OFFSET, false);
+ ERR_FAIL_NULL_V(mem_new, ERR_OUT_OF_MEMORY);
+
+ SafeNumeric<USize> *_refc_ptr = _get_refcount_ptr(mem_new);
+ T *_data_ptr = _get_data_ptr(mem_new);
+
+ new (_refc_ptr) SafeNumeric<USize>(rc); //refcount
- _ptr = (T *)(_ptrnew);
+ _ptr = _data_ptr;
}
*_get_size() = p_size;
@@ -340,14 +402,14 @@ Error CowData<T>::resize(int p_size) {
}
template <class T>
-int CowData<T>::find(const T &p_val, int p_from) const {
- int ret = -1;
+typename CowData<T>::Size CowData<T>::find(const T &p_val, Size p_from) const {
+ Size ret = -1;
if (p_from < 0 || size() == 0) {
return ret;
}
- for (int i = p_from; i < size(); i++) {
+ for (Size i = p_from; i < size(); i++) {
if (get(i) == p_val) {
ret = i;
break;
@@ -358,8 +420,8 @@ int CowData<T>::find(const T &p_val, int p_from) const {
}
template <class T>
-int CowData<T>::rfind(const T &p_val, int p_from) const {
- const int s = size();
+typename CowData<T>::Size CowData<T>::rfind(const T &p_val, Size p_from) const {
+ const Size s = size();
if (p_from < 0) {
p_from = s + p_from;
@@ -368,7 +430,7 @@ int CowData<T>::rfind(const T &p_val, int p_from) const {
p_from = s - 1;
}
- for (int i = p_from; i >= 0; i--) {
+ for (Size i = p_from; i >= 0; i--) {
if (get(i) == p_val) {
return i;
}
@@ -377,9 +439,9 @@ int CowData<T>::rfind(const T &p_val, int p_from) const {
}
template <class T>
-int CowData<T>::count(const T &p_val) const {
- int amount = 0;
- for (int i = 0; i < size(); i++) {
+typename CowData<T>::Size CowData<T>::count(const T &p_val) const {
+ Size amount = 0;
+ for (Size i = 0; i < size(); i++) {
if (get(i) == p_val) {
amount++;
}
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 2a212f3dcb..a16f655524 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -310,7 +310,7 @@ struct HashMapHasherDefault {
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return hash_fmix32(p_uchar); }
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
- static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.ptr()); }
+ static _FORCE_INLINE_ uint32_t hash(const CharString &p_char_string) { return hash_djb2(p_char_string.get_data()); }
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
@@ -429,7 +429,7 @@ struct HashMapComparatorDefault<Vector3> {
constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
-const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
+inline constexpr uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
5,
13,
23,
@@ -462,7 +462,7 @@ const uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
};
// Computed with elem_i = UINT64_C (0 x FFFFFFFF FFFFFFFF ) / d_i + 1, where d_i is the i-th element of the above array.
-const uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = {
+inline constexpr uint64_t hash_table_size_primes_inv[HASH_TABLE_SIZE_MAX] = {
3689348814741910324,
1418980313362273202,
802032351030850071,
diff --git a/core/templates/list.h b/core/templates/list.h
index 6393b942ff..354e826a43 100644
--- a/core/templates/list.h
+++ b/core/templates/list.h
@@ -132,6 +132,8 @@ public:
data->erase(this);
}
+ void transfer_to_back(List<T, A> *p_dst_list);
+
_FORCE_INLINE_ Element() {}
};
@@ -762,4 +764,41 @@ public:
}
};
+template <class T, class A>
+void List<T, A>::Element::transfer_to_back(List<T, A> *p_dst_list) {
+ // Detach from current.
+
+ if (data->first == this) {
+ data->first = data->first->next_ptr;
+ }
+ if (data->last == this) {
+ data->last = data->last->prev_ptr;
+ }
+ if (prev_ptr) {
+ prev_ptr->next_ptr = next_ptr;
+ }
+ if (next_ptr) {
+ next_ptr->prev_ptr = prev_ptr;
+ }
+ data->size_cache--;
+
+ // Attach to the back of the new one.
+
+ if (!p_dst_list->_data) {
+ p_dst_list->_data = memnew_allocator(_Data, A);
+ p_dst_list->_data->first = this;
+ p_dst_list->_data->last = nullptr;
+ p_dst_list->_data->size_cache = 0;
+ prev_ptr = nullptr;
+ } else {
+ p_dst_list->_data->last->next_ptr = this;
+ prev_ptr = p_dst_list->_data->last;
+ }
+ p_dst_list->_data->last = this;
+ next_ptr = nullptr;
+
+ data = p_dst_list->_data;
+ p_dst_list->_data->size_cache++;
+}
+
#endif // LIST_H
diff --git a/core/templates/local_vector.h b/core/templates/local_vector.h
index b454821a8f..17ddbf6161 100644
--- a/core/templates/local_vector.h
+++ b/core/templates/local_vector.h
@@ -64,7 +64,7 @@ public:
CRASH_COND_MSG(!data, "Out of memory");
}
- if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) {
+ if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
memnew_placement(&data[count++], T(p_elem));
} else {
data[count++] = p_elem;
@@ -77,7 +77,7 @@ public:
for (U i = p_index; i < count; i++) {
data[i] = data[i + 1];
}
- if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
+ if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
}
@@ -90,7 +90,7 @@ public:
if (count > p_index) {
data[p_index] = data[count];
}
- if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
+ if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
data[count].~T();
}
}
@@ -133,7 +133,7 @@ public:
_FORCE_INLINE_ U size() const { return count; }
void resize(U p_size) {
if (p_size < count) {
- if constexpr (!std::is_trivially_destructible<T>::value && !force_trivial) {
+ if constexpr (!std::is_trivially_destructible_v<T> && !force_trivial) {
for (U i = p_size; i < count; i++) {
data[i].~T();
}
@@ -145,7 +145,7 @@ public:
data = (T *)memrealloc(data, capacity * sizeof(T));
CRASH_COND_MSG(!data, "Out of memory");
}
- if constexpr (!std::is_trivially_constructible<T>::value && !force_trivial) {
+ if constexpr (!std::is_trivially_constructible_v<T> && !force_trivial) {
for (U i = count; i < p_size; i++) {
memnew_placement(&data[i], T);
}
diff --git a/core/templates/paged_allocator.h b/core/templates/paged_allocator.h
index 72425a8c3d..48110d37e5 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -40,7 +40,7 @@
#include <type_traits>
#include <typeinfo>
-template <class T, bool thread_safe = false>
+template <class T, bool thread_safe = false, uint32_t DEFAULT_PAGE_SIZE = 4096>
class PagedAllocator {
T **page_pool = nullptr;
T ***available_pool = nullptr;
@@ -53,12 +53,8 @@ class PagedAllocator {
SpinLock spin_lock;
public:
- enum {
- DEFAULT_PAGE_SIZE = 4096
- };
-
template <class... Args>
- T *alloc(const Args &&...p_args) {
+ T *alloc(Args &&...p_args) {
if (thread_safe) {
spin_lock.lock();
}
@@ -99,9 +95,13 @@ public:
}
}
+ template <class... Args>
+ T *new_allocation(Args &&...p_args) { return alloc(p_args...); }
+ void delete_allocation(T *p_mem) { free(p_mem); }
+
private:
void _reset(bool p_allow_unfreed) {
- if (!p_allow_unfreed || !std::is_trivially_destructible<T>::value) {
+ if (!p_allow_unfreed || !std::is_trivially_destructible_v<T>) {
ERR_FAIL_COND(allocs_available < pages_allocated * page_size);
}
if (pages_allocated) {
diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h
index 863e3eef11..21053dd033 100644
--- a/core/templates/paged_array.h
+++ b/core/templates/paged_array.h
@@ -53,7 +53,12 @@ class PagedArrayPool {
SpinLock spin_lock;
public:
- uint32_t alloc_page() {
+ struct PageInfo {
+ T *page = nullptr;
+ uint32_t page_id = 0;
+ };
+
+ PageInfo alloc_page() {
spin_lock.lock();
if (unlikely(pages_available == 0)) {
uint32_t pages_used = pages_allocated;
@@ -69,13 +74,11 @@ public:
}
pages_available--;
- uint32_t page = available_page_pool[pages_available];
+ uint32_t page_id = available_page_pool[pages_available];
+ T *page = page_pool[page_id];
spin_lock.unlock();
- return page;
- }
- T *get_page(uint32_t p_page_id) {
- return page_pool[p_page_id];
+ return PageInfo{ page, page_id };
}
void free_page(uint32_t p_page_id) {
@@ -190,16 +193,16 @@ public:
_grow_page_array(); //keep out of inline
}
- uint32_t page_id = page_pool->alloc_page();
- page_data[page_count] = page_pool->get_page(page_id);
- page_ids[page_count] = page_id;
+ typename PagedArrayPool<T>::PageInfo page_info = page_pool->alloc_page();
+ page_data[page_count] = page_info.page;
+ page_ids[page_count] = page_info.page_id;
}
// place the new value
uint32_t page = count >> page_size_shift;
uint32_t offset = count & page_size_mask;
- if (!std::is_trivially_constructible<T>::value) {
+ if constexpr (!std::is_trivially_constructible_v<T>) {
memnew_placement(&page_data[page][offset], T(p_value));
} else {
page_data[page][offset] = p_value;
@@ -211,7 +214,7 @@ public:
_FORCE_INLINE_ void pop_back() {
ERR_FAIL_COND(count == 0);
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
uint32_t page = (count - 1) >> page_size_shift;
uint32_t offset = (count - 1) & page_size_mask;
page_data[page][offset].~T();
@@ -226,9 +229,15 @@ public:
count--;
}
+ void remove_at_unordered(uint64_t p_index) {
+ ERR_FAIL_UNSIGNED_INDEX(p_index, count);
+ (*this)[p_index] = (*this)[count - 1];
+ pop_back();
+ }
+
void clear() {
//destruct if needed
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
for (uint64_t i = 0; i < count; i++) {
uint32_t page = i >> page_size_shift;
uint32_t offset = i & page_size_mask;
@@ -309,13 +318,13 @@ public:
uint32_t to_copy = MIN(page_size - new_remainder, remainder);
for (uint32_t i = 0; i < to_copy; i++) {
- if (!std::is_trivially_constructible<T>::value) {
+ if constexpr (!std::is_trivially_constructible_v<T>) {
memnew_placement(&dst_page[i + new_remainder], T(remainder_page[i + remainder - to_copy]));
} else {
dst_page[i + new_remainder] = remainder_page[i + remainder - to_copy];
}
- if (!std::is_trivially_destructible<T>::value) {
+ if constexpr (!std::is_trivially_destructible_v<T>) {
remainder_page[i + remainder - to_copy].~T();
}
}
diff --git a/core/templates/rb_map.h b/core/templates/rb_map.h
index d373713669..152fddd011 100644
--- a/core/templates/rb_map.h
+++ b/core/templates/rb_map.h
@@ -111,11 +111,16 @@ public:
return *this;
}
- _FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
- _FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
+ _FORCE_INLINE_ bool operator==(const Iterator &p_it) const { return E == p_it.E; }
+ _FORCE_INLINE_ bool operator!=(const Iterator &p_it) const { return E != p_it.E; }
explicit operator bool() const {
return E != nullptr;
}
+
+ Iterator &operator=(const Iterator &p_it) {
+ E = p_it.E;
+ return *this;
+ }
Iterator(Element *p_E) { E = p_E; }
Iterator() {}
Iterator(const Iterator &p_it) { E = p_it.E; }
@@ -138,11 +143,16 @@ public:
return *this;
}
- _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
- _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
+ _FORCE_INLINE_ bool operator==(const ConstIterator &p_it) const { return E == p_it.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &p_it) const { return E != p_it.E; }
explicit operator bool() const {
return E != nullptr;
}
+
+ ConstIterator &operator=(const ConstIterator &p_it) {
+ E = p_it.E;
+ return *this;
+ }
ConstIterator(const Element *p_E) { E = p_E; }
ConstIterator() {}
ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
diff --git a/core/templates/ring_buffer.h b/core/templates/ring_buffer.h
index d878894946..54148a59bf 100644
--- a/core/templates/ring_buffer.h
+++ b/core/templates/ring_buffer.h
@@ -197,7 +197,7 @@ public:
int old_size = size();
int new_size = 1 << p_power;
int mask = new_size - 1;
- data.resize(1 << p_power);
+ data.resize(int64_t(1) << int64_t(p_power));
if (old_size < new_size && read_pos > write_pos) {
for (int i = 0; i < write_pos; i++) {
data.write[(old_size + i) & mask] = data[i];
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index 20fb0c6501..7bbceadc8d 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -54,7 +54,7 @@
#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \
static_assert(sizeof(SafeNumeric<m_type>) == sizeof(m_type)); \
static_assert(alignof(SafeNumeric<m_type>) == alignof(m_type)); \
- static_assert(std::is_trivially_destructible<std::atomic<m_type>>::value);
+ static_assert(std::is_trivially_destructible_v<std::atomic<m_type>>);
#define SAFE_FLAG_TYPE_PUN_GUARANTEES \
static_assert(sizeof(SafeFlag) == sizeof(bool)); \
static_assert(alignof(SafeFlag) == alignof(bool));
diff --git a/core/templates/self_list.h b/core/templates/self_list.h
index fdf91beacc..afa0501c75 100644
--- a/core/templates/self_list.h
+++ b/core/templates/self_list.h
@@ -159,6 +159,9 @@ public:
_FORCE_INLINE_ SelfList<T> *first() { return _first; }
_FORCE_INLINE_ const SelfList<T> *first() const { return _first; }
+ // Forbid copying, which has broken behavior.
+ void operator=(const List &) = delete;
+
_FORCE_INLINE_ List() {}
_FORCE_INLINE_ ~List() {
// A self list must be empty on destruction.
@@ -185,6 +188,9 @@ public:
_FORCE_INLINE_ const SelfList<T> *prev() const { return _prev; }
_FORCE_INLINE_ T *self() const { return _self; }
+ // Forbid copying, which has broken behavior.
+ void operator=(const SelfList<T> &) = delete;
+
_FORCE_INLINE_ SelfList(T *p_self) {
_self = p_self;
}
diff --git a/core/templates/vector.h b/core/templates/vector.h
index d8bac0870f..361a7f56d5 100644
--- a/core/templates/vector.h
+++ b/core/templates/vector.h
@@ -48,7 +48,7 @@
template <class T>
class VectorWriteProxy {
public:
- _FORCE_INLINE_ T &operator[](int p_index) {
+ _FORCE_INLINE_ T &operator[](typename CowData<T>::Size p_index) {
CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size());
return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index];
@@ -61,6 +61,7 @@ class Vector {
public:
VectorWriteProxy<T> write;
+ typedef typename CowData<T>::Size Size;
private:
CowData<T> _cowdata;
@@ -70,9 +71,9 @@ public:
_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } //alias
void fill(T p_elem);
- void remove_at(int p_index) { _cowdata.remove_at(p_index); }
+ void remove_at(Size p_index) { _cowdata.remove_at(p_index); }
_FORCE_INLINE_ bool erase(const T &p_val) {
- int idx = find(p_val);
+ Size idx = find(p_val);
if (idx >= 0) {
remove_at(idx);
return true;
@@ -87,17 +88,17 @@ public:
_FORCE_INLINE_ void clear() { resize(0); }
_FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); }
- _FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); }
- _FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); }
- _FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
- _FORCE_INLINE_ int size() const { return _cowdata.size(); }
- Error resize(int p_size) { return _cowdata.resize(p_size); }
- Error resize_zeroed(int p_size) { return _cowdata.template resize<true>(p_size); }
- _FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
- Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
- int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
- int rfind(const T &p_val, int p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
- int count(const T &p_val) const { return _cowdata.count(p_val); }
+ _FORCE_INLINE_ T get(Size p_index) { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ const T &get(Size p_index) const { return _cowdata.get(p_index); }
+ _FORCE_INLINE_ void set(Size p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
+ _FORCE_INLINE_ Size size() const { return _cowdata.size(); }
+ Error resize(Size p_size) { return _cowdata.resize(p_size); }
+ Error resize_zeroed(Size p_size) { return _cowdata.template resize<true>(p_size); }
+ _FORCE_INLINE_ const T &operator[](Size p_index) const { return _cowdata.get(p_index); }
+ Error insert(Size p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
+ Size find(const T &p_val, Size p_from = 0) const { return _cowdata.find(p_val, p_from); }
+ Size rfind(const T &p_val, Size p_from = -1) const { return _cowdata.rfind(p_val, p_from); }
+ Size count(const T &p_val) const { return _cowdata.count(p_val); }
void append_array(Vector<T> p_other);
@@ -109,7 +110,7 @@ public:
template <class Comparator, bool Validate = SORT_ARRAY_VALIDATE_ENABLED, class... Args>
void sort_custom(Args &&...args) {
- int len = _cowdata.size();
+ Size len = _cowdata.size();
if (len == 0) {
return;
}
@@ -119,12 +120,12 @@ public:
sorter.sort(data, len);
}
- int bsearch(const T &p_value, bool p_before) {
+ Size bsearch(const T &p_value, bool p_before) {
return bsearch_custom<_DefaultComparator<T>>(p_value, p_before);
}
template <class Comparator, class Value, class... Args>
- int bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
+ Size bsearch_custom(const Value &p_value, bool p_before, Args &&...args) {
SearchArray<T, Comparator> search{ args... };
return search.bisect(ptrw(), size(), p_value, p_before);
}
@@ -134,7 +135,7 @@ public:
}
void ordered_insert(const T &p_val) {
- int i;
+ Size i;
for (i = 0; i < _cowdata.size(); i++) {
if (p_val < operator[](i)) {
break;
@@ -157,28 +158,28 @@ public:
return ret;
}
- Vector<T> slice(int p_begin, int p_end = INT_MAX) const {
+ Vector<T> slice(Size p_begin, Size p_end = CowData<T>::MAX_INT) const {
Vector<T> result;
- const int s = size();
+ const Size s = size();
- int begin = CLAMP(p_begin, -s, s);
+ Size begin = CLAMP(p_begin, -s, s);
if (begin < 0) {
begin += s;
}
- int end = CLAMP(p_end, -s, s);
+ Size end = CLAMP(p_end, -s, s);
if (end < 0) {
end += s;
}
ERR_FAIL_COND_V(begin > end, result);
- int result_size = end - begin;
+ Size result_size = end - begin;
result.resize(result_size);
const T *const r = ptr();
T *const w = result.ptrw();
- for (int i = 0; i < result_size; ++i) {
+ for (Size i = 0; i < result_size; ++i) {
w[i] = r[begin + i];
}
@@ -186,11 +187,11 @@ public:
}
bool operator==(const Vector<T> &p_arr) const {
- int s = size();
+ Size s = size();
if (s != p_arr.size()) {
return false;
}
- for (int i = 0; i < s; i++) {
+ for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return false;
}
@@ -199,11 +200,11 @@ public:
}
bool operator!=(const Vector<T> &p_arr) const {
- int s = size();
+ Size s = size();
if (s != p_arr.size()) {
return true;
}
- for (int i = 0; i < s; i++) {
+ for (Size i = 0; i < s; i++) {
if (operator[](i) != p_arr[i]) {
return true;
}
@@ -280,7 +281,7 @@ public:
Error err = _cowdata.resize(p_init.size());
ERR_FAIL_COND(err);
- int i = 0;
+ Size i = 0;
for (const T &element : p_init) {
_cowdata.set(i++, element);
}
@@ -292,7 +293,7 @@ public:
template <class T>
void Vector<T>::reverse() {
- for (int i = 0; i < size() / 2; i++) {
+ for (Size i = 0; i < size() / 2; i++) {
T *p = ptrw();
SWAP(p[i], p[size() - i - 1]);
}
@@ -300,13 +301,13 @@ void Vector<T>::reverse() {
template <class T>
void Vector<T>::append_array(Vector<T> p_other) {
- const int ds = p_other.size();
+ const Size ds = p_other.size();
if (ds == 0) {
return;
}
- const int bs = size();
+ const Size bs = size();
resize(bs + ds);
- for (int i = 0; i < ds; ++i) {
+ for (Size i = 0; i < ds; ++i) {
ptrw()[bs + i] = p_other[i];
}
}
@@ -323,7 +324,7 @@ bool Vector<T>::push_back(T p_elem) {
template <class T>
void Vector<T>::fill(T p_elem) {
T *p = ptrw();
- for (int i = 0; i < size(); i++) {
+ for (Size i = 0; i < size(); i++) {
p[i] = p_elem;
}
}
diff --git a/core/typedefs.h b/core/typedefs.h
index 24c247fd38..8807ee3c99 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -92,6 +92,8 @@
#undef Error
#undef OK
#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum
+#undef MemoryBarrier
+#undef MONO_FONT
#endif
// Make room for our constexpr's below by overriding potential system-specific macros.
@@ -232,6 +234,10 @@ constexpr T get_num_bits(T x) {
#define BSWAP16(x) __builtin_bswap16(x)
#define BSWAP32(x) __builtin_bswap32(x)
#define BSWAP64(x) __builtin_bswap64(x)
+#elif defined(_MSC_VER)
+#define BSWAP16(x) _byteswap_ushort(x)
+#define BSWAP32(x) _byteswap_ulong(x)
+#define BSWAP64(x) _byteswap_uint64(x)
#else
static inline uint16_t BSWAP16(uint16_t x) {
return (x >> 8) | (x << 8);
diff --git a/core/variant/array.cpp b/core/variant/array.cpp
index ab0315ae34..5d6fbb8bed 100644
--- a/core/variant/array.cpp
+++ b/core/variant/array.cpp
@@ -316,17 +316,17 @@ void Array::erase(const Variant &p_value) {
}
Variant Array::front() const {
- ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array.");
+ ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array.");
return operator[](0);
}
Variant Array::back() const {
- ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array.");
+ ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array.");
return operator[](_p->array.size() - 1);
}
Variant Array::pick_random() const {
- ERR_FAIL_COND_V_MSG(_p->array.size() == 0, Variant(), "Can't take value from empty array.");
+ ERR_FAIL_COND_V_MSG(_p->array.is_empty(), Variant(), "Can't take value from empty array.");
return operator[](Math::rand() % _p->array.size());
}
diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h
index 34b54f1d00..a44b938395 100644
--- a/core/variant/binder_common.h
+++ b/core/variant/binder_common.h
@@ -51,7 +51,7 @@ template <class T>
struct VariantCaster {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
- if constexpr (std::is_base_of<Object, TStripped>::value) {
+ if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@@ -63,7 +63,7 @@ template <class T>
struct VariantCaster<T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
- if constexpr (std::is_base_of<Object, TStripped>::value) {
+ if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@@ -75,7 +75,7 @@ template <class T>
struct VariantCaster<const T &> {
static _FORCE_INLINE_ T cast(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
- if constexpr (std::is_base_of<Object, TStripped>::value) {
+ if constexpr (std::is_base_of_v<Object, TStripped>) {
return Object::cast_to<TStripped>(p_variant);
} else {
return p_variant;
@@ -176,6 +176,7 @@ VARIANT_ENUM_CAST(Variant::Operator);
VARIANT_ENUM_CAST(Key);
VARIANT_BITFIELD_CAST(KeyModifierMask);
+VARIANT_ENUM_CAST(KeyLocation);
static inline Key &operator|=(Key &a, BitField<KeyModifierMask> b) {
a = static_cast<Key>(static_cast<int>(a) | static_cast<int>(b.operator int64_t()));
@@ -225,7 +226,7 @@ template <typename T>
struct VariantObjectClassChecker {
static _FORCE_INLINE_ bool check(const Variant &p_variant) {
using TStripped = std::remove_pointer_t<T>;
- if constexpr (std::is_base_of<Object, TStripped>::value) {
+ if constexpr (std::is_base_of_v<Object, TStripped>) {
Object *obj = p_variant;
return Object::cast_to<TStripped>(p_variant) || !obj;
} else {
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 0b1174c873..55f687bdf9 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -31,13 +31,12 @@
#include "callable.h"
#include "callable_bind.h"
-#include "core/object/message_queue.h"
#include "core/object/object.h"
#include "core/object/ref_counted.h"
#include "core/object/script_language.h"
void Callable::call_deferredp(const Variant **p_arguments, int p_argcount) const {
- MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount);
+ MessageQueue::get_singleton()->push_callablep(*this, p_arguments, p_argcount, true);
}
void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const {
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 141ce25fa6..9f65a73c6f 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -150,6 +150,15 @@ Variant Dictionary::get(const Variant &p_key, const Variant &p_default) const {
return *result;
}
+Variant Dictionary::get_or_add(const Variant &p_key, const Variant &p_default) {
+ const Variant *result = getptr(p_key);
+ if (!result) {
+ operator[](p_key) = p_default;
+ return p_default;
+ }
+ return *result;
+}
+
int Dictionary::size() const {
return _p->variant_map.size();
}
@@ -242,7 +251,7 @@ void Dictionary::clear() {
void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) {
for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
if (p_overwrite || !has(E.key)) {
- this->operator[](E.key) = E.value;
+ operator[](E.key) = E.value;
}
}
}
diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h
index 8935d35ed9..f94a0da80a 100644
--- a/core/variant/dictionary.h
+++ b/core/variant/dictionary.h
@@ -58,6 +58,7 @@ public:
Variant get_valid(const Variant &p_key) const;
Variant get(const Variant &p_key, const Variant &p_default) const;
+ Variant get_or_add(const Variant &p_key, const Variant &p_default);
int size() const;
bool is_empty() const;
diff --git a/core/variant/type_info.h b/core/variant/type_info.h
index e89658d25b..49c4db8229 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -43,14 +43,10 @@ struct EnableIf<false, T> {
};
template <typename, typename>
-struct TypesAreSame {
- static bool const value = false;
-};
+inline constexpr bool types_are_same_v = false;
-template <typename A>
-struct TypesAreSame<A, A> {
- static bool const value = true;
-};
+template <typename T>
+inline constexpr bool types_are_same_v<T, T> = true;
template <typename B, typename D>
struct TypeInherits {
@@ -60,7 +56,7 @@ struct TypeInherits {
static char (&test(...))[2];
static bool const value = sizeof(test(get_d())) == sizeof(char) &&
- !TypesAreSame<B volatile const, void volatile const>::value;
+ !types_are_same_v<B volatile const, void volatile const>;
};
namespace GodotTypeInfo {
@@ -287,14 +283,17 @@ class BitField {
int64_t value = 0;
public:
- _FORCE_INLINE_ void set_flag(T p_flag) { value |= (int64_t)p_flag; }
+ _FORCE_INLINE_ BitField<T> &set_flag(T p_flag) {
+ value |= (int64_t)p_flag;
+ return *this;
+ }
_FORCE_INLINE_ bool has_flag(T p_flag) const { return value & (int64_t)p_flag; }
_FORCE_INLINE_ bool is_empty() const { return value == 0; }
_FORCE_INLINE_ void clear_flag(T p_flag) { value &= ~(int64_t)p_flag; }
_FORCE_INLINE_ void clear() { value = 0; }
- _FORCE_INLINE_ BitField() = default;
- _FORCE_INLINE_ BitField(int64_t p_value) { value = p_value; }
- _FORCE_INLINE_ BitField(T p_value) { value = (int64_t)p_value; }
+ _FORCE_INLINE_ constexpr BitField() = default;
+ _FORCE_INLINE_ constexpr BitField(int64_t p_value) { value = p_value; }
+ _FORCE_INLINE_ constexpr BitField(T p_value) { value = (int64_t)p_value; }
_FORCE_INLINE_ operator int64_t() const { return value; }
_FORCE_INLINE_ operator Variant() const { return value; }
};
diff --git a/core/variant/typed_array.h b/core/variant/typed_array.h
index 037c5c7c2e..ed973b9daa 100644
--- a/core/variant/typed_array.h
+++ b/core/variant/typed_array.h
@@ -88,6 +88,8 @@ struct VariantInternalAccessor<const TypedArray<T> &> {
} \
};
+// All Variant::OBJECT types are intentionally omitted from this list because they are handled by
+// the unspecialized TypedArray definition.
MAKE_TYPED_ARRAY(bool, Variant::BOOL)
MAKE_TYPED_ARRAY(uint8_t, Variant::INT)
MAKE_TYPED_ARRAY(int8_t, Variant::INT)
@@ -107,11 +109,14 @@ MAKE_TYPED_ARRAY(Rect2i, Variant::RECT2I)
MAKE_TYPED_ARRAY(Vector3, Variant::VECTOR3)
MAKE_TYPED_ARRAY(Vector3i, Variant::VECTOR3I)
MAKE_TYPED_ARRAY(Transform2D, Variant::TRANSFORM2D)
+MAKE_TYPED_ARRAY(Vector4, Variant::VECTOR4)
+MAKE_TYPED_ARRAY(Vector4i, Variant::VECTOR4I)
MAKE_TYPED_ARRAY(Plane, Variant::PLANE)
MAKE_TYPED_ARRAY(Quaternion, Variant::QUATERNION)
MAKE_TYPED_ARRAY(AABB, Variant::AABB)
MAKE_TYPED_ARRAY(Basis, Variant::BASIS)
MAKE_TYPED_ARRAY(Transform3D, Variant::TRANSFORM3D)
+MAKE_TYPED_ARRAY(Projection, Variant::PROJECTION)
MAKE_TYPED_ARRAY(Color, Variant::COLOR)
MAKE_TYPED_ARRAY(StringName, Variant::STRING_NAME)
MAKE_TYPED_ARRAY(NodePath, Variant::NODE_PATH)
@@ -120,15 +125,15 @@ MAKE_TYPED_ARRAY(Callable, Variant::CALLABLE)
MAKE_TYPED_ARRAY(Signal, Variant::SIGNAL)
MAKE_TYPED_ARRAY(Dictionary, Variant::DICTIONARY)
MAKE_TYPED_ARRAY(Array, Variant::ARRAY)
-MAKE_TYPED_ARRAY(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY)
-MAKE_TYPED_ARRAY(Vector<int32_t>, Variant::PACKED_INT32_ARRAY)
-MAKE_TYPED_ARRAY(Vector<int64_t>, Variant::PACKED_INT64_ARRAY)
-MAKE_TYPED_ARRAY(Vector<float>, Variant::PACKED_FLOAT32_ARRAY)
-MAKE_TYPED_ARRAY(Vector<double>, Variant::PACKED_FLOAT64_ARRAY)
-MAKE_TYPED_ARRAY(Vector<String>, Variant::PACKED_STRING_ARRAY)
-MAKE_TYPED_ARRAY(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
-MAKE_TYPED_ARRAY(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
-MAKE_TYPED_ARRAY(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY(PackedByteArray, Variant::PACKED_BYTE_ARRAY)
+MAKE_TYPED_ARRAY(PackedInt32Array, Variant::PACKED_INT32_ARRAY)
+MAKE_TYPED_ARRAY(PackedInt64Array, Variant::PACKED_INT64_ARRAY)
+MAKE_TYPED_ARRAY(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY)
+MAKE_TYPED_ARRAY(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY)
+MAKE_TYPED_ARRAY(PackedStringArray, Variant::PACKED_STRING_ARRAY)
+MAKE_TYPED_ARRAY(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
+MAKE_TYPED_ARRAY(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
+MAKE_TYPED_ARRAY(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
MAKE_TYPED_ARRAY(IPAddress, Variant::STRING)
template <class T>
@@ -205,11 +210,14 @@ MAKE_TYPED_ARRAY_INFO(Rect2i, Variant::RECT2I)
MAKE_TYPED_ARRAY_INFO(Vector3, Variant::VECTOR3)
MAKE_TYPED_ARRAY_INFO(Vector3i, Variant::VECTOR3I)
MAKE_TYPED_ARRAY_INFO(Transform2D, Variant::TRANSFORM2D)
+MAKE_TYPED_ARRAY_INFO(Vector4, Variant::VECTOR4)
+MAKE_TYPED_ARRAY_INFO(Vector4i, Variant::VECTOR4I)
MAKE_TYPED_ARRAY_INFO(Plane, Variant::PLANE)
MAKE_TYPED_ARRAY_INFO(Quaternion, Variant::QUATERNION)
MAKE_TYPED_ARRAY_INFO(AABB, Variant::AABB)
MAKE_TYPED_ARRAY_INFO(Basis, Variant::BASIS)
MAKE_TYPED_ARRAY_INFO(Transform3D, Variant::TRANSFORM3D)
+MAKE_TYPED_ARRAY_INFO(Projection, Variant::PROJECTION)
MAKE_TYPED_ARRAY_INFO(Color, Variant::COLOR)
MAKE_TYPED_ARRAY_INFO(StringName, Variant::STRING_NAME)
MAKE_TYPED_ARRAY_INFO(NodePath, Variant::NODE_PATH)
@@ -218,15 +226,15 @@ MAKE_TYPED_ARRAY_INFO(Callable, Variant::CALLABLE)
MAKE_TYPED_ARRAY_INFO(Signal, Variant::SIGNAL)
MAKE_TYPED_ARRAY_INFO(Dictionary, Variant::DICTIONARY)
MAKE_TYPED_ARRAY_INFO(Array, Variant::ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<uint8_t>, Variant::PACKED_BYTE_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<int32_t>, Variant::PACKED_INT32_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<int64_t>, Variant::PACKED_INT64_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<float>, Variant::PACKED_FLOAT32_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<double>, Variant::PACKED_FLOAT64_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<String>, Variant::PACKED_STRING_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<Vector2>, Variant::PACKED_VECTOR2_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<Vector3>, Variant::PACKED_VECTOR3_ARRAY)
-MAKE_TYPED_ARRAY_INFO(Vector<Color>, Variant::PACKED_COLOR_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedByteArray, Variant::PACKED_BYTE_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedInt32Array, Variant::PACKED_INT32_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedInt64Array, Variant::PACKED_INT64_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedFloat32Array, Variant::PACKED_FLOAT32_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedFloat64Array, Variant::PACKED_FLOAT64_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedStringArray, Variant::PACKED_STRING_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedVector2Array, Variant::PACKED_VECTOR2_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedVector3Array, Variant::PACKED_VECTOR3_ARRAY)
+MAKE_TYPED_ARRAY_INFO(PackedColorArray, Variant::PACKED_COLOR_ARRAY)
MAKE_TYPED_ARRAY_INFO(IPAddress, Variant::STRING)
#undef MAKE_TYPED_ARRAY
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 4c0212075b..67f2c10099 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -931,7 +931,7 @@ bool Variant::is_zero() const {
return *reinterpret_cast<const ::RID *>(_data._mem) == ::RID();
}
case OBJECT: {
- return _get_obj().obj == nullptr;
+ return get_validated_object() == nullptr;
}
case CALLABLE: {
return reinterpret_cast<const Callable *>(_data._mem)->is_null();
@@ -1246,53 +1246,53 @@ void Variant::zero() {
case NIL:
break;
case BOOL:
- this->_data._bool = false;
+ _data._bool = false;
break;
case INT:
- this->_data._int = 0;
+ _data._int = 0;
break;
case FLOAT:
- this->_data._float = 0;
+ _data._float = 0;
break;
case VECTOR2:
- *reinterpret_cast<Vector2 *>(this->_data._mem) = Vector2();
+ *reinterpret_cast<Vector2 *>(_data._mem) = Vector2();
break;
case VECTOR2I:
- *reinterpret_cast<Vector2i *>(this->_data._mem) = Vector2i();
+ *reinterpret_cast<Vector2i *>(_data._mem) = Vector2i();
break;
case RECT2:
- *reinterpret_cast<Rect2 *>(this->_data._mem) = Rect2();
+ *reinterpret_cast<Rect2 *>(_data._mem) = Rect2();
break;
case RECT2I:
- *reinterpret_cast<Rect2i *>(this->_data._mem) = Rect2i();
+ *reinterpret_cast<Rect2i *>(_data._mem) = Rect2i();
break;
case VECTOR3:
- *reinterpret_cast<Vector3 *>(this->_data._mem) = Vector3();
+ *reinterpret_cast<Vector3 *>(_data._mem) = Vector3();
break;
case VECTOR3I:
- *reinterpret_cast<Vector3i *>(this->_data._mem) = Vector3i();
+ *reinterpret_cast<Vector3i *>(_data._mem) = Vector3i();
break;
case VECTOR4:
- *reinterpret_cast<Vector4 *>(this->_data._mem) = Vector4();
+ *reinterpret_cast<Vector4 *>(_data._mem) = Vector4();
break;
case VECTOR4I:
- *reinterpret_cast<Vector4i *>(this->_data._mem) = Vector4i();
+ *reinterpret_cast<Vector4i *>(_data._mem) = Vector4i();
break;
case PLANE:
- *reinterpret_cast<Plane *>(this->_data._mem) = Plane();
+ *reinterpret_cast<Plane *>(_data._mem) = Plane();
break;
case QUATERNION:
- *reinterpret_cast<Quaternion *>(this->_data._mem) = Quaternion();
+ *reinterpret_cast<Quaternion *>(_data._mem) = Quaternion();
break;
case COLOR:
- *reinterpret_cast<Color *>(this->_data._mem) = Color();
+ *reinterpret_cast<Color *>(_data._mem) = Color();
break;
default:
Type prev_type = type;
- this->clear();
+ clear();
if (type != prev_type) {
// clear() changes type to NIL, so it needs to be restored.
Callable::CallError ce;
@@ -1793,31 +1793,31 @@ String Variant::stringify(int recursion_count) const {
}
// Packed arrays cannot contain recursive structures, the recursion_count increment is not needed.
case PACKED_VECTOR2_ARRAY: {
- return stringify_vector(operator Vector<Vector2>(), recursion_count);
+ return stringify_vector(operator PackedVector2Array(), recursion_count);
}
case PACKED_VECTOR3_ARRAY: {
- return stringify_vector(operator Vector<Vector3>(), recursion_count);
+ return stringify_vector(operator PackedVector3Array(), recursion_count);
}
case PACKED_COLOR_ARRAY: {
- return stringify_vector(operator Vector<Color>(), recursion_count);
+ return stringify_vector(operator PackedColorArray(), recursion_count);
}
case PACKED_STRING_ARRAY: {
- return stringify_vector(operator Vector<String>(), recursion_count);
+ return stringify_vector(operator PackedStringArray(), recursion_count);
}
case PACKED_BYTE_ARRAY: {
- return stringify_vector(operator Vector<uint8_t>(), recursion_count);
+ return stringify_vector(operator PackedByteArray(), recursion_count);
}
case PACKED_INT32_ARRAY: {
- return stringify_vector(operator Vector<int32_t>(), recursion_count);
+ return stringify_vector(operator PackedInt32Array(), recursion_count);
}
case PACKED_INT64_ARRAY: {
- return stringify_vector(operator Vector<int64_t>(), recursion_count);
+ return stringify_vector(operator PackedInt64Array(), recursion_count);
}
case PACKED_FLOAT32_ARRAY: {
- return stringify_vector(operator Vector<float>(), recursion_count);
+ return stringify_vector(operator PackedFloat32Array(), recursion_count);
}
case PACKED_FLOAT64_ARRAY: {
- return stringify_vector(operator Vector<double>(), recursion_count);
+ return stringify_vector(operator PackedFloat64Array(), recursion_count);
}
case ARRAY: {
ERR_FAIL_COND_V_MSG(recursion_count > MAX_RECURSION, "[...]", "Maximum array recursion reached!");
@@ -2207,31 +2207,31 @@ inline DA _convert_array_from_variant(const Variant &p_variant) {
return _convert_array<DA, Array>(p_variant.operator Array());
}
case Variant::PACKED_BYTE_ARRAY: {
- return _convert_array<DA, Vector<uint8_t>>(p_variant.operator Vector<uint8_t>());
+ return _convert_array<DA, PackedByteArray>(p_variant.operator PackedByteArray());
}
case Variant::PACKED_INT32_ARRAY: {
- return _convert_array<DA, Vector<int32_t>>(p_variant.operator Vector<int32_t>());
+ return _convert_array<DA, PackedInt32Array>(p_variant.operator PackedInt32Array());
}
case Variant::PACKED_INT64_ARRAY: {
- return _convert_array<DA, Vector<int64_t>>(p_variant.operator Vector<int64_t>());
+ return _convert_array<DA, PackedInt64Array>(p_variant.operator PackedInt64Array());
}
case Variant::PACKED_FLOAT32_ARRAY: {
- return _convert_array<DA, Vector<float>>(p_variant.operator Vector<float>());
+ return _convert_array<DA, PackedFloat32Array>(p_variant.operator PackedFloat32Array());
}
case Variant::PACKED_FLOAT64_ARRAY: {
- return _convert_array<DA, Vector<double>>(p_variant.operator Vector<double>());
+ return _convert_array<DA, PackedFloat64Array>(p_variant.operator PackedFloat64Array());
}
case Variant::PACKED_STRING_ARRAY: {
- return _convert_array<DA, Vector<String>>(p_variant.operator Vector<String>());
+ return _convert_array<DA, PackedStringArray>(p_variant.operator PackedStringArray());
}
case Variant::PACKED_VECTOR2_ARRAY: {
- return _convert_array<DA, Vector<Vector2>>(p_variant.operator Vector<Vector2>());
+ return _convert_array<DA, PackedVector2Array>(p_variant.operator PackedVector2Array());
}
case Variant::PACKED_VECTOR3_ARRAY: {
- return _convert_array<DA, Vector<Vector3>>(p_variant.operator Vector<Vector3>());
+ return _convert_array<DA, PackedVector3Array>(p_variant.operator PackedVector3Array());
}
case Variant::PACKED_COLOR_ARRAY: {
- return _convert_array<DA, Vector<Color>>(p_variant.operator Vector<Color>());
+ return _convert_array<DA, PackedColorArray>(p_variant.operator PackedColorArray());
}
default: {
return DA();
@@ -2247,75 +2247,75 @@ Variant::operator Array() const {
}
}
-Variant::operator Vector<uint8_t>() const {
+Variant::operator PackedByteArray() const {
if (type == PACKED_BYTE_ARRAY) {
return static_cast<PackedArrayRef<uint8_t> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<uint8_t>>(*this);
+ return _convert_array_from_variant<PackedByteArray>(*this);
}
}
-Variant::operator Vector<int32_t>() const {
+Variant::operator PackedInt32Array() const {
if (type == PACKED_INT32_ARRAY) {
return static_cast<PackedArrayRef<int32_t> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<int>>(*this);
+ return _convert_array_from_variant<PackedInt32Array>(*this);
}
}
-Variant::operator Vector<int64_t>() const {
+Variant::operator PackedInt64Array() const {
if (type == PACKED_INT64_ARRAY) {
return static_cast<PackedArrayRef<int64_t> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<int64_t>>(*this);
+ return _convert_array_from_variant<PackedInt64Array>(*this);
}
}
-Variant::operator Vector<float>() const {
+Variant::operator PackedFloat32Array() const {
if (type == PACKED_FLOAT32_ARRAY) {
return static_cast<PackedArrayRef<float> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<float>>(*this);
+ return _convert_array_from_variant<PackedFloat32Array>(*this);
}
}
-Variant::operator Vector<double>() const {
+Variant::operator PackedFloat64Array() const {
if (type == PACKED_FLOAT64_ARRAY) {
return static_cast<PackedArrayRef<double> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<double>>(*this);
+ return _convert_array_from_variant<PackedFloat64Array>(*this);
}
}
-Variant::operator Vector<String>() const {
+Variant::operator PackedStringArray() const {
if (type == PACKED_STRING_ARRAY) {
return static_cast<PackedArrayRef<String> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<String>>(*this);
+ return _convert_array_from_variant<PackedStringArray>(*this);
}
}
-Variant::operator Vector<Vector3>() const {
- if (type == PACKED_VECTOR3_ARRAY) {
- return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array;
+Variant::operator PackedVector2Array() const {
+ if (type == PACKED_VECTOR2_ARRAY) {
+ return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<Vector3>>(*this);
+ return _convert_array_from_variant<PackedVector2Array>(*this);
}
}
-Variant::operator Vector<Vector2>() const {
- if (type == PACKED_VECTOR2_ARRAY) {
- return static_cast<PackedArrayRef<Vector2> *>(_data.packed_array)->array;
+Variant::operator PackedVector3Array() const {
+ if (type == PACKED_VECTOR3_ARRAY) {
+ return static_cast<PackedArrayRef<Vector3> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<Vector2>>(*this);
+ return _convert_array_from_variant<PackedVector3Array>(*this);
}
}
-Variant::operator Vector<Color>() const {
+Variant::operator PackedColorArray() const {
if (type == PACKED_COLOR_ARRAY) {
return static_cast<PackedArrayRef<Color> *>(_data.packed_array)->array;
} else {
- return _convert_array_from_variant<Vector<Color>>(*this);
+ return _convert_array_from_variant<PackedColorArray>(*this);
}
}
@@ -2350,7 +2350,7 @@ Variant::operator Vector<Plane>() const {
}
Variant::operator Vector<Face3>() const {
- Vector<Vector3> va = operator Vector<Vector3>();
+ PackedVector3Array va = operator PackedVector3Array();
Vector<Face3> faces;
int va_size = va.size();
if (va_size == 0) {
@@ -2386,7 +2386,7 @@ Variant::operator Vector<Variant>() const {
}
Variant::operator Vector<StringName>() const {
- Vector<String> from = operator Vector<String>();
+ PackedStringArray from = operator PackedStringArray();
Vector<StringName> to;
int len = from.size();
to.resize(len);
@@ -2646,78 +2646,79 @@ Variant::Variant(const Array &p_array) {
memnew_placement(_data._mem, Array(p_array));
}
-Variant::Variant(const Vector<Plane> &p_array) {
- type = ARRAY;
-
- Array *plane_array = memnew_placement(_data._mem, Array);
-
- plane_array->resize(p_array.size());
-
- for (int i = 0; i < p_array.size(); i++) {
- plane_array->operator[](i) = Variant(p_array[i]);
- }
-}
-
-Variant::Variant(const Vector<::RID> &p_array) {
- type = ARRAY;
-
- Array *rid_array = memnew_placement(_data._mem, Array);
-
- rid_array->resize(p_array.size());
-
- for (int i = 0; i < p_array.size(); i++) {
- rid_array->set(i, Variant(p_array[i]));
- }
-}
-
-Variant::Variant(const Vector<uint8_t> &p_byte_array) {
+Variant::Variant(const PackedByteArray &p_byte_array) {
type = PACKED_BYTE_ARRAY;
_data.packed_array = PackedArrayRef<uint8_t>::create(p_byte_array);
}
-Variant::Variant(const Vector<int32_t> &p_int32_array) {
+Variant::Variant(const PackedInt32Array &p_int32_array) {
type = PACKED_INT32_ARRAY;
_data.packed_array = PackedArrayRef<int32_t>::create(p_int32_array);
}
-Variant::Variant(const Vector<int64_t> &p_int64_array) {
+Variant::Variant(const PackedInt64Array &p_int64_array) {
type = PACKED_INT64_ARRAY;
_data.packed_array = PackedArrayRef<int64_t>::create(p_int64_array);
}
-Variant::Variant(const Vector<float> &p_float32_array) {
+Variant::Variant(const PackedFloat32Array &p_float32_array) {
type = PACKED_FLOAT32_ARRAY;
_data.packed_array = PackedArrayRef<float>::create(p_float32_array);
}
-Variant::Variant(const Vector<double> &p_float64_array) {
+Variant::Variant(const PackedFloat64Array &p_float64_array) {
type = PACKED_FLOAT64_ARRAY;
_data.packed_array = PackedArrayRef<double>::create(p_float64_array);
}
-Variant::Variant(const Vector<String> &p_string_array) {
+Variant::Variant(const PackedStringArray &p_string_array) {
type = PACKED_STRING_ARRAY;
_data.packed_array = PackedArrayRef<String>::create(p_string_array);
}
-Variant::Variant(const Vector<Vector3> &p_vector3_array) {
- type = PACKED_VECTOR3_ARRAY;
- _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array);
-}
-
-Variant::Variant(const Vector<Vector2> &p_vector2_array) {
+Variant::Variant(const PackedVector2Array &p_vector2_array) {
type = PACKED_VECTOR2_ARRAY;
_data.packed_array = PackedArrayRef<Vector2>::create(p_vector2_array);
}
-Variant::Variant(const Vector<Color> &p_color_array) {
+Variant::Variant(const PackedVector3Array &p_vector3_array) {
+ type = PACKED_VECTOR3_ARRAY;
+ _data.packed_array = PackedArrayRef<Vector3>::create(p_vector3_array);
+}
+
+Variant::Variant(const PackedColorArray &p_color_array) {
type = PACKED_COLOR_ARRAY;
_data.packed_array = PackedArrayRef<Color>::create(p_color_array);
}
+/* helpers */
+Variant::Variant(const Vector<::RID> &p_array) {
+ type = ARRAY;
+
+ Array *rid_array = memnew_placement(_data._mem, Array);
+
+ rid_array->resize(p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ rid_array->set(i, Variant(p_array[i]));
+ }
+}
+
+Variant::Variant(const Vector<Plane> &p_array) {
+ type = ARRAY;
+
+ Array *plane_array = memnew_placement(_data._mem, Array);
+
+ plane_array->resize(p_array.size());
+
+ for (int i = 0; i < p_array.size(); i++) {
+ plane_array->operator[](i) = Variant(p_array[i]);
+ }
+}
+
Variant::Variant(const Vector<Face3> &p_face_array) {
- Vector<Vector3> vertices;
+ PackedVector3Array vertices;
int face_count = p_face_array.size();
vertices.resize(face_count * 3);
@@ -2737,7 +2738,6 @@ Variant::Variant(const Vector<Face3> &p_face_array) {
*this = vertices;
}
-/* helpers */
Variant::Variant(const Vector<Variant> &p_array) {
type = NIL;
Array arr;
@@ -2750,7 +2750,7 @@ Variant::Variant(const Vector<Variant> &p_array) {
Variant::Variant(const Vector<StringName> &p_array) {
type = NIL;
- Vector<String> v;
+ PackedStringArray v;
int len = p_array.size();
v.resize(len);
for (int i = 0; i < len; i++) {
@@ -3100,7 +3100,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_BYTE_ARRAY: {
- const Vector<uint8_t> &arr = PackedArrayRef<uint8_t>::get_array(_data.packed_array);
+ const PackedByteArray &arr = PackedArrayRef<uint8_t>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
const uint8_t *r = arr.ptr();
@@ -3111,7 +3111,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_INT32_ARRAY: {
- const Vector<int32_t> &arr = PackedArrayRef<int32_t>::get_array(_data.packed_array);
+ const PackedInt32Array &arr = PackedArrayRef<int32_t>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
const int32_t *r = arr.ptr();
@@ -3122,7 +3122,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_INT64_ARRAY: {
- const Vector<int64_t> &arr = PackedArrayRef<int64_t>::get_array(_data.packed_array);
+ const PackedInt64Array &arr = PackedArrayRef<int64_t>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
const int64_t *r = arr.ptr();
@@ -3133,7 +3133,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_FLOAT32_ARRAY: {
- const Vector<float> &arr = PackedArrayRef<float>::get_array(_data.packed_array);
+ const PackedFloat32Array &arr = PackedArrayRef<float>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
@@ -3149,7 +3149,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_FLOAT64_ARRAY: {
- const Vector<double> &arr = PackedArrayRef<double>::get_array(_data.packed_array);
+ const PackedFloat64Array &arr = PackedArrayRef<double>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
@@ -3166,7 +3166,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_STRING_ARRAY: {
uint32_t hash = HASH_MURMUR3_SEED;
- const Vector<String> &arr = PackedArrayRef<String>::get_array(_data.packed_array);
+ const PackedStringArray &arr = PackedArrayRef<String>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
@@ -3182,7 +3182,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_VECTOR2_ARRAY: {
uint32_t hash = HASH_MURMUR3_SEED;
- const Vector<Vector2> &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array);
+ const PackedVector2Array &arr = PackedArrayRef<Vector2>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
@@ -3199,7 +3199,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_VECTOR3_ARRAY: {
uint32_t hash = HASH_MURMUR3_SEED;
- const Vector<Vector3> &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array);
+ const PackedVector3Array &arr = PackedArrayRef<Vector3>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
@@ -3217,7 +3217,7 @@ uint32_t Variant::recursive_hash(int recursion_count) const {
} break;
case PACKED_COLOR_ARRAY: {
uint32_t hash = HASH_MURMUR3_SEED;
- const Vector<Color> &arr = PackedArrayRef<Color>::get_array(_data.packed_array);
+ const PackedColorArray &arr = PackedArrayRef<Color>::get_array(_data.packed_array);
int len = arr.size();
if (likely(len)) {
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 17e701cf66..d685444c30 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -70,6 +70,7 @@ typedef Vector<int32_t> PackedInt32Array;
typedef Vector<int64_t> PackedInt64Array;
typedef Vector<float> PackedFloat32Array;
typedef Vector<double> PackedFloat64Array;
+typedef Vector<real_t> PackedRealArray;
typedef Vector<String> PackedStringArray;
typedef Vector<Vector2> PackedVector2Array;
typedef Vector<Vector3> PackedVector3Array;
@@ -175,7 +176,7 @@ private:
struct PackedArrayRefBase {
SafeRefCount refcount;
_FORCE_INLINE_ PackedArrayRefBase *reference() {
- if (this->refcount.ref()) {
+ if (refcount.ref()) {
return this;
} else {
return nullptr;
@@ -403,21 +404,21 @@ public:
operator Dictionary() const;
operator Array() const;
- operator Vector<uint8_t>() const;
- operator Vector<int32_t>() const;
- operator Vector<int64_t>() const;
- operator Vector<float>() const;
- operator Vector<double>() const;
- operator Vector<String>() const;
- operator Vector<Vector3>() const;
- operator Vector<Color>() const;
+ operator PackedByteArray() const;
+ operator PackedInt32Array() const;
+ operator PackedInt64Array() const;
+ operator PackedFloat32Array() const;
+ operator PackedFloat64Array() const;
+ operator PackedStringArray() const;
+ operator PackedVector3Array() const;
+ operator PackedVector2Array() const;
+ operator PackedColorArray() const;
+
+ operator Vector<::RID>() const;
operator Vector<Plane>() const;
operator Vector<Face3>() const;
-
operator Vector<Variant>() const;
operator Vector<StringName>() const;
- operator Vector<::RID>() const;
- operator Vector<Vector2>() const;
// some core type enums to convert to
operator Side() const;
@@ -472,21 +473,21 @@ public:
Variant(const Dictionary &p_dictionary);
Variant(const Array &p_array);
+ Variant(const PackedByteArray &p_byte_array);
+ Variant(const PackedInt32Array &p_int32_array);
+ Variant(const PackedInt64Array &p_int64_array);
+ Variant(const PackedFloat32Array &p_float32_array);
+ Variant(const PackedFloat64Array &p_float64_array);
+ Variant(const PackedStringArray &p_string_array);
+ Variant(const PackedVector2Array &p_vector2_array);
+ Variant(const PackedVector3Array &p_vector3_array);
+ Variant(const PackedColorArray &p_color_array);
+
+ Variant(const Vector<::RID> &p_array); // helper
Variant(const Vector<Plane> &p_array); // helper
- Variant(const Vector<uint8_t> &p_byte_array);
- Variant(const Vector<int32_t> &p_int32_array);
- Variant(const Vector<int64_t> &p_int64_array);
- Variant(const Vector<float> &p_float32_array);
- Variant(const Vector<double> &p_float64_array);
- Variant(const Vector<String> &p_string_array);
- Variant(const Vector<Vector3> &p_vector3_array);
- Variant(const Vector<Color> &p_color_array);
Variant(const Vector<Face3> &p_face_array);
-
Variant(const Vector<Variant> &p_array);
Variant(const Vector<StringName> &p_array);
- Variant(const Vector<::RID> &p_array); // helper
- Variant(const Vector<Vector2> &p_array); // helper
Variant(const IPAddress &p_address);
@@ -501,6 +502,7 @@ public:
VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyAxis)
VARIANT_ENUM_CLASS_CONSTRUCTOR(JoyButton)
VARIANT_ENUM_CLASS_CONSTRUCTOR(Key)
+ VARIANT_ENUM_CLASS_CONSTRUCTOR(KeyLocation)
VARIANT_ENUM_CLASS_CONSTRUCTOR(MIDIMessage)
VARIANT_ENUM_CLASS_CONSTRUCTOR(MouseButton)
@@ -571,6 +573,7 @@ public:
static ValidatedBuiltInMethod get_validated_builtin_method(Variant::Type p_type, const StringName &p_method);
static PTRBuiltInMethod get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method);
+ static MethodInfo get_builtin_method_info(Variant::Type p_type, const StringName &p_method);
static int get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method);
static Variant::Type get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument);
static String get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument);
@@ -706,9 +709,20 @@ public:
bool has_key(const Variant &p_key, bool &r_valid) const;
/* Generic */
-
- void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr);
- Variant get(const Variant &p_index, bool *r_valid = nullptr) const;
+ enum VariantSetError {
+ SET_OK,
+ SET_KEYED_ERR,
+ SET_NAMED_ERR,
+ SET_INDEXED_ERR
+ };
+ enum VariantGetError {
+ GET_OK,
+ GET_KEYED_ERR,
+ GET_NAMED_ERR,
+ GET_INDEXED_ERR
+ };
+ void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr, VariantSetError *err_code = nullptr);
+ Variant get(const Variant &p_index, bool *r_valid = nullptr, VariantGetError *err_code = nullptr) const;
bool in(const Variant &p_index, bool *r_valid = nullptr) const;
bool iter_init(Variant &r_iter, bool &r_valid) const;
@@ -768,8 +782,8 @@ public:
static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = nullptr);
static void get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums);
- static void get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations);
- static int get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid = nullptr);
+ static void get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations);
+ static int get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid = nullptr);
typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud);
typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value);
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f041d2c95e..b551a7059e 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -666,7 +666,7 @@ struct _VariantCall {
CharString cs;
cs.resize(p_instance->size() + 1);
memcpy(cs.ptrw(), r, p_instance->size());
- cs[p_instance->size()] = 0;
+ cs[(int)p_instance->size()] = 0;
s = cs.get_data();
}
@@ -885,7 +885,7 @@ struct _VariantCall {
ERR_FAIL_COND_V_MSG(size % sizeof(int32_t), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit integer) to convert to PackedInt32Array.");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(int32_t));
- ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed.
+ ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
memcpy(dest.ptrw(), r, dest.size() * sizeof(int32_t));
return dest;
}
@@ -899,7 +899,7 @@ struct _VariantCall {
ERR_FAIL_COND_V_MSG(size % sizeof(int64_t), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit integer) to convert to PackedInt64Array.");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(int64_t));
- ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed.
+ ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
memcpy(dest.ptrw(), r, dest.size() * sizeof(int64_t));
return dest;
}
@@ -913,7 +913,7 @@ struct _VariantCall {
ERR_FAIL_COND_V_MSG(size % sizeof(float), dest, "PackedByteArray size must be a multiple of 4 (size of 32-bit float) to convert to PackedFloat32Array.");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(float));
- ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed.
+ ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
memcpy(dest.ptrw(), r, dest.size() * sizeof(float));
return dest;
}
@@ -927,7 +927,7 @@ struct _VariantCall {
ERR_FAIL_COND_V_MSG(size % sizeof(double), dest, "PackedByteArray size must be a multiple of 8 (size of 64-bit double) to convert to PackedFloat64Array.");
const uint8_t *r = p_instance->ptr();
dest.resize(size / sizeof(double));
- ERR_FAIL_COND_V(dest.size() == 0, dest); // Avoid UB in case resize failed.
+ ERR_FAIL_COND_V(dest.is_empty(), dest); // Avoid UB in case resize failed.
memcpy(dest.ptrw(), r, dest.size() * sizeof(double));
return dest;
}
@@ -1071,14 +1071,14 @@ struct _VariantCall {
static ConstantData *constant_data;
- static void add_constant(int p_type, StringName p_constant_name, int64_t p_constant_value) {
+ static void add_constant(int p_type, const StringName &p_constant_name, int64_t p_constant_value) {
constant_data[p_type].value[p_constant_name] = p_constant_value;
#ifdef DEBUG_ENABLED
constant_data[p_type].value_ordered.push_back(p_constant_name);
#endif
}
- static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) {
+ static void add_variant_constant(int p_type, const StringName &p_constant_name, const Variant &p_constant_value) {
constant_data[p_type].variant_value[p_constant_name] = p_constant_value;
#ifdef DEBUG_ENABLED
constant_data[p_type].variant_value_ordered.push_back(p_constant_name);
@@ -1091,7 +1091,7 @@ struct _VariantCall {
static EnumData *enum_data;
- static void add_enum_constant(int p_type, StringName p_enum_type_name, StringName p_enumeration_name, int p_enum_value) {
+ static void add_enum_constant(int p_type, const StringName &p_enum_type_name, const StringName &p_enumeration_name, int p_enum_value) {
enum_data[p_type].value[p_enum_type_name][p_enumeration_name] = p_enum_value;
}
};
@@ -1114,6 +1114,46 @@ struct VariantBuiltInMethodInfo {
Variant::Type return_type;
int argument_count = 0;
Variant::Type (*get_argument_type)(int p_arg) = nullptr;
+
+ MethodInfo get_method_info(const StringName &p_name) const {
+ MethodInfo mi;
+ mi.name = p_name;
+
+ if (has_return_type) {
+ mi.return_val.type = return_type;
+ if (mi.return_val.type == Variant::NIL) {
+ mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ }
+
+ if (is_const) {
+ mi.flags |= METHOD_FLAG_CONST;
+ }
+ if (is_vararg) {
+ mi.flags |= METHOD_FLAG_VARARG;
+ }
+ if (is_static) {
+ mi.flags |= METHOD_FLAG_STATIC;
+ }
+
+ for (int i = 0; i < argument_count; i++) {
+ PropertyInfo pi;
+#ifdef DEBUG_METHODS_ENABLED
+ pi.name = argument_names[i];
+#else
+ pi.name = "arg" + itos(i + 1);
+#endif
+ pi.type = (*get_argument_type)(i);
+ if (pi.type == Variant::NIL) {
+ pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
+ }
+ mi.arguments.push_back(pi);
+ }
+
+ mi.default_arguments = default_arguments;
+
+ return mi;
+ }
};
typedef OAHashMap<StringName, VariantBuiltInMethodInfo> BuiltinMethodMap;
@@ -1268,6 +1308,13 @@ Variant::PTRBuiltInMethod Variant::get_ptr_builtin_method(Variant::Type p_type,
return method->ptrcall;
}
+MethodInfo Variant::get_builtin_method_info(Variant::Type p_type, const StringName &p_method) {
+ ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, MethodInfo());
+ const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
+ ERR_FAIL_NULL_V(method, MethodInfo());
+ return method->get_method_info(p_method);
+}
+
int Variant::get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method) {
ERR_FAIL_INDEX_V(p_type, Variant::VARIANT_MAX, 0);
const VariantBuiltInMethodInfo *method = builtin_method_info[p_type].lookup_ptr(p_method);
@@ -1378,43 +1425,7 @@ void Variant::get_method_list(List<MethodInfo> *p_list) const {
for (const StringName &E : builtin_method_names[type]) {
const VariantBuiltInMethodInfo *method = builtin_method_info[type].lookup_ptr(E);
ERR_CONTINUE(!method);
-
- MethodInfo mi;
- mi.name = E;
-
- //return type
- if (method->has_return_type) {
- mi.return_val.type = method->return_type;
- if (mi.return_val.type == Variant::NIL) {
- mi.return_val.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
- }
-
- if (method->is_const) {
- mi.flags |= METHOD_FLAG_CONST;
- }
- if (method->is_vararg) {
- mi.flags |= METHOD_FLAG_VARARG;
- }
- if (method->is_static) {
- mi.flags |= METHOD_FLAG_STATIC;
- }
- for (int i = 0; i < method->argument_count; i++) {
- PropertyInfo pi;
-#ifdef DEBUG_METHODS_ENABLED
- pi.name = method->argument_names[i];
-#else
- pi.name = "arg" + itos(i + 1);
-#endif
- pi.type = method->get_argument_type(i);
- if (pi.type == Variant::NIL) {
- pi.usage |= PROPERTY_USAGE_NIL_IS_VARIANT;
- }
- mi.arguments.push_back(pi);
- }
-
- mi.default_arguments = method->default_arguments;
- p_list->push_back(mi);
+ p_list->push_back(method->get_method_info(E));
}
}
}
@@ -1493,7 +1504,7 @@ void Variant::get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums
}
}
-void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations) {
+void Variant::get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations) {
ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX);
_VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type];
@@ -1505,7 +1516,7 @@ void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_
}
}
-int Variant::get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid) {
+int Variant::get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid) {
if (r_valid) {
*r_valid = false;
}
@@ -1796,6 +1807,8 @@ static void _register_variant_builtin_methods() {
bind_method(Vector2i, aspect, sarray(), varray());
bind_method(Vector2i, max_axis_index, sarray(), varray());
bind_method(Vector2i, min_axis_index, sarray(), varray());
+ bind_method(Vector2i, distance_to, sarray("to"), varray());
+ bind_method(Vector2i, distance_squared_to, sarray("to"), varray());
bind_method(Vector2i, length, sarray(), varray());
bind_method(Vector2i, length_squared, sarray(), varray());
bind_method(Vector2i, sign, sarray(), varray());
@@ -1886,6 +1899,8 @@ static void _register_variant_builtin_methods() {
bind_method(Vector3i, min_axis_index, sarray(), varray());
bind_method(Vector3i, max_axis_index, sarray(), varray());
+ bind_method(Vector3i, distance_to, sarray("to"), varray());
+ bind_method(Vector3i, distance_squared_to, sarray("to"), varray());
bind_method(Vector3i, length, sarray(), varray());
bind_method(Vector3i, length_squared, sarray(), varray());
bind_method(Vector3i, sign, sarray(), varray());
@@ -1932,6 +1947,8 @@ static void _register_variant_builtin_methods() {
bind_method(Vector4i, abs, sarray(), varray());
bind_method(Vector4i, clamp, sarray("min", "max"), varray());
bind_method(Vector4i, snapped, sarray("step"), varray());
+ bind_method(Vector4i, distance_to, sarray("to"), varray());
+ bind_method(Vector4i, distance_squared_to, sarray("to"), varray());
/* Plane */
@@ -2195,6 +2212,7 @@ static void _register_variant_builtin_methods() {
bind_method(Dictionary, values, sarray(), varray());
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
+ bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, make_read_only, sarray(), varray());
bind_method(Dictionary, is_read_only, sarray(), varray());
diff --git a/core/variant/variant_callable.cpp b/core/variant/variant_callable.cpp
new file mode 100644
index 0000000000..dc31b6d1ac
--- /dev/null
+++ b/core/variant/variant_callable.cpp
@@ -0,0 +1,81 @@
+/**************************************************************************/
+/* variant_callable.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "variant_callable.h"
+
+#include "core/templates/hashfuncs.h"
+
+bool VariantCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() == p_b->hash();
+}
+
+bool VariantCallable::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+ return p_a->hash() < p_b->hash();
+}
+
+uint32_t VariantCallable::hash() const {
+ return h;
+}
+
+String VariantCallable::get_as_text() const {
+ return vformat("%s::%s (Callable)", Variant::get_type_name(variant.get_type()), method);
+}
+
+CallableCustom::CompareEqualFunc VariantCallable::get_compare_equal_func() const {
+ return compare_equal;
+}
+
+CallableCustom::CompareLessFunc VariantCallable::get_compare_less_func() const {
+ return compare_less;
+}
+
+bool VariantCallable::is_valid() const {
+ return Variant::has_builtin_method(variant.get_type(), method);
+}
+
+StringName VariantCallable::get_method() const {
+ return method;
+}
+
+ObjectID VariantCallable::get_object() const {
+ return ObjectID();
+}
+
+void VariantCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+ Variant v = variant;
+ v.callp(method, p_arguments, p_argcount, r_return_value, r_call_error);
+}
+
+VariantCallable::VariantCallable(const Variant &p_variant, const StringName &p_method) {
+ variant = p_variant;
+ method = p_method;
+ h = variant.hash();
+ h = hash_murmur3_one_64(Variant::get_builtin_method_hash(variant.get_type(), method), h);
+}
diff --git a/core/variant/variant_callable.h b/core/variant/variant_callable.h
new file mode 100644
index 0000000000..3f2b058aaf
--- /dev/null
+++ b/core/variant/variant_callable.h
@@ -0,0 +1,58 @@
+/**************************************************************************/
+/* variant_callable.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef VARIANT_CALLABLE_H
+#define VARIANT_CALLABLE_H
+
+#include "core/variant/callable.h"
+#include "core/variant/variant.h"
+
+class VariantCallable : public CallableCustom {
+ Variant variant;
+ StringName method;
+ uint32_t h = 0;
+
+ static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+ static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+public:
+ uint32_t hash() const override;
+ String get_as_text() const override;
+ CompareEqualFunc get_compare_equal_func() const override;
+ CompareLessFunc get_compare_less_func() const override;
+ bool is_valid() const override;
+ StringName get_method() const override;
+ ObjectID get_object() const override;
+ void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override;
+
+ VariantCallable(const Variant &p_variant, const StringName &p_method);
+};
+
+#endif // VARIANT_CALLABLE_H
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 116210e8de..171074188f 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -1302,192 +1302,192 @@ struct VariantInitializer<Object *> {
};
template <class T>
-struct VariantZeroAssigner {
+struct VariantDefaultInitializer {
};
template <>
-struct VariantZeroAssigner<bool> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_bool(v) = false; }
+struct VariantDefaultInitializer<bool> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_bool(v) = false; }
};
template <>
-struct VariantZeroAssigner<int64_t> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int(v) = 0; }
+struct VariantDefaultInitializer<int64_t> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int(v) = 0; }
};
template <>
-struct VariantZeroAssigner<double> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; }
+struct VariantDefaultInitializer<double> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; }
};
template <>
-struct VariantZeroAssigner<float> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float(v) = 0.0; }
+struct VariantDefaultInitializer<float> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float(v) = 0.0; }
};
template <>
-struct VariantZeroAssigner<String> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string(v) = String(); }
+struct VariantDefaultInitializer<String> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string(v) = String(); }
};
template <>
-struct VariantZeroAssigner<Vector2> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); }
+struct VariantDefaultInitializer<Vector2> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2(v) = Vector2(); }
};
template <>
-struct VariantZeroAssigner<Vector2i> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); }
+struct VariantDefaultInitializer<Vector2i> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2i(v) = Vector2i(); }
};
template <>
-struct VariantZeroAssigner<Rect2> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); }
+struct VariantDefaultInitializer<Rect2> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2(v) = Rect2(); }
};
template <>
-struct VariantZeroAssigner<Rect2i> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); }
+struct VariantDefaultInitializer<Rect2i> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rect2i(v) = Rect2i(); }
};
template <>
-struct VariantZeroAssigner<Vector3> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); }
+struct VariantDefaultInitializer<Vector3> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3(v) = Vector3(); }
};
template <>
-struct VariantZeroAssigner<Vector3i> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); }
+struct VariantDefaultInitializer<Vector3i> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3i(v) = Vector3i(); }
};
template <>
-struct VariantZeroAssigner<Vector4> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); }
+struct VariantDefaultInitializer<Vector4> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4(v) = Vector4(); }
};
template <>
-struct VariantZeroAssigner<Vector4i> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); }
+struct VariantDefaultInitializer<Vector4i> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector4i(v) = Vector4i(); }
};
template <>
-struct VariantZeroAssigner<Transform2D> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); }
+struct VariantDefaultInitializer<Transform2D> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform2d(v) = Transform2D(); }
};
template <>
-struct VariantZeroAssigner<Plane> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_plane(v) = Plane(); }
+struct VariantDefaultInitializer<Plane> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_plane(v) = Plane(); }
};
template <>
-struct VariantZeroAssigner<Quaternion> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); }
+struct VariantDefaultInitializer<Quaternion> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_quaternion(v) = Quaternion(); }
};
template <>
-struct VariantZeroAssigner<AABB> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); }
+struct VariantDefaultInitializer<AABB> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_aabb(v) = AABB(); }
};
template <>
-struct VariantZeroAssigner<Basis> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_basis(v) = Basis(); }
+struct VariantDefaultInitializer<Basis> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_basis(v) = Basis(); }
};
template <>
-struct VariantZeroAssigner<Transform3D> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); }
+struct VariantDefaultInitializer<Transform3D> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_transform(v) = Transform3D(); }
};
template <>
-struct VariantZeroAssigner<Projection> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_projection(v) = Projection(); }
+struct VariantDefaultInitializer<Projection> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_projection(v) = Projection(); }
};
template <>
-struct VariantZeroAssigner<Color> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color(v) = Color(); }
+struct VariantDefaultInitializer<Color> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color(v) = Color(); }
};
template <>
-struct VariantZeroAssigner<StringName> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); }
+struct VariantDefaultInitializer<StringName> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_name(v) = StringName(); }
};
template <>
-struct VariantZeroAssigner<NodePath> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); }
+struct VariantDefaultInitializer<NodePath> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_node_path(v) = NodePath(); }
};
template <>
-struct VariantZeroAssigner<::RID> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_rid(v) = RID(); }
+struct VariantDefaultInitializer<::RID> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_rid(v) = RID(); }
};
template <>
-struct VariantZeroAssigner<Callable> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_callable(v) = Callable(); }
+struct VariantDefaultInitializer<Callable> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_callable(v) = Callable(); }
};
template <>
-struct VariantZeroAssigner<Signal> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_signal(v) = Signal(); }
+struct VariantDefaultInitializer<Signal> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_signal(v) = Signal(); }
};
template <>
-struct VariantZeroAssigner<Dictionary> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); }
+struct VariantDefaultInitializer<Dictionary> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_dictionary(v) = Dictionary(); }
};
template <>
-struct VariantZeroAssigner<Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_array(v) = Array(); }
+struct VariantDefaultInitializer<Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_array(v) = Array(); }
};
template <>
-struct VariantZeroAssigner<PackedByteArray> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); }
+struct VariantDefaultInitializer<PackedByteArray> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_byte_array(v) = PackedByteArray(); }
};
template <>
-struct VariantZeroAssigner<PackedInt32Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); }
+struct VariantDefaultInitializer<PackedInt32Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int32_array(v) = PackedInt32Array(); }
};
template <>
-struct VariantZeroAssigner<PackedInt64Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); }
+struct VariantDefaultInitializer<PackedInt64Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_int64_array(v) = PackedInt64Array(); }
};
template <>
-struct VariantZeroAssigner<PackedFloat32Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); }
+struct VariantDefaultInitializer<PackedFloat32Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float32_array(v) = PackedFloat32Array(); }
};
template <>
-struct VariantZeroAssigner<PackedFloat64Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); }
+struct VariantDefaultInitializer<PackedFloat64Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_float64_array(v) = PackedFloat64Array(); }
};
template <>
-struct VariantZeroAssigner<PackedStringArray> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); }
+struct VariantDefaultInitializer<PackedStringArray> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_string_array(v) = PackedStringArray(); }
};
template <>
-struct VariantZeroAssigner<PackedVector2Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); }
+struct VariantDefaultInitializer<PackedVector2Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector2_array(v) = PackedVector2Array(); }
};
template <>
-struct VariantZeroAssigner<PackedVector3Array> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); }
+struct VariantDefaultInitializer<PackedVector3Array> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_vector3_array(v) = PackedVector3Array(); }
};
template <>
-struct VariantZeroAssigner<PackedColorArray> {
- static _FORCE_INLINE_ void zero(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); }
+struct VariantDefaultInitializer<PackedColorArray> {
+ static _FORCE_INLINE_ void init(Variant *v) { *VariantInternal::get_color_array(v) = PackedColorArray(); }
};
template <class T>
@@ -1504,7 +1504,7 @@ struct VariantTypeChanger {
VariantInitializer<T>::init(v);
}
- VariantZeroAssigner<T>::zero(v);
+ VariantDefaultInitializer<T>::init(v);
}
};
diff --git a/core/variant/variant_op.cpp b/core/variant/variant_op.cpp
index aed83ac010..4f9c38dc4c 100644
--- a/core/variant/variant_op.cpp
+++ b/core/variant/variant_op.cpp
@@ -412,6 +412,15 @@ void Variant::_register_variant_operators() {
register_op<OperatorEvaluatorDivNZ<Vector4, Vector4i, double>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::FLOAT);
register_op<OperatorEvaluatorDivNZ<Vector4i, Vector4i, int64_t>>(Variant::OP_DIVIDE, Variant::VECTOR4I, Variant::INT);
+ register_op<OperatorEvaluatorDiv<Transform2D, Transform2D, int64_t>>(Variant::OP_DIVIDE, Variant::TRANSFORM2D, Variant::INT);
+ register_op<OperatorEvaluatorDiv<Transform2D, Transform2D, double>>(Variant::OP_DIVIDE, Variant::TRANSFORM2D, Variant::FLOAT);
+
+ register_op<OperatorEvaluatorDiv<Transform3D, Transform3D, int64_t>>(Variant::OP_DIVIDE, Variant::TRANSFORM3D, Variant::INT);
+ register_op<OperatorEvaluatorDiv<Transform3D, Transform3D, double>>(Variant::OP_DIVIDE, Variant::TRANSFORM3D, Variant::FLOAT);
+
+ register_op<OperatorEvaluatorDiv<Basis, Basis, int64_t>>(Variant::OP_DIVIDE, Variant::BASIS, Variant::INT);
+ register_op<OperatorEvaluatorDiv<Basis, Basis, double>>(Variant::OP_DIVIDE, Variant::BASIS, Variant::FLOAT);
+
register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, double>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::FLOAT);
register_op<OperatorEvaluatorDiv<Quaternion, Quaternion, int64_t>>(Variant::OP_DIVIDE, Variant::QUATERNION, Variant::INT);
diff --git a/core/variant/variant_op.h b/core/variant/variant_op.h
index 9e6367ab6d..17ad126891 100644
--- a/core/variant/variant_op.h
+++ b/core/variant/variant_op.h
@@ -549,14 +549,14 @@ public:
class OperatorEvaluatorEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.get_validated_object();
- const Object *b = p_right.get_validated_object();
+ const ObjectID &a = VariantInternal::get_object_id(&p_left);
+ const ObjectID &b = VariantInternal::get_object_id(&p_right);
*r_ret = a == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->get_validated_object();
- const Object *b = right->get_validated_object();
+ const ObjectID &a = VariantInternal::get_object_id(left);
+ const ObjectID &b = VariantInternal::get_object_id(right);
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -568,12 +568,12 @@ public:
class OperatorEvaluatorEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *a = p_left.get_validated_object();
+ const Object *a = p_left.operator Object *();
*r_ret = a == nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *a = left->get_validated_object();
+ const Object *a = left->operator Object *();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a == nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -585,12 +585,12 @@ public:
class OperatorEvaluatorEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- const Object *b = p_right.get_validated_object();
+ const Object *b = p_right.operator Object *();
*r_ret = nullptr == b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- const Object *b = right->get_validated_object();
+ const Object *b = right->operator Object *();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr == b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -620,14 +620,14 @@ public:
class OperatorEvaluatorNotEqualObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.get_validated_object();
- Object *b = p_right.get_validated_object();
+ const ObjectID &a = VariantInternal::get_object_id(&p_left);
+ const ObjectID &b = VariantInternal::get_object_id(&p_right);
*r_ret = a != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->get_validated_object();
- Object *b = right->get_validated_object();
+ const ObjectID &a = VariantInternal::get_object_id(left);
+ const ObjectID &b = VariantInternal::get_object_id(right);
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -639,12 +639,12 @@ public:
class OperatorEvaluatorNotEqualObjectNil {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *a = p_left.get_validated_object();
+ Object *a = p_left.operator Object *();
*r_ret = a != nullptr;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *a = left->get_validated_object();
+ Object *a = left->operator Object *();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = a != nullptr;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
@@ -656,12 +656,12 @@ public:
class OperatorEvaluatorNotEqualNilObject {
public:
static void evaluate(const Variant &p_left, const Variant &p_right, Variant *r_ret, bool &r_valid) {
- Object *b = p_right.get_validated_object();
+ Object *b = p_right.operator Object *();
*r_ret = nullptr != b;
r_valid = true;
}
static inline void validated_evaluate(const Variant *left, const Variant *right, Variant *r_ret) {
- Object *b = right->get_validated_object();
+ Object *b = right->operator Object *();
*VariantGetInternalPtr<bool>::get_ptr(r_ret) = nullptr != b;
}
static void ptr_evaluate(const void *left, const void *right, void *r_ret) {
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index 86e7654090..e35751fd61 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -1026,7 +1026,10 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
Ref<Resource> res;
Error err = p_res_parser->ext_func(p_res_parser->userdata, p_stream, res, line, r_err_str);
if (err) {
- return err;
+ // If the file is missing, the error can be ignored.
+ if (err != ERR_FILE_NOT_FOUND && err != ERR_CANT_OPEN) {
+ return err;
+ }
}
value = res;
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 05f7abf32c..50c9c10987 100644
--- a/core/variant/variant_setget.cpp
+++ b/core/variant/variant_setget.cpp
@@ -30,6 +30,8 @@
#include "variant_setget.h"
+#include "variant_callable.h"
+
struct VariantSetterGetterInfo {
void (*setter)(Variant *base, const Variant *value, bool &valid);
void (*getter)(const Variant *base, Variant *value);
@@ -264,42 +266,45 @@ void Variant::set_named(const StringName &p_member, const Variant &p_value, bool
}
Variant Variant::get_named(const StringName &p_member, bool &r_valid) const {
- Variant ret;
uint32_t s = variant_setters_getters[type].size();
if (s) {
for (uint32_t i = 0; i < s; i++) {
if (variant_setters_getters_names[type][i] == p_member) {
+ Variant ret;
variant_setters_getters[type][i].getter(this, &ret);
r_valid = true;
return ret;
}
}
+ }
- r_valid = false;
-
- } else if (type == Variant::OBJECT) {
- Object *obj = get_validated_object();
- if (!obj) {
- r_valid = false;
- return "Instance base is null.";
- } else {
- return obj->get(p_member, &r_valid);
- }
- } else if (type == Variant::DICTIONARY) {
- const Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member);
- if (v) {
- r_valid = true;
-
- return *v;
- } else {
- r_valid = false;
- }
-
- } else {
- r_valid = false;
+ switch (type) {
+ case Variant::OBJECT: {
+ Object *obj = get_validated_object();
+ if (!obj) {
+ r_valid = false;
+ return "Instance base is null.";
+ } else {
+ return obj->get(p_member, &r_valid);
+ }
+ } break;
+ case Variant::DICTIONARY: {
+ const Variant *v = VariantGetInternalPtr<Dictionary>::get_ptr(this)->getptr(p_member);
+ if (v) {
+ r_valid = true;
+ return *v;
+ }
+ } break;
+ default: {
+ if (Variant::has_builtin_method(type, p_member)) {
+ r_valid = true;
+ return Callable(memnew(VariantCallable(*this, p_member)));
+ }
+ } break;
}
- return ret;
+ r_valid = false;
+ return Variant();
}
/**** INDEXED SETTERS AND GETTERS ****/
@@ -1166,30 +1171,48 @@ bool Variant::has_key(const Variant &p_key, bool &r_valid) const {
}
}
-void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid) {
+void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid, VariantSetError *err_code) {
+ if (err_code) {
+ *err_code = VariantSetError::SET_OK;
+ }
if (type == DICTIONARY || type == OBJECT) {
bool valid;
set_keyed(p_index, p_value, valid);
if (r_valid) {
*r_valid = valid;
+ if (!valid && err_code) {
+ *err_code = VariantSetError::SET_KEYED_ERR;
+ }
}
} else {
bool valid = false;
if (p_index.get_type() == STRING_NAME) {
set_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), p_value, valid);
+ if (!valid && err_code) {
+ *err_code = VariantSetError::SET_NAMED_ERR;
+ }
} else if (p_index.get_type() == INT) {
bool obb;
set_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), p_value, valid, obb);
if (obb) {
valid = false;
+ if (err_code) {
+ *err_code = VariantSetError::SET_INDEXED_ERR;
+ }
}
} else if (p_index.get_type() == STRING) { // less efficient version of named
set_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), p_value, valid);
+ if (!valid && err_code) {
+ *err_code = VariantSetError::SET_NAMED_ERR;
+ }
} else if (p_index.get_type() == FLOAT) { // less efficient version of indexed
bool obb;
set_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), p_value, valid, obb);
if (obb) {
valid = false;
+ if (err_code) {
+ *err_code = VariantSetError::SET_INDEXED_ERR;
+ }
}
}
if (r_valid) {
@@ -1198,31 +1221,49 @@ void Variant::set(const Variant &p_index, const Variant &p_value, bool *r_valid)
}
}
-Variant Variant::get(const Variant &p_index, bool *r_valid) const {
+Variant Variant::get(const Variant &p_index, bool *r_valid, VariantGetError *err_code) const {
+ if (err_code) {
+ *err_code = VariantGetError::GET_OK;
+ }
Variant ret;
if (type == DICTIONARY || type == OBJECT) {
bool valid;
ret = get_keyed(p_index, valid);
if (r_valid) {
*r_valid = valid;
+ if (!valid && err_code) {
+ *err_code = VariantGetError::GET_KEYED_ERR;
+ }
}
} else {
bool valid = false;
if (p_index.get_type() == STRING_NAME) {
ret = get_named(*VariantGetInternalPtr<StringName>::get_ptr(&p_index), valid);
+ if (!valid && err_code) {
+ *err_code = VariantGetError::GET_NAMED_ERR;
+ }
} else if (p_index.get_type() == INT) {
bool obb;
ret = get_indexed(*VariantGetInternalPtr<int64_t>::get_ptr(&p_index), valid, obb);
if (obb) {
valid = false;
+ if (err_code) {
+ *err_code = VariantGetError::GET_INDEXED_ERR;
+ }
}
} else if (p_index.get_type() == STRING) { // less efficient version of named
ret = get_named(*VariantGetInternalPtr<String>::get_ptr(&p_index), valid);
+ if (!valid && err_code) {
+ *err_code = VariantGetError::GET_NAMED_ERR;
+ }
} else if (p_index.get_type() == FLOAT) { // less efficient version of indexed
bool obb;
ret = get_indexed(*VariantGetInternalPtr<double>::get_ptr(&p_index), valid, obb);
if (obb) {
valid = false;
+ if (err_code) {
+ *err_code = VariantGetError::GET_INDEXED_ERR;
+ }
}
}
if (r_valid) {
diff --git a/core/version.h b/core/version.h
index 5ddb09284e..abb81312ac 100644
--- a/core/version.h
+++ b/core/version.h
@@ -33,6 +33,12 @@
#include "core/version_generated.gen.h"
+// Copied from typedefs.h to stay lean.
+#ifndef _STR
+#define _STR(m_x) #m_x
+#define _MKSTR(m_x) _STR(m_x)
+#endif
+
// Godot versions are of the form <major>.<minor> for the initial release,
// and then <major>.<minor>.<patch> for subsequent bugfix releases where <patch> != 0
// That's arbitrary, but we find it pretty and it's the current policy.