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.cpp8
-rw-r--r--core/core_bind.cpp3
-rw-r--r--core/core_builders.py26
-rw-r--r--core/core_constants.cpp2
-rw-r--r--core/extension/extension_api_dump.cpp42
-rw-r--r--core/extension/gdextension.cpp53
-rw-r--r--core/extension/gdextension.h13
-rw-r--r--core/extension/gdextension_compat_hashes.cpp4
-rw-r--r--core/extension/gdextension_compat_hashes.h1
-rw-r--r--core/extension/gdextension_interface.cpp6
-rw-r--r--core/extension/gdextension_interface.h9
-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.cpp128
-rw-r--r--core/input/input.h21
-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_zip.cpp20
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/marshalls.cpp8
-rw-r--r--core/io/resource.cpp4
-rw-r--r--core/io/resource.h1
-rw-r--r--core/io/resource_format_binary.cpp2
-rw-r--r--core/io/resource_importer.h1
-rw-r--r--core/io/resource_loader.cpp9
-rw-r--r--core/io/resource_uid.cpp2
-rw-r--r--core/math/convex_hull.cpp2
-rw-r--r--core/math/geometry_2d.h8
-rw-r--r--core/object/callable_method_pointer.h6
-rw-r--r--core/object/class_db.cpp28
-rw-r--r--core/object/class_db.h3
-rw-r--r--core/object/make_virtuals.py4
-rw-r--r--core/object/message_queue.cpp4
-rw-r--r--core/object/object.cpp34
-rw-r--r--core/object/object.h10
-rw-r--r--core/object/script_language.cpp63
-rw-r--r--core/object/script_language.h8
-rw-r--r--core/object/script_language_extension.cpp1
-rw-r--r--core/object/script_language_extension.h13
-rw-r--r--core/object/undo_redo.cpp73
-rw-r--r--core/object/undo_redo.h4
-rw-r--r--core/object/worker_thread_pool.cpp9
-rw-r--r--core/object/worker_thread_pool.h1
-rw-r--r--core/os/os.cpp6
-rw-r--r--core/register_core_types.cpp1
-rw-r--r--core/string/string_name.cpp12
-rw-r--r--core/string/translation.cpp10
-rw-r--r--core/string/ustring.cpp7
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/templates/paged_allocator.h2
-rw-r--r--core/templates/rb_map.h2
-rw-r--r--core/templates/safe_refcount.h6
-rw-r--r--core/variant/callable.cpp7
-rw-r--r--core/variant/variant.cpp8
-rw-r--r--core/variant/variant_parser.cpp2
-rw-r--r--core/variant/variant_utility.cpp134
61 files changed, 635 insertions, 270 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..93934f2320 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -852,8 +852,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 +870,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) {
@@ -1343,6 +1344,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")) {
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index 05fe393a2f..981d9b0025 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -1211,8 +1211,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_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..f3e988633c 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);
}
}
@@ -750,7 +750,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
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);
+ d2["description"] = fix_doc_description(operator_doc.description);
break;
}
}
@@ -805,7 +805,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 +853,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 +871,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 +934,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 +968,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 +982,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 +1040,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 +1117,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 +1160,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 +1209,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 +1224,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 a4d4b9161e..2bac1f6592 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,7 +219,7 @@ 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) {
@@ -234,7 +235,7 @@ public:
void *ret_opaque = nullptr;
if (r_ret) {
VariantInternal::initialize(r_ret, return_value_info.type);
- ret_opaque = VariantInternal::get_opaque_pointer(r_ret);
+ ret_opaque = r_ret->get_type() == Variant::NIL ? r_ret : VariantInternal::get_opaque_pointer(r_ret);
}
ptrcall(p_object, argptrs, ret_opaque);
@@ -663,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.");
@@ -677,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);
@@ -704,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;
@@ -713,12 +714,20 @@ 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);
+ Error err = OS::get_singleton()->open_dynamic_library(abs_path, library, true, &library_path);
if (err != OK) {
ERR_PRINT("GDExtension dynamic library not found: " + abs_path);
return err;
}
+#if defined(WINDOWS_ENABLED) && defined(TOOLS_ENABLED)
+ // If we copied the file, let's change the library path to point at the original,
+ // because that's what we want to check to see if it's changed.
+ if (library_copied) {
+ library_path = library_path.get_base_dir() + "\\" + p_path.get_file();
+ }
+#endif
+
void *entry_funcptr = nullptr;
err = OS::get_singleton()->get_dynamic_library_symbol_handle(library, p_entry_symbol, entry_funcptr, false);
@@ -776,7 +785,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));
}
@@ -835,6 +844,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.");
@@ -899,6 +912,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);
}
@@ -910,12 +925,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.
@@ -949,6 +964,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) {
@@ -976,10 +1000,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..2dac4a3a5d 100644
--- a/core/extension/gdextension_compat_hashes.cpp
+++ b/core/extension/gdextension_compat_hashes.cpp
@@ -840,4 +840,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..29393dcb2d 100644
--- a/core/extension/gdextension_compat_hashes.h
+++ b/core/extension/gdextension_compat_hashes.h
@@ -48,6 +48,7 @@ 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);
};
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index 843f9ceecf..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;
}
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index ae7c5e0d2a..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:
*
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..257452b3d8 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -695,54 +695,34 @@ 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;
- }
- }
+ const int event_index = InputMap::get_singleton()->event_get_index(p_event, E.key);
+ if (event_index == -1) {
+ continue;
+ }
- 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 &action = action_state[E.key];
+ if (!p_event->is_echo()) {
+ if (p_event->is_action_pressed(E.key)) {
+ if (!action.pressed) {
+ action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
- }
+ action.pressed |= ((uint64_t)1 << event_index);
+ } else {
+ action.pressed &= ~((uint64_t)1 << event_index);
+ action.pressed &= ~(1 << MAX_EVENT); // Always release the event from action_press() method.
- if (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);
+ if (!action.pressed) {
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ }
+ _update_action_strength(action, MAX_EVENT, 0.0);
+ _update_action_raw_strength(action, MAX_EVENT, 0.0);
}
+ action.exact = InputMap::get_singleton()->event_is_action(p_event, E.key, true);
}
+ _update_action_strength(action, event_index, p_event->get_action_strength(E.key));
+ _update_action_raw_strength(action, event_index, p_event->get_action_raw_strength(E.key));
}
if (event_dispatch_function) {
@@ -859,13 +839,13 @@ void Input::action_press(const StringName &p_action, float p_strength) {
// Create or retrieve existing action.
Action &action = action_state[p_action];
- action.pressed++;
- if (action.pressed == 1) {
+ if (!action.pressed) {
action.pressed_physics_frame = Engine::get_singleton()->get_physics_frames();
action.pressed_process_frame = Engine::get_singleton()->get_process_frames();
}
- action.strength = p_strength;
- action.raw_strength = p_strength;
+ action.pressed |= 1 << MAX_EVENT;
+ _update_action_strength(action, MAX_EVENT, p_strength);
+ _update_action_raw_strength(action, MAX_EVENT, p_strength);
action.exact = true;
}
@@ -873,13 +853,15 @@ 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.pressed = 0;
+ action.strength = 0.0;
+ action.raw_strength = 0.0;
+ action.released_physics_frame = Engine::get_singleton()->get_physics_frames();
+ action.released_process_frame = Engine::get_singleton()->get_process_frames();
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ action.strengths[i] = 0.0;
+ action.raw_strengths[i] = 0.0;
}
- action.strength = 0.0f;
- action.raw_strength = 0.0f;
action.exact = true;
}
@@ -1096,7 +1078,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 +1119,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 +1190,38 @@ void Input::_axis_event(int p_device, JoyAxis p_axis, float p_value) {
parse_input_event(ievent);
}
+void Input::_update_action_strength(Action &p_action, int p_event_index, float p_strength) {
+ ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
+
+ float old_strength = p_action.strengths[p_event_index];
+ p_action.strengths[p_event_index] = p_strength;
+
+ if (p_strength > p_action.strength) {
+ p_action.strength = p_strength;
+ } else if (Math::is_equal_approx(old_strength, p_action.strength)) {
+ p_action.strength = p_strength;
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ p_action.strength = MAX(p_action.strength, p_action.strengths[i]);
+ }
+ }
+}
+
+void Input::_update_action_raw_strength(Action &p_action, int p_event_index, float p_strength) {
+ ERR_FAIL_INDEX(p_event_index, (int)MAX_EVENT + 1);
+
+ float old_strength = p_action.raw_strengths[p_event_index];
+ p_action.raw_strengths[p_event_index] = p_strength;
+
+ if (p_strength > p_action.raw_strength) {
+ p_action.raw_strength = p_strength;
+ } else if (Math::is_equal_approx(old_strength, p_action.raw_strength)) {
+ p_action.raw_strength = p_strength;
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ p_action.raw_strength = MAX(p_action.raw_strength, p_action.raw_strengths[i]);
+ }
+ }
+}
+
Input::JoyEvent Input::_get_mapped_button_event(const JoyDeviceMapping &mapping, JoyButton p_button) {
JoyEvent event;
@@ -1242,7 +1257,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 +1303,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..dd613c4877 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 = 31;
+
public:
enum MouseMode {
MOUSE_MODE_VISIBLE,
@@ -103,11 +105,22 @@ private:
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;
+ uint64_t pressed = 0;
bool exact = true;
float strength = 0.0f;
float raw_strength = 0.0f;
+ LocalVector<float> strengths;
+ LocalVector<float> raw_strengths;
+
+ Action() {
+ strengths.resize(MAX_EVENT + 1);
+ raw_strengths.resize(MAX_EVENT + 1);
+
+ for (uint64_t i = 0; i <= MAX_EVENT; i++) {
+ strengths[i] = 0.0;
+ raw_strengths[i] = 0.0;
+ }
+ }
};
HashMap<StringName, Action> action_state;
@@ -221,12 +234,14 @@ 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_strength(Action &p_action, int p_event_index, float p_strength);
+ void _update_action_raw_strength(Action &p_action, int p_event_index, float p_strength);
void _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_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..ce08b417a8 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3773,7 +3773,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..3d384d9345 100644
--- a/core/io/marshalls.cpp
+++ b/core/io/marshalls.cpp
@@ -1155,10 +1155,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:
@@ -1619,8 +1621,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..64fa597a67 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.
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..2a33f723dc 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);
}
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..529128b9a2 100644
--- a/core/io/resource_loader.cpp
+++ b/core/io/resource_loader.cpp
@@ -341,6 +341,8 @@ 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);
+ } else if (!load_task.local_path.is_resource_file()) {
+ load_task.resource->set_path_cache(load_task.local_path);
}
if (load_task.xl_remapped) {
@@ -918,7 +920,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 +1055,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();
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/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/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/object/callable_method_pointer.h b/core/object/callable_method_pointer.h
index 2dbb7e468e..db78b982e4 100644
--- a/core/object/callable_method_pointer.h
+++ b/core/object/callable_method_pointer.h
@@ -99,7 +99,7 @@ public:
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.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
@@ -154,7 +154,7 @@ public:
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.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
@@ -209,7 +209,7 @@ public:
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.");
+ ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
#endif
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
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..79a8df6c8a 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -47,8 +47,8 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
}\\
_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 (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\
+ return true;\\
}\\
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
_gdvirtual_##m_name = nullptr;\\
diff --git a/core/object/message_queue.cpp b/core/object/message_queue.cpp
index 506f8291eb..de71295ee5 100644
--- a/core/object/message_queue.cpp
+++ b/core/object/message_queue.cpp
@@ -36,7 +36,7 @@
#include "core/object/script_language.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) { \
@@ -537,7 +537,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 056334c8e1..40df13849b 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;
}
@@ -1109,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;
}
@@ -1133,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) + ".");
@@ -1313,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) {
@@ -1352,6 +1359,8 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
}
}
+ Object *target_object = p_callable.get_object();
+
SignalData::Slot slot;
Connection conn;
@@ -1359,7 +1368,9 @@ Error Object::connect(const StringName &p_signal, const Callable &p_callable, ui
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;
}
@@ -1398,9 +1409,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) ||
@@ -1420,7 +1428,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)) {
diff --git a/core/object/object.h b/core/object/object.h
index a444db0f70..7b53fcaa41 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.
@@ -801,6 +801,8 @@ public:
NOTIFICATION_POSTINITIALIZE = 0,
NOTIFICATION_PREDELETE = 1,
NOTIFICATION_EXTENSION_RELOADED = 2,
+ // Internal notification to send after NOTIFICATION_PREDELETE, not bound to scripting.
+ NOTIFICATION_PREDELETE_CLEANUP = 3,
};
/* TYPE API */
diff --git a/core/object/script_language.cpp b/core/object/script_language.cpp
index 011f4203ea..086f8a666e 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) {
@@ -160,12 +161,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 +181,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 +223,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 +281,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 +291,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 +580,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);
}
}
diff --git a/core/object/script_language.h b/core/object/script_language.h
index 5ff7cd8582..85e64c8d62 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;
@@ -239,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>(); }
diff --git a/core/object/script_language_extension.cpp b/core/object/script_language_extension.cpp
index a07bf63a02..e326baf7eb 100644
--- a/core/object/script_language_extension.cpp
+++ b/core/object/script_language_extension.cpp
@@ -92,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");
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index 89bba80b90..00ab1cd6c0 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -241,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 {
@@ -303,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"];
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 2fcd0867e6..784acadab4 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) {
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index d4d9387765..f323a979f7 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;
diff --git a/core/os/os.cpp b/core/os/os.cpp
index 991b179e1f..c7390f14ff 100644
--- a/core/os/os.cpp
+++ b/core/os/os.cpp
@@ -490,6 +490,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;
}
diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp
index b4ac533779..4ad9dd43c4 100644
--- a/core/register_core_types.cpp
+++ b/core/register_core_types.cpp
@@ -360,6 +360,7 @@ void unregister_core_extensions() {
if (_is_core_extensions_registered) {
gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE);
}
+ GDExtension::finalize_gdextensions();
}
void unregister_core_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..a443ed308d 100644
--- a/core/string/translation.cpp
+++ b/core/string/translation.cpp
@@ -520,11 +520,11 @@ String TranslationServer::get_country_name(const String &p_country) const {
void TranslationServer::set_locale(const String &p_locale) {
locale = standardize_locale(p_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 +816,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 +837,11 @@ void TranslationServer::reload_pseudolocalization() {
pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+ ResourceLoader::reload_translation_remaps();
+
if (OS::get_singleton()->get_main_loop()) {
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
}
- ResourceLoader::reload_translation_remaps();
}
StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 9be7c04158..60e2d539f8 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -4699,11 +4699,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 += " ";
}
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/paged_allocator.h b/core/templates/paged_allocator.h
index deb2937771..72425a8c3d 100644
--- a/core/templates/paged_allocator.h
+++ b/core/templates/paged_allocator.h
@@ -144,7 +144,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/rb_map.h b/core/templates/rb_map.h
index 0a33809bb2..d373713669 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();
}
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/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/variant.cpp b/core/variant/variant.cpp
index 63ea3274ce..4c0212075b 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -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_parser.cpp b/core/variant/variant_parser.cpp
index fea1622222..86e7654090 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -1075,7 +1075,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_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;
}
}