summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/config/project_settings.cpp21
-rw-r--r--core/core_constants.cpp1
-rw-r--r--core/debugger/remote_debugger.cpp36
-rw-r--r--core/extension/extension_api_dump.cpp1
-rw-r--r--core/extension/gdextension_interface.cpp9
-rw-r--r--core/extension/gdextension_interface.h15
-rw-r--r--core/extension/gdextension_library_loader.cpp4
-rw-r--r--core/extension/gdextension_library_loader.h1
-rw-r--r--core/extension/gdextension_loader.h1
-rw-r--r--core/extension/gdextension_manager.cpp3
-rw-r--r--core/extension/make_wrappers.py4
-rw-r--r--core/input/input_map.compat.inc41
-rw-r--r--core/input/input_map.cpp5
-rw-r--r--core/input/input_map.h7
-rw-r--r--core/io/image.cpp2
-rw-r--r--core/io/resource.cpp33
-rw-r--r--core/io/resource.h1
-rw-r--r--core/io/resource_format_binary.cpp40
-rw-r--r--core/math/geometry_2d.cpp12
-rw-r--r--core/object/make_virtuals.py30
-rw-r--r--core/object/object.h9
-rw-r--r--core/object/ref_counted.h102
-rw-r--r--core/object/script_language_extension.h132
-rw-r--r--core/string/translation_domain.cpp292
-rw-r--r--core/string/translation_domain.h42
-rw-r--r--core/string/translation_server.cpp234
-rw-r--r--core/string/translation_server.h21
-rw-r--r--core/string/ustring.cpp25
-rw-r--r--core/string/ustring.h2
-rw-r--r--core/templates/hash_map.cpp43
-rw-r--r--core/templates/hash_map.h43
-rw-r--r--core/templates/hashfuncs.h95
-rw-r--r--core/templates/rid_owner.h139
-rw-r--r--core/variant/callable.cpp19
-rw-r--r--core/variant/dictionary.cpp5
-rw-r--r--core/variant/dictionary.h1
-rw-r--r--core/variant/variant.cpp132
-rw-r--r--core/variant/variant.h18
-rw-r--r--core/variant/variant_call.cpp32
-rw-r--r--core/variant/variant_construct.cpp30
-rw-r--r--core/variant/variant_construct.h9
-rw-r--r--core/variant/variant_internal.h29
42 files changed, 1087 insertions, 634 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 7951ee9edd..01f15f9c8e 100644
--- a/core/config/project_settings.cpp
+++ b/core/config/project_settings.cpp
@@ -348,7 +348,6 @@ bool ProjectSettings::_get(const StringName &p_name, Variant &r_ret) const {
_THREAD_SAFE_METHOD_
if (!props.has(p_name)) {
- WARN_PRINT("Property not found: " + String(p_name));
return false;
}
r_ret = props[p_name].variant;
@@ -1168,22 +1167,16 @@ bool ProjectSettings::is_project_loaded() const {
}
bool ProjectSettings::_property_can_revert(const StringName &p_name) const {
- if (!props.has(p_name)) {
- return false;
- }
-
- return props[p_name].initial != props[p_name].variant;
+ return props.has(p_name);
}
bool ProjectSettings::_property_get_revert(const StringName &p_name, Variant &r_property) const {
- if (!props.has(p_name)) {
- return false;
+ const RBMap<StringName, ProjectSettings::VariantContainer>::Element *value = props.find(p_name);
+ if (value) {
+ r_property = value->value().initial.duplicate();
+ return true;
}
-
- // Duplicate so that if value is array or dictionary, changing the setting will not change the stored initial value.
- r_property = props[p_name].initial.duplicate();
-
- return true;
+ return false;
}
void ProjectSettings::set_setting(const String &p_setting, const Variant &p_value) {
@@ -1398,7 +1391,7 @@ void ProjectSettings::_add_builtin_input_map() {
}
Dictionary action;
- action["deadzone"] = Variant(0.5f);
+ action["deadzone"] = Variant(0.2f);
action["events"] = events;
String action_name = "input/" + E.key;
diff --git a/core/core_constants.cpp b/core/core_constants.cpp
index 68af5abf66..25da49fa5c 100644
--- a/core/core_constants.cpp
+++ b/core/core_constants.cpp
@@ -677,6 +677,7 @@ void register_global_constants() {
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_NODE_TYPE);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_HIDE_QUATERNION_EDIT);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_PASSWORD);
+ BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_TOOL_BUTTON);
BIND_CORE_ENUM_CONSTANT(PROPERTY_HINT_MAX);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NONE);
diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp
index e2ed7245a2..fc1b7b74f9 100644
--- a/core/debugger/remote_debugger.cpp
+++ b/core/debugger/remote_debugger.cpp
@@ -37,6 +37,7 @@
#include "core/debugger/script_debugger.h"
#include "core/input/input.h"
#include "core/io/resource_loader.h"
+#include "core/math/expression.h"
#include "core/object/script_language.h"
#include "core/os/os.h"
#include "servers/display_server.h"
@@ -529,6 +530,41 @@ void RemoteDebugger::debug(bool p_can_continue, bool p_is_error_breakpoint) {
} else if (command == "set_skip_breakpoints") {
ERR_FAIL_COND(data.is_empty());
script_debugger->set_skip_breakpoints(data[0]);
+ } else if (command == "evaluate") {
+ String expression_str = data[0];
+ int frame = data[1];
+
+ ScriptInstance *breaked_instance = script_debugger->get_break_language()->debug_get_stack_level_instance(frame);
+ if (!breaked_instance) {
+ break;
+ }
+
+ List<String> locals;
+ List<Variant> local_vals;
+
+ script_debugger->get_break_language()->debug_get_stack_level_locals(frame, &locals, &local_vals);
+ ERR_FAIL_COND(locals.size() != local_vals.size());
+
+ PackedStringArray locals_vector;
+ for (const String &S : locals) {
+ locals_vector.append(S);
+ }
+
+ Array local_vals_array;
+ for (const Variant &V : local_vals) {
+ local_vals_array.append(V);
+ }
+
+ Expression expression;
+ expression.parse(expression_str, locals_vector);
+ const Variant return_val = expression.execute(local_vals_array, breaked_instance->get_owner());
+
+ DebuggerMarshalls::ScriptStackVariable stvar;
+ stvar.name = expression_str;
+ stvar.value = return_val;
+ stvar.type = 3;
+
+ send_message("evaluation_return", stvar.serialize());
} else {
bool captured = false;
ERR_CONTINUE(_try_capture(command, data, captured) != OK);
diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp
index 4042d6b80d..c5f7502c12 100644
--- a/core/extension/extension_api_dump.cpp
+++ b/core/extension/extension_api_dump.cpp
@@ -1017,6 +1017,7 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
d2["name"] = String(method_name);
d2["is_const"] = (F.flags & METHOD_FLAG_CONST) ? true : false;
d2["is_static"] = (F.flags & METHOD_FLAG_STATIC) ? true : false;
+ d2["is_required"] = (F.flags & METHOD_FLAG_VIRTUAL_REQUIRED) ? true : false;
d2["is_vararg"] = false;
d2["is_virtual"] = true;
// virtual functions have no hash since no MethodBind is involved
diff --git a/core/extension/gdextension_interface.cpp b/core/extension/gdextension_interface.cpp
index ddf90f6130..66b0161160 100644
--- a/core/extension/gdextension_interface.cpp
+++ b/core/extension/gdextension_interface.cpp
@@ -507,6 +507,14 @@ static GDExtensionBool gdextension_variant_has_key(GDExtensionConstVariantPtr p_
return ret;
}
+static GDObjectInstanceID gdextension_variant_get_object_instance_id(GDExtensionConstVariantPtr p_self) {
+ const Variant *self = (const Variant *)p_self;
+ if (likely(self->get_type() == Variant::OBJECT)) {
+ return self->operator ObjectID();
+ }
+ return 0;
+}
+
static void gdextension_variant_get_type_name(GDExtensionVariantType p_type, GDExtensionUninitializedVariantPtr r_ret) {
String name = Variant::get_type_name((Variant::Type)p_type);
memnew_placement(r_ret, String(name));
@@ -1610,6 +1618,7 @@ void gdextension_setup_interface() {
REGISTER_INTERFACE_FUNC(variant_has_method);
REGISTER_INTERFACE_FUNC(variant_has_member);
REGISTER_INTERFACE_FUNC(variant_has_key);
+ REGISTER_INTERFACE_FUNC(variant_get_object_instance_id);
REGISTER_INTERFACE_FUNC(variant_get_type_name);
REGISTER_INTERFACE_FUNC(variant_can_convert);
REGISTER_INTERFACE_FUNC(variant_can_convert_strict);
diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h
index 9e3ce25698..374dbfd071 100644
--- a/core/extension/gdextension_interface.h
+++ b/core/extension/gdextension_interface.h
@@ -1308,6 +1308,21 @@ typedef GDExtensionBool (*GDExtensionInterfaceVariantHasMember)(GDExtensionVaria
typedef GDExtensionBool (*GDExtensionInterfaceVariantHasKey)(GDExtensionConstVariantPtr p_self, GDExtensionConstVariantPtr p_key, GDExtensionBool *r_valid);
/**
+ * @name variant_get_object_instance_id
+ * @since 4.4
+ *
+ * Gets the object instance ID from a variant of type GDEXTENSION_VARIANT_TYPE_OBJECT.
+ *
+ * If the variant isn't of type GDEXTENSION_VARIANT_TYPE_OBJECT, then zero will be returned.
+ * The instance ID will be returned even if the object is no longer valid - use `object_get_instance_by_id()` to check if the object is still valid.
+ *
+ * @param p_self A pointer to the Variant.
+ *
+ * @return The instance ID for the contained object.
+ */
+typedef GDObjectInstanceID (*GDExtensionInterfaceVariantGetObjectInstanceId)(GDExtensionConstVariantPtr p_self);
+
+/**
* @name variant_get_type_name
* @since 4.1
*
diff --git a/core/extension/gdextension_library_loader.cpp b/core/extension/gdextension_library_loader.cpp
index 5ba4933c35..d5f2eb668f 100644
--- a/core/extension/gdextension_library_loader.cpp
+++ b/core/extension/gdextension_library_loader.cpp
@@ -259,6 +259,10 @@ bool GDExtensionLibraryLoader::has_library_changed() const {
return false;
}
+bool GDExtensionLibraryLoader::library_exists() const {
+ return FileAccess::exists(resource_path);
+}
+
Error GDExtensionLibraryLoader::parse_gdextension_file(const String &p_path) {
resource_path = p_path;
diff --git a/core/extension/gdextension_library_loader.h b/core/extension/gdextension_library_loader.h
index f4372a75d4..f781611b30 100644
--- a/core/extension/gdextension_library_loader.h
+++ b/core/extension/gdextension_library_loader.h
@@ -77,6 +77,7 @@ public:
virtual void close_library() override;
virtual bool is_library_open() const override;
virtual bool has_library_changed() const override;
+ virtual bool library_exists() const override;
Error parse_gdextension_file(const String &p_path);
};
diff --git a/core/extension/gdextension_loader.h b/core/extension/gdextension_loader.h
index 7d779858b7..2289550329 100644
--- a/core/extension/gdextension_loader.h
+++ b/core/extension/gdextension_loader.h
@@ -42,6 +42,7 @@ public:
virtual void close_library() = 0;
virtual bool is_library_open() const = 0;
virtual bool has_library_changed() const = 0;
+ virtual bool library_exists() const = 0;
};
#endif // GDEXTENSION_LOADER_H
diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp
index 01efe0d96e..fff938858f 100644
--- a/core/extension/gdextension_manager.cpp
+++ b/core/extension/gdextension_manager.cpp
@@ -302,7 +302,8 @@ bool GDExtensionManager::ensure_extensions_loaded(const HashSet<String> &p_exten
for (const String &loaded_extension : loaded_extensions) {
if (!p_extensions.has(loaded_extension)) {
// The extension may not have a .gdextension file.
- if (!FileAccess::exists(loaded_extension)) {
+ const Ref<GDExtension> extension = GDExtensionManager::get_singleton()->get_extension(loaded_extension);
+ if (!extension->get_loader()->library_exists()) {
extensions_removed.push_back(loaded_extension);
}
}
diff --git a/core/extension/make_wrappers.py b/core/extension/make_wrappers.py
index 54f4fd5579..665b6f0f91 100644
--- a/core/extension/make_wrappers.py
+++ b/core/extension/make_wrappers.py
@@ -55,10 +55,10 @@ def generate_mod_version(argcount, const=False, returns=False):
proto_ex = """
#define EXBIND$VER($RETTYPE m_name$ARG) \\
-GDVIRTUAL$VER($RETTYPE_##m_name$ARG)\\
+GDVIRTUAL$VER_REQUIRED($RETTYPE_##m_name$ARG)\\
virtual $RETVAL m_name($FUNCARGS) $CONST override { \\
$RETPRE\\
- GDVIRTUAL_REQUIRED_CALL(_##m_name$CALLARGS$RETREF);\\
+ GDVIRTUAL_CALL(_##m_name$CALLARGS$RETREF);\\
$RETPOST\\
}
"""
diff --git a/core/input/input_map.compat.inc b/core/input/input_map.compat.inc
new file mode 100644
index 0000000000..da4bd962b6
--- /dev/null
+++ b/core/input/input_map.compat.inc
@@ -0,0 +1,41 @@
+/**************************************************************************/
+/* input_map.compat.inc */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef DISABLE_DEPRECATED
+
+void InputMap::_add_action_bind_compat_97281(const StringName &p_action, float p_deadzone) {
+ add_action(p_action, p_deadzone);
+}
+
+void InputMap::_bind_compatibility_methods() {
+ ClassDB::bind_compatibility_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::_add_action_bind_compat_97281, DEFVAL(0.5f));
+}
+
+#endif // DISABLE_DEPRECATED
diff --git a/core/input/input_map.cpp b/core/input/input_map.cpp
index 9a772c87c9..27a50c79f6 100644
--- a/core/input/input_map.cpp
+++ b/core/input/input_map.cpp
@@ -29,6 +29,7 @@
/**************************************************************************/
#include "input_map.h"
+#include "input_map.compat.inc"
#include "core/config/project_settings.h"
#include "core/input/input.h"
@@ -43,7 +44,7 @@ int InputMap::ALL_DEVICES = -1;
void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("has_action", "action"), &InputMap::has_action);
ClassDB::bind_method(D_METHOD("get_actions"), &InputMap::_get_actions);
- ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f));
+ ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.2f));
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
@@ -306,7 +307,7 @@ void InputMap::load_from_project_settings() {
String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length());
Dictionary action = GLOBAL_GET(pi.name);
- float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.5f;
+ float deadzone = action.has("deadzone") ? (float)action["deadzone"] : 0.2f;
Array events = action["events"];
add_action(name, deadzone);
diff --git a/core/input/input_map.h b/core/input/input_map.h
index 3774a131e6..b29687d144 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -69,12 +69,17 @@ private:
protected:
static void _bind_methods();
+#ifndef DISABLE_DEPRECATED
+ void _add_action_bind_compat_97281(const StringName &p_action, float p_deadzone = 0.5);
+ static void _bind_compatibility_methods();
+#endif // DISABLE_DEPRECATED
+
public:
static _FORCE_INLINE_ InputMap *get_singleton() { return singleton; }
bool has_action(const StringName &p_action) const;
List<StringName> get_actions() const;
- void add_action(const StringName &p_action, float p_deadzone = 0.5);
+ void add_action(const StringName &p_action, float p_deadzone = 0.2);
void erase_action(const StringName &p_action);
float action_get_deadzone(const StringName &p_action);
diff --git a/core/io/image.cpp b/core/io/image.cpp
index aa391b77dd..d782af931f 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -2751,7 +2751,7 @@ Error Image::compress_from_channels(CompressMode p_mode, UsedChannels p_channels
case COMPRESS_S3TC: {
// BC3 is unsupported currently.
- if ((p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) {
+ if ((p_channels == USED_CHANNELS_R || p_channels == USED_CHANNELS_RGB || p_channels == USED_CHANNELS_L) && _image_compress_bc_rd_func) {
Error result = _image_compress_bc_rd_func(this, p_channels);
// If the image was compressed successfully, we return here. If not, we fall back to the default compression scheme.
diff --git a/core/io/resource.cpp b/core/io/resource.cpp
index 5f8a4b85a4..0ff4fbe490 100644
--- a/core/io/resource.cpp
+++ b/core/io/resource.cpp
@@ -99,31 +99,42 @@ void Resource::set_path_cache(const String &p_path) {
GDVIRTUAL_CALL(_set_path_cache, p_path);
}
+static thread_local RandomPCG unique_id_gen(0, RandomPCG::DEFAULT_INC);
+
+void Resource::seed_scene_unique_id(uint32_t p_seed) {
+ unique_id_gen.seed(p_seed);
+}
+
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.
- OS::DateTime dt = OS::get_singleton()->get_datetime();
- uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
- hash = hash_murmur3_one_32(dt.year, hash);
- hash = hash_murmur3_one_32(dt.month, hash);
- hash = hash_murmur3_one_32(dt.day, hash);
- hash = hash_murmur3_one_32(dt.hour, hash);
- hash = hash_murmur3_one_32(dt.minute, hash);
- hash = hash_murmur3_one_32(dt.second, hash);
- hash = hash_murmur3_one_32(Math::rand(), hash);
+ if (unique_id_gen.get_seed() == 0) {
+ OS::DateTime dt = OS::get_singleton()->get_datetime();
+ uint32_t hash = hash_murmur3_one_32(OS::get_singleton()->get_ticks_usec());
+ hash = hash_murmur3_one_32(dt.year, hash);
+ hash = hash_murmur3_one_32(dt.month, hash);
+ hash = hash_murmur3_one_32(dt.day, hash);
+ hash = hash_murmur3_one_32(dt.hour, hash);
+ hash = hash_murmur3_one_32(dt.minute, hash);
+ hash = hash_murmur3_one_32(dt.second, hash);
+ hash = hash_murmur3_one_32(Math::rand(), hash);
+ unique_id_gen.seed(hash);
+ }
+
+ uint32_t random_num = unique_id_gen.rand();
static constexpr uint32_t characters = 5;
static constexpr uint32_t char_count = ('z' - 'a');
static constexpr uint32_t base = char_count + ('9' - '0');
String id;
for (uint32_t i = 0; i < characters; i++) {
- uint32_t c = hash % base;
+ uint32_t c = random_num % base;
if (c < char_count) {
id += String::chr('a' + c);
} else {
id += String::chr('0' + (c - char_count));
}
- hash /= base;
+ random_num /= base;
}
return id;
diff --git a/core/io/resource.h b/core/io/resource.h
index 8966c0233c..015f7ad197 100644
--- a/core/io/resource.h
+++ b/core/io/resource.h
@@ -114,6 +114,7 @@ public:
virtual 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 void seed_scene_unique_id(uint32_t p_seed);
static String generate_scene_unique_id();
void set_scene_unique_id(const String &p_id);
String get_scene_unique_id() const;
diff --git a/core/io/resource_format_binary.cpp b/core/io/resource_format_binary.cpp
index b4826c356e..109999d612 100644
--- a/core/io/resource_format_binary.cpp
+++ b/core/io/resource_format_binary.cpp
@@ -845,29 +845,27 @@ Error ResourceLoaderBinary::load() {
}
}
- if (ClassDB::has_property(res->get_class_name(), name)) {
- if (value.get_type() == Variant::ARRAY) {
- Array set_array = value;
- bool is_get_valid = false;
- Variant get_value = res->get(name, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
- Array get_array = get_value;
- if (!set_array.is_same_typed(get_array)) {
- value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
- }
+ if (value.get_type() == Variant::ARRAY) {
+ Array set_array = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(name, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::ARRAY) {
+ Array get_array = get_value;
+ if (!set_array.is_same_typed(get_array)) {
+ value = Array(set_array, get_array.get_typed_builtin(), get_array.get_typed_class_name(), get_array.get_typed_script());
}
}
+ }
- if (value.get_type() == Variant::DICTIONARY) {
- Dictionary set_dict = value;
- bool is_get_valid = false;
- Variant get_value = res->get(name, &is_get_valid);
- if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
- Dictionary get_dict = get_value;
- if (!set_dict.is_same_typed(get_dict)) {
- value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
- get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
- }
+ if (value.get_type() == Variant::DICTIONARY) {
+ Dictionary set_dict = value;
+ bool is_get_valid = false;
+ Variant get_value = res->get(name, &is_get_valid);
+ if (is_get_valid && get_value.get_type() == Variant::DICTIONARY) {
+ Dictionary get_dict = get_value;
+ if (!set_dict.is_same_typed(get_dict)) {
+ value = Dictionary(set_dict, get_dict.get_typed_key_builtin(), get_dict.get_typed_key_class_name(), get_dict.get_typed_key_script(),
+ get_dict.get_typed_value_builtin(), get_dict.get_typed_value_class_name(), get_dict.get_typed_value_script());
}
}
}
@@ -2136,6 +2134,8 @@ static String _resource_get_class(Ref<Resource> p_resource) {
}
Error ResourceFormatSaverBinaryInstance::save(const String &p_path, const Ref<Resource> &p_resource, uint32_t p_flags) {
+ Resource::seed_scene_unique_id(p_path.hash());
+
Error err;
Ref<FileAccess> f;
if (p_flags & ResourceSaver::FLAG_COMPRESS) {
diff --git a/core/math/geometry_2d.cpp b/core/math/geometry_2d.cpp
index d60619b27f..a49826958a 100644
--- a/core/math/geometry_2d.cpp
+++ b/core/math/geometry_2d.cpp
@@ -35,7 +35,8 @@
#define STB_RECT_PACK_IMPLEMENTATION
#include "thirdparty/misc/stb_rect_pack.h"
-#define PRECISION 5 // Based on CMP_EPSILON.
+const int clipper_precision = 5; // Based on CMP_EPSILON.
+const double clipper_scale = Math::pow(10.0, clipper_precision);
Vector<Vector<Vector2>> Geometry2D::decompose_polygon_in_convex(const Vector<Point2> &polygon) {
Vector<Vector<Vector2>> decomp;
@@ -224,7 +225,7 @@ Vector<Vector<Point2>> Geometry2D::_polypaths_do_operation(PolyBooleanOperation
path_b[i] = PointD(p_polypath_b[i].x, p_polypath_b[i].y);
}
- ClipperD clp(PRECISION); // Scale points up internally to attain the desired precision.
+ ClipperD clp(clipper_precision); // Scale points up internally to attain the desired precision.
clp.PreserveCollinear(false); // Remove redundant vertices.
if (is_a_open) {
clp.AddOpenSubject({ path_a });
@@ -298,9 +299,10 @@ Vector<Vector<Point2>> Geometry2D::_polypath_offset(const Vector<Point2> &p_poly
}
// Inflate/deflate.
- PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, PRECISION, 0.0);
- // Here the miter_limit = 2.0 and arc_tolerance = 0.0 are Clipper2 defaults,
- // and the PRECISION is used to scale points up internally, to attain the desired precision.
+ PathsD paths = InflatePaths({ polypath }, p_delta, jt, et, 2.0, clipper_precision, 0.25 * clipper_scale);
+ // Here the points are scaled up internally and
+ // the arc_tolerance is scaled accordingly
+ // to attain the desired precision.
Vector<Vector<Point2>> polypaths;
for (PathsD::size_type i = 0; i < paths.size(); ++i) {
diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py
index 7a85753072..3df26e3b73 100644
--- a/core/object/make_virtuals.py
+++ b/core/object/make_virtuals.py
@@ -2,7 +2,6 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
StringName _gdvirtual_##m_name##_sn = #m_name;\\
mutable bool _gdvirtual_##m_name##_initialized = false;\\
mutable void *_gdvirtual_##m_name = nullptr;\\
- template <bool required>\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST {\\
ScriptInstance *_script_instance = ((Object *)(this))->get_script_instance();\\
if (_script_instance) {\\
@@ -36,10 +35,8 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
}\\
return true;\\
}\\
- if (required) {\\
- ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");\\
- $RVOID\\
- }\\
+ $REQCHECK\\
+ $RVOID\\
return false;\\
}\\
_FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const {\\
@@ -73,10 +70,11 @@ proto = """#define GDVIRTUAL$VER($RET m_name $ARG)\\
"""
-def generate_version(argcount, const=False, returns=False):
+def generate_version(argcount, const=False, returns=False, required=False):
s = proto
sproto = str(argcount)
method_info = ""
+ method_flags = "METHOD_FLAG_VIRTUAL"
if returns:
sproto += "R"
s = s.replace("$RET", "m_ret,")
@@ -86,17 +84,27 @@ def generate_version(argcount, const=False, returns=False):
method_info += "\t\tmethod_info.return_val_metadata = GetTypeInfo<m_ret>::METADATA;"
else:
s = s.replace("$RET ", "")
- s = s.replace("\t\t\t$RVOID\\\n", "")
+ s = s.replace("\t\t$RVOID\\\n", "")
s = s.replace("\t\t\t$CALLPTRRETDEF\\\n", "")
if const:
sproto += "C"
+ method_flags += " | METHOD_FLAG_CONST"
s = s.replace("$CONST", "const")
- s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL | METHOD_FLAG_CONST")
else:
s = s.replace("$CONST ", "")
- s = s.replace("$METHOD_FLAGS", "METHOD_FLAG_VIRTUAL")
+ if required:
+ sproto += "_REQUIRED"
+ method_flags += " | METHOD_FLAG_VIRTUAL_REQUIRED"
+ s = s.replace(
+ "$REQCHECK",
+ 'ERR_PRINT_ONCE("Required virtual method " + get_class() + "::" + #m_name + " must be overridden before calling.");',
+ )
+ else:
+ s = s.replace("\t\t$REQCHECK\\\n", "")
+
+ s = s.replace("$METHOD_FLAGS", method_flags)
s = s.replace("$VER", sproto)
argtext = ""
callargtext = ""
@@ -198,6 +206,10 @@ def run(target, source, env):
txt += generate_version(i, False, True)
txt += generate_version(i, True, False)
txt += generate_version(i, True, True)
+ txt += generate_version(i, False, False, True)
+ txt += generate_version(i, False, True, True)
+ txt += generate_version(i, True, False, True)
+ txt += generate_version(i, True, True, True)
txt += "#endif // GDVIRTUAL_GEN_H\n"
diff --git a/core/object/object.h b/core/object/object.h
index 763e2974b9..110d2790c5 100644
--- a/core/object/object.h
+++ b/core/object/object.h
@@ -87,6 +87,7 @@ enum PropertyHint {
PROPERTY_HINT_PASSWORD,
PROPERTY_HINT_LAYERS_AVOIDANCE,
PROPERTY_HINT_DICTIONARY_TYPE,
+ PROPERTY_HINT_TOOL_BUTTON,
PROPERTY_HINT_MAX,
};
@@ -215,6 +216,7 @@ enum MethodFlags {
METHOD_FLAG_VARARG = 16,
METHOD_FLAG_STATIC = 32,
METHOD_FLAG_OBJECT_CORE = 64,
+ METHOD_FLAG_VIRTUAL_REQUIRED = 128,
METHOD_FLAGS_DEFAULT = METHOD_FLAG_NORMAL,
};
@@ -368,11 +370,8 @@ struct ObjectGDExtension {
#endif
};
-#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
-#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<false>(__VA_ARGS__)
-
-#define GDVIRTUAL_REQUIRED_CALL(m_name, ...) _gdvirtual_##m_name##_call<true>(__VA_ARGS__)
-#define GDVIRTUAL_REQUIRED_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call<true>(__VA_ARGS__)
+#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call(__VA_ARGS__)
+#define GDVIRTUAL_CALL_PTR(m_obj, m_name, ...) m_obj->_gdvirtual_##m_name##_call(__VA_ARGS__)
#ifdef DEBUG_METHODS_ENABLED
#define GDVIRTUAL_BIND(m_name, ...) ::ClassDB::add_virtual_method(get_class_static(), _gdvirtual_##m_name##_get_method_info(), true, sarray(__VA_ARGS__));
diff --git a/core/object/ref_counted.h b/core/object/ref_counted.h
index f0706b4d08..22eb5a7a3f 100644
--- a/core/object/ref_counted.h
+++ b/core/object/ref_counted.h
@@ -57,24 +57,30 @@ template <typename T>
class Ref {
T *reference = nullptr;
- void ref(const Ref &p_from) {
- if (p_from.reference == reference) {
+ _FORCE_INLINE_ void ref(const Ref &p_from) {
+ ref_pointer<false>(p_from.reference);
+ }
+
+ template <bool Init>
+ _FORCE_INLINE_ void ref_pointer(T *p_refcounted) {
+ if (p_refcounted == reference) {
return;
}
- unref();
-
- reference = p_from.reference;
+ // This will go out of scope and get unref'd.
+ Ref cleanup_ref;
+ cleanup_ref.reference = reference;
+ reference = p_refcounted;
if (reference) {
- reference->reference();
- }
- }
-
- void ref_pointer(T *p_ref) {
- ERR_FAIL_NULL(p_ref);
-
- if (p_ref->init_ref()) {
- reference = p_ref;
+ if constexpr (Init) {
+ if (!reference->init_ref()) {
+ reference = nullptr;
+ }
+ } else {
+ if (!reference->reference()) {
+ reference = nullptr;
+ }
+ }
}
}
@@ -124,15 +130,11 @@ public:
template <typename T_Other>
void operator=(const Ref<T_Other> &p_from) {
- RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
- if (!refb) {
- unref();
- return;
- }
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = nullptr;
+ ref_pointer<false>(Object::cast_to<T>(p_from.ptr()));
+ }
+
+ void operator=(T *p_from) {
+ ref_pointer<true>(p_from);
}
void operator=(const Variant &p_variant) {
@@ -142,16 +144,7 @@ public:
return;
}
- unref();
-
- if (!object) {
- return;
- }
-
- T *r = Object::cast_to<T>(object);
- if (r && r->reference()) {
- reference = r;
- }
+ ref_pointer<false>(Object::cast_to<T>(object));
}
template <typename T_Other>
@@ -159,48 +152,25 @@ public:
if (reference == p_ptr) {
return;
}
- unref();
- T *r = Object::cast_to<T>(p_ptr);
- if (r) {
- ref_pointer(r);
- }
+ ref_pointer<true>(Object::cast_to<T>(p_ptr));
}
Ref(const Ref &p_from) {
- ref(p_from);
+ this->operator=(p_from);
}
template <typename T_Other>
Ref(const Ref<T_Other> &p_from) {
- RefCounted *refb = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_from.ptr()));
- if (!refb) {
- unref();
- return;
- }
- Ref r;
- r.reference = Object::cast_to<T>(refb);
- ref(r);
- r.reference = nullptr;
+ this->operator=(p_from);
}
- Ref(T *p_reference) {
- if (p_reference) {
- ref_pointer(p_reference);
- }
+ Ref(T *p_from) {
+ this->operator=(p_from);
}
- Ref(const Variant &p_variant) {
- Object *object = p_variant.get_validated_object();
-
- if (!object) {
- return;
- }
-
- T *r = Object::cast_to<T>(object);
- if (r && r->reference()) {
- reference = r;
- }
+ Ref(const Variant &p_from) {
+ this->operator=(p_from);
}
inline bool is_valid() const { return reference != nullptr; }
@@ -222,7 +192,7 @@ public:
ref(memnew(T(p_params...)));
}
- Ref() {}
+ Ref() = default;
~Ref() {
unref();
@@ -299,13 +269,13 @@ struct GetTypeInfo<const Ref<T> &> {
template <typename T>
struct VariantInternalAccessor<Ref<T>> {
static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); }
- static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); }
+ static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::object_assign(v, p_ref); }
};
template <typename T>
struct VariantInternalAccessor<const Ref<T> &> {
static _FORCE_INLINE_ Ref<T> get(const Variant *v) { return Ref<T>(*VariantInternal::get_object(v)); }
- static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::refcounted_object_assign(v, p_ref.ptr()); }
+ static _FORCE_INLINE_ void set(Variant *v, const Ref<T> &p_ref) { VariantInternal::object_assign(v, p_ref); }
};
#endif // REF_COUNTED_H
diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h
index bc773c5ad3..d2dce34d4f 100644
--- a/core/object/script_language_extension.h
+++ b/core/object/script_language_extension.h
@@ -57,16 +57,16 @@ public:
EXBIND1RC(bool, inherits_script, const Ref<Script> &)
EXBIND0RC(StringName, get_instance_base_type)
- GDVIRTUAL1RC(GDExtensionPtr<void>, _instance_create, Object *)
+ GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _instance_create, Object *)
virtual ScriptInstance *instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_instance_create, p_this, ret);
+ GDVIRTUAL_CALL(_instance_create, p_this, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
- GDVIRTUAL1RC(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
+ GDVIRTUAL1RC_REQUIRED(GDExtensionPtr<void>, _placeholder_instance_create, Object *)
PlaceHolderScriptInstance *placeholder_instance_create(Object *p_this) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_placeholder_instance_create, p_this, ret);
+ GDVIRTUAL_CALL(_placeholder_instance_create, p_this, ret);
return reinterpret_cast<PlaceHolderScriptInstance *>(ret.operator void *());
}
@@ -76,12 +76,12 @@ public:
EXBIND1(set_source_code, const String &)
EXBIND1R(Error, reload, bool)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_documentation)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_documentation)
GDVIRTUAL0RC(String, _get_class_icon_path)
#ifdef TOOLS_ENABLED
virtual Vector<DocData::ClassDoc> get_documentation() const override {
TypedArray<Dictionary> doc;
- GDVIRTUAL_REQUIRED_CALL(_get_documentation, doc);
+ GDVIRTUAL_CALL(_get_documentation, doc);
Vector<DocData::ClassDoc> class_doc;
for (int i = 0; i < doc.size(); i++) {
@@ -114,10 +114,10 @@ public:
return Script::get_script_method_argument_count(p_method, r_is_valid);
}
- GDVIRTUAL1RC(Dictionary, _get_method_info, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _get_method_info, const StringName &)
virtual MethodInfo get_method_info(const StringName &p_method) const override {
Dictionary mi;
- GDVIRTUAL_REQUIRED_CALL(_get_method_info, p_method, mi);
+ GDVIRTUAL_CALL(_get_method_info, p_method, mi);
return MethodInfo::from_dict(mi);
}
@@ -133,47 +133,47 @@ public:
EXBIND0RC(ScriptLanguage *, get_language)
EXBIND1RC(bool, has_script_signal, const StringName &)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_signal_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_signal_list)
virtual void get_script_signal_list(List<MethodInfo> *r_signals) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_signal_list, sl);
+ GDVIRTUAL_CALL(_get_script_signal_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_signals->push_back(MethodInfo::from_dict(sl[i]));
}
}
- GDVIRTUAL1RC(bool, _has_property_default_value, const StringName &)
- GDVIRTUAL1RC(Variant, _get_property_default_value, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(bool, _has_property_default_value, const StringName &)
+ GDVIRTUAL1RC_REQUIRED(Variant, _get_property_default_value, const StringName &)
virtual bool get_property_default_value(const StringName &p_property, Variant &r_value) const override {
bool has_dv = false;
- if (!GDVIRTUAL_REQUIRED_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
+ if (!GDVIRTUAL_CALL(_has_property_default_value, p_property, has_dv) || !has_dv) {
return false;
}
Variant ret;
- GDVIRTUAL_REQUIRED_CALL(_get_property_default_value, p_property, ret);
+ GDVIRTUAL_CALL(_get_property_default_value, p_property, ret);
r_value = ret;
return true;
}
EXBIND0(update_exports)
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_method_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_method_list)
virtual void get_script_method_list(List<MethodInfo> *r_methods) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_method_list, sl);
+ GDVIRTUAL_CALL(_get_script_method_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_methods->push_back(MethodInfo::from_dict(sl[i]));
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_script_property_list)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_script_property_list)
virtual void get_script_property_list(List<PropertyInfo> *r_propertys) const override {
TypedArray<Dictionary> sl;
- GDVIRTUAL_REQUIRED_CALL(_get_script_property_list, sl);
+ GDVIRTUAL_CALL(_get_script_property_list, sl);
for (int i = 0; i < sl.size(); i++) {
r_propertys->push_back(PropertyInfo::from_dict(sl[i]));
}
@@ -181,21 +181,21 @@ public:
EXBIND1RC(int, get_member_line, const StringName &)
- GDVIRTUAL0RC(Dictionary, _get_constants)
+ GDVIRTUAL0RC_REQUIRED(Dictionary, _get_constants)
virtual void get_constants(HashMap<StringName, Variant> *p_constants) override {
Dictionary constants;
- GDVIRTUAL_REQUIRED_CALL(_get_constants, constants);
+ GDVIRTUAL_CALL(_get_constants, constants);
List<Variant> keys;
constants.get_key_list(&keys);
for (const Variant &K : keys) {
p_constants->insert(K, constants[K]);
}
}
- GDVIRTUAL0RC(TypedArray<StringName>, _get_members)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<StringName>, _get_members)
virtual void get_members(HashSet<StringName> *p_members) override {
TypedArray<StringName> members;
- GDVIRTUAL_REQUIRED_CALL(_get_members, members);
+ GDVIRTUAL_CALL(_get_members, members);
for (int i = 0; i < members.size(); i++) {
p_members->insert(members[i]);
}
@@ -203,11 +203,11 @@ public:
EXBIND0RC(bool, is_placeholder_fallback_enabled)
- GDVIRTUAL0RC(Variant, _get_rpc_config)
+ GDVIRTUAL0RC_REQUIRED(Variant, _get_rpc_config)
virtual Variant get_rpc_config() const override {
Variant ret;
- GDVIRTUAL_REQUIRED_CALL(_get_rpc_config, ret);
+ GDVIRTUAL_CALL(_get_rpc_config, ret);
return ret;
}
@@ -233,22 +233,22 @@ public:
/* EDITOR FUNCTIONS */
- GDVIRTUAL0RC(Vector<String>, _get_reserved_words)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_reserved_words)
virtual void get_reserved_words(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_reserved_words, ret);
+ GDVIRTUAL_CALL(_get_reserved_words, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
}
EXBIND1RC(bool, is_control_flow_keyword, const String &)
- GDVIRTUAL0RC(Vector<String>, _get_comment_delimiters)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_comment_delimiters)
virtual void get_comment_delimiters(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_comment_delimiters, ret);
+ GDVIRTUAL_CALL(_get_comment_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@@ -264,11 +264,11 @@ public:
}
}
- GDVIRTUAL0RC(Vector<String>, _get_string_delimiters)
+ GDVIRTUAL0RC_REQUIRED(Vector<String>, _get_string_delimiters)
virtual void get_string_delimiters(List<String> *p_words) const override {
Vector<String> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_string_delimiters, ret);
+ GDVIRTUAL_CALL(_get_string_delimiters, ret);
for (int i = 0; i < ret.size(); i++) {
p_words->push_back(ret[i]);
}
@@ -276,11 +276,11 @@ public:
EXBIND3RC(Ref<Script>, make_template, const String &, const String &, const String &)
- GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName)
+ GDVIRTUAL1RC_REQUIRED(TypedArray<Dictionary>, _get_built_in_templates, StringName)
virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret);
+ GDVIRTUAL_CALL(_get_built_in_templates, p_object, ret);
Vector<ScriptTemplate> stret;
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
@@ -304,10 +304,10 @@ public:
EXBIND0R(bool, is_using_templates)
- GDVIRTUAL6RC(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
+ GDVIRTUAL6RC_REQUIRED(Dictionary, _validate, const String &, const String &, bool, bool, bool, bool)
virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
+ GDVIRTUAL_CALL(_validate, p_script, p_path, r_functions != nullptr, r_errors != nullptr, r_warnings != nullptr, r_safe_lines != nullptr, ret);
if (!ret.has("valid")) {
return false;
}
@@ -371,10 +371,10 @@ public:
}
EXBIND1RC(String, validate_path, const String &)
- GDVIRTUAL0RC(Object *, _create_script)
+ GDVIRTUAL0RC_REQUIRED(Object *, _create_script)
Script *create_script() const override {
Object *ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_create_script, ret);
+ GDVIRTUAL_CALL(_create_script, ret);
return Object::cast_to<Script>(ret);
}
#ifndef DISABLE_DEPRECATED
@@ -400,11 +400,11 @@ public:
return ScriptNameCasing::SCRIPT_NAME_CASING_SNAKE_CASE;
}
- GDVIRTUAL3RC(Dictionary, _complete_code, const String &, const String &, Object *)
+ GDVIRTUAL3RC_REQUIRED(Dictionary, _complete_code, const String &, const String &, Object *)
virtual Error complete_code(const String &p_code, const String &p_path, Object *p_owner, List<CodeCompletionOption> *r_options, bool &r_force, String &r_call_hint) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_complete_code, p_code, p_path, p_owner, ret);
+ GDVIRTUAL_CALL(_complete_code, p_code, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@@ -449,11 +449,11 @@ public:
return result;
}
- GDVIRTUAL4RC(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
+ GDVIRTUAL4RC_REQUIRED(Dictionary, _lookup_code, const String &, const String &, const String &, Object *)
virtual Error lookup_code(const String &p_code, const String &p_symbol, const String &p_path, Object *p_owner, LookupResult &r_result) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
+ GDVIRTUAL_CALL(_lookup_code, p_code, p_symbol, p_path, p_owner, ret);
if (!ret.has("result")) {
return ERR_UNAVAILABLE;
}
@@ -474,10 +474,10 @@ public:
return result;
}
- GDVIRTUAL3RC(String, _auto_indent_code, const String &, int, int)
+ GDVIRTUAL3RC_REQUIRED(String, _auto_indent_code, const String &, int, int)
virtual void auto_indent_code(String &p_code, int p_from_line, int p_to_line) const override {
String ret;
- GDVIRTUAL_REQUIRED_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
+ GDVIRTUAL_CALL(_auto_indent_code, p_code, p_from_line, p_to_line, ret);
p_code = ret;
}
EXBIND2(add_global_constant, const StringName &, const Variant &)
@@ -496,10 +496,10 @@ public:
EXBIND1RC(String, debug_get_stack_level_function, int)
EXBIND1RC(String, debug_get_stack_level_source, int)
- GDVIRTUAL3R(Dictionary, _debug_get_stack_level_locals, int, int, int)
+ GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_locals, int, int, int)
virtual void debug_get_stack_level_locals(int p_level, List<String> *p_locals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_locals, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -516,10 +516,10 @@ public:
}
}
}
- GDVIRTUAL3R(Dictionary, _debug_get_stack_level_members, int, int, int)
+ GDVIRTUAL3R_REQUIRED(Dictionary, _debug_get_stack_level_members, int, int, int)
virtual void debug_get_stack_level_members(int p_level, List<String> *p_members, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_members, p_level, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -536,17 +536,17 @@ public:
}
}
}
- GDVIRTUAL1R(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
+ GDVIRTUAL1R_REQUIRED(GDExtensionPtr<void>, _debug_get_stack_level_instance, int)
virtual ScriptInstance *debug_get_stack_level_instance(int p_level) override {
GDExtensionPtr<void> ret = nullptr;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_stack_level_instance, p_level, ret);
+ GDVIRTUAL_CALL(_debug_get_stack_level_instance, p_level, ret);
return reinterpret_cast<ScriptInstance *>(ret.operator void *());
}
- GDVIRTUAL2R(Dictionary, _debug_get_globals, int, int)
+ GDVIRTUAL2R_REQUIRED(Dictionary, _debug_get_globals, int, int)
virtual void debug_get_globals(List<String> *p_globals, List<Variant> *p_values, int p_max_subitems = -1, int p_max_depth = -1) override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
+ GDVIRTUAL_CALL(_debug_get_globals, p_max_subitems, p_max_depth, ret);
if (ret.size() == 0) {
return;
}
@@ -566,10 +566,10 @@ public:
EXBIND4R(String, debug_parse_stack_level_expression, int, const String &, int, int)
- GDVIRTUAL0R(TypedArray<Dictionary>, _debug_get_current_stack_info)
+ GDVIRTUAL0R_REQUIRED(TypedArray<Dictionary>, _debug_get_current_stack_info)
virtual Vector<StackInfo> debug_get_current_stack_info() override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
+ GDVIRTUAL_CALL(_debug_get_current_stack_info, ret);
Vector<StackInfo> sret;
for (const Variant &var : ret) {
StackInfo si;
@@ -590,29 +590,29 @@ public:
EXBIND2(reload_tool_script, const Ref<Script> &, bool)
/* LOADER FUNCTIONS */
- GDVIRTUAL0RC(PackedStringArray, _get_recognized_extensions)
+ GDVIRTUAL0RC_REQUIRED(PackedStringArray, _get_recognized_extensions)
virtual void get_recognized_extensions(List<String> *p_extensions) const override {
PackedStringArray ret;
- GDVIRTUAL_REQUIRED_CALL(_get_recognized_extensions, ret);
+ GDVIRTUAL_CALL(_get_recognized_extensions, ret);
for (int i = 0; i < ret.size(); i++) {
p_extensions->push_back(ret[i]);
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_functions)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_functions)
virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
+ GDVIRTUAL_CALL(_get_public_functions, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_functions->push_back(mi);
}
}
- GDVIRTUAL0RC(Dictionary, _get_public_constants)
+ GDVIRTUAL0RC_REQUIRED(Dictionary, _get_public_constants)
virtual void get_public_constants(List<Pair<String, Variant>> *p_constants) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_constants, ret);
+ GDVIRTUAL_CALL(_get_public_constants, ret);
for (int i = 0; i < ret.size(); i++) {
Dictionary d = ret[i];
ERR_CONTINUE(!d.has("name"));
@@ -620,10 +620,10 @@ public:
p_constants->push_back(Pair<String, Variant>(d["name"], d["value"]));
}
}
- GDVIRTUAL0RC(TypedArray<Dictionary>, _get_public_annotations)
+ GDVIRTUAL0RC_REQUIRED(TypedArray<Dictionary>, _get_public_annotations)
virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
TypedArray<Dictionary> ret;
- GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
+ GDVIRTUAL_CALL(_get_public_annotations, ret);
for (const Variant &var : ret) {
MethodInfo mi = MethodInfo::from_dict(var);
p_annotations->push_back(mi);
@@ -634,19 +634,19 @@ public:
EXBIND0(profiling_stop)
EXBIND1(profiling_set_save_native_calls, bool)
- GDVIRTUAL2R(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
+ GDVIRTUAL2R_REQUIRED(int, _profiling_get_accumulated_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_accumulated_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
+ GDVIRTUAL_CALL(_profiling_get_accumulated_data, p_info_arr, p_info_max, ret);
return ret;
}
- GDVIRTUAL2R(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
+ GDVIRTUAL2R_REQUIRED(int, _profiling_get_frame_data, GDExtensionPtr<ScriptLanguageExtensionProfilingInfo>, int)
virtual int profiling_get_frame_data(ProfilingInfo *p_info_arr, int p_info_max) override {
int ret = 0;
- GDVIRTUAL_REQUIRED_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
+ GDVIRTUAL_CALL(_profiling_get_frame_data, p_info_arr, p_info_max, ret);
return ret;
}
@@ -654,11 +654,11 @@ public:
EXBIND1RC(bool, handles_global_class_type, const String &)
- GDVIRTUAL1RC(Dictionary, _get_global_class_name, const String &)
+ GDVIRTUAL1RC_REQUIRED(Dictionary, _get_global_class_name, const String &)
virtual String get_global_class_name(const String &p_path, String *r_base_type = nullptr, String *r_icon_path = nullptr) const override {
Dictionary ret;
- GDVIRTUAL_REQUIRED_CALL(_get_global_class_name, p_path, ret);
+ GDVIRTUAL_CALL(_get_global_class_name, p_path, ret);
if (!ret.has("name")) {
return String();
}
diff --git a/core/string/translation_domain.cpp b/core/string/translation_domain.cpp
index b44eb40366..6a5e1b2af8 100644
--- a/core/string/translation_domain.cpp
+++ b/core/string/translation_domain.cpp
@@ -33,6 +33,170 @@
#include "core/string/translation.h"
#include "core/string/translation_server.h"
+struct _character_accent_pair {
+ const char32_t character;
+ const char32_t *accented_character;
+};
+
+static _character_accent_pair _character_to_accented[] = {
+ { 'A', U"Å" },
+ { 'B', U"ß" },
+ { 'C', U"Ç" },
+ { 'D', U"Ð" },
+ { 'E', U"É" },
+ { 'F', U"F́" },
+ { 'G', U"Ĝ" },
+ { 'H', U"Ĥ" },
+ { 'I', U"Ĩ" },
+ { 'J', U"Ĵ" },
+ { 'K', U"ĸ" },
+ { 'L', U"Ł" },
+ { 'M', U"Ḿ" },
+ { 'N', U"й" },
+ { 'O', U"Ö" },
+ { 'P', U"Ṕ" },
+ { 'Q', U"Q́" },
+ { 'R', U"Ř" },
+ { 'S', U"Ŝ" },
+ { 'T', U"Ŧ" },
+ { 'U', U"Ũ" },
+ { 'V', U"Ṽ" },
+ { 'W', U"Ŵ" },
+ { 'X', U"X́" },
+ { 'Y', U"Ÿ" },
+ { 'Z', U"Ž" },
+ { 'a', U"á" },
+ { 'b', U"ḅ" },
+ { 'c', U"ć" },
+ { 'd', U"d́" },
+ { 'e', U"é" },
+ { 'f', U"f́" },
+ { 'g', U"ǵ" },
+ { 'h', U"h̀" },
+ { 'i', U"í" },
+ { 'j', U"ǰ" },
+ { 'k', U"ḱ" },
+ { 'l', U"ł" },
+ { 'm', U"m̀" },
+ { 'n', U"ή" },
+ { 'o', U"ô" },
+ { 'p', U"ṕ" },
+ { 'q', U"q́" },
+ { 'r', U"ŕ" },
+ { 's', U"š" },
+ { 't', U"ŧ" },
+ { 'u', U"ü" },
+ { 'v', U"ṽ" },
+ { 'w', U"ŵ" },
+ { 'x', U"x́" },
+ { 'y', U"ý" },
+ { 'z', U"ź" },
+};
+
+String TranslationDomain::_get_override_string(const String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += '*';
+ }
+ return res;
+}
+
+String TranslationDomain::_double_vowels(const String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ res += p_message[i];
+ if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
+ p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
+ res += p_message[i];
+ }
+ }
+ return res;
+};
+
+String TranslationDomain::_replace_with_accented_string(const String &p_message) const {
+ String res;
+ for (int i = 0; i < p_message.length(); i++) {
+ if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
+ res += p_message[i];
+ res += p_message[i + 1];
+ i++;
+ continue;
+ }
+ const char32_t *accented = _get_accented_version(p_message[i]);
+ if (accented) {
+ res += accented;
+ } else {
+ res += p_message[i];
+ }
+ }
+ return res;
+}
+
+String TranslationDomain::_wrap_with_fakebidi_characters(const String &p_message) const {
+ String res;
+ char32_t fakebidiprefix = U'\u202e';
+ char32_t fakebidisuffix = U'\u202c';
+ res += fakebidiprefix;
+ // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
+ for (int i = 0; i < p_message.length(); i++) {
+ if (p_message[i] == '\n') {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += fakebidiprefix;
+ } else if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
+ res += fakebidisuffix;
+ res += p_message[i];
+ res += p_message[i + 1];
+ res += fakebidiprefix;
+ i++;
+ } else {
+ res += p_message[i];
+ }
+ }
+ res += fakebidisuffix;
+ return res;
+}
+
+String TranslationDomain::_add_padding(const String &p_message, int p_length) const {
+ String underscores = String("_").repeat(p_length * pseudolocalization.expansion_ratio / 2);
+ String prefix = pseudolocalization.prefix + underscores;
+ String suffix = underscores + pseudolocalization.suffix;
+
+ return prefix + p_message + suffix;
+}
+
+const char32_t *TranslationDomain::_get_accented_version(char32_t p_character) const {
+ if (!is_ascii_alphabet_char(p_character)) {
+ return nullptr;
+ }
+
+ for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
+ if (_character_to_accented[i].character == p_character) {
+ return _character_to_accented[i].accented_character;
+ }
+ }
+
+ return nullptr;
+}
+
+bool TranslationDomain::_is_placeholder(const String &p_message, int p_index) const {
+ return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
+ (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
+ p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+}
+
StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const {
StringName res;
int best_score = 0;
@@ -129,9 +293,9 @@ StringName TranslationDomain::translate(const StringName &p_message, const Strin
}
if (!res) {
- return p_message;
+ return pseudolocalization.enabled ? pseudolocalize(p_message) : p_message;
}
- return res;
+ return pseudolocalization.enabled ? pseudolocalize(res) : res;
}
StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@@ -152,6 +316,100 @@ StringName TranslationDomain::translate_plural(const StringName &p_message, cons
return res;
}
+bool TranslationDomain::is_pseudolocalization_enabled() const {
+ return pseudolocalization.enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_enabled(bool p_enabled) {
+ pseudolocalization.enabled = p_enabled;
+}
+
+bool TranslationDomain::is_pseudolocalization_accents_enabled() const {
+ return pseudolocalization.accents_enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_accents_enabled(bool p_enabled) {
+ pseudolocalization.accents_enabled = p_enabled;
+}
+
+bool TranslationDomain::is_pseudolocalization_double_vowels_enabled() const {
+ return pseudolocalization.double_vowels_enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_double_vowels_enabled(bool p_enabled) {
+ pseudolocalization.double_vowels_enabled = p_enabled;
+}
+
+bool TranslationDomain::is_pseudolocalization_fake_bidi_enabled() const {
+ return pseudolocalization.fake_bidi_enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_fake_bidi_enabled(bool p_enabled) {
+ pseudolocalization.fake_bidi_enabled = p_enabled;
+}
+
+bool TranslationDomain::is_pseudolocalization_override_enabled() const {
+ return pseudolocalization.override_enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_override_enabled(bool p_enabled) {
+ pseudolocalization.override_enabled = p_enabled;
+}
+
+bool TranslationDomain::is_pseudolocalization_skip_placeholders_enabled() const {
+ return pseudolocalization.skip_placeholders_enabled;
+}
+
+void TranslationDomain::set_pseudolocalization_skip_placeholders_enabled(bool p_enabled) {
+ pseudolocalization.skip_placeholders_enabled = p_enabled;
+}
+
+float TranslationDomain::get_pseudolocalization_expansion_ratio() const {
+ return pseudolocalization.expansion_ratio;
+}
+
+void TranslationDomain::set_pseudolocalization_expansion_ratio(float p_ratio) {
+ pseudolocalization.expansion_ratio = p_ratio;
+}
+
+String TranslationDomain::get_pseudolocalization_prefix() const {
+ return pseudolocalization.prefix;
+}
+
+void TranslationDomain::set_pseudolocalization_prefix(const String &p_prefix) {
+ pseudolocalization.prefix = p_prefix;
+}
+
+String TranslationDomain::get_pseudolocalization_suffix() const {
+ return pseudolocalization.suffix;
+}
+
+void TranslationDomain::set_pseudolocalization_suffix(const String &p_suffix) {
+ pseudolocalization.suffix = p_suffix;
+}
+
+StringName TranslationDomain::pseudolocalize(const StringName &p_message) const {
+ String message = p_message;
+ int length = message.length();
+ if (pseudolocalization.override_enabled) {
+ message = _get_override_string(message);
+ }
+
+ if (pseudolocalization.double_vowels_enabled) {
+ message = _double_vowels(message);
+ }
+
+ if (pseudolocalization.accents_enabled) {
+ message = _replace_with_accented_string(message);
+ }
+
+ if (pseudolocalization.fake_bidi_enabled) {
+ message = _wrap_with_fakebidi_characters(message);
+ }
+
+ return _add_padding(message, length);
+}
+
void TranslationDomain::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object);
ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation);
@@ -159,6 +417,36 @@ void TranslationDomain::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear);
ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName()));
ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName()));
+
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationDomain::is_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_enabled);
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_accents_enabled"), &TranslationDomain::is_pseudolocalization_accents_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_accents_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_accents_enabled);
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_double_vowels_enabled"), &TranslationDomain::is_pseudolocalization_double_vowels_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_double_vowels_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_double_vowels_enabled);
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_fake_bidi_enabled"), &TranslationDomain::is_pseudolocalization_fake_bidi_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_fake_bidi_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_fake_bidi_enabled);
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_override_enabled"), &TranslationDomain::is_pseudolocalization_override_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_override_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_override_enabled);
+ ClassDB::bind_method(D_METHOD("is_pseudolocalization_skip_placeholders_enabled"), &TranslationDomain::is_pseudolocalization_skip_placeholders_enabled);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_skip_placeholders_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_skip_placeholders_enabled);
+ ClassDB::bind_method(D_METHOD("get_pseudolocalization_expansion_ratio"), &TranslationDomain::get_pseudolocalization_expansion_ratio);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_expansion_ratio", "ratio"), &TranslationDomain::set_pseudolocalization_expansion_ratio);
+ ClassDB::bind_method(D_METHOD("get_pseudolocalization_prefix"), &TranslationDomain::get_pseudolocalization_prefix);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_prefix", "prefix"), &TranslationDomain::set_pseudolocalization_prefix);
+ ClassDB::bind_method(D_METHOD("get_pseudolocalization_suffix"), &TranslationDomain::get_pseudolocalization_suffix);
+ ClassDB::bind_method(D_METHOD("set_pseudolocalization_suffix", "suffix"), &TranslationDomain::set_pseudolocalization_suffix);
+ ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationDomain::pseudolocalize);
+
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_accents_enabled"), "set_pseudolocalization_accents_enabled", "is_pseudolocalization_accents_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_double_vowels_enabled"), "set_pseudolocalization_double_vowels_enabled", "is_pseudolocalization_double_vowels_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_fake_bidi_enabled"), "set_pseudolocalization_fake_bidi_enabled", "is_pseudolocalization_fake_bidi_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_override_enabled"), "set_pseudolocalization_override_enabled", "is_pseudolocalization_override_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_skip_placeholders_enabled"), "set_pseudolocalization_skip_placeholders_enabled", "is_pseudolocalization_skip_placeholders_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::FLOAT, "pseudolocalization_expansion_ratio"), "set_pseudolocalization_expansion_ratio", "get_pseudolocalization_expansion_ratio");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_prefix"), "set_pseudolocalization_prefix", "get_pseudolocalization_prefix");
+ ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_suffix"), "set_pseudolocalization_suffix", "get_pseudolocalization_suffix");
}
TranslationDomain::TranslationDomain() {
diff --git a/core/string/translation_domain.h b/core/string/translation_domain.h
index 6139967217..55592d3b35 100644
--- a/core/string/translation_domain.h
+++ b/core/string/translation_domain.h
@@ -38,7 +38,28 @@ class Translation;
class TranslationDomain : public RefCounted {
GDCLASS(TranslationDomain, RefCounted);
+ struct PseudolocalizationConfig {
+ bool enabled = false;
+ bool accents_enabled = true;
+ bool double_vowels_enabled = false;
+ bool fake_bidi_enabled = false;
+ bool override_enabled = false;
+ bool skip_placeholders_enabled = true;
+ float expansion_ratio = 0.0;
+ String prefix = "[";
+ String suffix = "]";
+ };
+
HashSet<Ref<Translation>> translations;
+ PseudolocalizationConfig pseudolocalization;
+
+ String _get_override_string(const String &p_message) const;
+ String _double_vowels(const String &p_message) const;
+ String _replace_with_accented_string(const String &p_message) const;
+ String _wrap_with_fakebidi_characters(const String &p_message) const;
+ String _add_padding(const String &p_message, int p_length) const;
+ const char32_t *_get_accented_version(char32_t p_character) const;
+ bool _is_placeholder(const String &p_message, int p_index) const;
protected:
static void _bind_methods();
@@ -59,6 +80,27 @@ public:
StringName translate(const StringName &p_message, const StringName &p_context) const;
StringName translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const;
+ bool is_pseudolocalization_enabled() const;
+ void set_pseudolocalization_enabled(bool p_enabled);
+ bool is_pseudolocalization_accents_enabled() const;
+ void set_pseudolocalization_accents_enabled(bool p_enabled);
+ bool is_pseudolocalization_double_vowels_enabled() const;
+ void set_pseudolocalization_double_vowels_enabled(bool p_enabled);
+ bool is_pseudolocalization_fake_bidi_enabled() const;
+ void set_pseudolocalization_fake_bidi_enabled(bool p_enabled);
+ bool is_pseudolocalization_override_enabled() const;
+ void set_pseudolocalization_override_enabled(bool p_enabled);
+ bool is_pseudolocalization_skip_placeholders_enabled() const;
+ void set_pseudolocalization_skip_placeholders_enabled(bool p_enabled);
+ float get_pseudolocalization_expansion_ratio() const;
+ void set_pseudolocalization_expansion_ratio(float p_ratio);
+ String get_pseudolocalization_prefix() const;
+ void set_pseudolocalization_prefix(const String &p_prefix);
+ String get_pseudolocalization_suffix() const;
+ void set_pseudolocalization_suffix(const String &p_suffix);
+
+ StringName pseudolocalize(const StringName &p_message) const;
+
TranslationDomain();
};
diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp
index c6b818a49b..89b37d0b8a 100644
--- a/core/string/translation_server.cpp
+++ b/core/string/translation_server.cpp
@@ -39,66 +39,6 @@
#include "main/main.h"
#endif
-struct _character_accent_pair {
- const char32_t character;
- const char32_t *accented_character;
-};
-
-static _character_accent_pair _character_to_accented[] = {
- { 'A', U"Å" },
- { 'B', U"ß" },
- { 'C', U"Ç" },
- { 'D', U"Ð" },
- { 'E', U"É" },
- { 'F', U"F́" },
- { 'G', U"Ĝ" },
- { 'H', U"Ĥ" },
- { 'I', U"Ĩ" },
- { 'J', U"Ĵ" },
- { 'K', U"ĸ" },
- { 'L', U"Ł" },
- { 'M', U"Ḿ" },
- { 'N', U"й" },
- { 'O', U"Ö" },
- { 'P', U"Ṕ" },
- { 'Q', U"Q́" },
- { 'R', U"Ř" },
- { 'S', U"Ŝ" },
- { 'T', U"Ŧ" },
- { 'U', U"Ũ" },
- { 'V', U"Ṽ" },
- { 'W', U"Ŵ" },
- { 'X', U"X́" },
- { 'Y', U"Ÿ" },
- { 'Z', U"Ž" },
- { 'a', U"á" },
- { 'b', U"ḅ" },
- { 'c', U"ć" },
- { 'd', U"d́" },
- { 'e', U"é" },
- { 'f', U"f́" },
- { 'g', U"ǵ" },
- { 'h', U"h̀" },
- { 'i', U"í" },
- { 'j', U"ǰ" },
- { 'k', U"ḱ" },
- { 'l', U"ł" },
- { 'm', U"m̀" },
- { 'n', U"ή" },
- { 'o', U"ô" },
- { 'p', U"ṕ" },
- { 'q', U"q́" },
- { 'r', U"ŕ" },
- { 's', U"š" },
- { 't', U"ŧ" },
- { 'u', U"ü" },
- { 'v', U"ṽ" },
- { 'w', U"ŵ" },
- { 'x', U"x́" },
- { 'y', U"ý" },
- { 'z', U"ź" },
-};
-
Vector<TranslationServer::LocaleScriptInfo> TranslationServer::locale_script_info;
HashMap<String, String> TranslationServer::language_map;
@@ -433,8 +373,7 @@ StringName TranslationServer::translate(const StringName &p_message, const Strin
return p_message;
}
- const StringName res = main_domain->translate(p_message, p_context);
- return pseudolocalization_enabled ? pseudolocalize(res) : res;
+ return main_domain->translate(p_message, p_context);
}
StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
@@ -510,15 +449,15 @@ void TranslationServer::setup() {
}
fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
- pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
- pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
- pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
- pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
- pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
- expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
- pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
- pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
+ main_domain->set_pseudolocalization_enabled(GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false));
+ main_domain->set_pseudolocalization_accents_enabled(GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true));
+ main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false));
+ main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false));
+ main_domain->set_pseudolocalization_override_enabled(GLOBAL_DEF("internationalization/pseudolocalization/override", false));
+ main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0));
+ main_domain->set_pseudolocalization_prefix(GLOBAL_DEF("internationalization/pseudolocalization/prefix", "["));
+ main_domain->set_pseudolocalization_suffix(GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]"));
+ main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true));
#ifdef TOOLS_ENABLED
ProjectSettings::get_singleton()->set_custom_property_info(PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_LOCALE_ID, ""));
@@ -567,11 +506,11 @@ StringName TranslationServer::doc_translate_plural(const StringName &p_message,
}
bool TranslationServer::is_pseudolocalization_enabled() const {
- return pseudolocalization_enabled;
+ return main_domain->is_pseudolocalization_enabled();
}
void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
- pseudolocalization_enabled = p_enabled;
+ main_domain->set_pseudolocalization_enabled(p_enabled);
ResourceLoader::reload_translation_remaps();
@@ -581,14 +520,14 @@ void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
}
void TranslationServer::reload_pseudolocalization() {
- pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
- pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
- pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
- pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
- expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
- pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
- pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
- pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
+ main_domain->set_pseudolocalization_accents_enabled(GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents"));
+ main_domain->set_pseudolocalization_double_vowels_enabled(GLOBAL_GET("internationalization/pseudolocalization/double_vowels"));
+ main_domain->set_pseudolocalization_fake_bidi_enabled(GLOBAL_GET("internationalization/pseudolocalization/fake_bidi"));
+ main_domain->set_pseudolocalization_override_enabled(GLOBAL_GET("internationalization/pseudolocalization/override"));
+ main_domain->set_pseudolocalization_expansion_ratio(GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio"));
+ main_domain->set_pseudolocalization_prefix(GLOBAL_GET("internationalization/pseudolocalization/prefix"));
+ main_domain->set_pseudolocalization_suffix(GLOBAL_GET("internationalization/pseudolocalization/suffix"));
+ main_domain->set_pseudolocalization_skip_placeholders_enabled(GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders"));
ResourceLoader::reload_translation_remaps();
@@ -598,138 +537,7 @@ void TranslationServer::reload_pseudolocalization() {
}
StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- int length = message.length();
- if (pseudolocalization_override_enabled) {
- message = get_override_string(message);
- }
-
- if (pseudolocalization_double_vowels_enabled) {
- message = double_vowels(message);
- }
-
- if (pseudolocalization_accents_enabled) {
- message = replace_with_accented_string(message);
- }
-
- if (pseudolocalization_fake_bidi_enabled) {
- message = wrap_with_fakebidi_characters(message);
- }
-
- StringName res = add_padding(message, length);
- return res;
-}
-
-StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
- String message = p_message;
- message = double_vowels(message);
- message = replace_with_accented_string(message);
- StringName res = "[!!! " + message + " !!!]";
- return res;
-}
-
-String TranslationServer::get_override_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += '*';
- }
- return res;
-}
-
-String TranslationServer::double_vowels(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- res += p_message[i];
- if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
- p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
- res += p_message[i];
- }
- }
- return res;
-};
-
-String TranslationServer::replace_with_accented_string(String &p_message) const {
- String res;
- for (int i = 0; i < p_message.length(); i++) {
- if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += p_message[i];
- res += p_message[i + 1];
- i++;
- continue;
- }
- const char32_t *accented = get_accented_version(p_message[i]);
- if (accented) {
- res += accented;
- } else {
- res += p_message[i];
- }
- }
- return res;
-}
-
-String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
- String res;
- char32_t fakebidiprefix = U'\u202e';
- char32_t fakebidisuffix = U'\u202c';
- res += fakebidiprefix;
- // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
- for (int i = 0; i < p_message.length(); i++) {
- if (p_message[i] == '\n') {
- res += fakebidisuffix;
- res += p_message[i];
- res += fakebidiprefix;
- } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
- res += fakebidisuffix;
- res += p_message[i];
- res += p_message[i + 1];
- res += fakebidiprefix;
- i++;
- } else {
- res += p_message[i];
- }
- }
- res += fakebidisuffix;
- return res;
-}
-
-String TranslationServer::add_padding(const String &p_message, int p_length) const {
- String underscores = String("_").repeat(p_length * expansion_ratio / 2);
- String prefix = pseudolocalization_prefix + underscores;
- String suffix = underscores + pseudolocalization_suffix;
-
- return prefix + p_message + suffix;
-}
-
-const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
- if (!is_ascii_alphabet_char(p_character)) {
- return nullptr;
- }
-
- for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
- if (_character_to_accented[i].character == p_character) {
- return _character_to_accented[i].accented_character;
- }
- }
-
- return nullptr;
-}
-
-bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
- return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
- (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
- p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
+ return main_domain->pseudolocalize(p_message);
}
#ifdef TOOLS_ENABLED
diff --git a/core/string/translation_server.h b/core/string/translation_server.h
index 272fa1f11c..a09230c019 100644
--- a/core/string/translation_server.h
+++ b/core/string/translation_server.h
@@ -48,25 +48,6 @@ class TranslationServer : public Object {
bool enabled = true;
- bool pseudolocalization_enabled = false;
- bool pseudolocalization_accents_enabled = false;
- bool pseudolocalization_double_vowels_enabled = false;
- bool pseudolocalization_fake_bidi_enabled = false;
- bool pseudolocalization_override_enabled = false;
- bool pseudolocalization_skip_placeholders_enabled = false;
- float expansion_ratio = 0.0;
- String pseudolocalization_prefix;
- String pseudolocalization_suffix;
-
- StringName tool_pseudolocalize(const StringName &p_message) const;
- String get_override_string(String &p_message) const;
- String double_vowels(String &p_message) const;
- String replace_with_accented_string(String &p_message) const;
- String wrap_with_fakebidi_characters(String &p_message) const;
- String add_padding(const String &p_message, int p_length) const;
- const char32_t *get_accented_version(char32_t p_character) const;
- bool is_placeholder(String &p_message, int p_index) const;
-
static TranslationServer *singleton;
bool _load_translations(const String &p_from);
String _standardize_locale(const String &p_locale, bool p_add_defaults) const;
@@ -93,6 +74,8 @@ class TranslationServer : public Object {
public:
_FORCE_INLINE_ static TranslationServer *get_singleton() { return singleton; }
+ Ref<TranslationDomain> get_editor_domain() const { return editor_domain; }
+
void set_enabled(bool p_enabled) { enabled = p_enabled; }
_FORCE_INLINE_ bool is_enabled() const { return enabled; }
diff --git a/core/string/ustring.cpp b/core/string/ustring.cpp
index 391a203d5b..e6f7492a18 100644
--- a/core/string/ustring.cpp
+++ b/core/string/ustring.cpp
@@ -221,18 +221,35 @@ void CharString::copy_from(const char *p_cstr) {
/* String */
/*************************************************************************/
-Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const {
- // Splits the URL into scheme, host, port, path. Strip credentials when present.
+Error String::parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const {
+ // Splits the URL into scheme, host, port, path, fragment. Strip credentials when present.
String base = *this;
r_scheme = "";
r_host = "";
r_port = 0;
r_path = "";
+ r_fragment = "";
+
int pos = base.find("://");
// Scheme
if (pos != -1) {
- r_scheme = base.substr(0, pos + 3).to_lower();
- base = base.substr(pos + 3, base.length() - pos - 3);
+ bool is_scheme_valid = true;
+ for (int i = 0; i < pos; i++) {
+ if (!is_ascii_alphanumeric_char(base[i]) && base[i] != '+' && base[i] != '-' && base[i] != '.') {
+ is_scheme_valid = false;
+ break;
+ }
+ }
+ if (is_scheme_valid) {
+ r_scheme = base.substr(0, pos + 3).to_lower();
+ base = base.substr(pos + 3, base.length() - pos - 3);
+ }
+ }
+ pos = base.find("#");
+ // Fragment
+ if (pos != -1) {
+ r_fragment = base.substr(pos + 1);
+ base = base.substr(0, pos);
}
pos = base.find("/");
// Path
diff --git a/core/string/ustring.h b/core/string/ustring.h
index 5d4b209c25..aa62c9cb18 100644
--- a/core/string/ustring.h
+++ b/core/string/ustring.h
@@ -452,7 +452,7 @@ public:
String c_escape_multiline() const;
String c_unescape() const;
String json_escape() const;
- Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path) const;
+ Error parse_url(String &r_scheme, String &r_host, int &r_port, String &r_path, String &r_fragment) const;
String property_name_encode() const;
diff --git a/core/templates/hash_map.cpp b/core/templates/hash_map.cpp
new file mode 100644
index 0000000000..93664dd2e1
--- /dev/null
+++ b/core/templates/hash_map.cpp
@@ -0,0 +1,43 @@
+/**************************************************************************/
+/* hash_map.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#include "hash_map.h"
+
+#include "core/variant/variant.h"
+
+bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right) {
+ bool valid = false;
+ Variant res;
+ Variant::evaluate(Variant::OP_LESS, p_left, p_right, res, valid);
+ if (!valid) {
+ res = false;
+ }
+ return res;
+}
diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h
index a3e8c2c788..329952e8d4 100644
--- a/core/templates/hash_map.h
+++ b/core/templates/hash_map.h
@@ -61,6 +61,8 @@ struct HashMapElement {
data(p_key, p_value) {}
};
+bool _hashmap_variant_less_than(const Variant &p_left, const Variant &p_right);
+
template <typename TKey, typename TValue,
typename Hasher = HashMapHasherDefault,
typename Comparator = HashMapComparatorDefault<TKey>,
@@ -271,6 +273,47 @@ public:
num_elements = 0;
}
+ void sort() {
+ if (elements == nullptr || num_elements < 2) {
+ return; // An empty or single element HashMap is already sorted.
+ }
+ // Use insertion sort because we want this operation to be fast for the
+ // common case where the input is already sorted or nearly sorted.
+ HashMapElement<TKey, TValue> *inserting = head_element->next;
+ while (inserting != nullptr) {
+ HashMapElement<TKey, TValue> *after = nullptr;
+ for (HashMapElement<TKey, TValue> *current = inserting->prev; current != nullptr; current = current->prev) {
+ if (_hashmap_variant_less_than(inserting->data.key, current->data.key)) {
+ after = current;
+ } else {
+ break;
+ }
+ }
+ HashMapElement<TKey, TValue> *next = inserting->next;
+ if (after != nullptr) {
+ // Modify the elements around `inserting` to remove it from its current position.
+ inserting->prev->next = next;
+ if (next == nullptr) {
+ tail_element = inserting->prev;
+ } else {
+ next->prev = inserting->prev;
+ }
+ // Modify `before` and `after` to insert `inserting` between them.
+ HashMapElement<TKey, TValue> *before = after->prev;
+ if (before == nullptr) {
+ head_element = inserting;
+ } else {
+ before->next = inserting;
+ }
+ after->prev = inserting;
+ // Point `inserting` to its new surroundings.
+ inserting->prev = before;
+ inserting->next = after;
+ }
+ inserting = next;
+ }
+ }
+
TValue &get(const TKey &p_key) {
uint32_t pos = 0;
bool exists = _lookup_pos(p_key, pos);
diff --git a/core/templates/hashfuncs.h b/core/templates/hashfuncs.h
index fc7a78bcf5..21eef10297 100644
--- a/core/templates/hashfuncs.h
+++ b/core/templates/hashfuncs.h
@@ -32,10 +32,17 @@
#define HASHFUNCS_H
#include "core/math/aabb.h"
+#include "core/math/basis.h"
+#include "core/math/color.h"
#include "core/math/math_defs.h"
#include "core/math/math_funcs.h"
+#include "core/math/plane.h"
+#include "core/math/projection.h"
+#include "core/math/quaternion.h"
#include "core/math/rect2.h"
#include "core/math/rect2i.h"
+#include "core/math/transform_2d.h"
+#include "core/math/transform_3d.h"
#include "core/math/vector2.h"
#include "core/math/vector2i.h"
#include "core/math/vector3.h"
@@ -414,6 +421,13 @@ struct HashMapComparatorDefault<double> {
};
template <>
+struct HashMapComparatorDefault<Color> {
+ static bool compare(const Color &p_lhs, const Color &p_rhs) {
+ return ((p_lhs.r == p_rhs.r) || (Math::is_nan(p_lhs.r) && Math::is_nan(p_rhs.r))) && ((p_lhs.g == p_rhs.g) || (Math::is_nan(p_lhs.g) && Math::is_nan(p_rhs.g))) && ((p_lhs.b == p_rhs.b) || (Math::is_nan(p_lhs.b) && Math::is_nan(p_rhs.b))) && ((p_lhs.a == p_rhs.a) || (Math::is_nan(p_lhs.a) && Math::is_nan(p_rhs.a)));
+ }
+};
+
+template <>
struct HashMapComparatorDefault<Vector2> {
static bool compare(const Vector2 &p_lhs, const Vector2 &p_rhs) {
return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y)));
@@ -427,6 +441,87 @@ struct HashMapComparatorDefault<Vector3> {
}
};
+template <>
+struct HashMapComparatorDefault<Vector4> {
+ static bool compare(const Vector4 &p_lhs, const Vector4 &p_rhs) {
+ return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Rect2> {
+ static bool compare(const Rect2 &p_lhs, const Rect2 &p_rhs) {
+ return HashMapComparatorDefault<Vector2>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector2>().compare(p_lhs.size, p_rhs.size);
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<AABB> {
+ static bool compare(const AABB &p_lhs, const AABB &p_rhs) {
+ return HashMapComparatorDefault<Vector3>().compare(p_lhs.position, p_rhs.position) && HashMapComparatorDefault<Vector3>().compare(p_lhs.size, p_rhs.size);
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Plane> {
+ static bool compare(const Plane &p_lhs, const Plane &p_rhs) {
+ return HashMapComparatorDefault<Vector3>().compare(p_lhs.normal, p_rhs.normal) && ((p_lhs.d == p_rhs.d) || (Math::is_nan(p_lhs.d) && Math::is_nan(p_rhs.d)));
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Transform2D> {
+ static bool compare(const Transform2D &p_lhs, const Transform2D &p_rhs) {
+ for (int i = 0; i < 3; ++i) {
+ if (!HashMapComparatorDefault<Vector2>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Basis> {
+ static bool compare(const Basis &p_lhs, const Basis &p_rhs) {
+ for (int i = 0; i < 3; ++i) {
+ if (!HashMapComparatorDefault<Vector3>().compare(p_lhs.rows[i], p_rhs.rows[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Transform3D> {
+ static bool compare(const Transform3D &p_lhs, const Transform3D &p_rhs) {
+ return HashMapComparatorDefault<Basis>().compare(p_lhs.basis, p_rhs.basis) && HashMapComparatorDefault<Vector3>().compare(p_lhs.origin, p_rhs.origin);
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Projection> {
+ static bool compare(const Projection &p_lhs, const Projection &p_rhs) {
+ for (int i = 0; i < 4; ++i) {
+ if (!HashMapComparatorDefault<Vector4>().compare(p_lhs.columns[i], p_rhs.columns[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct HashMapComparatorDefault<Quaternion> {
+ static bool compare(const Quaternion &p_lhs, const Quaternion &p_rhs) {
+ return ((p_lhs.x == p_rhs.x) || (Math::is_nan(p_lhs.x) && Math::is_nan(p_rhs.x))) && ((p_lhs.y == p_rhs.y) || (Math::is_nan(p_lhs.y) && Math::is_nan(p_rhs.y))) && ((p_lhs.z == p_rhs.z) || (Math::is_nan(p_lhs.z) && Math::is_nan(p_rhs.z))) && ((p_lhs.w == p_rhs.w) || (Math::is_nan(p_lhs.w) && Math::is_nan(p_rhs.w)));
+ }
+};
+
constexpr uint32_t HASH_TABLE_SIZE_MAX = 29;
inline constexpr uint32_t hash_table_size_primes[HASH_TABLE_SIZE_MAX] = {
diff --git a/core/templates/rid_owner.h b/core/templates/rid_owner.h
index 537413e2ba..4200159054 100644
--- a/core/templates/rid_owner.h
+++ b/core/templates/rid_owner.h
@@ -32,7 +32,7 @@
#define RID_OWNER_H
#include "core/os/memory.h"
-#include "core/os/spin_lock.h"
+#include "core/os/mutex.h"
#include "core/string/print_string.h"
#include "core/templates/hash_set.h"
#include "core/templates/list.h"
@@ -69,42 +69,54 @@ public:
template <typename T, bool THREAD_SAFE = false>
class RID_Alloc : public RID_AllocBase {
- T **chunks = nullptr;
+ struct Chunk {
+ T data;
+ uint32_t validator;
+ };
+ Chunk **chunks = nullptr;
uint32_t **free_list_chunks = nullptr;
- uint32_t **validator_chunks = nullptr;
uint32_t elements_in_chunk;
uint32_t max_alloc = 0;
uint32_t alloc_count = 0;
+ uint32_t chunk_limit = 0;
const char *description = nullptr;
- mutable SpinLock spin_lock;
+ mutable Mutex mutex;
_FORCE_INLINE_ RID _allocate_rid() {
if constexpr (THREAD_SAFE) {
- spin_lock.lock();
+ mutex.lock();
}
if (alloc_count == max_alloc) {
//allocate a new chunk
uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
+ if (THREAD_SAFE && chunk_count == chunk_limit) {
+ mutex.unlock();
+ if (description != nullptr) {
+ ERR_FAIL_V_MSG(RID(), vformat("Element limit for RID of type '%s' reached.", String(description)));
+ } else {
+ ERR_FAIL_V_MSG(RID(), "Element limit reached.");
+ }
+ }
//grow chunks
- chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
- chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); //but don't initialize
-
- //grow validators
- validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
- validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
+ if constexpr (!THREAD_SAFE) {
+ chunks = (Chunk **)memrealloc(chunks, sizeof(Chunk *) * (chunk_count + 1));
+ }
+ chunks[chunk_count] = (Chunk *)memalloc(sizeof(Chunk) * elements_in_chunk); //but don't initialize
//grow free lists
- free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ if constexpr (!THREAD_SAFE) {
+ free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
+ }
free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
//initialize
for (uint32_t i = 0; i < elements_in_chunk; i++) {
// Don't initialize chunk.
- validator_chunks[chunk_count][i] = 0xFFFFFFFF;
+ chunks[chunk_count][i].validator = 0xFFFFFFFF;
free_list_chunks[chunk_count][i] = alloc_count + i;
}
@@ -122,14 +134,13 @@ class RID_Alloc : public RID_AllocBase {
id <<= 32;
id |= free_index;
- validator_chunks[free_chunk][free_element] = validator;
-
- validator_chunks[free_chunk][free_element] |= 0x80000000; //mark uninitialized bit
+ chunks[free_chunk][free_element].validator = validator;
+ chunks[free_chunk][free_element].validator |= 0x80000000; //mark uninitialized bit
alloc_count++;
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
return _make_from_id(id);
@@ -156,16 +167,10 @@ public:
if (p_rid == RID()) {
return nullptr;
}
- if constexpr (THREAD_SAFE) {
- spin_lock.lock();
- }
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
- if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
- }
return nullptr;
}
@@ -174,38 +179,26 @@ public:
uint32_t validator = uint32_t(id >> 32);
+ Chunk &c = chunks[idx_chunk][idx_element];
if (unlikely(p_initialize)) {
- if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) {
- if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
- }
+ if (unlikely(!(c.validator & 0x80000000))) {
ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID");
}
- if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) {
- if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
- }
+ if (unlikely((c.validator & 0x7FFFFFFF) != validator)) {
ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID");
}
- validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; //initialized
+ c.validator &= 0x7FFFFFFF; //initialized
- } else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
- if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
- }
- if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
+ } else if (unlikely(c.validator != validator)) {
+ if ((c.validator & 0x80000000) && c.validator != 0xFFFFFFFF) {
ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID");
}
return nullptr;
}
- T *ptr = &chunks[idx_chunk][idx_element];
-
- if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
- }
+ T *ptr = &c.data;
return ptr;
}
@@ -222,14 +215,14 @@ public:
_FORCE_INLINE_ bool owns(const RID &p_rid) const {
if constexpr (THREAD_SAFE) {
- spin_lock.lock();
+ mutex.lock();
}
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
return false;
}
@@ -239,10 +232,10 @@ public:
uint32_t validator = uint32_t(id >> 32);
- bool owned = (validator != 0x7FFFFFFF) && (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator;
+ bool owned = (validator != 0x7FFFFFFF) && (chunks[idx_chunk][idx_element].validator & 0x7FFFFFFF) == validator;
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
return owned;
@@ -250,14 +243,14 @@ public:
_FORCE_INLINE_ void free(const RID &p_rid) {
if constexpr (THREAD_SAFE) {
- spin_lock.lock();
+ mutex.lock();
}
uint64_t id = p_rid.get_id();
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
if (unlikely(idx >= max_alloc)) {
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
ERR_FAIL();
}
@@ -266,26 +259,26 @@ public:
uint32_t idx_element = idx % elements_in_chunk;
uint32_t validator = uint32_t(id >> 32);
- if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) {
+ if (unlikely(chunks[idx_chunk][idx_element].validator & 0x80000000)) {
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
- ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID.");
- } else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
+ ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID");
+ } else if (unlikely(chunks[idx_chunk][idx_element].validator != validator)) {
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
ERR_FAIL();
}
- chunks[idx_chunk][idx_element].~T();
- validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
+ chunks[idx_chunk][idx_element].data.~T();
+ chunks[idx_chunk][idx_element].validator = 0xFFFFFFFF; // go invalid
alloc_count--;
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
}
@@ -294,34 +287,35 @@ public:
}
void get_owned_list(List<RID> *p_owned) const {
if constexpr (THREAD_SAFE) {
- spin_lock.lock();
+ mutex.lock();
}
for (size_t i = 0; i < max_alloc; i++) {
- uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
if (validator != 0xFFFFFFFF) {
p_owned->push_back(_make_from_id((validator << 32) | i));
}
}
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
}
//used for fast iteration in the elements or RIDs
void fill_owned_buffer(RID *p_rid_buffer) const {
if constexpr (THREAD_SAFE) {
- spin_lock.lock();
+ mutex.lock();
}
uint32_t idx = 0;
for (size_t i = 0; i < max_alloc; i++) {
- uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
if (validator != 0xFFFFFFFF) {
p_rid_buffer[idx] = _make_from_id((validator << 32) | i);
idx++;
}
}
+
if constexpr (THREAD_SAFE) {
- spin_lock.unlock();
+ mutex.unlock();
}
}
@@ -329,8 +323,13 @@ public:
description = p_descrption;
}
- RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) {
+ RID_Alloc(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) {
elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
+ if constexpr (THREAD_SAFE) {
+ chunk_limit = (p_maximum_number_of_elements / elements_in_chunk) + 1;
+ chunks = (Chunk **)memalloc(sizeof(Chunk *) * chunk_limit);
+ free_list_chunks = (uint32_t **)memalloc(sizeof(uint32_t *) * chunk_limit);
+ }
}
~RID_Alloc() {
@@ -339,12 +338,12 @@ public:
alloc_count, description ? description : typeid(T).name()));
for (size_t i = 0; i < max_alloc; i++) {
- uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
+ uint64_t validator = chunks[i / elements_in_chunk][i % elements_in_chunk].validator;
if (validator & 0x80000000) {
continue; //uninitialized
}
if (validator != 0xFFFFFFFF) {
- chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
+ chunks[i / elements_in_chunk][i % elements_in_chunk].data.~T();
}
}
}
@@ -352,14 +351,12 @@ public:
uint32_t chunk_count = max_alloc / elements_in_chunk;
for (uint32_t i = 0; i < chunk_count; i++) {
memfree(chunks[i]);
- memfree(validator_chunks[i]);
memfree(free_list_chunks[i]);
}
if (chunks) {
memfree(chunks);
memfree(free_list_chunks);
- memfree(validator_chunks);
}
}
};
@@ -419,8 +416,8 @@ public:
alloc.set_description(p_descrption);
}
- RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) :
- alloc(p_target_chunk_byte_size) {}
+ RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
+ alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
};
template <typename T, bool THREAD_SAFE = false>
@@ -473,8 +470,8 @@ public:
void set_description(const char *p_descrption) {
alloc.set_description(p_descrption);
}
- RID_Owner(uint32_t p_target_chunk_byte_size = 65536) :
- alloc(p_target_chunk_byte_size) {}
+ RID_Owner(uint32_t p_target_chunk_byte_size = 65536, uint32_t p_maximum_number_of_elements = 262144) :
+ alloc(p_target_chunk_byte_size, p_maximum_number_of_elements) {}
};
#endif // RID_OWNER_H
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index bb2d0313f6..5ce90cd8ff 100644
--- a/core/variant/callable.cpp
+++ b/core/variant/callable.cpp
@@ -315,31 +315,32 @@ bool Callable::operator<(const Callable &p_callable) const {
}
void Callable::operator=(const Callable &p_callable) {
+ CallableCustom *cleanup_ref = nullptr;
if (is_custom()) {
if (p_callable.is_custom()) {
if (custom == p_callable.custom) {
return;
}
}
-
- if (custom->ref_count.unref()) {
- memdelete(custom);
- custom = nullptr;
- }
+ cleanup_ref = custom;
+ custom = nullptr;
}
if (p_callable.is_custom()) {
method = StringName();
- if (!p_callable.custom->ref_count.ref()) {
- object = 0;
- } else {
- object = 0;
+ object = 0;
+ if (p_callable.custom->ref_count.ref()) {
custom = p_callable.custom;
}
} else {
method = p_callable.method;
object = p_callable.object;
}
+
+ if (cleanup_ref != nullptr && cleanup_ref->ref_count.unref()) {
+ memdelete(cleanup_ref);
+ }
+ cleanup_ref = nullptr;
}
Callable::operator String() const {
diff --git a/core/variant/dictionary.cpp b/core/variant/dictionary.cpp
index 2db754438f..501ca69205 100644
--- a/core/variant/dictionary.cpp
+++ b/core/variant/dictionary.cpp
@@ -294,6 +294,11 @@ void Dictionary::clear() {
_p->variant_map.clear();
}
+void Dictionary::sort() {
+ ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
+ _p->variant_map.sort();
+}
+
void Dictionary::merge(const Dictionary &p_dictionary, bool p_overwrite) {
ERR_FAIL_COND_MSG(_p->read_only, "Dictionary is in read-only state.");
for (const KeyValue<Variant, Variant> &E : p_dictionary._p->variant_map) {
diff --git a/core/variant/dictionary.h b/core/variant/dictionary.h
index 5f3ce40219..bbfb5b3083 100644
--- a/core/variant/dictionary.h
+++ b/core/variant/dictionary.h
@@ -64,6 +64,7 @@ public:
int size() const;
bool is_empty() const;
void clear();
+ void sort();
void merge(const Dictionary &p_dictionary, bool p_overwrite = false);
Dictionary merged(const Dictionary &p_dictionary, bool p_overwrite = false) const;
diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp
index 186643b024..e2865a06be 100644
--- a/core/variant/variant.cpp
+++ b/core/variant/variant.cpp
@@ -1072,17 +1072,69 @@ bool Variant::is_null() const {
}
}
+void Variant::ObjData::ref(const ObjData &p_from) {
+ // Mirrors Ref::ref in refcounted.h
+ if (p_from.id == id) {
+ return;
+ }
+
+ ObjData cleanup_ref = *this;
+
+ *this = p_from;
+ if (id.is_ref_counted()) {
+ RefCounted *reference = static_cast<RefCounted *>(obj);
+ // Assuming reference is not null because id.is_ref_counted() was true.
+ if (!reference->reference()) {
+ *this = ObjData();
+ }
+ }
+
+ cleanup_ref.unref();
+}
+
+void Variant::ObjData::ref_pointer(Object *p_object) {
+ // Mirrors Ref::ref_pointer in refcounted.h
+ if (p_object == obj) {
+ return;
+ }
+
+ ObjData cleanup_ref = *this;
+
+ if (p_object) {
+ *this = ObjData{ p_object->get_instance_id(), p_object };
+ if (p_object->is_ref_counted()) {
+ RefCounted *reference = static_cast<RefCounted *>(p_object);
+ if (!reference->init_ref()) {
+ *this = ObjData();
+ }
+ }
+ } else {
+ *this = ObjData();
+ }
+
+ cleanup_ref.unref();
+}
+
+void Variant::ObjData::unref() {
+ // Mirrors Ref::unref in refcounted.h
+ if (id.is_ref_counted()) {
+ RefCounted *reference = static_cast<RefCounted *>(obj);
+ // Assuming reference is not null because id.is_ref_counted() was true.
+ if (reference->unreference()) {
+ memdelete(reference);
+ }
+ }
+ *this = ObjData();
+}
+
void Variant::reference(const Variant &p_variant) {
- switch (type) {
- case NIL:
- case BOOL:
- case INT:
- case FLOAT:
- break;
- default:
- clear();
+ if (type == OBJECT && p_variant.type == OBJECT) {
+ _get_obj().ref(p_variant._get_obj());
+ return;
}
+ clear();
+
type = p_variant.type;
switch (p_variant.type) {
@@ -1165,18 +1217,7 @@ void Variant::reference(const Variant &p_variant) {
} break;
case OBJECT: {
memnew_placement(_data._mem, ObjData);
-
- if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) {
- RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj);
- if (!ref_counted->reference()) {
- _get_obj().obj = nullptr;
- _get_obj().id = ObjectID();
- break;
- }
- }
-
- _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj);
- _get_obj().id = p_variant._get_obj().id;
+ _get_obj().ref(p_variant._get_obj());
} break;
case CALLABLE: {
memnew_placement(_data._mem, Callable(*reinterpret_cast<const Callable *>(p_variant._data._mem)));
@@ -1375,15 +1416,7 @@ void Variant::_clear_internal() {
reinterpret_cast<NodePath *>(_data._mem)->~NodePath();
} break;
case OBJECT: {
- if (_get_obj().id.is_ref_counted()) {
- // We are safe that there is a reference here.
- RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj);
- if (ref_counted->unreference()) {
- memdelete(ref_counted);
- }
- }
- _get_obj().obj = nullptr;
- _get_obj().id = ObjectID();
+ _get_obj().unref();
} break;
case RID: {
// Not much need probably.
@@ -2589,24 +2622,8 @@ Variant::Variant(const ::RID &p_rid) :
Variant::Variant(const Object *p_object) :
type(OBJECT) {
- memnew_placement(_data._mem, ObjData);
-
- if (p_object) {
- if (p_object->is_ref_counted()) {
- RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(p_object));
- if (!ref_counted->init_ref()) {
- _get_obj().obj = nullptr;
- _get_obj().id = ObjectID();
- return;
- }
- }
-
- _get_obj().obj = const_cast<Object *>(p_object);
- _get_obj().id = p_object->get_instance_id();
- } else {
- _get_obj().obj = nullptr;
- _get_obj().id = ObjectID();
- }
+ _get_obj() = ObjData();
+ _get_obj().ref_pointer(const_cast<Object *>(p_object));
}
Variant::Variant(const Callable &p_callable) :
@@ -2828,26 +2845,7 @@ void Variant::operator=(const Variant &p_variant) {
*reinterpret_cast<::RID *>(_data._mem) = *reinterpret_cast<const ::RID *>(p_variant._data._mem);
} break;
case OBJECT: {
- if (_get_obj().id.is_ref_counted()) {
- //we are safe that there is a reference here
- RefCounted *ref_counted = static_cast<RefCounted *>(_get_obj().obj);
- if (ref_counted->unreference()) {
- memdelete(ref_counted);
- }
- }
-
- if (p_variant._get_obj().obj && p_variant._get_obj().id.is_ref_counted()) {
- RefCounted *ref_counted = static_cast<RefCounted *>(p_variant._get_obj().obj);
- if (!ref_counted->reference()) {
- _get_obj().obj = nullptr;
- _get_obj().id = ObjectID();
- break;
- }
- }
-
- _get_obj().obj = const_cast<Object *>(p_variant._get_obj().obj);
- _get_obj().id = p_variant._get_obj().id;
-
+ _get_obj().ref(p_variant._get_obj());
} break;
case CALLABLE: {
*reinterpret_cast<Callable *>(_data._mem) = *reinterpret_cast<const Callable *>(p_variant._data._mem);
diff --git a/core/variant/variant.h b/core/variant/variant.h
index d4e4b330cd..c76b849abd 100644
--- a/core/variant/variant.h
+++ b/core/variant/variant.h
@@ -62,6 +62,10 @@
#include "core/variant/dictionary.h"
class Object;
+class RefCounted;
+
+template <typename T>
+class Ref;
struct PropertyInfo;
struct MethodInfo;
@@ -175,6 +179,20 @@ private:
struct ObjData {
ObjectID id;
Object *obj = nullptr;
+
+ void ref(const ObjData &p_from);
+ void ref_pointer(Object *p_object);
+ void ref_pointer(RefCounted *p_object);
+ void unref();
+
+ template <typename T>
+ _ALWAYS_INLINE_ void ref(const Ref<T> &p_from) {
+ if (p_from.is_valid()) {
+ ref(ObjData{ p_from->get_instance_id(), p_from.ptr() });
+ } else {
+ unref();
+ }
+ }
};
/* array helpers */
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index 63fb5e8d94..29e11462c9 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -657,7 +657,23 @@ static _FORCE_INLINE_ void vc_ptrcall(void (*method)(T *, P...), void *p_base, c
} \
};
+#define VARCALL_PACKED_GETTER(m_packed_type, m_return_type) \
+ static m_return_type func_##m_packed_type##_get(m_packed_type *p_instance, int64_t p_index) { \
+ return p_instance->get(p_index); \
+ }
+
struct _VariantCall {
+ VARCALL_PACKED_GETTER(PackedByteArray, uint8_t)
+ VARCALL_PACKED_GETTER(PackedColorArray, Color)
+ VARCALL_PACKED_GETTER(PackedFloat32Array, float)
+ VARCALL_PACKED_GETTER(PackedFloat64Array, double)
+ VARCALL_PACKED_GETTER(PackedInt32Array, int32_t)
+ VARCALL_PACKED_GETTER(PackedInt64Array, int64_t)
+ VARCALL_PACKED_GETTER(PackedStringArray, String)
+ VARCALL_PACKED_GETTER(PackedVector2Array, Vector2)
+ VARCALL_PACKED_GETTER(PackedVector3Array, Vector3)
+ VARCALL_PACKED_GETTER(PackedVector4Array, Vector4)
+
static String func_PackedByteArray_get_string_from_ascii(PackedByteArray *p_instance) {
String s;
if (p_instance->size() > 0) {
@@ -2256,6 +2272,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, is_empty, sarray(), varray());
bind_method(Dictionary, clear, sarray(), varray());
bind_method(Dictionary, assign, sarray("dictionary"), varray());
+ bind_method(Dictionary, sort, sarray(), varray());
bind_method(Dictionary, merge, sarray("dictionary", "overwrite"), varray(false));
bind_method(Dictionary, merged, sarray("dictionary", "overwrite"), varray(false));
bind_method(Dictionary, has, sarray("key"), varray());
@@ -2268,6 +2285,7 @@ static void _register_variant_builtin_methods_misc() {
bind_method(Dictionary, duplicate, sarray("deep"), varray(false));
bind_method(Dictionary, get, sarray("key", "default"), varray(Variant()));
bind_method(Dictionary, get_or_add, sarray("key", "default"), varray(Variant()));
+ bind_method(Dictionary, set, sarray("key", "value"), varray());
bind_method(Dictionary, is_typed, sarray(), varray());
bind_method(Dictionary, is_typed_key, sarray(), varray());
bind_method(Dictionary, is_typed_value, sarray(), varray());
@@ -2293,6 +2311,8 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, clear, sarray(), varray());
bind_method(Array, hash, sarray(), varray());
bind_method(Array, assign, sarray("array"), varray());
+ bind_method(Array, get, sarray("index"), varray());
+ bind_method(Array, set, sarray("index", "value"), varray());
bind_method(Array, push_back, sarray("value"), varray());
bind_method(Array, push_front, sarray("value"), varray());
bind_method(Array, append, sarray("value"), varray());
@@ -2337,6 +2357,18 @@ static void _register_variant_builtin_methods_array() {
bind_method(Array, make_read_only, sarray(), varray());
bind_method(Array, is_read_only, sarray(), varray());
+ /* Packed*Array get (see VARCALL_PACKED_GETTER macro) */
+ bind_function(PackedByteArray, get, _VariantCall::func_PackedByteArray_get, sarray("index"), varray());
+ bind_function(PackedColorArray, get, _VariantCall::func_PackedColorArray_get, sarray("index"), varray());
+ bind_function(PackedFloat32Array, get, _VariantCall::func_PackedFloat32Array_get, sarray("index"), varray());
+ bind_function(PackedFloat64Array, get, _VariantCall::func_PackedFloat64Array_get, sarray("index"), varray());
+ bind_function(PackedInt32Array, get, _VariantCall::func_PackedInt32Array_get, sarray("index"), varray());
+ bind_function(PackedInt64Array, get, _VariantCall::func_PackedInt64Array_get, sarray("index"), varray());
+ bind_function(PackedStringArray, get, _VariantCall::func_PackedStringArray_get, sarray("index"), varray());
+ bind_function(PackedVector2Array, get, _VariantCall::func_PackedVector2Array_get, sarray("index"), varray());
+ bind_function(PackedVector3Array, get, _VariantCall::func_PackedVector3Array_get, sarray("index"), varray());
+ bind_function(PackedVector4Array, get, _VariantCall::func_PackedVector4Array_get, sarray("index"), varray());
+
/* Byte Array */
bind_method(PackedByteArray, size, sarray(), varray());
bind_method(PackedByteArray, is_empty, sarray(), varray());
diff --git a/core/variant/variant_construct.cpp b/core/variant/variant_construct.cpp
index fb75a874e7..6c37d5e4b7 100644
--- a/core/variant/variant_construct.cpp
+++ b/core/variant/variant_construct.cpp
@@ -323,36 +323,6 @@ String Variant::get_constructor_argument_name(Variant::Type p_type, int p_constr
return construct_data[p_type][p_constructor].arg_names[p_argument];
}
-void VariantInternal::refcounted_object_assign(Variant *v, const RefCounted *rc) {
- if (!rc || !const_cast<RefCounted *>(rc)->init_ref()) {
- v->_get_obj().obj = nullptr;
- v->_get_obj().id = ObjectID();
- return;
- }
-
- v->_get_obj().obj = const_cast<RefCounted *>(rc);
- v->_get_obj().id = rc->get_instance_id();
-}
-
-void VariantInternal::object_assign(Variant *v, const Object *o) {
- if (o) {
- if (o->is_ref_counted()) {
- RefCounted *ref_counted = const_cast<RefCounted *>(static_cast<const RefCounted *>(o));
- if (!ref_counted->init_ref()) {
- v->_get_obj().obj = nullptr;
- v->_get_obj().id = ObjectID();
- return;
- }
- }
-
- v->_get_obj().obj = const_cast<Object *>(o);
- v->_get_obj().id = o->get_instance_id();
- } else {
- v->_get_obj().obj = nullptr;
- v->_get_obj().id = ObjectID();
- }
-}
-
void Variant::get_constructor_list(Type p_type, List<MethodInfo> *r_list) {
ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX);
diff --git a/core/variant/variant_construct.h b/core/variant/variant_construct.h
index 68210a9451..f625419da7 100644
--- a/core/variant/variant_construct.h
+++ b/core/variant/variant_construct.h
@@ -156,14 +156,14 @@ public:
if (p_args[0]->get_type() == Variant::NIL) {
VariantInternal::clear(&r_ret);
VariantTypeChanger<Object *>::change(&r_ret);
- VariantInternal::object_assign_null(&r_ret);
+ VariantInternal::object_reset_data(&r_ret);
r_error.error = Callable::CallError::CALL_OK;
} else if (p_args[0]->get_type() == Variant::OBJECT) {
- VariantInternal::clear(&r_ret);
VariantTypeChanger<Object *>::change(&r_ret);
VariantInternal::object_assign(&r_ret, p_args[0]);
r_error.error = Callable::CallError::CALL_OK;
} else {
+ VariantInternal::clear(&r_ret);
r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
r_error.argument = 0;
r_error.expected = Variant::OBJECT;
@@ -171,7 +171,6 @@ public:
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
- VariantInternal::clear(r_ret);
VariantTypeChanger<Object *>::change(r_ret);
VariantInternal::object_assign(r_ret, p_args[0]);
}
@@ -203,13 +202,13 @@ public:
VariantInternal::clear(&r_ret);
VariantTypeChanger<Object *>::change(&r_ret);
- VariantInternal::object_assign_null(&r_ret);
+ VariantInternal::object_reset_data(&r_ret);
}
static inline void validated_construct(Variant *r_ret, const Variant **p_args) {
VariantInternal::clear(r_ret);
VariantTypeChanger<Object *>::change(r_ret);
- VariantInternal::object_assign_null(r_ret);
+ VariantInternal::object_reset_data(r_ret);
}
static void ptr_construct(void *base, const void **p_args) {
PtrConstruct<Object *>::construct(nullptr, base);
diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h
index 58a45c0a1f..58652d26e0 100644
--- a/core/variant/variant_internal.h
+++ b/core/variant/variant_internal.h
@@ -220,7 +220,7 @@ public:
// Should be in the same order as Variant::Type for consistency.
// Those primitive and vector types don't need an `init_` method:
// Nil, bool, float, Vector2/i, Rect2/i, Vector3/i, Plane, Quat, RID.
- // Object is a special case, handled via `object_assign_null`.
+ // Object is a special case, handled via `object_reset_data`.
_FORCE_INLINE_ static void init_string(Variant *v) {
memnew_placement(v->_data._mem, String);
v->type = Variant::STRING;
@@ -319,7 +319,7 @@ public:
v->type = Variant::PACKED_VECTOR4_ARRAY;
}
_FORCE_INLINE_ static void init_object(Variant *v) {
- object_assign_null(v);
+ object_reset_data(v);
v->type = Variant::OBJECT;
}
@@ -327,19 +327,28 @@ public:
v->clear();
}
- static void object_assign(Variant *v, const Object *o); // Needs RefCounted, so it's implemented elsewhere.
- static void refcounted_object_assign(Variant *v, const RefCounted *rc);
+ _FORCE_INLINE_ static void object_assign(Variant *v, const Variant *vo) {
+ v->_get_obj().ref(vo->_get_obj());
+ }
+
+ _FORCE_INLINE_ static void object_assign(Variant *v, Object *o) {
+ v->_get_obj().ref_pointer(o);
+ }
- _FORCE_INLINE_ static void object_assign(Variant *v, const Variant *o) {
- object_assign(v, o->_get_obj().obj);
+ _FORCE_INLINE_ static void object_assign(Variant *v, const Object *o) {
+ v->_get_obj().ref_pointer(const_cast<Object *>(o));
+ }
+
+ template <typename T>
+ _FORCE_INLINE_ static void object_assign(Variant *v, const Ref<T> &r) {
+ v->_get_obj().ref(r);
}
- _FORCE_INLINE_ static void object_assign_null(Variant *v) {
- v->_get_obj().obj = nullptr;
- v->_get_obj().id = ObjectID();
+ _FORCE_INLINE_ static void object_reset_data(Variant *v) {
+ v->_get_obj() = Variant::ObjData();
}
- static void update_object_id(Variant *v) {
+ _FORCE_INLINE_ static void update_object_id(Variant *v) {
const Object *o = v->_get_obj().obj;
if (o) {
v->_get_obj().id = o->get_instance_id();