summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/engine.cpp16
-rw-r--r--core/config/project_settings.cpp114
-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_builders.py26
-rw-r--r--core/core_constants.cpp2
-rw-r--r--core/core_constants.h2
-rw-r--r--core/debugger/engine_debugger.cpp2
-rw-r--r--core/extension/extension_api_dump.cpp11
-rw-r--r--core/extension/gdextension.cpp26
-rw-r--r--core/extension/gdextension.h4
-rw-r--r--core/extension/gdextension_compat_hashes.cpp10
-rw-r--r--core/extension/gdextension_compat_hashes.h2
-rw-r--r--core/input/input.cpp189
-rw-r--r--core/input/input.h27
-rw-r--r--core/input/input_map.cpp22
-rw-r--r--core/input/input_map.h5
-rw-r--r--core/io/file_access_pack.cpp4
-rw-r--r--core/io/http_client.cpp2
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/image.h1
-rw-r--r--core/io/marshalls.cpp4
-rw-r--r--core/io/resource.cpp10
-rw-r--r--core/io/resource.h1
-rw-r--r--core/io/resource_format_binary.cpp8
-rw-r--r--core/io/resource_loader.cpp24
-rw-r--r--core/io/resource_saver.cpp10
-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.cpp5
-rw-r--r--core/math/basis.cpp23
-rw-r--r--core/math/basis.h15
-rw-r--r--core/math/dynamic_bvh.h18
-rw-r--r--core/math/geometry_3d.h16
-rw-r--r--core/math/math_funcs.h16
-rw-r--r--core/math/rect2.h4
-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/vector2i.h8
-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/make_virtuals.py236
-rw-r--r--core/object/message_queue.cpp8
-rw-r--r--core/object/object.cpp15
-rw-r--r--core/object/object.h14
-rw-r--r--core/object/script_language.cpp69
-rw-r--r--core/object/script_language.h11
-rw-r--r--core/object/script_language_extension.cpp3
-rw-r--r--core/object/script_language_extension.h3
-rw-r--r--core/object/undo_redo.cpp8
-rw-r--r--core/object/undo_redo.h2
-rw-r--r--core/object/worker_thread_pool.cpp14
-rw-r--r--core/object/worker_thread_pool.h3
-rw-r--r--core/os/condition_variable.h11
-rw-r--r--core/os/keyboard.cpp4
-rw-r--r--core/os/mutex.cpp8
-rw-r--r--core/os/mutex.h27
-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/semaphore.h16
-rw-r--r--core/os/thread.cpp6
-rw-r--r--core/os/thread.h9
-rw-r--r--core/register_core_types.cpp21
-rw-r--r--core/string/string_name.cpp2
-rw-r--r--core/string/translation.cpp16
-rw-r--r--core/string/ustring.cpp112
-rw-r--r--core/string/ustring.h10
-rw-r--r--core/templates/hashfuncs.h2
-rw-r--r--core/templates/list.h39
-rw-r--r--core/templates/paged_allocator.h6
-rw-r--r--core/templates/paged_array.h21
-rw-r--r--core/templates/rb_map.h18
-rw-r--r--core/typedefs.h5
-rw-r--r--core/variant/callable_bind.cpp4
-rw-r--r--core/variant/dictionary.cpp9
-rw-r--r--core/variant/dictionary.h1
-rw-r--r--core/variant/type_info.h11
-rw-r--r--core/variant/variant.cpp2
-rw-r--r--core/variant/variant.h26
-rw-r--r--core/variant/variant_call.cpp102
-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
93 files changed, 1577 insertions, 672 deletions
diff --git a/core/config/engine.cpp b/core/config/engine.cpp
index 0e27d556ec..24080c056a 100644
--- a/core/config/engine.cpp
+++ b/core/config/engine.cpp
@@ -176,14 +176,14 @@ TypedArray<Dictionary> Engine::get_copyright_info() const {
Dictionary Engine::get_donor_info() const {
Dictionary donors;
- donors["platinum_sponsors"] = array_from_info(DONORS_SPONSOR_PLATINUM);
- donors["gold_sponsors"] = array_from_info(DONORS_SPONSOR_GOLD);
- donors["silver_sponsors"] = array_from_info(DONORS_SPONSOR_SILVER);
- donors["bronze_sponsors"] = array_from_info(DONORS_SPONSOR_BRONZE);
- donors["mini_sponsors"] = array_from_info(DONORS_SPONSOR_MINI);
- donors["gold_donors"] = array_from_info(DONORS_GOLD);
- donors["silver_donors"] = array_from_info(DONORS_SILVER);
- donors["bronze_donors"] = array_from_info(DONORS_BRONZE);
+ donors["patrons"] = array_from_info(DONORS_PATRONS);
+ donors["platinum_sponsors"] = array_from_info(DONORS_SPONSORS_PLATINUM);
+ donors["gold_sponsors"] = array_from_info(DONORS_SPONSORS_GOLD);
+ donors["silver_sponsors"] = array_from_info(DONORS_SPONSORS_SILVER);
+ donors["diamond_members"] = array_from_info(DONORS_MEMBERS_DIAMOND);
+ donors["titanium_members"] = array_from_info(DONORS_MEMBERS_TITANIUM);
+ donors["platinum_members"] = array_from_info(DONORS_MEMBERS_PLATINUM);
+ donors["gold_members"] = array_from_info(DONORS_MEMBERS_GOLD);
return donors;
}
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index cbbfe3de75..90e2e27320 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;
}
@@ -852,8 +862,8 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
}
if (!p_custom_features.is_empty()) {
+ // Store how many properties are saved, add one for custom features, which must always go first.
file->store_32(count + 1);
- //store how many properties are saved, add one for custom featuers, which must always go first
String key = CoreStringNames::get_singleton()->_custom_features;
file->store_pascal_string(key);
@@ -870,7 +880,8 @@ Error ProjectSettings::_save_settings_binary(const String &p_file, const RBMap<S
file->store_buffer(buff.ptr(), buff.size());
} else {
- file->store_32(count); //store how many properties are saved
+ // Store how many properties are saved.
+ file->store_32(count);
}
for (const KeyValue<String, List<String>> &E : p_props) {
@@ -1240,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);
@@ -1315,8 +1393,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);
@@ -1332,8 +1410,8 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF("display/window/size/extend_to_title", false);
GLOBAL_DEF("display/window/size/no_focus", false);
- GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "0,7680,1,or_greater"), 0); // 8K resolution
- GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "0,4320,1,or_greater"), 0); // 8K resolution
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_width_override", PROPERTY_HINT_RANGE, "1,7680,1,or_greater"), 0); // 8K resolution
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "display/window/size/window_height_override", PROPERTY_HINT_RANGE, "1,4320,1,or_greater"), 0); // 8K resolution
GLOBAL_DEF("display/window/energy_saving/keep_screen_on", true);
GLOBAL_DEF("display/window/energy_saving/keep_screen_on.editor", false);
@@ -1384,20 +1462,28 @@ 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(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_builders.py b/core/core_builders.py
index e40ebbb14d..8b6b87ad83 100644
--- a/core/core_builders.py
+++ b/core/core_builders.py
@@ -117,24 +117,24 @@ def make_authors_header(target, source, env):
def make_donors_header(target, source, env):
sections = [
+ "Patrons",
"Platinum sponsors",
"Gold sponsors",
"Silver sponsors",
- "Bronze sponsors",
- "Mini sponsors",
- "Gold donors",
- "Silver donors",
- "Bronze donors",
+ "Diamond members",
+ "Titanium members",
+ "Platinum members",
+ "Gold members",
]
sections_id = [
- "DONORS_SPONSOR_PLATINUM",
- "DONORS_SPONSOR_GOLD",
- "DONORS_SPONSOR_SILVER",
- "DONORS_SPONSOR_BRONZE",
- "DONORS_SPONSOR_MINI",
- "DONORS_GOLD",
- "DONORS_SILVER",
- "DONORS_BRONZE",
+ "DONORS_PATRONS",
+ "DONORS_SPONSORS_PLATINUM",
+ "DONORS_SPONSORS_GOLD",
+ "DONORS_SPONSORS_SILVER",
+ "DONORS_MEMBERS_DIAMOND",
+ "DONORS_MEMBERS_TITANIUM",
+ "DONORS_MEMBERS_PLATINUM",
+ "DONORS_MEMBERS_GOLD",
]
src = source[0]
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 2f70fdf219..3b96fc20c6 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -845,7 +845,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/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 6c3d0a6148..ce01531b5c 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -666,24 +666,23 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte
HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
-void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
+void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) {
ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered.");
gdextension_interface_functions.insert(p_function_name, p_function_pointer);
}
-GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) {
+GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) {
GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name);
ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + ".");
return *function;
}
Error GDExtension::open_library(const String &p_path, const String &p_entry_symbol) {
- library_path = p_path;
-
String abs_path = ProjectSettings::get_singleton()->globalize_path(p_path);
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
// If running on the editor on Windows, we copy the library and open the copy.
// This is so the original file isn't locked and can be updated by a compiler.
+ bool library_copied = false;
if (Engine::get_singleton()->is_editor_hint()) {
if (!FileAccess::exists(abs_path)) {
ERR_PRINT("GDExtension library not found: " + library_path);
@@ -705,6 +704,7 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
return ERR_CANT_CREATE;
}
FileAccess::set_hidden_attribute(copy_path, true);
+ library_copied = true;
// Save the copied path so it can be deleted later.
temp_lib_path = copy_path;
@@ -714,11 +714,17 @@ 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);
- if (err != OK) {
- ERR_PRINT("GDExtension dynamic library not found: " + abs_path);
- return err;
+ Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &library_path);
+ ERR_FAIL_COND_V_MSG(err == ERR_FILE_NOT_FOUND, err, "GDExtension dynamic library not found: " + abs_path);
+ ERR_FAIL_COND_V_MSG(err != OK, err, "Can't open GDExtension dynamic library: " + abs_path);
+
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If we copied the file, let's change the library path to point at the original,
+ // because that's what we want to check to see if it's changed.
+ if (library_copied) {
+ library_path = library_path.get_base_dir() + "\\" + p_path.get_file();
}
+#endif
void *entry_funcptr = nullptr;
@@ -904,6 +910,8 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
return ERR_FILE_NOT_FOUND;
}
+ bool is_static_library = library_path.ends_with(".a") || library_path.ends_with(".xcframework");
+
if (!library_path.is_resource_file() && !library_path.is_absolute_path()) {
library_path = p_path.get_base_dir().path_join(library_path);
}
@@ -920,7 +928,7 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
FileAccess::get_modified_time(library_path));
#endif
- err = p_extension->open_library(library_path, entry_symbol);
+ err = p_extension->open_library(is_static_library ? String() : library_path, entry_symbol);
if (err != OK) {
#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
// If the DLL fails to load, make sure that temporary DLL copies are cleaned up.
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 0d20b8e50c..0b39581751 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -154,8 +154,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();
diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp
index 2dac4a3a5d..dd4cd20d09 100644
--- a/core/extension/gdextension_compat_hashes.cpp
+++ b/core/extension/gdextension_compat_hashes.cpp
@@ -32,6 +32,7 @@
#ifndef DISABLE_DEPRECATED
+#include "core/object/class_db.h"
#include "core/variant/variant.h"
HashMap<StringName, LocalVector<GDExtensionCompatHashes::Mapping>> GDExtensionCompatHashes::mappings;
@@ -52,7 +53,7 @@ bool GDExtensionCompatHashes::lookup_current_hash(const StringName &p_class, con
return false;
}
-bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes) {
+bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid) {
LocalVector<Mapping> *methods = mappings.getptr(p_class);
if (!methods) {
return false;
@@ -61,6 +62,13 @@ bool GDExtensionCompatHashes::get_legacy_hashes(const StringName &p_class, const
bool found = false;
for (const Mapping &mapping : *methods) {
if (mapping.method == p_method) {
+ if (p_check_valid) {
+ MethodBind *mb = ClassDB::get_method_with_compatibility(p_class, p_method, mapping.current_hash);
+ if (!mb) {
+ WARN_PRINT(vformat("Compatibility hash %d mapped to non-existent hash %d. Please update gdextension_compat_hashes.cpp.", mapping.legacy_hash, mapping.current_hash));
+ continue;
+ }
+ }
r_hashes.push_back(mapping.legacy_hash);
found = true;
}
diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h
index 29393dcb2d..813859d9e6 100644
--- a/core/extension/gdextension_compat_hashes.h
+++ b/core/extension/gdextension_compat_hashes.h
@@ -50,7 +50,7 @@ public:
static void initialize();
static void finalize();
static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash);
- static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes);
+ static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid = true);
};
#endif // DISABLE_DEPRECATED
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 2d48bdd4cf..2ba4b1d1e8 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -143,9 +143,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,6 +198,7 @@ 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) {
@@ -241,8 +248,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 +292,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 +312,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 +323,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 +333,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 +344,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 +353,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 +367,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 +452,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 : "";
@@ -695,52 +719,38 @@ void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_em
}
for (const KeyValue<StringName, InputMap::Action> &E : InputMap::get_singleton()->get_action_map()) {
- if (InputMap::get_singleton()->event_is_action(p_event, E.key)) {
- Action &action = action_state[E.key];
- bool is_joypad_axis = jm.is_valid();
- bool is_pressed = false;
- if (!p_event->is_echo()) {
- if (p_event->is_action_pressed(E.key)) {
- bool is_joypad_axis_valid_zone_enter = false;
- if (is_joypad_axis) {
- if (!action.axis_pressed) {
- is_joypad_axis_valid_zone_enter = true;
- action.pressed++;
- action.axis_pressed = true;
- }
- } else {
- action.pressed++;
- }
- if (action.pressed == 1 && (is_joypad_axis_valid_zone_enter || !is_joypad_axis)) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- }
- is_pressed = true;
- } else {
- bool is_released = true;
- if (is_joypad_axis) {
- if (action.axis_pressed) {
- action.axis_pressed = false;
- } else {
- is_released = false;
- }
- }
-
- if (is_released) {
- if (action.pressed == 1) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- }
- action.pressed = MAX(action.pressed - 1, 0);
- }
- }
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- }
+ const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key);
+ if (event_index == -1) {
+ continue;
+ }
+ ERR_FAIL_COND_MSG(event_index >= (int)MAX_EVENT, vformat("Input singleton does not support more than %d events assigned to an action.", MAX_EVENT));
+
+ int device_id = p_event->get_device();
+ bool is_pressed = p_event->is_action_pressed(E.key, true);
+ ActionState &action_state = action_states[E.key];
+
+ // Update the action's per-device state.
+ ActionState::DeviceState &device_state = action_state.device_states[device_id];
+ device_state.pressed[event_index] = is_pressed;
+ device_state.strength[event_index] = p_event->get_action_strength(E.key);
+ device_state.raw_strength[event_index] = p_event->get_action_raw_strength(E.key);
+
+ // Update the action's global state and cache.
+ if (!is_pressed) {
+ action_state.api_pressed = false; // Always release the event from action_press() method.
+ action_state.api_strength = 0.0;
+ }
+ action_state.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- if (is_pressed || action.pressed == 0) {
- action.strength = p_event->get_action_strength(E.key);
- action.raw_strength = p_event->get_action_raw_strength(E.key);
- }
+ bool was_pressed = action_state.cache.pressed;
+ _update_action_cache(E.key, action_state);
+ if (action_state.cache.pressed && !was_pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ if (!action_state.cache.pressed && was_pressed) {
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
}
}
@@ -856,30 +866,30 @@ Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, con
void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
- Action &action = action_state[p_action];
+ ActionState &action_state = action_states[p_action];
- action.pressed++;
- if (action.pressed == 1) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
+ if (!action_state.cache.pressed) {
+ action_state.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.strength = p_strength;
- action.raw_strength = p_strength;
- action.exact = true;
+ action_state.exact = true;
+ action_state.api_pressed = true;
+ action_state.api_strength = 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];
-
- action.pressed--;
- if (action.pressed == 0) {
- action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.released_process_frame = Engine::get_singleton()->get_process_frames();
- }
- action.strength = 0.0f;
- action.raw_strength = 0.0f;
- action.exact = true;
+ ActionState &action_state = action_states[p_action];
+ action_state.cache.pressed = 0;
+ action_state.cache.strength = 0.0;
+ action_state.cache.raw_strength = 0.0;
+ action_state.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action_state.released_process_frame = Engine::get_singleton()->get_process_frames();
+ action_state.device_states.clear();
+ action_state.exact = true;
+ action_state.api_pressed = false;
+ action_state.api_strength = 0.0;
}
void Input::set_emulate_touch_from_mouse(bool p_emulate) {
@@ -1037,10 +1047,8 @@ void Input::release_pressed_events() {
joy_buttons_pressed.clear();
_joy_axis.clear();
- for (KeyValue<StringName, Input::Action> &E : action_state) {
- if (E.value.pressed > 0) {
- // Make sure the action is really released.
- E.value.pressed = 1;
+ for (KeyValue<StringName, Input::ActionState> &E : action_states) {
+ if (E.value.cache.pressed) {
action_release(E.key);
}
}
@@ -1207,6 +1215,29 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
parse_input_event(ievent);
}
+void Input::_update_action_cache(const StringName &p_action_name, ActionState &r_action_state) {
+ // Update the action cache, computed from the per-device and per-event states.
+ r_action_state.cache.pressed = false;
+ r_action_state.cache.strength = 0.0;
+ r_action_state.cache.raw_strength = 0.0;
+
+ int max_event = InputMap::get_singleton()->action_get_events(p_action_name)->size();
+ for (const KeyValue<int, ActionState::DeviceState> &kv : r_action_state.device_states) {
+ const ActionState::DeviceState &device_state = kv.value;
+ for (int i = 0; i < max_event; i++) {
+ r_action_state.cache.pressed = r_action_state.cache.pressed || device_state.pressed[i];
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, device_state.strength[i]);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, device_state.raw_strength[i]);
+ }
+ }
+
+ if (r_action_state.api_pressed) {
+ r_action_state.cache.pressed = true;
+ r_action_state.cache.strength = MAX(r_action_state.cache.strength, r_action_state.api_strength);
+ r_action_state.cache.raw_strength = MAX(r_action_state.cache.raw_strength, r_action_state.api_strength); // Use the strength as raw_strength for API-pressed states.
+ }
+}
+
Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {
JoyEvent event;
@@ -1488,7 +1519,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 bedc3fa0e3..b98406e884 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -44,6 +44,8 @@ class Input : public Object {
static Input *singleton;
+ static constexpr uint64_t MAX_EVENT = 32;
+
public:
enum MouseMode {
MOUSE_MODE_VISIBLE,
@@ -98,19 +100,31 @@ private:
int64_t mouse_window = 0;
bool legacy_just_pressed_behavior = false;
- struct Action {
+ struct ActionState {
uint64_t pressed_physics_frame = UINT64_MAX;
uint64_t pressed_process_frame = UINT64_MAX;
uint64_t released_physics_frame = UINT64_MAX;
uint64_t released_process_frame = UINT64_MAX;
- int pressed = 0;
- bool axis_pressed = false;
bool exact = true;
- float strength = 0.0f;
- float raw_strength = 0.0f;
+
+ struct DeviceState {
+ bool pressed[MAX_EVENT] = { false };
+ float strength[MAX_EVENT] = { 0.0 };
+ float raw_strength[MAX_EVENT] = { 0.0 };
+ };
+ bool api_pressed = false;
+ float api_strength = 0.0;
+ HashMap<int, DeviceState> device_states;
+
+ // Cache.
+ struct ActionStateCache {
+ bool pressed = false;
+ float strength = false;
+ float raw_strength = false;
+ } cache;
};
- HashMap<StringName, Action> action_state;
+ HashMap<StringName, ActionState> action_states;
bool emulate_touch_from_mouse = false;
bool emulate_mouse_from_touch = false;
@@ -227,6 +241,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_cache(const StringName &p_action_name, ActionState &r_action_state);
void _parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated);
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index ddfde0e7cd..70041ecfd6 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -127,16 +127,21 @@ List<StringName> InputMap::get_actions() const {
return actions;
}
-List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
+List<Ref<InputEvent>>::Element *InputMap::_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
ERR_FAIL_COND_V(!p_event.is_valid(), nullptr);
+ int i = 0;
for (List<Ref<InputEvent>>::Element *E = p_action.inputs.front(); E; E = E->next()) {
int device = E->get()->get_device();
if (device == ALL_DEVICES || device == p_event->get_device()) {
if (E->get()->action_match(p_event, p_exact_match, p_action.deadzone, r_pressed, r_strength, r_raw_strength)) {
+ if (r_event_index) {
+ *r_event_index = i;
+ }
return E;
}
}
+ i++;
}
return nullptr;
@@ -179,6 +184,7 @@ void InputMap::action_erase_event(const StringName &p_action, const Ref<InputEve
List<Ref<InputEvent>>::Element *E = _find_event(input_map[p_action], p_event, true);
if (E) {
input_map[p_action].inputs.erase(E);
+
if (Input::get_singleton()->is_action_pressed(p_action)) {
Input::get_singleton()->action_release(p_action);
}
@@ -216,7 +222,13 @@ bool InputMap::event_is_action(const Ref<InputEvent> &p_event, const StringName
return event_get_action_status(p_event, p_action, p_exact_match);
}
-bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength) const {
+int InputMap::event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match) const {
+ int index = -1;
+ event_get_action_status(p_event, p_action, p_exact_match, nullptr, nullptr, nullptr, &index);
+ return index;
+}
+
+bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match, bool *r_pressed, float *r_strength, float *r_raw_strength, int *r_event_index) const {
HashMap<StringName, Action>::Iterator E = input_map.find(p_action);
ERR_FAIL_COND_V_MSG(!E, false, suggest_actions(p_action));
@@ -236,7 +248,7 @@ bool InputMap::event_get_action_status(const Ref<InputEvent> &p_event, const Str
return input_event_action->get_action() == p_action;
}
- List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength);
+ List<Ref<InputEvent>>::Element *event = _find_event(E->value, p_event, p_exact_match, r_pressed, r_strength, r_raw_strength, r_event_index);
return event != nullptr;
}
@@ -742,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)) {
@@ -754,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/input/input_map.h b/core/input/input_map.h
index b4d5beacb3..6407ea489e 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -61,7 +61,7 @@ private:
HashMap<String, List<Ref<InputEvent>>> default_builtin_cache;
HashMap<String, List<Ref<InputEvent>>> default_builtin_with_overrides_cache;
- List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
+ List<Ref<InputEvent>>::Element *_find_event(Action &p_action, const Ref<InputEvent> &p_event, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const;
TypedArray<InputEvent> _action_get_events(const StringName &p_action);
TypedArray<StringName> _get_actions();
@@ -86,7 +86,8 @@ public:
const List<Ref<InputEvent>> *action_get_events(const StringName &p_action);
bool event_is_action(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
- bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr) const;
+ int event_get_index(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false) const;
+ bool event_get_action_status(const Ref<InputEvent> &p_event, const StringName &p_action, bool p_exact_match = false, bool *r_pressed = nullptr, float *r_strength = nullptr, float *r_raw_strength = nullptr, int *r_event_index = nullptr) const;
const HashMap<StringName, Action> &get_action_map() const;
void load_from_project_settings();
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..9aa7c9794a 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;
}
@@ -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;
diff --git a/core/io/image.h b/core/io/image.h
index a21d05187b..a5025ee8a1 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;
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/resource.cpp b/core/io/resource.cpp
index e0d42a274a..04ecabaf27 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -90,6 +90,10 @@ String Resource::get_path() const {
return path_cache;
}
+void Resource::set_path_cache(const String &p_path) {
+ path_cache = p_path;
+}
+
String Resource::generate_scene_unique_id() {
// Generate a unique enough hash, but still user-readable.
// If it's not unique it does not matter because the saver will try again.
@@ -485,12 +489,14 @@ RWLock ResourceCache::path_cache_lock;
#endif
void ResourceCache::clear() {
- if (resources.size()) {
- ERR_PRINT("Resources still in use at exit (run with --verbose for details).");
+ if (!resources.is_empty()) {
if (OS::get_singleton()->is_stdout_verbose()) {
+ ERR_PRINT(vformat("%d resources still in use at exit.", resources.size()));
for (const KeyValue<String, Resource *> &E : resources) {
print_line(vformat("Resource still in use: %s (%s)", E.key, E.value->get_class()));
}
+ } else {
+ ERR_PRINT(vformat("%d resources still in use at exit (run with --verbose for details).", resources.size()));
}
}
diff --git a/core/io/resource.h b/core/io/resource.h
index a9b1a88f6b..610c2150db 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -103,6 +103,7 @@ public:
virtual void set_path(const String &p_path, bool p_take_over = false);
String get_path() const;
+ void set_path_cache(const String &p_path); // Set raw path without involving resource cache.
_FORCE_INLINE_ bool is_built_in() const { return path_cache.is_empty() || path_cache.contains("::") || path_cache.begins_with("local://"); }
static String generate_scene_unique_id();
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index ea97e5ecce..20c494516b 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -774,6 +774,8 @@ Error ResourceLoaderBinary::load() {
res = Ref<Resource>(r);
if (!path.is_empty() && cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) {
r->set_path(path, cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); //if got here because the resource with same path has different type, replace it
+ } else if (!path.is_resource_file()) {
+ r->set_path_cache(path);
}
r->set_scene_unique_id(id);
}
@@ -1452,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 6721ec0953..0c7764392a 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -340,7 +340,9 @@ 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);
+ 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);
}
if (load_task.xl_remapped) {
@@ -359,6 +361,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();
@@ -461,7 +474,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
@@ -918,7 +931,7 @@ String ResourceLoader::_path_remap(const String &p_path, bool *r_translation_rem
}
// Fallback to p_path if new_path does not exist.
- if (!FileAccess::exists(new_path)) {
+ if (!FileAccess::exists(new_path + ".import") && !FileAccess::exists(new_path)) {
WARN_PRINT(vformat("Translation remap '%s' does not exist. Falling back to '%s'.", new_path, p_path));
new_path = p_path;
}
@@ -1111,11 +1124,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_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/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..e1d49dacc0 100644
--- a/core/math/aabb.cpp
+++ b/core/math/aabb.cpp
@@ -117,6 +117,11 @@ AABB AABB::intersection(const AABB &p_aabb) const {
return AABB(min, max - min);
}
+#ifdef MINGW_ENABLED
+#undef near
+#undef far
+#endif
+
bool AABB::intersects_ray(const Vector3 &p_from, const Vector3 &p_dir, Vector3 *r_clip, Vector3 *r_normal) const {
#ifdef MATH_CHECKS
if (unlikely(size.x < 0 || size.y < 0 || size.z < 0)) {
diff --git a/core/math/basis.cpp b/core/math/basis.cpp
index 9796ac59c2..cd8c87b158 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
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/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_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/rect2.h b/core/math/rect2.h
index 6ccb76cd10..5f403458fd 100644
--- a/core/math/rect2.h
+++ b/core/math/rect2.h
@@ -285,6 +285,10 @@ struct _NO_DISCARD_ Rect2 {
return Rect2(Point2(position.x + MIN(size.x, (real_t)0), position.y + MIN(size.y, (real_t)0)), size.abs());
}
+ _FORCE_INLINE_ Rect2 round() const {
+ return Rect2(position.round(), size.round());
+ }
+
Vector2 get_support(const Vector2 &p_normal) const {
Vector2 half_extents = size * 0.5f;
Vector2 ofs = position + half_extents;
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/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/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/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..a394c957d0 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,6 +35,8 @@
#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.
@@ -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 2e5b897bce..5a776e2106 100644
--- a/core/object/object.cpp
+++ b/core/object/object.cpp
@@ -198,6 +198,7 @@ bool Object::_predelete() {
notification(NOTIFICATION_PREDELETE, true);
if (_predelete_ok) {
_class_name_ptr = nullptr; // Must restore, so constructors/destructors have proper class name access at each stage.
+ notification(NOTIFICATION_PREDELETE_CLEANUP, true);
}
return _predelete_ok;
}
@@ -1346,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.");
@@ -1363,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;
@@ -1375,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;
}
@@ -1396,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) {
diff --git a/core/object/object.h b/core/object/object.h
index a444db0f70..d697f14b7e 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -72,12 +72,12 @@ enum PropertyHint {
PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color
PROPERTY_HINT_OBJECT_ID,
PROPERTY_HINT_TYPE_STRING, ///< a type string, the hint is the base type to choose
- PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, ///< so something else can provide this (used in scripts)
+ PROPERTY_HINT_NODE_PATH_TO_EDITED_NODE, // Deprecated.
PROPERTY_HINT_OBJECT_TOO_BIG, ///< object is too big to send
PROPERTY_HINT_NODE_PATH_VALID_TYPES,
PROPERTY_HINT_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
PROPERTY_HINT_GLOBAL_SAVE_FILE, ///< a file path must be passed, hint_text (optionally) is a filter "*.png,*.wav,*.doc,". This opens a save dialog
- PROPERTY_HINT_INT_IS_OBJECTID,
+ PROPERTY_HINT_INT_IS_OBJECTID, // Deprecated.
PROPERTY_HINT_INT_IS_POINTER,
PROPERTY_HINT_ARRAY_TYPE,
PROPERTY_HINT_LOCALE_ID,
@@ -105,7 +105,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_SCRIPT_VARIABLE = 1 << 12,
PROPERTY_USAGE_STORE_IF_NULL = 1 << 13,
PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED = 1 << 14,
- PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15,
+ PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE = 1 << 15, // Deprecated.
PROPERTY_USAGE_CLASS_IS_ENUM = 1 << 16,
PROPERTY_USAGE_NIL_IS_VARIANT = 1 << 17,
PROPERTY_USAGE_ARRAY = 1 << 18, // Used in the inspector to group properties as elements of an array.
@@ -115,7 +115,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT = 1 << 22,
PROPERTY_USAGE_RESOURCE_NOT_PERSISTENT = 1 << 23,
PROPERTY_USAGE_KEYING_INCREMENTS = 1 << 24, // Used in inspector to increment property when keyed in animation player.
- PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // when loading, the resource for this property can be set at the end of loading.
+ PROPERTY_USAGE_DEFERRED_SET_RESOURCE = 1 << 25, // Deprecated.
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected.
PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector.
@@ -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;
@@ -801,6 +801,8 @@ public:
NOTIFICATION_POSTINITIALIZE = 0,
NOTIFICATION_PREDELETE = 1,
NOTIFICATION_EXTENSION_RELOADED = 2,
+ // Internal notification to send after NOTIFICATION_PREDELETE, not bound to scripting.
+ NOTIFICATION_PREDELETE_CLEANUP = 3,
};
/* TYPE API */
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 011f4203ea..0b2d5e41cf 100644
--- a/core/object/script_language.cpp
+++ b/core/object/script_language.cpp
@@ -39,10 +39,11 @@
ScriptLanguage *ScriptServer::_languages[MAX_LANGUAGES];
int ScriptServer::_language_count = 0;
+bool ScriptServer::languages_ready = false;
+Mutex ScriptServer::languages_mutex;
bool ScriptServer::scripting_enabled = true;
bool ScriptServer::reload_scripts_on_save = false;
-SafeFlag ScriptServer::languages_finished; // Used until GH-76581 is fixed properly.
ScriptEditRequestFunction ScriptServer::edit_request_func = nullptr;
void Script::_notification(int p_what) {
@@ -137,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);
@@ -160,12 +163,13 @@ bool ScriptServer::is_scripting_enabled() {
}
ScriptLanguage *ScriptServer::get_language(int p_idx) {
+ MutexLock lock(languages_mutex);
ERR_FAIL_INDEX_V(p_idx, _language_count, nullptr);
-
return _languages[p_idx];
}
Error ScriptServer::register_language(ScriptLanguage *p_language) {
+ MutexLock lock(languages_mutex);
ERR_FAIL_NULL_V(p_language, ERR_INVALID_PARAMETER);
ERR_FAIL_COND_V_MSG(_language_count >= MAX_LANGUAGES, ERR_UNAVAILABLE, "Script languages limit has been reach, cannot register more.");
for (int i = 0; i < _language_count; i++) {
@@ -179,6 +183,8 @@ Error ScriptServer::register_language(ScriptLanguage *p_language) {
}
Error ScriptServer::unregister_language(const ScriptLanguage *p_language) {
+ MutexLock lock(languages_mutex);
+
for (int i = 0; i < _language_count; i++) {
if (_languages[i] == p_language) {
_language_count--;
@@ -219,17 +225,53 @@ void ScriptServer::init_languages() {
}
}
- for (int i = 0; i < _language_count; i++) {
- _languages[i]->init();
+ HashSet<ScriptLanguage *> langs_to_init;
+ {
+ MutexLock lock(languages_mutex);
+ for (int i = 0; i < _language_count; i++) {
+ if (_languages[i]) {
+ langs_to_init.insert(_languages[i]);
+ }
+ }
+ }
+
+ for (ScriptLanguage *E : langs_to_init) {
+ E->init();
+ }
+
+ {
+ MutexLock lock(languages_mutex);
+ languages_ready = true;
}
}
void ScriptServer::finish_languages() {
- for (int i = 0; i < _language_count; i++) {
- _languages[i]->finish();
+ HashSet<ScriptLanguage *> langs_to_finish;
+
+ {
+ MutexLock lock(languages_mutex);
+ for (int i = 0; i < _language_count; i++) {
+ if (_languages[i]) {
+ langs_to_finish.insert(_languages[i]);
+ }
+ }
+ }
+
+ for (ScriptLanguage *E : langs_to_finish) {
+ E->finish();
}
+
+ {
+ MutexLock lock(languages_mutex);
+ languages_ready = false;
+ }
+
global_classes_clear();
- languages_finished.set();
+}
+
+bool ScriptServer::are_languages_initialized() {
+ MutexLock lock(languages_mutex);
+ return languages_ready;
}
void ScriptServer::set_reload_scripts_on_save(bool p_enable) {
@@ -241,7 +283,8 @@ bool ScriptServer::is_reload_scripts_on_save_enabled() {
}
void ScriptServer::thread_enter() {
- if (!languages_finished.is_set()) {
+ MutexLock lock(languages_mutex);
+ if (!languages_ready) {
return;
}
for (int i = 0; i < _language_count; i++) {
@@ -250,7 +293,8 @@ void ScriptServer::thread_enter() {
}
void ScriptServer::thread_exit() {
- if (!languages_finished.is_set()) {
+ MutexLock lock(languages_mutex);
+ if (!languages_ready) {
return;
}
for (int i = 0; i < _language_count; i++) {
@@ -538,9 +582,6 @@ void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properti
} else {
for (const PropertyInfo &E : properties) {
PropertyInfo pinfo = E;
- if (!values.has(pinfo.name)) {
- pinfo.usage |= PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE;
- }
p_properties->push_back(E);
}
}
@@ -592,6 +633,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 3e4041d173..66106bf139 100644
--- a/core/object/script_language.h
+++ b/core/object/script_language.h
@@ -52,9 +52,11 @@ class ScriptServer {
static ScriptLanguage *_languages[MAX_LANGUAGES];
static int _language_count;
+ static bool languages_ready;
+ static Mutex languages_mutex;
+
static bool scripting_enabled;
static bool reload_scripts_on_save;
- static SafeFlag languages_finished; // Used until GH-76581 is fixed properly.
struct GlobalScriptClass {
StringName language;
@@ -98,8 +100,7 @@ public:
static void init_languages();
static void finish_languages();
-
- static bool are_languages_finished() { return languages_finished.is_set(); }
+ static bool are_languages_initialized();
};
class PlaceHolderScriptInstance;
@@ -242,7 +243,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 ""; }
@@ -383,10 +384,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..8b01667519 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;
@@ -607,6 +607,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 a8f2ac5bfe..3c1d4ef95e 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -301,6 +301,8 @@ void UndoRedo::commit_action(bool p_execute) {
return; //still nested
}
+ bool add_message = !merging;
+
if (merging) {
version--;
merging = false;
@@ -314,7 +316,7 @@ void UndoRedo::commit_action(bool p_execute) {
_redo(p_execute); // perform action
committing--;
- if (callback && actions.size() > 0) {
+ if (add_message && callback && actions.size() > 0) {
callback(callback_ud, actions[actions.size() - 1].name);
}
}
@@ -463,6 +465,10 @@ bool UndoRedo::has_redo() const {
return (current_action + 1) < actions.size();
}
+bool UndoRedo::is_merging() const {
+ return merging;
+}
+
uint64_t UndoRedo::get_version() const {
return version;
}
diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h
index 74a6bea732..b3a3322e4b 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -131,6 +131,8 @@ public:
bool has_undo() const;
bool has_redo() const;
+ bool is_merging() const;
+
uint64_t get_version() const;
void set_commit_notify_callback(CommitNotifyCallback p_callback, void *p_ud);
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 2fcd0867e6..631767219f 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/core/object/worker_thread_pool.cpp
@@ -30,6 +30,7 @@
#include "worker_thread_pool.h"
+#include "core/object/script_language.h"
#include "core/os/os.h"
#include "core/os/thread_safe.h"
@@ -60,6 +61,14 @@ void WorkerThreadPool::_process_task(Task *p_task) {
set_current_thread_safe_for_nodes(false);
pool_thread_index = thread_ids[Thread::get_caller_id()];
ThreadData &curr_thread = threads[pool_thread_index];
+ // Since the WorkerThreadPool is started before the script server,
+ // its pre-created threads can't have ScriptServer::thread_enter() called on them early.
+ // Therefore, we do it late at the first opportunity, so in case the task
+ // about to be run uses scripting, guarantees are held.
+ if (!curr_thread.ready_for_scripting && ScriptServer::are_languages_initialized()) {
+ ScriptServer::thread_enter();
+ curr_thread.ready_for_scripting = true;
+ }
task_mutex.lock();
p_task->pool_thread_index = pool_thread_index;
if (low_priority) {
@@ -526,6 +535,11 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
task_mutex.unlock();
}
+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) {
ERR_FAIL_COND(threads.size() > 0);
if (p_thread_count < 0) {
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index d4d9387765..dd56f95cae 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -106,6 +106,7 @@ private:
uint32_t index;
Thread thread;
Task *current_low_prio_task = nullptr;
+ bool ready_for_scripting = false;
};
TightLocalVector<ThreadData> threads;
@@ -196,6 +197,8 @@ public:
_FORCE_INLINE_ int get_thread_count() const { return threads.size(); }
static WorkerThreadPool *get_singleton() { return singleton; }
+ static int get_thread_index();
+
void init(int p_thread_count = -1, bool p_use_native_threads_low_priority = true, float p_low_priority_task_ratio = 0.3);
void finish();
WorkerThreadPool();
diff --git a/core/os/condition_variable.h b/core/os/condition_variable.h
index 6037ff327d..6a6996019d 100644
--- a/core/os/condition_variable.h
+++ b/core/os/condition_variable.h
@@ -31,7 +31,14 @@
#ifndef CONDITION_VARIABLE_H
#define CONDITION_VARIABLE_H
+#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 +47,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 {
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/mutex.cpp b/core/os/mutex.cpp
index 7dbb60590b..5d4e457c5f 100644
--- a/core/os/mutex.cpp
+++ b/core/os/mutex.cpp
@@ -40,7 +40,7 @@ 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>>;
+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>>;
diff --git a/core/os/mutex.h b/core/os/mutex.h
index cee0f8af74..03af48ca7c 100644
--- a/core/os/mutex.h
+++ b/core/os/mutex.h
@@ -34,7 +34,14 @@
#include "core/error/error_macros.h"
#include "core/typedefs.h"
+#ifdef MINGW_ENABLED
+#define MINGW_STDTHREAD_REDUNDANCY_WARNING
+#include "thirdparty/mingw-std-threads/mingw.mutex.h"
+#define THREADING_NAMESPACE mingw_stdthread
+#else
#include <mutex>
+#define THREADING_NAMESPACE std
+#endif
template <class MutexT>
class MutexLock;
@@ -73,9 +80,9 @@ template <int Tag>
class SafeBinaryMutex {
friend class MutexLock<SafeBinaryMutex>;
- using StdMutexType = std::mutex;
+ using StdMutexType = THREADING_NAMESPACE::mutex;
- mutable std::mutex mutex;
+ mutable THREADING_NAMESPACE::mutex mutex;
static thread_local uint32_t count;
public:
@@ -115,7 +122,7 @@ template <class MutexT>
class MutexLock {
friend class ConditionVariable;
- std::unique_lock<typename MutexT::StdMutexType> lock;
+ THREADING_NAMESPACE::unique_lock<typename MutexT::StdMutexType> lock;
public:
_ALWAYS_INLINE_ explicit MutexLock(const MutexT &p_mutex) :
@@ -128,7 +135,7 @@ template <int Tag>
class MutexLock<SafeBinaryMutex<Tag>> {
friend class ConditionVariable;
- std::unique_lock<std::mutex> lock;
+ THREADING_NAMESPACE::unique_lock<THREADING_NAMESPACE::mutex> lock;
public:
_ALWAYS_INLINE_ explicit MutexLock(const SafeBinaryMutex<Tag> &p_mutex) :
@@ -140,12 +147,12 @@ public:
};
};
-using Mutex = MutexImpl<std::recursive_mutex>; // Recursive, for general use
-using BinaryMutex = MutexImpl<std::mutex>; // Non-recursive, handle with care
+using Mutex = MutexImpl<THREADING_NAMESPACE::recursive_mutex>; // Recursive, for general use
+using BinaryMutex = MutexImpl<THREADING_NAMESPACE::mutex>; // Non-recursive, handle with care
-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>>;
+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>>;
#endif // MUTEX_H
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 991b179e1f..26ae286979 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 {
@@ -490,6 +498,12 @@ bool OS::has_feature(const String &p_feature) {
}
#endif
+#if defined(IOS_SIMULATOR)
+ if (p_feature == "simulator") {
+ return true;
+ }
+#endif
+
if (_check_internal_feature_support(p_feature)) {
return true;
}
@@ -612,17 +626,22 @@ String OS::get_benchmark_file() {
return benchmark_file;
}
-void OS::benchmark_begin_measure(const String &p_what) {
+void OS::benchmark_begin_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- start_benchmark_from[p_what] = OS::get_singleton()->get_ticks_usec();
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' already exists.", p_context, p_what));
+
+ benchmark_marks_from[mark_key] = OS::get_singleton()->get_ticks_usec();
#endif
}
-void OS::benchmark_end_measure(const String &p_what) {
+void OS::benchmark_end_measure(const String &p_context, const String &p_what) {
#ifdef TOOLS_ENABLED
- uint64_t total = OS::get_singleton()->get_ticks_usec() - start_benchmark_from[p_what];
- double total_f = double(total) / double(1000000);
+ Pair<String, String> mark_key(p_context, p_what);
+ ERR_FAIL_COND_MSG(!benchmark_marks_from.has(mark_key), vformat("Benchmark key '%s:%s' doesn't exist.", p_context, p_what));
- startup_benchmark_json[p_what] = total_f;
+ uint64_t total = OS::get_singleton()->get_ticks_usec() - benchmark_marks_from[mark_key];
+ double total_f = double(total) / double(1000000);
+ benchmark_marks_final[mark_key] = total_f;
#endif
}
@@ -631,19 +650,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/semaphore.h b/core/os/semaphore.h
index 66dfb3ee02..8bb1529bbd 100644
--- a/core/os/semaphore.h
+++ b/core/os/semaphore.h
@@ -37,13 +37,21 @@
#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;
@@ -57,7 +65,7 @@ public:
}
_ALWAYS_INLINE_ void wait() const {
- std::unique_lock lock(mutex);
+ THREADING_NAMESPACE::unique_lock lock(mutex);
#ifdef DEBUG_ENABLED
++awaiters;
#endif
@@ -116,7 +124,7 @@ 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
diff --git a/core/os/thread.cpp b/core/os/thread.cpp
index 03e2c5409d..2ba90ba42c 100644
--- a/core/os/thread.cpp
+++ b/core/os/thread.cpp
@@ -69,8 +69,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 +81,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;
}
diff --git a/core/os/thread.h b/core/os/thread.h
index 3e307adfff..cc954678f9 100644
--- a/core/os/thread.h
+++ b/core/os/thread.h
@@ -42,7 +42,14 @@
#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;
@@ -82,7 +89,7 @@ private:
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);
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index 4ad9dd43c4..2785d1daa5 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -120,7 +120,8 @@ 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);
@@ -296,7 +297,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() {
@@ -311,6 +312,8 @@ void register_core_settings() {
}
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 +349,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.
@@ -435,5 +448,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/string_name.cpp b/core/string/string_name.cpp
index 5a8df07410..658297d805 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -122,7 +122,7 @@ void StringName::unref() {
if (_data && _data->refcount.unref()) {
MutexLock lock(mutex);
- if (_data->static_count.get() > 0) {
+ if (CoreGlobals::leak_reporting_enabled && _data->static_count.get() > 0) {
if (_data->cname) {
ERR_PRINT("BUG: Unreferenced static string to 0: " + String(_data->cname));
} else {
diff --git a/core/string/translation.cpp b/core/string/translation.cpp
index 02380c92bb..8fcf2b24b5 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -518,13 +518,17 @@ String TranslationServer::get_country_name(const String &p_country) const {
}
void TranslationServer::set_locale(const String &p_locale) {
- locale = standardize_locale(p_locale);
+ String new_locale = standardize_locale(p_locale);
+ if (locale == new_locale) {
+ return;
+ }
+
+ locale = new_locale;
+ ResourceLoader::reload_translation_remaps();
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
-
- ResourceLoader::reload_translation_remaps();
}
String TranslationServer::get_locale() const {
@@ -816,10 +820,11 @@ bool TranslationServer::is_pseudolocalization_enabled() const {
void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
pseudolocalization_enabled = p_enabled;
+ ResourceLoader::reload_translation_remaps();
+
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
- ResourceLoader::reload_translation_remaps();
}
void TranslationServer::set_editor_pseudolocalization(bool p_enabled) {
@@ -836,10 +841,11 @@ void TranslationServer::reload_pseudolocalization() {
pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+ ResourceLoader::reload_translation_remaps();
+
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
- ResourceLoader::reload_translation_remaps();
}
StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 9be7c04158..6afe28a6a7 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -305,7 +305,11 @@ void String::copy_from(const char *p_cstr) {
char32_t *dst = this->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;
@@ -338,7 +342,11 @@ void String::copy_from(const char *p_cstr, const int p_clip_to) {
char32_t *dst = this->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;
@@ -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 "";
}
@@ -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,7 +3515,7 @@ 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 String::format(const Variant &values, const String &placeholder) const {
String new_string = String(this->ptr());
if (values.get_type() == Variant::ARRAY) {
@@ -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 {
@@ -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;
}
@@ -4699,11 +4734,16 @@ String String::property_name_encode() const {
static const char32_t invalid_node_name_characters[] = { '.', ':', '@', '/', '\"', UNIQUE_NODE_PREFIX[0], 0 };
-String String::get_invalid_node_name_characters() {
+String String::get_invalid_node_name_characters(bool p_allow_internal) {
// Do not use this function for critical validation.
String r;
const char32_t *c = invalid_node_name_characters;
while (*c) {
+ if (p_allow_internal && *c == '@') {
+ c++;
+ continue;
+ }
+
if (c != invalid_node_name_characters) {
r += " ";
}
@@ -4817,6 +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;
@@ -4857,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);
@@ -4875,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 {
@@ -5069,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':
@@ -5167,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 f45392eee1..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;
@@ -437,7 +437,7 @@ public:
String property_name_encode() const;
// node functions
- static String get_invalid_node_name_characters();
+ static String get_invalid_node_name_characters(bool p_allow_internal = false);
String validate_node_name() const;
String validate_identifier() const;
String validate_filename() const;
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index 2a212f3dcb..05960292f5 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); }
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/paged_allocator.h b/core/templates/paged_allocator.h
index 72425a8c3d..6f3f78d4d2 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -58,7 +58,7 @@ public:
};
template <class... Args>
- T *alloc(const Args &&...p_args) {
+ T *alloc(Args &&...p_args) {
if (thread_safe) {
spin_lock.lock();
}
@@ -99,6 +99,10 @@ 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) {
diff --git a/core/templates/paged_array.h b/core/templates/paged_array.h
index 863e3eef11..69a792958a 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,9 +193,9 @@ 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
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/typedefs.h b/core/typedefs.h
index 24c247fd38..8b04960b9a 100644
--- a/core/typedefs.h
+++ b/core/typedefs.h
@@ -92,6 +92,7 @@
#undef Error
#undef OK
#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum
+#undef MemoryBarrier
#endif
// Make room for our constexpr's below by overriding potential system-specific macros.
@@ -232,6 +233,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/callable_bind.cpp b/core/variant/callable_bind.cpp
index 9a6380a55f..6d6c60cbd4 100644
--- a/core/variant/callable_bind.cpp
+++ b/core/variant/callable_bind.cpp
@@ -133,7 +133,7 @@ void CallableCustomBind::get_bound_arguments(Vector<Variant> &r_arguments, int &
}
void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
- const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
+ const Variant **args = (const Variant **)alloca(sizeof(Variant *) * (binds.size() + p_argcount));
for (int i = 0; i < p_argcount; i++) {
args[i] = (const Variant *)p_arguments[i];
}
@@ -145,7 +145,7 @@ void CallableCustomBind::call(const Variant **p_arguments, int p_argcount, Varia
}
Error CallableCustomBind::rpc(int p_peer_id, const Variant **p_arguments, int p_argcount, Callable::CallError &r_call_error) const {
- const Variant **args = (const Variant **)alloca(sizeof(const Variant **) * (binds.size() + p_argcount));
+ const Variant **args = (const Variant **)alloca(sizeof(Variant *) * (binds.size() + p_argcount));
for (int i = 0; i < p_argcount; i++) {
args[i] = (const Variant *)p_arguments[i];
}
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 141ce25fa6..8b61a8993a 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();
}
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..c1f2f86a96 100644
--- a/core/variant/type_info.h
+++ b/core/variant/type_info.h
@@ -287,14 +287,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/variant.cpp b/core/variant/variant.cpp
index 4c0212075b..c352d800f9 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();
diff --git a/core/variant/variant.h b/core/variant/variant.h
index 21342ae6ef..602d287f22 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;
@@ -338,6 +339,9 @@ public:
_FORCE_INLINE_ bool is_num() const {
return type == INT || type == FLOAT;
}
+ _FORCE_INLINE_ bool is_string() const {
+ return type == STRING || type == STRING_NAME;
+ }
_FORCE_INLINE_ bool is_array() const {
return type >= ARRAY;
}
@@ -568,6 +572,7 @@ public:
static ValidatedBuiltInMethod get_validated_builtin_method(Variant::Type p_type, const StringName &p_method);
static PTRBuiltInMethod get_ptr_builtin_method(Variant::Type p_type, const StringName &p_method);
+ static MethodInfo get_builtin_method_info(Variant::Type p_type, const StringName &p_method);
static int get_builtin_method_argument_count(Variant::Type p_type, const StringName &p_method);
static Variant::Type get_builtin_method_argument_type(Variant::Type p_type, const StringName &p_method, int p_argument);
static String get_builtin_method_argument_name(Variant::Type p_type, const StringName &p_method, int p_argument);
@@ -703,9 +708,20 @@ public:
bool has_key(const Variant &p_key, bool &r_valid) const;
/* Generic */
-
- void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr);
- Variant get(const Variant &p_index, bool *r_valid = nullptr) const;
+ enum VariantSetError {
+ SET_OK,
+ SET_KEYED_ERR,
+ SET_NAMED_ERR,
+ SET_INDEXED_ERR
+ };
+ enum VariantGetError {
+ GET_OK,
+ GET_KEYED_ERR,
+ GET_NAMED_ERR,
+ GET_INDEXED_ERR
+ };
+ void set(const Variant &p_index, const Variant &p_value, bool *r_valid = nullptr, VariantSetError *err_code = nullptr);
+ Variant get(const Variant &p_index, bool *r_valid = nullptr, VariantGetError *err_code = nullptr) const;
bool in(const Variant &p_index, bool *r_valid = nullptr) const;
bool iter_init(Variant &r_iter, bool &r_valid) const;
@@ -765,8 +781,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..03836985f3 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -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) {