diff options
Diffstat (limited to 'core')
68 files changed, 1185 insertions, 693 deletions
diff --git a/core/SCsub b/core/SCsub index c8267ae960..8bda230b87 100644 --- a/core/SCsub +++ b/core/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/config/SCsub b/core/config/SCsub index bf70285490..1417a258c1 100644 --- a/core/config/SCsub +++ b/core/config/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/config/engine.cpp b/core/config/engine.cpp index 9cdc21fe8e..d77c913314 100644 --- a/core/config/engine.cpp +++ b/core/config/engine.cpp @@ -267,6 +267,14 @@ bool Engine::is_extra_gpu_memory_tracking_enabled() const { return extra_gpu_memory_tracking; } +void Engine::set_print_to_stdout(bool p_enabled) { + CoreGlobals::print_line_enabled = p_enabled; +} + +bool Engine::is_printing_to_stdout() const { + return CoreGlobals::print_line_enabled; +} + void Engine::set_print_error_messages(bool p_enabled) { CoreGlobals::print_error_enabled = p_enabled; } diff --git a/core/config/engine.h b/core/config/engine.h index f858eba328..a0b1ffa981 100644 --- a/core/config/engine.h +++ b/core/config/engine.h @@ -128,6 +128,9 @@ public: void set_time_scale(double p_scale); double get_time_scale() const; + void set_print_to_stdout(bool p_enabled); + bool is_printing_to_stdout() const; + void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; void print_header(const String &p_string) const; diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 7951ee9edd..562bde978e 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1398,7 +1398,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_bind.cpp b/core/core_bind.cpp index 6a2da8aafb..891e3a28c9 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1853,6 +1853,14 @@ String Engine::get_write_movie_path() const { return ::Engine::get_singleton()->get_write_movie_path(); } +void Engine::set_print_to_stdout(bool p_enabled) { + ::Engine::get_singleton()->set_print_to_stdout(p_enabled); +} + +bool Engine::is_printing_to_stdout() const { + return ::Engine::get_singleton()->is_printing_to_stdout(); +} + void Engine::set_print_error_messages(bool p_enabled) { ::Engine::get_singleton()->set_print_error_messages(p_enabled); } @@ -1921,10 +1929,14 @@ void Engine::_bind_methods() { ClassDB::bind_method(D_METHOD("get_write_movie_path"), &Engine::get_write_movie_path); + ClassDB::bind_method(D_METHOD("set_print_to_stdout", "enabled"), &Engine::set_print_to_stdout); + ClassDB::bind_method(D_METHOD("is_printing_to_stdout"), &Engine::is_printing_to_stdout); + ClassDB::bind_method(D_METHOD("set_print_error_messages", "enabled"), &Engine::set_print_error_messages); ClassDB::bind_method(D_METHOD("is_printing_error_messages"), &Engine::is_printing_error_messages); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_error_messages"), "set_print_error_messages", "is_printing_error_messages"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "print_to_stdout"), "set_print_to_stdout", "is_printing_to_stdout"); ADD_PROPERTY(PropertyInfo(Variant::INT, "physics_ticks_per_second"), "set_physics_ticks_per_second", "get_physics_ticks_per_second"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_physics_steps_per_frame"), "set_max_physics_steps_per_frame", "get_max_physics_steps_per_frame"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_fps"), "set_max_fps", "get_max_fps"); diff --git a/core/core_bind.h b/core/core_bind.h index a9311f6268..ce0bde3c05 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -569,6 +569,9 @@ public: // `set_write_movie_path()` is not exposed to the scripting API as changing it at run-time has no effect. String get_write_movie_path() const; + void set_print_to_stdout(bool p_enabled); + bool is_printing_to_stdout() const; + void set_print_error_messages(bool p_enabled); bool is_printing_error_messages() const; 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/crypto/SCsub b/core/crypto/SCsub index 8cff3cf679..3cea6bfb47 100644 --- a/core/crypto/SCsub +++ b/core/crypto/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/debugger/SCsub b/core/debugger/SCsub index 19a6549225..ab81175894 100644 --- a/core/debugger/SCsub +++ b/core/debugger/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") 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/error/SCsub b/core/error/SCsub index dfd6248a94..08089d31b0 100644 --- a/core/error/SCsub +++ b/core/error/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/extension/SCsub b/core/extension/SCsub index 6ab2d2b0a6..8688ca5b6e 100644 --- a/core/extension/SCsub +++ b/core/extension/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") 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/SCsub b/core/input/SCsub index d8e6f33156..521f7702e4 100644 --- a/core/input/SCsub +++ b/core/input/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/input/input_builders.py b/core/input/input_builders.py index ae848f4e7c..3685e726b4 100644 --- a/core/input/input_builders.py +++ b/core/input/input_builders.py @@ -33,7 +33,7 @@ def make_default_controller_mappings(target, source, env): guid = line_parts[0] if guid in platform_mappings[current_platform]: g.write( - "// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( + "// WARNING: DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format( src_path, current_platform, platform_mappings[current_platform][guid] ) ) 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/SCsub b/core/io/SCsub index 19a6549225..ab81175894 100644 --- a/core/io/SCsub +++ b/core/io/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/io/file_access_pack.cpp b/core/io/file_access_pack.cpp index eec27ce0aa..1340382eaa 100644 --- a/core/io/file_access_pack.cpp +++ b/core/io/file_access_pack.cpp @@ -102,6 +102,22 @@ void PackedData::add_pack_source(PackSource *p_source) { } } +uint8_t *PackedData::get_file_hash(const String &p_path) { + PathMD5 pmd5(p_path.md5_buffer()); + HashMap<PathMD5, PackedFile, PathMD5>::Iterator E = files.find(pmd5); + if (!E || E->value.offset == 0) { + return nullptr; + } + + return E->value.md5; +} + +void PackedData::clear() { + files.clear(); + _free_packed_dirs(root); + root = memnew(PackedDir); +} + PackedData *PackedData::singleton = nullptr; PackedData::PackedData() { diff --git a/core/io/file_access_pack.h b/core/io/file_access_pack.h index 595a36bca4..57b7a5f87f 100644 --- a/core/io/file_access_pack.h +++ b/core/io/file_access_pack.h @@ -111,6 +111,7 @@ private: public: void add_pack_source(PackSource *p_source); void add_path(const String &p_pkg_path, const String &p_path, uint64_t p_ofs, uint64_t p_size, const uint8_t *p_md5, PackSource *p_src, bool p_replace_files, bool p_encrypted = false); // for PackSource + uint8_t *get_file_hash(const String &p_path); void set_disabled(bool p_disabled) { disabled = p_disabled; } _FORCE_INLINE_ bool is_disabled() const { return disabled; } @@ -118,6 +119,8 @@ public: static PackedData *get_singleton() { return singleton; } Error add_pack(const String &p_path, bool p_replace_files, uint64_t p_offset); + void clear(); + _FORCE_INLINE_ Ref<FileAccess> try_open_path(const String &p_path); _FORCE_INLINE_ bool has_path(const String &p_path); diff --git a/core/io/packet_peer.cpp b/core/io/packet_peer.cpp index 0329ace313..614f81c42a 100644 --- a/core/io/packet_peer.cpp +++ b/core/io/packet_peer.cpp @@ -299,7 +299,7 @@ Ref<StreamPeer> PacketPeerStream::get_stream_peer() const { void PacketPeerStream::set_input_buffer_max_size(int p_max_size) { ERR_FAIL_COND_MSG(p_max_size < 0, "Max size of input buffer size cannot be smaller than 0."); - //warning may lose packets + // WARNING: May lose packets. ERR_FAIL_COND_MSG(ring_buffer.data_left(), "Buffer in use, resizing would cause loss of data."); ring_buffer.resize(nearest_shift(next_power_of_2(p_max_size + 4)) - 1); input_buffer.resize(next_power_of_2(p_max_size + 4)); diff --git a/core/io/resource_importer.cpp b/core/io/resource_importer.cpp index a572dd562e..1ae50d2d0d 100644 --- a/core/io/resource_importer.cpp +++ b/core/io/resource_importer.cpp @@ -507,7 +507,7 @@ bool ResourceFormatImporter::are_import_settings_valid(const String &p_path) con for (int i = 0; i < importers.size(); i++) { if (importers[i]->get_importer_name() == pat.importer) { - if (!importers[i]->are_import_settings_valid(p_path)) { //importer thinks this is not valid + if (!importers[i]->are_import_settings_valid(p_path, pat.metadata)) { //importer thinks this is not valid return false; } } diff --git a/core/io/resource_importer.h b/core/io/resource_importer.h index 6ea5d0972a..221f38494b 100644 --- a/core/io/resource_importer.h +++ b/core/io/resource_importer.h @@ -153,7 +153,7 @@ public: virtual void import_threaded_end() {} virtual Error import_group_file(const String &p_group_file, const HashMap<String, HashMap<StringName, Variant>> &p_source_file_options, const HashMap<String, String> &p_base_paths) { return ERR_UNAVAILABLE; } - virtual bool are_import_settings_valid(const String &p_path) const { return true; } + virtual bool are_import_settings_valid(const String &p_path, const Dictionary &p_meta) const { return true; } virtual String get_import_settings_string() const { return String(); } }; diff --git a/core/math/SCsub b/core/math/SCsub index c8fdac207e..6ea3ab6b12 100644 --- a/core/math/SCsub +++ b/core/math/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/math/convex_hull.cpp b/core/math/convex_hull.cpp index 80662c1b07..620a7541e4 100644 --- a/core/math/convex_hull.cpp +++ b/core/math/convex_hull.cpp @@ -204,7 +204,7 @@ public: static Int128 mul(uint64_t a, uint64_t b); Int128 operator-() const { - return Int128((uint64_t) - (int64_t)low, ~high + (low == 0)); + return Int128(uint64_t(-int64_t(low)), ~high + (low == 0)); } Int128 operator+(const Int128 &b) const { diff --git a/core/math/transform_2d.h b/core/math/transform_2d.h index 476577508f..1ee7d3d84f 100644 --- a/core/math/transform_2d.h +++ b/core/math/transform_2d.h @@ -39,16 +39,19 @@ class String; struct [[nodiscard]] Transform2D { - // Warning #1: basis of Transform2D is stored differently from Basis. In terms of columns array, the basis matrix looks like "on paper": + // WARNING: The basis of Transform2D is stored differently from Basis. + // In terms of columns array, the basis matrix looks like "on paper": // M = (columns[0][0] columns[1][0]) // (columns[0][1] columns[1][1]) - // This is such that the columns, which can be interpreted as basis vectors of the coordinate system "painted" on the object, can be accessed as columns[i]. - // Note that this is the opposite of the indices in mathematical texts, meaning: $M_{12}$ in a math book corresponds to columns[1][0] here. + // This is such that the columns, which can be interpreted as basis vectors + // of the coordinate system "painted" on the object, can be accessed as columns[i]. + // NOTE: This is the opposite of the indices in mathematical texts, + // meaning: $M_{12}$ in a math book corresponds to columns[1][0] here. // This requires additional care when working with explicit indices. // See https://en.wikipedia.org/wiki/Row-_and_column-major_order for further reading. - // Warning #2: 2D be aware that unlike 3D code, 2D code uses a left-handed coordinate system: Y-axis points down, - // and angle is measure from +X to +Y in a clockwise-fashion. + // WARNING: Be aware that unlike 3D code, 2D code uses a left-handed coordinate system: + // Y-axis points down, and angle is measure from +X to +Y in a clockwise-fashion. Vector2 columns[3]; diff --git a/core/math/vector4.h b/core/math/vector4.h index 8632f69f57..9197e3587a 100644 --- a/core/math/vector4.h +++ b/core/math/vector4.h @@ -55,16 +55,16 @@ struct [[nodiscard]] Vector4 { real_t z; real_t w; }; - real_t components[4] = { 0, 0, 0, 0 }; + real_t coord[4] = { 0, 0, 0, 0 }; }; _FORCE_INLINE_ real_t &operator[](int p_axis) { DEV_ASSERT((unsigned int)p_axis < 4); - return components[p_axis]; + return coord[p_axis]; } _FORCE_INLINE_ const real_t &operator[](int p_axis) const { DEV_ASSERT((unsigned int)p_axis < 4); - return components[p_axis]; + return coord[p_axis]; } Vector4::Axis min_axis_index() const; diff --git a/core/object/SCsub b/core/object/SCsub index 7c00bb719e..3d0d2c14dd 100644 --- a/core/object/SCsub +++ b/core/object/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/object/callable_method_pointer.h b/core/object/callable_method_pointer.h index 1b29e1778a..86c66593bd 100644 --- a/core/object/callable_method_pointer.h +++ b/core/object/callable_method_pointer.h @@ -37,6 +37,8 @@ #include "core/variant/binder_common.h" #include "core/variant/callable.h" +#include <type_traits> + class CallableCustomMethodPointerBase : public CallableCustom { uint32_t *comp_ptr = nullptr; uint32_t comp_size; @@ -77,12 +79,13 @@ public: virtual uint32_t hash() const; }; -template <typename T, typename... P> +template <typename T, typename R, typename... P> class CallableCustomMethodPointer : public CallableCustomMethodPointerBase { struct Data { T *instance; uint64_t object_id; - void (T::*method)(P...); + R(T::*method) + (P...); } data; public: @@ -100,10 +103,14 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) { + CallableCustomMethodPointer(T *p_instance, R (T::*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; data.object_id = p_instance->get_instance_id(); @@ -118,7 +125,7 @@ Callable create_custom_callable_function_pointer(T *p_instance, const char *p_func_text, #endif void (T::*p_method)(P...)) { - typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointer<T, void, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -126,51 +133,13 @@ Callable create_custom_callable_function_pointer(T *p_instance, return Callable(ccmp); } -// VERSION WITH RETURN - -template <typename T, typename R, typename... P> -class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase { - struct Data { - T *instance; - uint64_t object_id; - R(T::*method) - (P...); - } data; - -public: - virtual ObjectID get_object() const { - if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) { - return ObjectID(); - } - return data.instance->get_instance_id(); - } - - virtual int get_argument_count(bool &r_is_valid) const { - r_is_valid = true; - return sizeof...(P); - } - - virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { - ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); - } - - CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) { - memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. - data.instance = p_instance; - data.object_id = p_instance->get_instance_id(); - data.method = p_method; - _setup((uint32_t *)&data, sizeof(Data)); - } -}; - template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (T::*p_method)(P...)) { - typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointer<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -178,10 +147,10 @@ Callable create_custom_callable_function_pointer(T *p_instance, return Callable(ccmp); } -// CONST VERSION WITH RETURN +// CONST VERSION template <typename T, typename R, typename... P> -class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase { +class CallableCustomMethodPointerC : public CallableCustomMethodPointerBase { struct Data { T *instance; uint64_t object_id; @@ -204,10 +173,14 @@ public: virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { ERR_FAIL_NULL_MSG(ObjectDB::get_instance(ObjectID(data.object_id)), "Invalid Object id '" + uitos(data.object_id) + "', can't call method."); - call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_argsc(data.instance, data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) { + CallableCustomMethodPointerC(T *p_instance, R (T::*p_method)(P...) const) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.instance = p_instance; data.object_id = p_instance->get_instance_id(); @@ -216,13 +189,27 @@ public: } }; +template <typename T, typename... P> +Callable create_custom_callable_function_pointer(T *p_instance, +#ifdef DEBUG_METHODS_ENABLED + const char *p_func_text, +#endif + void (T::*p_method)(P...) const) { + typedef CallableCustomMethodPointerC<T, void, P...> CCMP; // Messes with memnew otherwise. + CCMP *ccmp = memnew(CCMP(p_instance, p_method)); +#ifdef DEBUG_METHODS_ENABLED + ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. +#endif + return Callable(ccmp); +} + template <typename T, typename R, typename... P> Callable create_custom_callable_function_pointer(T *p_instance, #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (T::*p_method)(P...) const) { - typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomMethodPointerC<T, R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_instance, p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -238,10 +225,11 @@ Callable create_custom_callable_function_pointer(T *p_instance, // STATIC VERSIONS -template <typename... P> +template <typename R, typename... P> class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { struct Data { - void (*method)(P...); + R(*method) + (P...); } data; public: @@ -259,24 +247,27 @@ public: } virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { - call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); - r_return_value = Variant(); + if constexpr (std::is_same<R, void>::value) { + call_with_variant_args_static(data.method, p_arguments, p_argcount, r_call_error); + } else { + call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); + } } - CallableCustomStaticMethodPointer(void (*p_method)(P...)) { + CallableCustomStaticMethodPointer(R (*p_method)(P...)) { memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. data.method = p_method; _setup((uint32_t *)&data, sizeof(Data)); } }; -template <typename T, typename... P> +template <typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif void (*p_method)(P...)) { - typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomStaticMethodPointer<void, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. @@ -285,44 +276,12 @@ Callable create_custom_callable_static_function_pointer( } template <typename R, typename... P> -class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { - struct Data { - R(*method) - (P...); - } data; - -public: - virtual bool is_valid() const override { - return true; - } - - virtual ObjectID get_object() const override { - return ObjectID(); - } - - virtual int get_argument_count(bool &r_is_valid) const override { - r_is_valid = true; - return sizeof...(P); - } - - virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override { - call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error); - } - - CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) { - memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes. - data.method = p_method; - _setup((uint32_t *)&data, sizeof(Data)); - } -}; - -template <typename R, typename... P> Callable create_custom_callable_static_function_pointer( #ifdef DEBUG_METHODS_ENABLED const char *p_func_text, #endif R (*p_method)(P...)) { - typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise. + typedef CallableCustomStaticMethodPointer<R, P...> CCMP; // Messes with memnew otherwise. CCMP *ccmp = memnew(CCMP(p_method)); #ifdef DEBUG_METHODS_ENABLED ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand. 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/object/undo_redo.cpp b/core/object/undo_redo.cpp index 4d67cd930e..03537dbeb1 100644 --- a/core/object/undo_redo.cpp +++ b/core/object/undo_redo.cpp @@ -48,7 +48,7 @@ void UndoRedo::Operation::delete_reference() { } } -void UndoRedo::_discard_redo() { +void UndoRedo::discard_redo() { if (current_action == actions.size() - 1) { return; } @@ -89,7 +89,7 @@ void UndoRedo::create_action(const String &p_name, MergeMode p_mode, bool p_back uint64_t ticks = OS::get_singleton()->get_ticks_msec(); if (action_level == 0) { - _discard_redo(); + discard_redo(); // Check if the merge operation is valid if (p_mode != MERGE_DISABLE && actions.size() && actions[actions.size() - 1].name == p_name && actions[actions.size() - 1].backward_undo_ops == p_backward_undo_ops && actions[actions.size() - 1].last_tick + 800 > ticks) { @@ -288,7 +288,7 @@ void UndoRedo::end_force_keep_in_merge_ends() { } void UndoRedo::_pop_history_tail() { - _discard_redo(); + discard_redo(); if (!actions.size()) { return; @@ -455,7 +455,7 @@ String UndoRedo::get_action_name(int p_id) { void UndoRedo::clear_history(bool p_increase_version) { ERR_FAIL_COND(action_level > 0); - _discard_redo(); + discard_redo(); while (actions.size()) { _pop_history_tail(); diff --git a/core/object/undo_redo.h b/core/object/undo_redo.h index 19d178635c..ded962670c 100644 --- a/core/object/undo_redo.h +++ b/core/object/undo_redo.h @@ -129,6 +129,7 @@ public: int get_current_action(); String get_action_name(int p_id); void clear_history(bool p_increase_version = true); + void discard_redo(); bool has_undo() const; bool has_redo() const; diff --git a/core/os/SCsub b/core/os/SCsub index 19a6549225..ab81175894 100644 --- a/core/os/SCsub +++ b/core/os/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/string/SCsub b/core/string/SCsub index 3217166f18..b06e32eb88 100644 --- a/core/string/SCsub +++ b/core/string/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") 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 2683addd4b..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 @@ -4626,7 +4643,7 @@ bool String::is_absolute_path() const { String String::validate_ascii_identifier() const { if (is_empty()) { - return "_"; // Empty string is not a valid identifier; + return "_"; // Empty string is not a valid identifier. } String result; @@ -4647,6 +4664,29 @@ String String::validate_ascii_identifier() const { return result; } +String String::validate_unicode_identifier() const { + if (is_empty()) { + return "_"; // Empty string is not a valid identifier. + } + + String result; + if (is_unicode_identifier_start(operator[](0))) { + result = *this; + } else { + result = "_" + *this; + } + + int len = result.length(); + char32_t *buffer = result.ptrw(); + for (int i = 0; i < len; i++) { + if (!is_unicode_identifier_continue(buffer[i])) { + buffer[i] = '_'; + } + } + + return result; +} + bool String::is_valid_ascii_identifier() const { int len = length(); diff --git a/core/string/ustring.h b/core/string/ustring.h index 11f15031f9..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; @@ -460,6 +460,7 @@ public: static String get_invalid_node_name_characters(bool p_allow_internal = false); String validate_node_name() const; String validate_ascii_identifier() const; + String validate_unicode_identifier() const; String validate_filename() const; bool is_valid_ascii_identifier() const; diff --git a/core/templates/SCsub b/core/templates/SCsub index 8c4c843a33..7f806d5609 100644 --- a/core/templates/SCsub +++ b/core/templates/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") 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/SCsub b/core/variant/SCsub index 7f4c8b7788..8264503a22 100644 --- a/core/variant/SCsub +++ b/core/variant/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +from misc.utility.scons_hints import * Import("env") diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index fa49767d46..0aa49f6d68 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -466,7 +466,7 @@ void call_with_variant_argsc(T *p_instance, void (T::*p_method)(P...) const, con return; } #endif - call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); + call_with_variant_argsc_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); } template <typename T, typename... P> @@ -830,7 +830,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar } template <typename... P> -void call_with_variant_args_static_ret(void (*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) { +void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) { #ifdef DEBUG_METHODS_ENABLED if ((size_t)p_argcount > sizeof...(P)) { r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; 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(); |