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/extension/gdextension_interface.cpp9
-rw-r--r--core/extension/gdextension_interface.h15
-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/json.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/ref_counted.h102
-rw-r--r--core/string/translation_domain.cpp296
-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/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.h31
-rw-r--r--core/variant/variant_call.cpp1
-rw-r--r--core/variant/variant_construct.cpp30
-rw-r--r--core/variant/variant_construct.h9
-rw-r--r--core/variant/variant_internal.h29
-rw-r--r--core/variant/variant_parser.cpp2
31 files changed, 914 insertions, 548 deletions
diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp
index 6e897e4d2d..d9cf65b63c 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;
@@ -1185,22 +1184,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) {
@@ -1415,7 +1408,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/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/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 726653c92e..5b9377fe59 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"
@@ -41,7 +42,7 @@ InputMap *InputMap::singleton = nullptr;
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);
@@ -304,7 +305,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 ad4a8ae1a5..45798490f7 100644
--- a/core/input/input_map.h
+++ b/core/input/input_map.h
@@ -64,12 +64,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/json.cpp b/core/io/json.cpp
index 664ff7857b..22219fca29 100644
--- a/core/io/json.cpp
+++ b/core/io/json.cpp
@@ -121,7 +121,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
d.get_key_list(&keys);
if (p_sort_keys) {
- keys.sort();
+ keys.sort_custom<StringLikeVariantOrder>();
}
bool first_key = true;
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/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/string/translation_domain.cpp b/core/string/translation_domain.cpp
index b44eb40366..53b9ce8379 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,104 @@ 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 {
+ if (p_message.is_empty()) {
+ return p_message;
+ }
+
+ 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 +421,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/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..3b1924e8ea 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 */
@@ -836,6 +854,19 @@ struct StringLikeVariantComparator {
static bool compare(const Variant &p_lhs, const Variant &p_rhs);
};
+struct StringLikeVariantOrder {
+ static _ALWAYS_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) {
+ if (p_lhs.is_string() && p_rhs.is_string()) {
+ return p_lhs.operator String() < p_rhs.operator String();
+ }
+ return p_lhs < p_rhs;
+ }
+
+ _ALWAYS_INLINE_ bool operator()(const Variant &p_lhs, const Variant &p_rhs) const {
+ return compare(p_lhs, p_rhs);
+ }
+};
+
Variant::ObjData &Variant::_get_obj() {
return *reinterpret_cast<ObjData *>(&_data._mem[0]);
}
diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp
index ab33c9db55..29e11462c9 100644
--- a/core/variant/variant_call.cpp
+++ b/core/variant/variant_call.cpp
@@ -2272,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());
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();
diff --git a/core/variant/variant_parser.cpp b/core/variant/variant_parser.cpp
index f5f96456d3..f05b9cd83a 100644
--- a/core/variant/variant_parser.cpp
+++ b/core/variant/variant_parser.cpp
@@ -2245,7 +2245,7 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
} else {
List<Variant> keys;
dict.get_key_list(&keys);
- keys.sort();
+ keys.sort_custom<StringLikeVariantOrder>();
if (keys.is_empty()) {
// Avoid unnecessary line break.