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.cpp117
-rw-r--r--core/config/project_settings.h14
-rw-r--r--core/core_bind.cpp4
-rw-r--r--core/core_bind.h1
-rw-r--r--core/core_builders.py26
-rw-r--r--core/core_constants.cpp2
-rw-r--r--core/extension/extension_api_dump.cpp53
-rw-r--r--core/extension/gdextension.cpp89
-rw-r--r--core/extension/gdextension.h13
-rw-r--r--core/extension/gdextension_compat_hashes.cpp14
-rw-r--r--core/extension/gdextension_compat_hashes.h3
-rw-r--r--core/extension/gdextension_interface.cpp12
-rw-r--r--core/extension/gdextension_interface.h20
-rw-r--r--core/extension/gdextension_manager.cpp6
-rw-r--r--core/extension/gdextension_manager.h1
-rw-r--r--core/input/gamecontrollerdb.txt12
-rw-r--r--core/input/input.cpp196
-rw-r--r--core/input/input.h29
-rw-r--r--core/input/input_map.cpp18
-rw-r--r--core/input/input_map.h5
-rw-r--r--core/io/dir_access.cpp6
-rw-r--r--core/io/dir_access.h2
-rw-r--r--core/io/file_access.h4
-rw-r--r--core/io/file_access_pack.cpp2
-rw-r--r--core/io/file_access_zip.cpp20
-rw-r--r--core/io/image.cpp3
-rw-r--r--core/io/marshalls.cpp12
-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_importer.h1
-rw-r--r--core/io/resource_loader.cpp29
-rw-r--r--core/io/resource_saver.cpp5
-rw-r--r--core/io/resource_uid.cpp2
-rw-r--r--core/math/a_star.cpp56
-rw-r--r--core/math/aabb.cpp5
-rw-r--r--core/math/basis.cpp23
-rw-r--r--core/math/basis.h1
-rw-r--r--core/math/convex_hull.cpp2
-rw-r--r--core/math/dynamic_bvh.h18
-rw-r--r--core/math/geometry_2d.h8
-rw-r--r--core/math/math_funcs.h16
-rw-r--r--core/math/rect2.h4
-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.h30
-rw-r--r--core/object/class_db.cpp28
-rw-r--r--core/object/class_db.h3
-rw-r--r--core/object/make_virtuals.py236
-rw-r--r--core/object/message_queue.cpp12
-rw-r--r--core/object/object.cpp60
-rw-r--r--core/object/object.h16
-rw-r--r--core/object/script_language.cpp69
-rw-r--r--core/object/script_language.h13
-rw-r--r--core/object/script_language_extension.cpp5
-rw-r--r--core/object/script_language_extension.h15
-rw-r--r--core/object/undo_redo.cpp73
-rw-r--r--core/object/undo_redo.h4
-rw-r--r--core/object/worker_thread_pool.cpp25
-rw-r--r--core/object/worker_thread_pool.h3
-rw-r--r--core/os/condition_variable.h11
-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.cpp22
-rw-r--r--core/string/string_name.cpp12
-rw-r--r--core/string/translation.cpp16
-rw-r--r--core/string/ustring.cpp77
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/templates/hashfuncs.h2
-rw-r--r--core/templates/list.h39
-rw-r--r--core/templates/paged_allocator.h8
-rw-r--r--core/templates/paged_array.h21
-rw-r--r--core/templates/rb_map.h20
-rw-r--r--core/templates/safe_refcount.h6
-rw-r--r--core/typedefs.h1
-rw-r--r--core/variant/callable.cpp7
-rw-r--r--core/variant/callable.h2
-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.cpp10
-rw-r--r--core/variant/variant.h19
-rw-r--r--core/variant/variant_call.cpp92
-rw-r--r--core/variant/variant_callable.cpp81
-rw-r--r--core/variant/variant_callable.h58
-rw-r--r--core/variant/variant_internal.h154
-rw-r--r--core/variant/variant_op.h32
-rw-r--r--core/variant/variant_parser.cpp7
-rw-r--r--core/variant/variant_setget.cpp53
-rw-r--r--core/variant/variant_utility.cpp134
99 files changed, 1770 insertions, 792 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 3049994240..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);
@@ -1343,6 +1421,9 @@ ProjectSettings::ProjectSettings() {
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f);
+ GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0);
+ GLOBAL_DEF("audio/general/ios/mix_with_others", false);
+
PackedStringArray extensions;
extensions.push_back("gd");
if (Engine::get_singleton()->has_singleton("GodotSharp")) {
@@ -1381,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 05fe393a2f..d91c659d1e 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);
@@ -1211,8 +1212,7 @@ void Thread::_start_func(void *ud) {
Ref<Thread> t = *tud;
memdelete(tud);
- Object *target_instance = t->target_callable.get_object();
- if (!target_instance) {
+ if (!t->target_callable.is_valid()) {
t->running.clear();
ERR_FAIL_MSG(vformat("Could not call function '%s' on previously freed instance to start thread %s.", t->target_callable.get_method(), t->get_id()));
}
diff --git a/core/core_bind.h b/core/core_bind.h
index 5f51b64eb7..715e26cf23 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();
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 33b3271495..2f70fdf219 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -794,6 +794,8 @@ void register_global_constants() {
void unregister_global_constants() {
_global_constants.clear();
+ _global_constants_map.clear();
+ _global_enums.clear();
}
int CoreConstants::get_global_constant_count() {
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 58cb51245a..543dabfb16 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -502,7 +502,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
if (constant_doc.name == name) {
- d["documentation"] = fix_doc_description(constant_doc.description);
+ d["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -521,7 +521,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
const DocData::EnumDoc *enum_doc = global_scope_doc->enums.getptr(E.key);
if (enum_doc) {
- d1["documentation"] = fix_doc_description(enum_doc->description);
+ d1["description"] = fix_doc_description(enum_doc->description);
}
}
Array values;
@@ -532,7 +532,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : global_scope_doc->constants) {
if (constant_doc.name == F.first) {
- d2["documentation"] = fix_doc_description(constant_doc.description);
+ d2["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -596,7 +596,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::MethodDoc &method_doc : global_scope_doc->methods) {
if (method_doc.name == name) {
- func["documentation"] = fix_doc_description(method_doc.description);
+ func["description"] = fix_doc_description(method_doc.description);
break;
}
}
@@ -647,7 +647,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::PropertyDoc &property_doc : builtin_doc->properties) {
if (property_doc.name == member_name) {
- d2["documentation"] = fix_doc_description(property_doc.description);
+ d2["description"] = fix_doc_description(property_doc.description);
break;
}
}
@@ -673,7 +673,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
if (constant_doc.name == constant_name) {
- d2["documentation"] = fix_doc_description(constant_doc.description);
+ d2["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -706,7 +706,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : builtin_doc->constants) {
if (constant_doc.name == enumeration) {
- values_dict["documentation"] = fix_doc_description(constant_doc.description);
+ values_dict["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -717,7 +717,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
const DocData::EnumDoc *enum_doc = builtin_doc->enums.getptr(enum_name);
if (enum_doc) {
- enum_dict["documentation"] = fix_doc_description(enum_doc->description);
+ enum_dict["description"] = fix_doc_description(enum_doc->description);
}
}
@@ -742,15 +742,20 @@ 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) {
- d2["documentation"] = fix_doc_description(operator_doc.description);
+ 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;
}
}
@@ -805,7 +810,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::MethodDoc &method_doc : builtin_doc->methods) {
if (method_doc.name == method_name) {
- d2["documentation"] = fix_doc_description(method_doc.description);
+ d2["description"] = fix_doc_description(method_doc.description);
break;
}
}
@@ -853,7 +858,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
}
}
if (constructor_found) {
- d2["documentation"] = fix_doc_description(constructor_doc.description);
+ d2["description"] = fix_doc_description(constructor_doc.description);
}
}
}
@@ -871,7 +876,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
}
if (p_include_docs && builtin_doc != nullptr) {
- d["documentation"] = fix_doc_description(builtin_doc->description);
+ d["brief_description"] = fix_doc_description(builtin_doc->brief_description);
+ d["description"] = fix_doc_description(builtin_doc->description);
}
builtins.push_back(d);
@@ -933,7 +939,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
if (constant_doc.name == F) {
- d2["documentation"] = fix_doc_description(constant_doc.description);
+ d2["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -967,7 +973,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::ConstantDoc &constant_doc : class_doc->constants) {
if (constant_doc.name == G->get()) {
- d3["documentation"] = fix_doc_description(constant_doc.description);
+ d3["description"] = fix_doc_description(constant_doc.description);
break;
}
}
@@ -981,7 +987,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
const DocData::EnumDoc *enum_doc = class_doc->enums.getptr(F);
if (enum_doc) {
- d2["documentation"] = fix_doc_description(enum_doc->description);
+ d2["description"] = fix_doc_description(enum_doc->description);
}
}
@@ -1039,7 +1045,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::MethodDoc &method_doc : class_doc->methods) {
if (method_doc.name == method_name) {
- d2["documentation"] = fix_doc_description(method_doc.description);
+ d2["description"] = fix_doc_description(method_doc.description);
break;
}
}
@@ -1116,7 +1122,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::MethodDoc &method_doc : class_doc->methods) {
if (method_doc.name == method_name) {
- d2["documentation"] = fix_doc_description(method_doc.description);
+ d2["description"] = fix_doc_description(method_doc.description);
break;
}
}
@@ -1159,7 +1165,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::MethodDoc &signal_doc : class_doc->signals) {
if (signal_doc.name == signal_name) {
- d2["documentation"] = fix_doc_description(signal_doc.description);
+ d2["description"] = fix_doc_description(signal_doc.description);
break;
}
}
@@ -1208,7 +1214,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
if (p_include_docs) {
for (const DocData::PropertyDoc &property_doc : class_doc->properties) {
if (property_doc.name == property_name) {
- d2["documentation"] = fix_doc_description(property_doc.description);
+ d2["description"] = fix_doc_description(property_doc.description);
break;
}
}
@@ -1223,7 +1229,8 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
}
if (p_include_docs && class_doc != nullptr) {
- d["documentation"] = fix_doc_description(class_doc->description);
+ d["brief_description"] = fix_doc_description(class_doc->brief_description);
+ d["description"] = fix_doc_description(class_doc->description);
}
classes.push_back(d);
diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp
index 7e280466a8..19ffe96a09 100644
--- a/core/extension/gdextension.cpp
+++ b/core/extension/gdextension.cpp
@@ -35,6 +35,7 @@
#include "core/object/method_bind.h"
#include "core/os/os.h"
#include "core/version.h"
+#include "gdextension_manager.h"
extern void gdextension_setup_interface();
extern GDExtensionInterfaceFunctionPtr gdextension_get_proc_address(const char *p_name);
@@ -218,36 +219,30 @@ public:
#ifdef TOOLS_ENABLED
ERR_FAIL_COND_MSG(!valid, vformat("Cannot call invalid GDExtension method bind '%s'. It's probably cached - you may need to restart Godot.", name));
#endif
- ERR_FAIL_COND_MSG(vararg, "Validated methods don't have ptrcall support. This is most likely an engine bug.");
+ ERR_FAIL_COND_MSG(vararg, "Vararg methods don't have validated call support. This is most likely an engine bug.");
GDExtensionClassInstancePtr extension_instance = is_static() ? nullptr : p_object->_get_extension_instance();
if (validated_call_func) {
// This is added here, but it's unlikely to be provided by most extensions.
validated_call_func(method_userdata, extension_instance, reinterpret_cast<GDExtensionConstVariantPtr *>(p_args), (GDExtensionVariantPtr)r_ret);
} else {
-#if 1
- // Slow code-path, but works for the time being.
- Callable::CallError ce;
- call(p_object, p_args, argument_count, ce);
-#else
- // This is broken, because it needs more information to do the calling properly
-
// If not provided, go via ptrcall, which is faster than resorting to regular call.
const void **argptrs = (const void **)alloca(argument_count * sizeof(void *));
for (uint32_t i = 0; i < argument_count; i++) {
argptrs[i] = VariantInternal::get_opaque_pointer(p_args[i]);
}
- bool returns = true;
- void *ret_opaque;
- if (returns) {
- ret_opaque = VariantInternal::get_opaque_pointer(r_ret);
- } else {
- ret_opaque = nullptr; // May be unnecessary as this is ignored, but just in case.
+ void *ret_opaque = nullptr;
+ if (r_ret) {
+ VariantInternal::initialize(r_ret, return_value_info.type);
+ ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret);
}
ptrcall(p_object, argptrs, ret_opaque);
-#endif
+
+ if (r_ret && r_ret->get_type() == Variant::OBJECT) {
+ VariantInternal::update_object_id(r_ret);
+ }
}
}
@@ -669,7 +664,7 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte
memnew_placement(r_path, String(self->library_path));
}
-HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
+HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions;
void GDExtension::register_interface_function(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.");
@@ -683,12 +678,11 @@ GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p
}
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);
@@ -710,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;
@@ -719,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;
@@ -743,6 +744,7 @@ Error GDExtension::open_library(const String &p_path, const String &p_entry_symb
return OK;
} else {
ERR_PRINT("GDExtension initialization function '" + p_entry_symbol + "' returned an error.");
+ OS::get_singleton()->close_dynamic_library(library);
return FAILED;
}
}
@@ -781,7 +783,7 @@ void GDExtension::initialize_library(InitializationLevel p_level) {
level_initialized = int32_t(p_level);
- ERR_FAIL_COND(initialization.initialize == nullptr);
+ ERR_FAIL_NULL(initialization.initialize);
initialization.initialize(initialization.userdata, GDExtensionInitializationLevel(p_level));
}
@@ -840,6 +842,10 @@ void GDExtension::initialize_gdextensions() {
register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path);
}
+void GDExtension::finalize_gdextensions() {
+ gdextension_interface_functions.clear();
+}
+
Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) {
ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library.");
@@ -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);
}
@@ -915,12 +923,12 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
#ifdef TOOLS_ENABLED
p_extension->set_reloadable(config->get_value("configuration", "reloadable", false) && Engine::get_singleton()->is_extension_reloading_enabled());
- p_extension->update_last_modified_time(MAX(
- FileAccess::get_modified_time(library_path),
- FileAccess::get_modified_time(p_path)));
+ p_extension->update_last_modified_time(
+ FileAccess::get_modified_time(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.
@@ -928,6 +936,10 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
DirAccess::remove_absolute(p_extension->get_temp_library_path());
}
#endif
+
+ // Unreference the extension so that this loading can be considered a failure.
+ p_extension.unref();
+
// Errors already logged in open_library()
return err;
}
@@ -937,7 +949,12 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
List<String> keys;
config->get_section_keys("icons", &keys);
for (const String &key : keys) {
- p_extension->class_icon_paths[key] = config->get_value("icons", key);
+ String icon_path = config->get_value("icons", key);
+ if (icon_path.is_relative_path()) {
+ icon_path = p_path.get_base_dir().path_join(icon_path);
+ }
+
+ p_extension->class_icon_paths[key] = icon_path;
}
}
@@ -945,6 +962,15 @@ Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path,
}
Ref<Resource> GDExtensionResourceLoader::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+ // We can't have two GDExtension resource object representing the same library, because
+ // loading (or unloading) a GDExtension affects global data. So, we need reuse the same
+ // object if one has already been loaded (even if caching is disabled at the resource
+ // loader level).
+ GDExtensionManager *manager = GDExtensionManager::get_singleton();
+ if (manager->is_extension_loaded(p_path)) {
+ return manager->get_extension(p_path);
+ }
+
Ref<GDExtension> lib;
Error err = load_gdextension_resource(p_path, lib);
if (err != OK && r_error) {
@@ -972,10 +998,13 @@ String GDExtensionResourceLoader::get_resource_type(const String &p_path) const
#ifdef TOOLS_ENABLED
bool GDExtension::has_library_changed() const {
- if (FileAccess::get_modified_time(get_path()) > last_modified_time) {
+ // Check only that the last modified time is different (rather than checking
+ // that it's newer) since some OS's (namely Windows) will preserve the modified
+ // time by default when copying files.
+ if (FileAccess::get_modified_time(get_path()) != resource_last_modified_time) {
return true;
}
- if (FileAccess::get_modified_time(library_path) > last_modified_time) {
+ if (FileAccess::get_modified_time(library_path) != library_last_modified_time) {
return true;
}
return false;
diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h
index 2996100c9a..0d20b8e50c 100644
--- a/core/extension/gdextension.h
+++ b/core/extension/gdextension.h
@@ -90,7 +90,8 @@ class GDExtension : public Resource {
int32_t level_initialized = -1;
#ifdef TOOLS_ENABLED
- uint64_t last_modified_time = 0;
+ uint64_t resource_last_modified_time = 0;
+ uint64_t library_last_modified_time = 0;
bool is_reloading = false;
Vector<GDExtensionMethodBind *> invalid_methods;
Vector<ObjectID> instance_bindings;
@@ -106,12 +107,16 @@ class GDExtension : public Resource {
void clear_instance_bindings();
#endif
+ static HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions;
+
protected:
static void _bind_methods();
public:
HashMap<String, String> class_icon_paths;
+ virtual bool editor_can_reload_from_file() override { return false; } // Reloading is handled in a special way.
+
static String get_extension_list_config_file();
static String find_extension_library(const String &p_path, Ref<ConfigFile> p_config, std::function<bool(String)> p_has_feature, PackedStringArray *r_tags = nullptr);
@@ -136,8 +141,9 @@ public:
void set_reloadable(bool p_reloadable) { reloadable = p_reloadable; }
bool has_library_changed() const;
- void update_last_modified_time(uint64_t p_last_modified_time) {
- last_modified_time = MAX(last_modified_time, p_last_modified_time);
+ void update_last_modified_time(uint64_t p_resource_last_modified_time, uint64_t p_library_last_modified_time) {
+ resource_last_modified_time = p_resource_last_modified_time;
+ library_last_modified_time = p_library_last_modified_time;
}
void track_instance_binding(Object *p_object);
@@ -151,6 +157,7 @@ public:
static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer);
static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name);
static void initialize_gdextensions();
+ static void finalize_gdextensions();
GDExtension();
~GDExtension();
diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp
index 9c8d6b3e7f..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;
}
@@ -840,4 +848,8 @@ void GDExtensionCompatHashes::initialize() {
// clang-format on
}
+void GDExtensionCompatHashes::finalize() {
+ mappings.clear();
+}
+
#endif // DISABLE_DEPRECATED
diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h
index 3a66ef0b97..813859d9e6 100644
--- a/core/extension/gdextension_compat_hashes.h
+++ b/core/extension/gdextension_compat_hashes.h
@@ -48,8 +48,9 @@ class GDExtensionCompatHashes {
public:
static void initialize();
+ static void finalize();
static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash);
- static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes);
+ static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes, bool p_check_valid = true);
};
#endif // DISABLE_DEPRECATED
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 2b4a37b1e0..e02e7aa701 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -159,9 +159,7 @@ public:
userdata = p_info->callable_userdata;
token = p_info->token;
- if (p_info->object != nullptr) {
- object = ((Object *)p_info->object)->get_instance_id();
- }
+ object = p_info->object_id;
call_func = p_info->call_func;
is_valid_func = p_info->is_valid_func;
@@ -400,7 +398,7 @@ static void gdextension_variant_iter_get(GDExtensionConstVariantPtr p_self, GDEx
Variant *iter = (Variant *)r_iter;
bool valid;
- memnew_placement(r_ret, Variant(self->iter_next(*iter, valid)));
+ memnew_placement(r_ret, Variant(self->iter_get(*iter, valid)));
*r_valid = valid;
}
@@ -1152,6 +1150,11 @@ static void gdextension_object_set_instance_binding(GDExtensionObjectPtr p_objec
o->set_instance_binding(p_token, p_binding, p_callbacks);
}
+static void gdextension_object_free_instance_binding(GDExtensionObjectPtr p_object, void *p_token) {
+ Object *o = (Object *)p_object;
+ o->free_instance_binding(p_token);
+}
+
static void gdextension_object_set_instance(GDExtensionObjectPtr p_object, GDExtensionConstStringNamePtr p_classname, GDExtensionClassInstancePtr p_instance) {
const StringName classname = *reinterpret_cast<const StringName *>(p_classname);
Object *o = (Object *)p_object;
@@ -1491,6 +1494,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(global_get_singleton);
REGISTER_INTERFACE_FUNC(object_get_instance_binding);
REGISTER_INTERFACE_FUNC(object_set_instance_binding);
+ REGISTER_INTERFACE_FUNC(object_free_instance_binding);
REGISTER_INTERFACE_FUNC(object_set_instance);
REGISTER_INTERFACE_FUNC(object_get_class_name);
REGISTER_INTERFACE_FUNC(object_cast_to);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index fbd1480e69..d58f0226d8 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -392,7 +392,7 @@ typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_user
typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);
typedef struct {
- /* Only `call_func` and `token` are strictly required, however, `object` should be passed if its not a static method.
+ /* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method.
*
* `token` should point to an address that uniquely identifies the GDExtension (for example, the
* `GDExtensionClassLibraryPtr` passed to the entry symbol function.
@@ -409,7 +409,7 @@ typedef struct {
void *callable_userdata;
void *token;
- GDExtensionObjectPtr object;
+ GDObjectInstanceID object_id;
GDExtensionCallableCustomCall call_func;
GDExtensionCallableCustomIsValid is_valid_func;
@@ -590,7 +590,10 @@ typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(co
*
* For example:
*
- * GDExtensionInterfaceGetGodotVersion *get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
+ * GDExtensionInterfaceGetGodotVersion get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version");
+ *
+ * (Note that snippet may cause "cast between incompatible function types" on some compilers, you can
+ * silence this by adding an intermediary `void*` cast.)
*
* You can then call it like a normal function:
*
@@ -2192,6 +2195,17 @@ typedef void *(*GDExtensionInterfaceObjectGetInstanceBinding)(GDExtensionObjectP
typedef void (*GDExtensionInterfaceObjectSetInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
/**
+ * @name object_free_instance_binding
+ * @since 4.2
+ *
+ * Free an Object's instance binding.
+ *
+ * @param p_o A pointer to the Object.
+ * @param p_library A token the library received by the GDExtension's entry point function.
+ */
+typedef void (*GDExtensionInterfaceObjectFreeInstanceBinding)(GDExtensionObjectPtr p_o, void *p_token);
+
+/**
* @name object_set_instance
* @since 4.1
*
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 0dc84f685f..a4d032f22f 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -293,3 +293,9 @@ GDExtensionManager::GDExtensionManager() {
GDExtensionCompatHashes::initialize();
#endif
}
+
+GDExtensionManager::~GDExtensionManager() {
+#ifndef DISABLE_DEPRECATED
+ GDExtensionCompatHashes::finalize();
+#endif
+}
diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h
index 8cd6d5a3e2..9386e356bb 100644
--- a/core/extension/gdextension_manager.h
+++ b/core/extension/gdextension_manager.h
@@ -86,6 +86,7 @@ public:
void reload_extensions();
GDExtensionManager();
+ ~GDExtensionManager();
};
VARIANT_ENUM_CAST(GDExtensionManager::LoadStatus)
diff --git a/core/input/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt
index 9a43c4e35d..77655e9b6a 100644
--- a/core/input/gamecontrollerdb.txt
+++ b/core/input/gamecontrollerdb.txt
@@ -414,6 +414,8 @@
03000000ad1b000023f0000000000000,MLG,a:b0,b:b1,back:b7,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a6,rightx:a3,righty:a4,start:b6,x:b2,y:b3,platform:Windows,
03000000ad1b00003ef0000000000000,MLG Fightstick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b8,rightshoulder:b5,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
03000000380700006382000000000000,MLG PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,platform:Windows,
+030000004523000015e0000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
+03000000491900000904000000000000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,misc1:b15,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000ffff00000000000000000000,Mocute M053,a:b3,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b11,leftstick:b7,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b6,righttrigger:b4,rightx:a3,righty:a4,start:b8,x:b1,y:b0,platform:Windows,
03000000d6200000e589000000000000,Moga 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,
03000000d62000007162000000000000,Moga Pro,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,rightx:a2,righty:a5,start:b7,x:b2,y:b3,platform:Windows,
@@ -582,6 +584,7 @@
03000000921200004547000000000000,Retro Bit Sega Genesis Controller Adapter,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b7,rightshoulder:b5,righttrigger:b2,start:b6,x:b3,y:b4,platform:Windows,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Windows,
03000000830500006020000000000000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Windows,
+0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
03000000bd12000013d0000000000000,Retrolink Sega Saturn Classic Controller,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b5,lefttrigger:b6,rightshoulder:b2,righttrigger:b7,start:b8,x:b3,y:b4,platform:Windows,
03000000bd12000015d0000000000000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Windows,
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Windows,
@@ -643,7 +646,6 @@
030000003b07000004a1000000000000,SFX,a:b0,b:b2,back:b7,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b9,righttrigger:b5,start:b8,x:b1,y:b3,platform:Windows,
03000000f82100001900000000000000,Shogun Bros Chameleon X1,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,platform:Windows,
03000000120c00001c1e000000000000,SnakeByte 4S PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Windows,
-0300000003040000c197000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
0300000081170000960a000000000000,SNES Controller,a:b4,b:b0,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b5,y:b1,platform:Windows,
03000000811700009d0a000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
030000008b2800000300000000000000,SNES Controller,a:b0,b:b4,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Windows,
@@ -772,6 +774,7 @@
03000000341a00000608000000000000,Xeox,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
03000000450c00002043000000000000,Xeox SL6556BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Windows,
030000006f0e00000300000000000000,XGear,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a5,righty:a2,start:b9,x:b3,y:b0,platform:Windows,
+03000000e0ff00000201000000000000,Xiaomi Black Shark (L),back:b0,dpdown:b11,dpleft:b9,dpright:b10,dpup:b8,leftshoulder:b5,lefttrigger:b7,leftx:a0,lefty:a1,platform:Windows,
03000000172700004431000000000000,Xiaomi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a7,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Windows,
03000000172700003350000000000000,Xiaomi XMGP01YM,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,rightx:a3,righty:a4,start:b11,x:b3,y:b4,platform:Windows,
03000000bc2000005060000000000000,Xiaomi XMGP01YM,+lefty:+a2,+righty:+a5,-lefty:-a1,-righty:-a4,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a3,start:b11,x:b3,y:b4,platform:Windows,
@@ -928,6 +931,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e0400000300000006010000,Microsoft SideWinder,a:b0,b:b1,back:b9,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b8,x:b3,y:b4,platform:Mac OS X,
030000005e0400000700000006010000,Microsoft SideWinder,a:b0,b:b1,back:b8,leftshoulder:b6,lefttrigger:b7,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b2,start:b9,x:b3,y:b4,platform:Mac OS X,
030000005e0400002700000001010000,Microsoft SideWinder Plug and Play,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,lefttrigger:b4,righttrigger:b5,x:b2,y:b3,platform:Mac OS X,
+030000004523000015e0000072050000,Mobapad Chitu HD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,misc1:b13,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Mac OS X,
03000000d62000007162000001000000,Moga Pro 2,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Mac OS X,
03000000c62400002a89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b21,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
03000000c62400002b89000000010000,MOGA XP5A Plus,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Mac OS X,
@@ -979,6 +983,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
03000000790000001100000000000000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000790000001100000005010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b6,lefttrigger:b7,rightshoulder:b5,righttrigger:b4,start:b9,x:b0,y:b3,platform:Mac OS X,
03000000830500006020000000010000,Retro Controller,a:b0,b:b1,back:b6,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b4,lefttrigger:b5,rightshoulder:b8,righttrigger:b9,start:b7,x:b2,y:b3,platform:Mac OS X,
+0300000003040000c197000000000000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a4,dpleft:-a0,dpright:+a0,dpup:-a4,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Mac OS X,
03000000790000001100000006010000,Retrolink SNES Controller,a:b2,b:b1,back:b8,dpdown:+a4,dpleft:-a3,dpright:+a3,dpup:-a4,leftshoulder:b4,rightshoulder:b5,start:b9,x:b3,y:b0,platform:Mac OS X,
03000000341200000400000000000000,RetroUSB N64 RetroPort,+rightx:b8,+righty:b10,-rightx:b9,-righty:b11,a:b7,b:b6,dpdown:b2,dpleft:b1,dpright:b0,dpup:b3,leftshoulder:b13,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b12,start:b4,platform:Mac OS X,
030000006b140000010d000000010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,platform:Mac OS X,
@@ -1335,8 +1340,10 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
030000005e040000000b000007040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000000b000008040000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b12,paddle2:b14,paddle3:b13,paddle4:b15,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000050b000003090000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b17,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a6,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
+050000005e0400008e02000030110000,Microsoft Xbox One Elite 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,paddle1:b11,paddle2:b13,paddle3:b12,paddle4:b14,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
030000005e040000120b00000b050000,Microsoft Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000030000000300000002000000,Miroof,a:b1,b:b0,back:b6,leftshoulder:b4,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b3,y:b2,platform:Linux,
+03000000790000001c18000010010000,Mobapad Chitu HD,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000004d4f435554452d3035335800,Mocute 053X,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000e80400006e0400001b010000,Mocute 053X M59,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
050000004d4f435554452d3035305800,Mocute 054X,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
@@ -1490,6 +1497,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
03000000321500000b10000011010000,Razer Wolverine PS5 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,touchpad:b13,x:b0,y:b3,platform:Linux,
03000000790000001100000010010000,Retro Controller,a:b1,b:b2,back:b8,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,lefttrigger:b7,rightshoulder:b4,righttrigger:b5,start:b9,x:b0,y:b3,platform:Linux,
+0300000003040000c197000011010000,Retrode Adapter,a:b0,b:b4,back:b2,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b6,rightshoulder:b7,start:b3,x:b1,y:b5,platform:Linux,
190000004b4800000111000000010000,RetroGame Joypad,a:b1,b:b0,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
0300000081170000990a000001010000,Retronic Adapter,a:b0,leftx:a0,lefty:a1,platform:Linux,
0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,platform:Linux,
@@ -1639,6 +1647,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
060000005e040000120b000007050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000b050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
060000005e040000120b00000f050000,Xbox Series Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
+060000005e040000120b00000d050000,Xbox Series X Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,misc1:b11,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,platform:Linux,
050000005e040000200b000013050000,Xbox Wireless Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,platform:Linux,
03000000450c00002043000010010000,XEOX SL6556 BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,platform:Linux,
05000000172700004431000029010000,XiaoMi Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,platform:Linux,
@@ -1792,6 +1801,7 @@ xinput,XInput Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,
33323763323132376537376266393366,Microsoft Dual Strike,a:b24,b:b23,back:b25,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b27,lefttrigger:b29,rightshoulder:b78,rightx:a0,righty:a1~,start:b26,x:b22,y:b21,platform:Android,
30306461613834333439303734316539,Microsoft SideWinder Pro,a:b0,b:b1,dpdown:+a1,dpleft:-a0,dpright:+a0,dpup:-a1,leftshoulder:b20,lefttrigger:b9,rightshoulder:b19,righttrigger:b10,start:b17,x:b2,y:b3,platform:Android,
32386235353630393033393135613831,Microsoft Xbox Series Controller,a:b0,b:b1,back:b4,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
+4d4f42415041442050726f2d48440000,Mobapad Chitu HD,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
4d4f435554452d303533582d4d35312d,Mocute 053X,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b9,leftstick:b7,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a5,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
33343361376163623438613466616531,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
39306635663061636563316166303966,Mocute M053,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b9,leftstick:b7,lefttrigger:b17,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:b18,rightx:a2,righty:a3,start:b6,x:b2,y:b3,platform:Android,
diff --git a/core/input/input.cpp b/core/input/input.cpp
index 19ea8c7317..7fe850069a 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,53 +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_pressed = false;
-
- if (!p_event->is_echo()) {
- if (p_event->is_action_pressed(E.key)) {
- if (jm.is_valid()) {
- // If axis is already pressed, don't increase the pressed counter.
- if (!action.axis_pressed) {
- action.pressed++;
- action.axis_pressed = true;
- }
- } else {
- action.pressed++;
- }
-
- is_pressed = true;
- if (action.pressed == 1) {
- action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
- action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
- }
- } else {
- bool is_released = true;
- if (jm.is_valid()) {
- // Same as above. Don't release axis when not pressed.
- 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();
}
}
@@ -857,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) {
@@ -1038,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);
}
}
@@ -1096,7 +1103,8 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
return;
}
- JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value);
+ JoyAxisRange range;
+ JoyEvent map = _get_mapped_axis_event(map_db[joy.mapping], p_axis, p_value, range);
if (map.type == TYPE_BUTTON) {
bool pressed = map.value > 0.5;
@@ -1136,7 +1144,7 @@ void Input::joy_axis(int p_device, JoyAxis p_axis, float p_value) {
if (map.type == TYPE_AXIS) {
JoyAxis axis = JoyAxis(map.index);
float value = map.value;
- if (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT) {
+ if (range == FULL_AXIS && (axis == JoyAxis::TRIGGER_LEFT || axis == JoyAxis::TRIGGER_RIGHT)) {
// Convert to a value between 0.0f and 1.0f.
value = 0.5f + value / 2.0f;
}
@@ -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;
@@ -1242,7 +1273,7 @@ Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping,
return event;
}
-Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value) {
+Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range) {
JoyEvent event;
for (int i = 0; i < mapping.bindings.size(); i++) {
@@ -1288,6 +1319,7 @@ Input::JoyEvent Input::_get_mapped_axis_event(const JoyDeviceMapping &mapping, J
case TYPE_AXIS:
event.index = (int)binding.output.axis.axis;
event.value = value;
+ r_range = binding.output.axis.range;
if (binding.output.axis.range != binding.input.axis.range) {
switch (binding.output.axis.range) {
case POSITIVE_HALF_AXIS:
diff --git a/core/input/input.h b/core/input/input.h
index 8ce5f64a6a..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;
@@ -221,12 +235,13 @@ private:
Vector<JoyDeviceMapping> map_db;
JoyEvent _get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button);
- JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value);
+ JoyEvent _get_mapped_axis_event(const JoyDeviceMapping &mapping, JoyAxis p_axis, float p_value, JoyAxisRange &r_range);
void _get_mapped_hat_events(const JoyDeviceMapping &mapping, HatDir p_hat, JoyEvent r_events[(size_t)HatDir::MAX]);
JoyButton _get_output_button(String output);
JoyAxis _get_output_axis(String output);
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..78b9ada884 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;
}
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/dir_access.cpp b/core/io/dir_access.cpp
index 22b63a0929..40c1a53958 100644
--- a/core/io/dir_access.cpp
+++ b/core/io/dir_access.cpp
@@ -546,6 +546,10 @@ bool DirAccess::get_include_hidden() const {
return include_hidden;
}
+bool DirAccess::is_case_sensitive(const String &p_path) const {
+ return true;
+}
+
void DirAccess::_bind_methods() {
ClassDB::bind_static_method("DirAccess", D_METHOD("open", "path"), &DirAccess::_open);
ClassDB::bind_static_method("DirAccess", D_METHOD("get_open_error"), &DirAccess::get_open_error);
@@ -583,6 +587,8 @@ void DirAccess::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_include_hidden", "enable"), &DirAccess::set_include_hidden);
ClassDB::bind_method(D_METHOD("get_include_hidden"), &DirAccess::get_include_hidden);
+ ClassDB::bind_method(D_METHOD("is_case_sensitive", "path"), &DirAccess::is_case_sensitive);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_navigational"), "set_include_navigational", "get_include_navigational");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "include_hidden"), "set_include_hidden", "get_include_hidden");
}
diff --git a/core/io/dir_access.h b/core/io/dir_access.h
index 52ed688deb..4ee69571f2 100644
--- a/core/io/dir_access.h
+++ b/core/io/dir_access.h
@@ -159,6 +159,8 @@ public:
void set_include_hidden(bool p_enable);
bool get_include_hidden() const;
+ virtual bool is_case_sensitive(const String &p_path) const;
+
DirAccess() {}
virtual ~DirAccess() {}
};
diff --git a/core/io/file_access.h b/core/io/file_access.h
index 7b9e66bb83..7d346ca2f4 100644
--- a/core/io/file_access.h
+++ b/core/io/file_access.h
@@ -223,8 +223,8 @@ public:
static Vector<uint8_t> get_file_as_bytes(const String &p_path, Error *r_error = nullptr);
static String get_file_as_string(const String &p_path, Error *r_error = nullptr);
- static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path); }
- static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path); };
+ static PackedByteArray _get_file_as_bytes(const String &p_path) { return get_file_as_bytes(p_path, &last_file_open_error); }
+ static String _get_file_as_string(const String &p_path) { return get_file_as_string(p_path, &last_file_open_error); }
template <class T>
static void make_default(AccessType p_access) {
diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp
index 74c5c1c191..265d9ef56c 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;
diff --git a/core/io/file_access_zip.cpp b/core/io/file_access_zip.cpp
index d085c29728..dd45332412 100644
--- a/core/io/file_access_zip.cpp
+++ b/core/io/file_access_zip.cpp
@@ -110,7 +110,7 @@ static void godot_free(voidpf opaque, voidpf address) {
} // extern "C"
void ZipArchive::close_handle(unzFile p_file) const {
- ERR_FAIL_COND_MSG(!p_file, "Cannot close a file if none is open.");
+ ERR_FAIL_NULL_MSG(p_file, "Cannot close a file if none is open.");
unzCloseCurrentFile(p_file);
unzClose(p_file);
}
@@ -136,7 +136,7 @@ unzFile ZipArchive::get_file_handle(String p_file) const {
io.free_mem = godot_free;
unzFile pkg = unzOpen2(packages[file.package].filename.utf8().get_data(), &io);
- ERR_FAIL_COND_V_MSG(!pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
+ ERR_FAIL_NULL_V_MSG(pkg, nullptr, "Cannot open file '" + packages[file.package].filename + "'.");
int unz_err = unzGoToFilePos(pkg, &file.file_pos);
if (unz_err != UNZ_OK || unzOpenCurrentFile(pkg) != UNZ_OK) {
unzClose(pkg);
@@ -168,7 +168,7 @@ bool ZipArchive::try_open_pack(const String &p_path, bool p_replace_files, uint6
io.zerror_file = godot_testerror;
unzFile zfile = unzOpen2(p_path.utf8().get_data(), &io);
- ERR_FAIL_COND_V(!zfile, false);
+ ERR_FAIL_NULL_V(zfile, false);
unz_global_info64 gi;
int err = unzGetGlobalInfo64(zfile, &gi);
@@ -241,7 +241,7 @@ Error FileAccessZip::open_internal(const String &p_path, int p_mode_flags) {
ZipArchive *arch = ZipArchive::get_singleton();
ERR_FAIL_NULL_V(arch, FAILED);
zfile = arch->get_file_handle(p_path);
- ERR_FAIL_COND_V(!zfile, FAILED);
+ ERR_FAIL_NULL_V(zfile, FAILED);
int err = unzGetCurrentFileInfo64(zfile, &file_info, nullptr, 0, nullptr, 0, nullptr, 0);
ERR_FAIL_COND_V(err != UNZ_OK, FAILED);
@@ -265,28 +265,28 @@ bool FileAccessZip::is_open() const {
}
void FileAccessZip::seek(uint64_t p_position) {
- ERR_FAIL_COND(!zfile);
+ ERR_FAIL_NULL(zfile);
unzSeekCurrentFile(zfile, p_position);
}
void FileAccessZip::seek_end(int64_t p_position) {
- ERR_FAIL_COND(!zfile);
+ ERR_FAIL_NULL(zfile);
unzSeekCurrentFile(zfile, get_length() + p_position);
}
uint64_t FileAccessZip::get_position() const {
- ERR_FAIL_COND_V(!zfile, 0);
+ ERR_FAIL_NULL_V(zfile, 0);
return unztell(zfile);
}
uint64_t FileAccessZip::get_length() const {
- ERR_FAIL_COND_V(!zfile, 0);
+ ERR_FAIL_NULL_V(zfile, 0);
return file_info.uncompressed_size;
}
bool FileAccessZip::eof_reached() const {
- ERR_FAIL_COND_V(!zfile, true);
+ ERR_FAIL_NULL_V(zfile, true);
return at_eof;
}
@@ -299,7 +299,7 @@ uint8_t FileAccessZip::get_8() const {
uint64_t FileAccessZip::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
- ERR_FAIL_COND_V(!zfile, -1);
+ ERR_FAIL_NULL_V(zfile, -1);
at_eof = unzeof(zfile);
if (at_eof) {
diff --git a/core/io/image.cpp b/core/io/image.cpp
index 15d0182dfc..c72064e4f7 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;
}
@@ -3773,7 +3774,7 @@ void Image::fix_alpha_edges() {
}
int closest_dist = max_dist;
- uint8_t closest_color[3];
+ uint8_t closest_color[3] = { 0 };
int from_x = MAX(0, j - max_radius);
int to_x = MIN(width - 1, j + max_radius);
diff --git a/core/io/marshalls.cpp b/core/io/marshalls.cpp
index bfef9ebeaf..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);
@@ -1155,10 +1157,12 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
#ifdef REAL_T_IS_DOUBLE
case Variant::VECTOR2:
case Variant::VECTOR3:
+ case Variant::VECTOR4:
case Variant::PACKED_VECTOR2_ARRAY:
case Variant::PACKED_VECTOR3_ARRAY:
case Variant::TRANSFORM2D:
case Variant::TRANSFORM3D:
+ case Variant::PROJECTION:
case Variant::QUATERNION:
case Variant::PLANE:
case Variant::BASIS:
@@ -1490,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;
@@ -1619,8 +1625,10 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
encode_uint32(datalen, buf);
buf += 4;
const uint8_t *r = data.ptr();
- memcpy(buf, &r[0], datalen * datasize);
- buf += datalen * datasize;
+ if (r) {
+ memcpy(buf, &r[0], datalen * datasize);
+ buf += datalen * datasize;
+ }
}
r_len += 4 + datalen * datasize;
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_importer.h b/core/io/resource_importer.h
index 0089544caa..e17644058a 100644
--- a/core/io/resource_importer.h
+++ b/core/io/resource_importer.h
@@ -136,6 +136,7 @@ public:
virtual void get_import_options(const String &p_path, List<ImportOption> *r_options, int p_preset = 0) const = 0;
virtual bool get_option_visibility(const String &p_path, const String &p_option, const HashMap<StringName, Variant> &p_options) const = 0;
+ virtual void handle_compatibility_options(HashMap<StringName, Variant> &p_import_params) const {}
virtual String get_option_group_file() const { return String(); }
virtual Error import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files = nullptr, Variant *r_metadata = nullptr) = 0;
diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp
index f7915261af..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;
}
@@ -1053,8 +1066,9 @@ void ResourceLoader::clear_thread_load_tasks() {
thread_load_mutex.lock();
}
- for (KeyValue<String, LoadToken *> &E : user_load_tokens) {
- memdelete(E.value);
+ while (user_load_tokens.begin()) {
+ // User load tokens remove themselves from the map on destruction.
+ memdelete(user_load_tokens.begin()->value);
}
user_load_tokens.clear();
@@ -1110,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..1c6c18b015 100644
--- a/core/io/resource_saver.cpp
+++ b/core/io/resource_saver.cpp
@@ -237,11 +237,10 @@ bool ResourceSaver::add_custom_resource_format_saver(String script_path) {
Ref<Script> s = res;
StringName ibt = s->get_instance_base_type();
bool valid_type = ClassDB::is_parent_class(ibt, "ResourceFormatSaver");
- ERR_FAIL_COND_V_MSG(!valid_type, false, "Script does not inherit a CustomResourceSaver: " + script_path + ".");
+ ERR_FAIL_COND_V_MSG(!valid_type, false, vformat("Failed to add a custom resource saver, script '%s' does not inherit 'ResourceFormatSaver'.", script_path));
Object *obj = ClassDB::instantiate(ibt);
-
- ERR_FAIL_NULL_V_MSG(obj, false, "Cannot instance script as custom resource saver, expected 'ResourceFormatSaver' inheritance, got: " + String(ibt) + ".");
+ ERR_FAIL_NULL_V_MSG(obj, false, vformat("Failed to add a custom resource saver, cannot instantiate '%s'.", ibt));
Ref<ResourceFormatSaver> crl = Object::cast_to<ResourceFormatSaver>(obj);
crl->set_script(s);
diff --git a/core/io/resource_uid.cpp b/core/io/resource_uid.cpp
index 216fc24fff..edff3e1f14 100644
--- a/core/io/resource_uid.cpp
+++ b/core/io/resource_uid.cpp
@@ -35,6 +35,8 @@
#include "core/io/dir_access.h"
#include "core/io/file_access.h"
+// These constants are off by 1, causing the 'z' and '9' characters never to be used.
+// This cannot be fixed without breaking compatibility; see GH-83843.
static constexpr uint32_t char_count = ('z' - 'a');
static constexpr uint32_t base = char_count + ('9' - '0');
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/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..b4d971464e 100644
--- a/core/math/basis.h
+++ b/core/math/basis.h
@@ -138,6 +138,7 @@ struct _NO_DISCARD_ Basis {
_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;
diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp
index f8456ec998..68d995fe67 100644
--- a/core/math/convex_hull.cpp
+++ b/core/math/convex_hull.cpp
@@ -2278,7 +2278,7 @@ Error ConvexHullComputer::convex_hull(const Vector<Vector3> &p_points, Geometry3
uint32_t edges_copied = 0;
for (uint32_t i = 0; i < ch.edges.size(); i++) {
- ERR_CONTINUE(edge_faces[i] == -1); // Sanity check
+ ERR_CONTINUE(edge_faces[i] == -1); // Safety check.
uint32_t a = (&ch.edges[i])->get_source_vertex();
uint32_t b = (&ch.edges[i])->get_target_vertex();
diff --git a/core/math/dynamic_bvh.h b/core/math/dynamic_bvh.h
index dbc1cb31de..9b49fcc3c8 100644
--- a/core/math/dynamic_bvh.h
+++ b/core/math/dynamic_bvh.h
@@ -328,7 +328,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) {
volume.min = p_box.position;
volume.max = p_box.position + p_box.size;
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -343,7 +344,8 @@ void DynamicBVH::aabb_query(const AABB &p_box, QueryResult &r_result) {
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
@@ -384,7 +386,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve
}
}
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -399,7 +402,8 @@ void DynamicBVH::convex_query(const Plane *p_planes, int p_plane_count, const Ve
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
@@ -436,7 +440,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
Vector3 bounds[2];
- const Node **stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **alloca_stack = (const Node **)alloca(ALLOCA_STACK_SIZE * sizeof(const Node *));
+ const Node **stack = alloca_stack;
stack[0] = bvh_root;
int32_t depth = 1;
int32_t threshold = ALLOCA_STACK_SIZE - 2;
@@ -456,7 +461,8 @@ void DynamicBVH::ray_query(const Vector3 &p_from, const Vector3 &p_to, QueryResu
if (depth > threshold) {
if (aux_stack.is_empty()) {
aux_stack.resize(ALLOCA_STACK_SIZE * 2);
- memcpy(aux_stack.ptr(), stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ memcpy(aux_stack.ptr(), alloca_stack, ALLOCA_STACK_SIZE * sizeof(const Node *));
+ alloca_stack = nullptr;
} else {
aux_stack.resize(aux_stack.size() * 2);
}
diff --git a/core/math/geometry_2d.h b/core/math/geometry_2d.h
index 0e5702e0af..b37fce9e9c 100644
--- a/core/math/geometry_2d.h
+++ b/core/math/geometry_2d.h
@@ -306,10 +306,12 @@ public:
Vector<Delaunay2D::Triangle> tr = Delaunay2D::triangulate(p_points);
Vector<int> triangles;
+ triangles.resize(3 * tr.size());
+ int *ptr = triangles.ptrw();
for (int i = 0; i < tr.size(); i++) {
- triangles.push_back(tr[i].points[0]);
- triangles.push_back(tr[i].points[1]);
- triangles.push_back(tr[i].points[2]);
+ *ptr++ = tr[i].points[0];
+ *ptr++ = tr[i].points[1];
+ *ptr++ = tr[i].points[2];
}
return triangles;
}
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/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 2dbb7e468e..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_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
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_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
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_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
-#endif
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
data.instance = p_instance;
-#ifdef DEBUG_ENABLED
data.object_id = p_instance->get_instance_id();
-#endif
data.method = p_method;
_setup((uint32_t *)&data, sizeof(Data));
}
diff --git a/core/object/class_db.cpp b/core/object/class_db.cpp
index c594f4a9b4..bf1bd0de93 100644
--- a/core/object/class_db.cpp
+++ b/core/object/class_db.cpp
@@ -98,9 +98,24 @@ void ClassDB::get_class_list(List<StringName> *p_classes) {
p_classes->push_back(E.key);
}
- p_classes->sort();
+ p_classes->sort_custom<StringName::AlphCompare>();
}
+#ifdef TOOLS_ENABLED
+void ClassDB::get_extensions_class_list(List<StringName> *p_classes) {
+ OBJTYPE_RLOCK;
+
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ if (E.value.api != API_EXTENSION && E.value.api != API_EDITOR_EXTENSION) {
+ continue;
+ }
+ p_classes->push_back(E.key);
+ }
+
+ p_classes->sort_custom<StringName::AlphCompare>();
+}
+#endif
+
void ClassDB::get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes) {
OBJTYPE_RLOCK;
@@ -165,8 +180,8 @@ ClassDB::APIType ClassDB::get_api_type(const StringName &p_class) {
}
uint32_t ClassDB::get_api_hash(APIType p_api) {
- OBJTYPE_RLOCK;
#ifdef DEBUG_METHODS_ENABLED
+ OBJTYPE_WLOCK;
if (api_hashes_cache.has(p_api)) {
return api_hashes_cache[p_api];
@@ -175,7 +190,9 @@ uint32_t ClassDB::get_api_hash(APIType p_api) {
uint64_t hash = hash_murmur3_one_64(HashMapHasherDefault::hash(VERSION_FULL_CONFIG));
List<StringName> class_list;
- ClassDB::get_class_list(&class_list);
+ for (const KeyValue<StringName, ClassInfo> &E : classes) {
+ class_list.push_back(E.key);
+ }
// Must be alphabetically sorted for hash to compute.
class_list.sort_custom<StringName::AlphCompare>();
@@ -859,8 +876,8 @@ void ClassDB::get_enum_constants(const StringName &p_class, const StringName &p_
}
void ClassDB::set_method_error_return_values(const StringName &p_class, const StringName &p_method, const Vector<Error> &p_values) {
- OBJTYPE_RLOCK;
#ifdef DEBUG_METHODS_ENABLED
+ OBJTYPE_WLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_NULL(type);
@@ -871,6 +888,7 @@ void ClassDB::set_method_error_return_values(const StringName &p_class, const St
Vector<Error> ClassDB::get_method_error_return_values(const StringName &p_class, const StringName &p_method) {
#ifdef DEBUG_METHODS_ENABLED
+ OBJTYPE_RLOCK;
ClassInfo *type = classes.getptr(p_class);
ERR_FAIL_NULL_V(type, Vector<Error>());
@@ -1415,6 +1433,8 @@ void ClassDB::_bind_compatibility(ClassInfo *type, MethodBind *p_method) {
}
void ClassDB::_bind_method_custom(const StringName &p_class, MethodBind *p_method, bool p_compatibility) {
+ OBJTYPE_WLOCK;
+
ClassInfo *type = classes.getptr(p_class);
if (!type) {
ERR_FAIL_MSG("Couldn't bind custom method '" + p_method->get_name() + "' for instance '" + p_class + "'.");
diff --git a/core/object/class_db.h b/core/object/class_db.h
index 5c2c59d508..7a4ee1afa4 100644
--- a/core/object/class_db.h
+++ b/core/object/class_db.h
@@ -251,6 +251,9 @@ public:
}
static void get_class_list(List<StringName> *p_classes);
+#ifdef TOOLS_ENABLED
+ static void get_extensions_class_list(List<StringName> *p_classes);
+#endif
static void get_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
static void get_direct_inheriters_from_class(const StringName &p_class, List<StringName> *p_classes);
static StringName get_parent_class_nocheck(const StringName &p_class);
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 0f3cf3916a..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) {\\
- return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
- }\\
- 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 506f8291eb..a394c957d0 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -35,8 +35,10 @@
#include "core/object/class_db.h"
#include "core/object/script_language.h"
+#include <stdio.h>
+
#ifdef DEV_ENABLED
-// Includes sanity checks to ensure that a queue set as a thread singleton override
+// Includes safety checks to ensure that a queue set as a thread singleton override
// is only ever called from the thread it was set for.
#define LOCK_MUTEX \
if (this != MessageQueue::thread_singleton) { \
@@ -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;
@@ -537,7 +539,7 @@ CallQueue::~CallQueue() {
if (!allocator_is_custom) {
memdelete(allocator);
}
- // This is done here to avoid a circular dependency between the sanity checks and the thread singleton pointer.
+ // This is done here to avoid a circular dependency between the safety checks and the thread singleton pointer.
if (this == MessageQueue::thread_singleton) {
MessageQueue::thread_singleton = nullptr;
}
diff --git a/core/object/object.cpp b/core/object/object.cpp
index f62b93d0ff..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;
}
@@ -666,8 +667,16 @@ bool Object::has_method(const StringName &p_method) const {
}
MethodBind *method = ClassDB::get_method(get_class_name(), p_method);
+ if (method != nullptr) {
+ return true;
+ }
- return method != nullptr;
+ const Script *scr = Object::cast_to<Script>(this);
+ if (scr != nullptr) {
+ return scr->has_static_method(p_method);
+ }
+
+ return false;
}
Variant Object::getvar(const Variant &p_key, bool *r_valid) const {
@@ -1101,8 +1110,7 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
Error err = OK;
for (const Connection &c : slot_conns) {
- Object *target = c.callable.get_object();
- if (!target) {
+ if (!c.callable.is_valid()) {
// Target might have been deleted during signal callback, this is expected and OK.
continue;
}
@@ -1125,7 +1133,8 @@ Error Object::emit_signalp(const StringName &p_name, const Variant **p_args, int
continue;
}
#endif
- if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
+ Object *target = c.callable.get_object();
+ if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && target && !ClassDB::class_exists(target->get_class_name())) {
//most likely object is not initialized yet, do not throw error.
} else {
ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
@@ -1305,8 +1314,14 @@ void Object::get_signals_connected_to_this(List<Connection> *p_connections) cons
Error Object::connect(const StringName &p_signal, const Callable &p_callable, uint32_t p_flags) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is null.");
- Object *target_object = p_callable.get_object();
- ERR_FAIL_NULL_V_MSG(target_object, ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+ if (p_callable.is_standard()) {
+ // FIXME: This branch should probably removed in favor of the `is_valid()` branch, but there exist some classes
+ // that call `connect()` before they are fully registered with ClassDB. Until all such classes can be found
+ // and registered soon enough this branch is needed to allow `connect()` to succeed.
+ ERR_FAIL_NULL_V_MSG(p_callable.get_object(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "' to callable '" + p_callable + "': the callable object is null.");
+ } else {
+ ERR_FAIL_COND_V_MSG(!p_callable.is_valid(), ERR_INVALID_PARAMETER, "Cannot connect to '" + p_signal + "': the provided callable is not valid: " + p_callable);
+ }
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
@@ -1332,32 +1347,34 @@ 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.");
}
}
+ Object *target_object = p_callable.get_object();
+
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;
- slot.cE = target_object->connections.push_back(conn);
+ if (target_object) {
+ slot.cE = target_object->connections.push_back(conn);
+ }
if (p_flags & CONNECT_REFERENCE_COUNTED) {
slot.reference_count = 1;
}
//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;
}
@@ -1378,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) {
@@ -1390,9 +1405,6 @@ void Object::disconnect(const StringName &p_signal, const Callable &p_callable)
bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable, bool p_force) {
ERR_FAIL_COND_V_MSG(p_callable.is_null(), false, "Cannot disconnect from '" + p_signal + "': the provided callable is null.");
- Object *target_object = p_callable.get_object();
- ERR_FAIL_NULL_V_MSG(target_object, false, "Cannot disconnect '" + p_signal + "' from callable '" + p_callable + "': the callable object is null.");
-
SignalData *s = signal_map.getptr(p_signal);
if (!s) {
bool signal_is_valid = ClassDB::has_signal(get_class_name(), p_signal) ||
@@ -1412,7 +1424,13 @@ bool Object::_disconnect(const StringName &p_signal, const Callable &p_callable,
}
}
- target_object->connections.erase(slot->cE);
+ if (slot->cE) {
+ Object *target_object = p_callable.get_object();
+ if (target_object) {
+ target_object->connections.erase(slot->cE);
+ }
+ }
+
s->slot_map.erase(*p_callable.get_base_comparator());
if (s->slot_map.is_empty() && ClassDB::has_signal(get_class_name(), p_signal)) {
@@ -1863,7 +1881,6 @@ bool Object::has_instance_binding(void *p_token) {
return found;
}
-#ifdef TOOLS_ENABLED
void Object::free_instance_binding(void *p_token) {
bool found = false;
_instance_binding_mutex.lock();
@@ -1888,6 +1905,7 @@ void Object::free_instance_binding(void *p_token) {
_instance_binding_mutex.unlock();
}
+#ifdef TOOLS_ENABLED
void Object::clear_internal_extension() {
ERR_FAIL_NULL(_extension);
diff --git a/core/object/object.h b/core/object/object.h
index 60dc031d4d..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 */
@@ -970,9 +972,9 @@ public:
// Used on creation by binding only.
void set_instance_binding(void *p_token, void *p_binding, const GDExtensionInstanceBindingCallbacks *p_callbacks);
bool has_instance_binding(void *p_token);
+ void free_instance_binding(void *p_token);
#ifdef TOOLS_ENABLED
- void free_instance_binding(void *p_token);
void clear_internal_extension();
void reset_internal_extension(ObjectGDExtension *p_extension);
#endif
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 ca08e9837a..69da50f074 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;
@@ -145,7 +146,10 @@ public:
virtual PropertyInfo get_class_category() const;
#endif // TOOLS_ENABLED
+ // TODO: In the next compat breakage rename to `*_script_*` to disambiguate from `Object::has_method()`.
virtual bool has_method(const StringName &p_method) const = 0;
+ virtual bool has_static_method(const StringName &p_method) const { return false; }
+
virtual MethodInfo get_method_info(const StringName &p_method) const = 0;
virtual bool is_tool() const = 0;
@@ -236,6 +240,7 @@ public:
virtual void get_reserved_words(List<String> *p_words) const = 0;
virtual bool is_control_flow_keyword(String p_string) const = 0;
virtual void get_comment_delimiters(List<String> *p_delimiters) const = 0;
+ virtual void get_doc_comment_delimiters(List<String> *p_delimiters) const = 0;
virtual void get_string_delimiters(List<String> *p_delimiters) const = 0;
virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); }
virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); }
@@ -379,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 ce1109781a..be62cabe25 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -55,6 +55,7 @@ void ScriptExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_class_icon_path);
GDVIRTUAL_BIND(_has_method, "method");
+ GDVIRTUAL_BIND(_has_static_method, "method");
GDVIRTUAL_BIND(_get_method_info, "method");
GDVIRTUAL_BIND(_is_tool);
@@ -91,6 +92,7 @@ void ScriptLanguageExtension::_bind_methods() {
GDVIRTUAL_BIND(_get_reserved_words);
GDVIRTUAL_BIND(_is_control_flow_keyword, "keyword");
GDVIRTUAL_BIND(_get_comment_delimiters);
+ GDVIRTUAL_BIND(_get_doc_comment_delimiters);
GDVIRTUAL_BIND(_get_string_delimiters);
GDVIRTUAL_BIND(_make_template, "template", "class_name", "base_class_name");
GDVIRTUAL_BIND(_get_built_in_templates, "object");
@@ -105,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);
@@ -143,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 beb8064a33..852b2aebd8 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -99,6 +99,7 @@ public:
#endif // TOOLS_ENABLED
EXBIND1RC(bool, has_method, const StringName &)
+ EXBIND1RC(bool, has_static_method, const StringName &)
GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
@@ -240,6 +241,16 @@ public:
}
}
+ GDVIRTUAL0RC(Vector<String>, _get_doc_comment_delimiters)
+
+ virtual void get_doc_comment_delimiters(List<String> *p_words) const override {
+ Vector<String> ret;
+ GDVIRTUAL_CALL(_get_doc_comment_delimiters, ret);
+ for (int i = 0; i < ret.size(); i++) {
+ p_words->push_back(ret[i]);
+ }
+ }
+
GDVIRTUAL0RC(Vector<String>, _get_string_delimiters)
virtual void get_string_delimiters(List<String> *p_words) const override {
@@ -302,6 +313,9 @@ public:
ERR_CONTINUE(!err.has("message"));
ScriptError serr;
+ if (err.has("path")) {
+ serr.path = err["path"];
+ }
serr.line = err["line"];
serr.column = err["column"];
serr.message = err["message"];
@@ -593,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 b0f9501985..3c1d4ef95e 100644
--- a/core/object/undo_redo.cpp
+++ b/core/object/undo_redo.cpp
@@ -71,9 +71,7 @@ bool UndoRedo::_redo(bool p_execute) {
}
current_action++;
- if (p_execute) {
- _process_operation_list(actions.write[current_action].do_ops.front());
- }
+ _process_operation_list(actions.write[current_action].do_ops.front(), p_execute);
version++;
emit_signal(SNAME("version_changed"));
@@ -136,17 +134,22 @@ void UndoRedo::add_do_method(const Callable &p_callable) {
ERR_FAIL_COND(action_level <= 0);
ERR_FAIL_COND((current_action + 1) >= actions.size());
- Object *object = p_callable.get_object();
- ERR_FAIL_NULL(object);
+ ObjectID object_id = p_callable.get_object_id();
+ Object *object = ObjectDB::get_instance(object_id);
+ ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
Operation do_op;
do_op.callable = p_callable;
- do_op.object = p_callable.get_object_id();
+ do_op.object = object_id;
if (Object::cast_to<RefCounted>(object)) {
do_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
do_op.type = Operation::TYPE_METHOD;
do_op.name = p_callable.get_method();
+ if (do_op.name == StringName()) {
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ do_op.name = static_cast<String>(p_callable);
+ }
actions.write[current_action + 1].do_ops.push_back(do_op);
}
@@ -161,18 +164,23 @@ void UndoRedo::add_undo_method(const Callable &p_callable) {
return;
}
- Object *object = p_callable.get_object();
- ERR_FAIL_NULL(object);
+ ObjectID object_id = p_callable.get_object_id();
+ Object *object = ObjectDB::get_instance(object_id);
+ ERR_FAIL_COND(object_id.is_valid() && object == nullptr);
Operation undo_op;
undo_op.callable = p_callable;
- undo_op.object = p_callable.get_object_id();
+ undo_op.object = object_id;
if (Object::cast_to<RefCounted>(object)) {
undo_op.ref = Ref<RefCounted>(Object::cast_to<RefCounted>(object));
}
undo_op.type = Operation::TYPE_METHOD;
undo_op.force_keep_in_merge_ends = force_keep_in_merge_ends;
undo_op.name = p_callable.get_method();
+ if (undo_op.name == StringName()) {
+ // There's no `get_method()` for custom callables, so use `operator String()` instead.
+ undo_op.name = static_cast<String>(p_callable);
+ }
actions.write[current_action + 1].undo_ops.push_back(undo_op);
}
@@ -293,6 +301,8 @@ void UndoRedo::commit_action(bool p_execute) {
return; //still nested
}
+ bool add_message = !merging;
+
if (merging) {
version--;
merging = false;
@@ -306,12 +316,12 @@ 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);
}
}
-void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
+void UndoRedo::_process_operation_list(List<Operation>::Element *E, bool p_execute) {
const int PREALLOCATE_ARGS_COUNT = 16;
LocalVector<const Variant *> args;
@@ -327,18 +337,20 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
switch (op.type) {
case Operation::TYPE_METHOD: {
- Callable::CallError ce;
- Variant ret;
- op.callable.callp(nullptr, 0, ret, ce);
- if (ce.error != Callable::CallError::CALL_OK) {
- ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
- }
+ if (p_execute) {
+ Callable::CallError ce;
+ Variant ret;
+ op.callable.callp(nullptr, 0, ret, ce);
+ if (ce.error != Callable::CallError::CALL_OK) {
+ ERR_PRINT("Error calling UndoRedo method operation '" + String(op.name) + "': " + Variant::get_call_error_text(obj, op.name, nullptr, 0, ce));
+ }
#ifdef TOOLS_ENABLED
- Resource *res = Object::cast_to<Resource>(obj);
- if (res) {
- res->set_edited(true);
- }
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
#endif
+ }
if (method_callback) {
Vector<Variant> binds;
@@ -363,13 +375,16 @@ void UndoRedo::_process_operation_list(List<Operation>::Element *E) {
}
} break;
case Operation::TYPE_PROPERTY: {
- obj->set(op.name, op.value);
+ if (p_execute) {
+ obj->set(op.name, op.value);
#ifdef TOOLS_ENABLED
- Resource *res = Object::cast_to<Resource>(obj);
- if (res) {
- res->set_edited(true);
- }
+ Resource *res = Object::cast_to<Resource>(obj);
+ if (res) {
+ res->set_edited(true);
+ }
#endif
+ }
+
if (property_callback) {
property_callback(prop_callback_ud, obj, op.name, op.value);
}
@@ -390,7 +405,7 @@ bool UndoRedo::undo() {
if (current_action < 0) {
return false; //nothing to redo
}
- _process_operation_list(actions.write[current_action].undo_ops.front());
+ _process_operation_list(actions.write[current_action].undo_ops.front(), true);
current_action--;
version--;
emit_signal(SNAME("version_changed"));
@@ -450,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 389d8714f7..b3a3322e4b 100644
--- a/core/object/undo_redo.h
+++ b/core/object/undo_redo.h
@@ -85,7 +85,7 @@ private:
uint64_t version = 1;
void _pop_history_tail();
- void _process_operation_list(List<Operation>::Element *E);
+ void _process_operation_list(List<Operation>::Element *E, bool p_execute);
void _discard_redo();
bool _redo(bool p_execute);
@@ -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 cfc3ac3bcf..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) {
@@ -75,10 +84,6 @@ void WorkerThreadPool::_process_task(Task *p_task) {
if (p_task->group) {
// Handling a group
bool do_post = false;
- Callable::CallError ce;
- Variant ret;
- Variant arg;
- Variant *argptr = &arg;
while (true) {
uint32_t work_index = p_task->group->index.postincrement();
@@ -91,8 +96,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
} else if (p_task->template_userdata) {
p_task->template_userdata->callback_indexed(work_index);
} else {
- arg = work_index;
- p_task->callable.callp((const Variant **)&argptr, 1, ret, ce);
+ p_task->callable.call(work_index);
}
// This is the only way to ensure posting is done when all tasks are really complete.
@@ -141,9 +145,7 @@ void WorkerThreadPool::_process_task(Task *p_task) {
p_task->template_userdata->callback();
memdelete(p_task->template_userdata);
} else {
- Callable::CallError ce;
- Variant ret;
- p_task->callable.callp(nullptr, 0, ret, ce);
+ p_task->callable.call();
}
task_mutex.lock();
@@ -533,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/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 b4ac533779..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,24 +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.
@@ -434,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 4402e44ad4..658297d805 100644
--- a/core/string/string_name.cpp
+++ b/core/string/string_name.cpp
@@ -100,11 +100,9 @@ void StringName::cleanup() {
lost_strings++;
if (OS::get_singleton()->is_stdout_verbose()) {
- if (d->cname) {
- print_line("Orphan StringName: " + String(d->cname));
- } else {
- print_line("Orphan StringName: " + String(d->name));
- }
+ String dname = String(d->cname ? d->cname : d->name);
+
+ print_line(vformat("Orphan StringName: %s (static: %d, total: %d)", dname, d->static_count.get(), d->refcount.get()));
}
}
@@ -113,7 +111,7 @@ void StringName::cleanup() {
}
}
if (lost_strings) {
- print_verbose("StringName: " + itos(lost_strings) + " unclaimed string names at exit.");
+ print_verbose(vformat("StringName: %d unclaimed string names at exit.", lost_strings));
}
configured = false;
}
@@ -124,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 5a42a3af12..a24cff4f11 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;
@@ -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') {
@@ -3974,24 +3994,22 @@ bool String::is_absolute_path() const {
}
}
-static _FORCE_INLINE_ bool _is_valid_identifier_bit(int p_index, char32_t p_char) {
- if (p_index == 0 && is_digit(p_char)) {
- return false; // No start with number plz.
- }
- return is_ascii_identifier_char(p_char);
-}
-
String String::validate_identifier() const {
if (is_empty()) {
return "_"; // Empty string is not a valid identifier;
}
- String result = *this;
+ String result;
+ if (is_digit(operator[](0))) {
+ result = "_" + *this;
+ } else {
+ result = *this;
+ }
+
int len = result.length();
char32_t *buffer = result.ptrw();
-
for (int i = 0; i < len; i++) {
- if (!_is_valid_identifier_bit(i, buffer[i])) {
+ if (!is_ascii_identifier_char(buffer[i])) {
buffer[i] = '_';
}
}
@@ -4006,10 +4024,14 @@ bool String::is_valid_identifier() const {
return false;
}
+ if (is_digit(operator[](0))) {
+ return false;
+ }
+
const char32_t *str = &operator[](0);
for (int i = 0; i < len; i++) {
- if (!_is_valid_identifier_bit(i, str[i])) {
+ if (!is_ascii_identifier_char(str[i])) {
return false;
}
}
@@ -4697,11 +4719,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 += " ";
}
@@ -4815,6 +4842,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;
@@ -4855,16 +4883,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);
@@ -4873,8 +4912,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 {
@@ -5067,6 +5106,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':
diff --git a/core/string/ustring.h b/core/string/ustring.h
index f45392eee1..897b06fc6d 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -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 deb2937771..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) {
@@ -144,7 +148,7 @@ public:
if (thread_safe) {
spin_lock.lock();
}
- ERR_FAIL_COND(page_pool != nullptr); //sanity check
+ ERR_FAIL_COND(page_pool != nullptr); // Safety check.
ERR_FAIL_COND(p_page_size == 0);
page_size = nearest_power_of_2_templated(p_page_size);
page_mask = page_size - 1;
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 0a33809bb2..152fddd011 100644
--- a/core/templates/rb_map.h
+++ b/core/templates/rb_map.h
@@ -96,6 +96,8 @@ public:
typedef KeyValue<K, V> ValueType;
struct Iterator {
+ friend class RBMap<K, V, C, A>;
+
_FORCE_INLINE_ KeyValue<K, V> &operator*() const {
return E->key_value();
}
@@ -109,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; }
@@ -136,11 +143,16 @@ public:
return *this;
}
- _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
- _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
+ _FORCE_INLINE_ bool operator==(const ConstIterator &p_it) const { return E == p_it.E; }
+ _FORCE_INLINE_ bool operator!=(const ConstIterator &p_it) const { return E != p_it.E; }
explicit operator bool() const {
return E != nullptr;
}
+
+ ConstIterator &operator=(const ConstIterator &p_it) {
+ E = p_it.E;
+ return *this;
+ }
ConstIterator(const Element *p_E) { E = p_E; }
ConstIterator() {}
ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
diff --git a/core/templates/safe_refcount.h b/core/templates/safe_refcount.h
index bfc9f6fc9a..20fb0c6501 100644
--- a/core/templates/safe_refcount.h
+++ b/core/templates/safe_refcount.h
@@ -182,7 +182,7 @@ class SafeRefCount {
SafeNumeric<uint32_t> count;
#ifdef DEV_ENABLED
- _ALWAYS_INLINE_ void _check_unref_sanity() {
+ _ALWAYS_INLINE_ void _check_unref_safety() {
// This won't catch every misuse, but it's better than nothing.
CRASH_COND_MSG(count.get() == 0,
"Trying to unreference a SafeRefCount which is already zero is wrong and a symptom of it being misused.\n"
@@ -202,14 +202,14 @@ public:
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
#ifdef DEV_ENABLED
- _check_unref_sanity();
+ _check_unref_safety();
#endif
return count.decrement() == 0;
}
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
#ifdef DEV_ENABLED
- _check_unref_sanity();
+ _check_unref_safety();
#endif
return count.decrement();
}
diff --git a/core/typedefs.h b/core/typedefs.h
index 24c247fd38..803b2e5ae0 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.
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index d7034f1c00..0b1174c873 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -47,6 +47,13 @@ void Callable::callp(const Variant **p_arguments, int p_argcount, Variant &r_ret
r_call_error.expected = 0;
r_return_value = Variant();
} else if (is_custom()) {
+ if (!is_valid()) {
+ r_call_error.error = CallError::CALL_ERROR_INSTANCE_IS_NULL;
+ r_call_error.argument = 0;
+ r_call_error.expected = 0;
+ r_return_value = Variant();
+ return;
+ }
custom->call(p_arguments, p_argcount, r_return_value, r_call_error);
} else {
Object *obj = ObjectDB::get_instance(ObjectID(object));
diff --git a/core/variant/callable.h b/core/variant/callable.h
index 086e5d2a00..3ae424e9bf 100644
--- a/core/variant/callable.h
+++ b/core/variant/callable.h
@@ -69,6 +69,8 @@ public:
int expected = 0;
};
+ template <typename... VarArgs>
+ Variant call(VarArgs... p_args) const;
void callp(const Variant **p_arguments, int p_argcount, Variant &r_return_value, CallError &r_call_error) const;
void call_deferredp(const Variant **p_arguments, int p_argcount) const;
Variant callv(const Array &p_arguments) const;
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 63ea3274ce..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();
@@ -1291,7 +1291,13 @@ void Variant::zero() {
break;
default:
+ Type prev_type = type;
this->clear();
+ if (type != prev_type) {
+ // clear() changes type to NIL, so it needs to be restored.
+ Callable::CallError ce;
+ Variant::construct(prev_type, *this, nullptr, 0, ce);
+ }
break;
}
}
@@ -2117,7 +2123,7 @@ Variant::operator ::RID() const {
} else if (type == OBJECT && _get_obj().obj) {
#ifdef DEBUG_ENABLED
if (EngineDebugger::is_active()) {
- ERR_FAIL_COND_V_MSG(ObjectDB::get_instance(_get_obj().id) == nullptr, ::RID(), "Invalid pointer (object was freed).");
+ ERR_FAIL_NULL_V_MSG(ObjectDB::get_instance(_get_obj().id), ::RID(), "Invalid pointer (object was freed).");
}
#endif
Callable::CallError ce;
diff --git a/core/variant/variant.h b/core/variant/variant.h
index d698f85754..e93733040a 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);
@@ -834,6 +839,20 @@ String vformat(const String &p_text, const VarArgs... p_args) {
}
template <typename... VarArgs>
+Variant Callable::call(VarArgs... p_args) const {
+ Variant args[sizeof...(p_args) + 1] = { p_args..., 0 }; // +1 makes sure zero sized arrays are also supported.
+ const Variant *argptrs[sizeof...(p_args) + 1];
+ for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+ argptrs[i] = &args[i];
+ }
+
+ Variant ret;
+ CallError ce;
+ callp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args), ret, ce);
+ return ret;
+}
+
+template <typename... VarArgs>
Callable Callable::bind(VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index f041d2c95e..1daad2058e 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -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));
}
}
}
@@ -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 782053b613..171074188f 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -418,7 +418,7 @@ public:
case Variant::PACKED_COLOR_ARRAY:
return get_color_array(v);
case Variant::OBJECT:
- return v->_get_obj().obj;
+ return get_object(v);
case Variant::VARIANT_MAX:
ERR_FAIL_V(nullptr);
}
@@ -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.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 fea1622222..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;
@@ -1075,7 +1078,7 @@ Error VariantParser::parse_value(Token &token, Variant &value, Stream *p_stream,
return ERR_PARSE_ERROR;
}
- static HashMap<StringName, Variant::Type> builtin_types;
+ static HashMap<String, Variant::Type> builtin_types;
if (builtin_types.is_empty()) {
for (int i = 1; i < Variant::VARIANT_MAX; i++) {
builtin_types[Variant::get_type_name((Variant::Type)i)] = (Variant::Type)i;
diff --git a/core/variant/variant_setget.cpp b/core/variant/variant_setget.cpp
index 05f7abf32c..b0e0a885f4 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 ****/
diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp
index 6f334d1859..cc48394b64 100644
--- a/core/variant/variant_utility.cpp
+++ b/core/variant/variant_utility.cpp
@@ -121,16 +121,27 @@ Variant VariantUtilityFunctions::floor(Variant x, Callable::CallError &r_error)
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).floor();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).floor();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).floor();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -154,16 +165,27 @@ Variant VariantUtilityFunctions::ceil(Variant x, Callable::CallError &r_error) {
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).ceil();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).ceil();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).ceil();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -187,16 +209,27 @@ Variant VariantUtilityFunctions::round(Variant x, Callable::CallError &r_error)
case Variant::VECTOR2: {
return VariantInternalAccessor<Vector2>::get(&x).round();
} break;
+ case Variant::VECTOR2I: {
+ return VariantInternalAccessor<Vector2i>::get(&x);
+ } break;
case Variant::VECTOR3: {
return VariantInternalAccessor<Vector3>::get(&x).round();
} break;
+ case Variant::VECTOR3I: {
+ return VariantInternalAccessor<Vector3i>::get(&x);
+ } break;
case Variant::VECTOR4: {
return VariantInternalAccessor<Vector4>::get(&x).round();
} break;
+ case Variant::VECTOR4I: {
+ return VariantInternalAccessor<Vector4i>::get(&x);
+ } break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -236,9 +269,11 @@ Variant VariantUtilityFunctions::abs(const Variant &x, Callable::CallError &r_er
return VariantInternalAccessor<Vector4i>::get(&x).abs();
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -278,9 +313,11 @@ Variant VariantUtilityFunctions::sign(const Variant &x, Callable::CallError &r_e
return VariantInternalAccessor<Vector4i>::get(&x).sign();
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ } break;
}
}
@@ -333,14 +370,40 @@ int VariantUtilityFunctions::step_decimals(float step) {
}
Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
- if (x.get_type() != step.get_type() && !((x.get_type() == Variant::INT && step.get_type() == Variant::FLOAT) || (x.get_type() == Variant::FLOAT && step.get_type() == Variant::INT))) {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
- r_error.argument = 1;
- r_error.expected = x.get_type();
- return Variant();
+ switch (x.get_type()) {
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::VECTOR2:
+ case Variant::VECTOR2I:
+ case Variant::VECTOR3:
+ case Variant::VECTOR3I:
+ case Variant::VECTOR4:
+ case Variant::VECTOR4I:
+ break;
+ default:
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "x" must be "int", "float", "Vector2", "Vector2i", "Vector3", "Vector3i", "Vector4", or "Vector4i".)";
+ }
+
+ if (x.get_type() != step.get_type()) {
+ if (x.get_type() == Variant::INT || x.get_type() == Variant::FLOAT) {
+ if (step.get_type() != Variant::INT && step.get_type() != Variant::FLOAT) {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "step" must be "int" or "float".)";
+ }
+ } else {
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 1;
+ r_error.expected = x.get_type();
+ return Variant();
+ }
}
+ r_error.error = Callable::CallError::CALL_OK;
switch (step.get_type()) {
case Variant::INT: {
return snappedi(x, VariantInternalAccessor<int64_t>::get(&step));
@@ -367,9 +430,8 @@ Variant VariantUtilityFunctions::snapped(const Variant &x, const Variant &step,
return VariantInternalAccessor<Vector4i>::get(&x).snapped(VariantInternalAccessor<Vector4i>::get(&step));
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ return Variant(); // Already handled.
+ } break;
}
}
@@ -382,7 +444,23 @@ int64_t VariantUtilityFunctions::snappedi(double x, int64_t step) {
}
Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, double weight, Callable::CallError &r_error) {
- r_error.error = Callable::CallError::CALL_OK;
+ switch (from.get_type()) {
+ case Variant::INT:
+ case Variant::FLOAT:
+ case Variant::VECTOR2:
+ case Variant::VECTOR3:
+ case Variant::VECTOR4:
+ case Variant::QUATERNION:
+ case Variant::BASIS:
+ case Variant::COLOR:
+ break;
+ default:
+ r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+ r_error.argument = 0;
+ r_error.expected = Variant::NIL;
+ return R"(Argument "from" must be "int", "float", "Vector2", "Vector3", "Vector4", "Quaternion", "Basis, or "Color".)";
+ }
+
if (from.get_type() != to.get_type()) {
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 1;
@@ -390,6 +468,7 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do
return Variant();
}
+ r_error.error = Callable::CallError::CALL_OK;
switch (from.get_type()) {
case Variant::INT: {
return lerpf(VariantInternalAccessor<int64_t>::get(&from), to, weight);
@@ -416,9 +495,8 @@ Variant VariantUtilityFunctions::lerp(const Variant &from, const Variant &to, do
return VariantInternalAccessor<Color>::get(&from).lerp(VariantInternalAccessor<Color>::get(&to), weight);
} break;
default: {
- r_error.error = Callable::CallError::CALL_ERROR_INVALID_METHOD;
- return Variant();
- }
+ return Variant(); // Already handled.
+ } break;
}
}