diff options
-rw-r--r-- | .github/workflows/web_builds.yml | 2 | ||||
-rw-r--r-- | core/templates/command_queue_mt.h | 2 | ||||
-rw-r--r-- | core/templates/simple_type.h | 21 | ||||
-rw-r--r-- | core/variant/binder_common.h | 16 | ||||
-rw-r--r-- | core/variant/variant_internal.h | 2 | ||||
-rw-r--r-- | doc/classes/@GlobalScope.xml | 1 | ||||
-rw-r--r-- | editor/editor_properties.cpp | 1 | ||||
-rw-r--r-- | editor/editor_translation.cpp | 170 | ||||
-rw-r--r-- | editor/editor_translation.h | 2 | ||||
-rw-r--r-- | editor/pot_generator.cpp | 7 | ||||
-rw-r--r-- | scene/main/node.cpp | 6 | ||||
-rw-r--r-- | tests/core/math/test_math_funcs.h | 3 |
12 files changed, 178 insertions, 55 deletions
diff --git a/.github/workflows/web_builds.yml b/.github/workflows/web_builds.yml index 47f7e4d458..cfa1571d1f 100644 --- a/.github/workflows/web_builds.yml +++ b/.github/workflows/web_builds.yml @@ -7,7 +7,7 @@ env: # Used for the cache key. Add version suffix to force clean build. GODOT_BASE_BRANCH: master SCONSFLAGS: verbose=yes warnings=extra werror=yes debug_symbols=no - EM_VERSION: 3.1.39 + EM_VERSION: 3.1.59 EM_CACHE_FOLDER: "emsdk-cache" concurrency: diff --git a/core/templates/command_queue_mt.h b/core/templates/command_queue_mt.h index c149861467..bb36f38d54 100644 --- a/core/templates/command_queue_mt.h +++ b/core/templates/command_queue_mt.h @@ -208,7 +208,7 @@ #define ARG(N) p##N #define PARAM(N) P##N p##N #define TYPE_PARAM(N) typename P##N -#define PARAM_DECL(N) typename GetSimpleTypeT<P##N>::type_t p##N +#define PARAM_DECL(N) GetSimpleTypeT<P##N> p##N #define DECL_CMD(N) \ template <typename T, typename M COMMA(N) COMMA_SEP_LIST(TYPE_PARAM, N)> \ diff --git a/core/templates/simple_type.h b/core/templates/simple_type.h index b2ae0110e2..197115ddb9 100644 --- a/core/templates/simple_type.h +++ b/core/templates/simple_type.h @@ -31,26 +31,9 @@ #ifndef SIMPLE_TYPE_H #define SIMPLE_TYPE_H -/* Batch of specializations to obtain the actual simple type */ +#include <type_traits> template <typename T> -struct GetSimpleTypeT { - typedef T type_t; -}; - -template <typename T> -struct GetSimpleTypeT<T &> { - typedef T type_t; -}; - -template <typename T> -struct GetSimpleTypeT<T const> { - typedef T type_t; -}; - -template <typename T> -struct GetSimpleTypeT<T const &> { - typedef T type_t; -}; +using GetSimpleTypeT = typename std::remove_cv_t<std::remove_reference_t<T>>; #endif // SIMPLE_TYPE_H diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 0fe4518b0f..61b90e2a26 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -362,42 +362,42 @@ void call_with_ptr_args_static_method_helper(void (*p_method)(P...), const void template <typename T, typename... P, size_t... Is> void call_with_validated_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, IndexSequence<Is...>) { - (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); + (p_instance->*p_method)((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...); } template <typename T, typename... P, size_t... Is> void call_with_validated_variant_argsc_helper(T *p_instance, void (T::*p_method)(P...) const, const Variant **p_args, IndexSequence<Is...>) { - (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); + (p_instance->*p_method)((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...); } template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { - VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); + VariantInternalAccessor<GetSimpleTypeT<R>>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...)); } template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) const, const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { - VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); + VariantInternalAccessor<GetSimpleTypeT<R>>::set(r_ret, (p_instance->*p_method)((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...)); } template <typename T, typename R, typename... P, size_t... Is> void call_with_validated_variant_args_static_retc_helper(T *p_instance, R (*p_method)(T *, P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { - VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, p_method(p_instance, (VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); + VariantInternalAccessor<GetSimpleTypeT<R>>::set(r_ret, p_method(p_instance, (VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...)); } template <typename T, typename... P, size_t... Is> void call_with_validated_variant_args_static_helper(T *p_instance, void (*p_method)(T *, P...), const Variant **p_args, IndexSequence<Is...>) { - p_method(p_instance, (VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); + p_method(p_instance, (VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...); } template <typename R, typename... P, size_t... Is> void call_with_validated_variant_args_static_method_ret_helper(R (*p_method)(P...), const Variant **p_args, Variant *r_ret, IndexSequence<Is...>) { - VariantInternalAccessor<typename GetSimpleTypeT<R>::type_t>::set(r_ret, p_method((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...)); + VariantInternalAccessor<GetSimpleTypeT<R>>::set(r_ret, p_method((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...)); } template <typename... P, size_t... Is> void call_with_validated_variant_args_static_method_helper(void (*p_method)(P...), const Variant **p_args, IndexSequence<Is...>) { - p_method((VariantInternalAccessor<typename GetSimpleTypeT<P>::type_t>::get(p_args[Is]))...); + p_method((VariantInternalAccessor<GetSimpleTypeT<P>>::get(p_args[Is]))...); } template <typename T, typename... P> diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index ec87081f15..c52ab6917b 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -1546,7 +1546,7 @@ struct VariantTypeChanger { template <typename T> struct VariantTypeAdjust { _FORCE_INLINE_ static void adjust(Variant *r_ret) { - VariantTypeChanger<typename GetSimpleTypeT<T>::type_t>::change(r_ret); + VariantTypeChanger<GetSimpleTypeT<T>>::change(r_ret); } }; diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 26bf515135..4b32acaaa0 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1097,6 +1097,7 @@ remap(75, 0, 100, -1, 1) # Returns 0.5 [/codeblock] For complex use cases where multiple ranges are needed, consider using [Curve] or [Gradient] instead. + [b]Note:[/b] If [code]istart == istop[/code], the return value is undefined (most likely NaN, INF, or -INF). </description> </method> <method name="rid_allocate_id"> diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 9cb5fcc18c..816d3d7778 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -2025,6 +2025,7 @@ void EditorPropertyQuaternion::_custom_value_changed(double val) { spin[1]->set_value_no_signal(temp_q.y); spin[2]->set_value_no_signal(temp_q.z); spin[3]->set_value_no_signal(temp_q.w); + _value_changed(-1, ""); } void EditorPropertyQuaternion::_value_changed(double val, const String &p_name) { diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp index 194d78326d..77154ec344 100644 --- a/editor/editor_translation.cpp +++ b/editor/editor_translation.cpp @@ -156,30 +156,166 @@ void load_extractable_translations(const String &p_locale) { } } -List<StringName> get_extractable_message_list() { +Vector<Vector<String>> get_extractable_message_list() { ExtractableTranslationList *etl = _extractable_translations; - List<StringName> msgids; - while (etl->data) { - if (!strcmp(etl->lang, "source")) { - Vector<uint8_t> data; - data.resize(etl->uncomp_size); - int ret = Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); - ERR_FAIL_COND_V_MSG(ret == -1, msgids, "Compressed file is corrupt."); + Vector<Vector<String>> list; - Ref<FileAccessMemory> fa; - fa.instantiate(); - fa->open_custom(data.ptr(), data.size()); - - Ref<Translation> tr = TranslationLoaderPO::load_translation(fa); + while (etl->data) { + if (strcmp(etl->lang, "source")) { + etl++; + continue; + } - if (tr.is_valid()) { - tr->get_message_list(&msgids); - break; + Vector<uint8_t> data; + data.resize(etl->uncomp_size); + int ret = Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE); + ERR_FAIL_COND_V_MSG(ret == -1, list, "Compressed file is corrupt."); + + Ref<FileAccessMemory> fa; + fa.instantiate(); + fa->open_custom(data.ptr(), data.size()); + + // Taken from TranslationLoaderPO, modified to work specifically with POTs. + { + const String path = fa->get_path(); + + fa->seek(0); + + enum Status { + STATUS_NONE, + STATUS_READING_ID, + STATUS_READING_STRING, + STATUS_READING_CONTEXT, + STATUS_READING_PLURAL, + }; + + Status status = STATUS_NONE; + + String msg_id; + String msg_id_plural; + String msg_context; + + int line = 1; + bool entered_context = false; + bool is_eof = false; + + while (!is_eof) { + String l = fa->get_line().strip_edges(); + is_eof = fa->eof_reached(); + + // If we reached last line and it's not a content line, break, otherwise let processing that last loop. + if (is_eof && l.is_empty()) { + if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT || status == STATUS_READING_PLURAL) { + ERR_FAIL_V_MSG(Vector<Vector<String>>(), "Unexpected EOF while reading POT file at: " + path + ":" + itos(line)); + } else { + break; + } + } + + if (l.begins_with("msgctxt")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_STRING && status != STATUS_READING_PLURAL, Vector<Vector<String>>(), + "Unexpected 'msgctxt', was expecting 'msgid_plural' or 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line)); + + // In POT files, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read + // and set "entered_context" to true to prevent adding twice. + if (!msg_id.is_empty()) { + Vector<String> msgs; + msgs.push_back(msg_id); + msgs.push_back(msg_context); + msgs.push_back(msg_id_plural); + list.push_back(msgs); + } + msg_context = ""; + l = l.substr(7, l.length()).strip_edges(); + status = STATUS_READING_CONTEXT; + entered_context = true; + } + + if (l.begins_with("msgid_plural")) { + if (status != STATUS_READING_ID) { + ERR_FAIL_V_MSG(Vector<Vector<String>>(), "Unexpected 'msgid_plural', was expecting 'msgid' before 'msgid_plural' while parsing: " + path + ":" + itos(line)); + } + l = l.substr(12, l.length()).strip_edges(); + status = STATUS_READING_PLURAL; + } else if (l.begins_with("msgid")) { + ERR_FAIL_COND_V_MSG(status == STATUS_READING_ID, Vector<Vector<String>>(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line)); + + if (!msg_id.is_empty() && !entered_context) { + Vector<String> msgs; + msgs.push_back(msg_id); + msgs.push_back(msg_context); + msgs.push_back(msg_id_plural); + list.push_back(msgs); + } + + l = l.substr(5, l.length()).strip_edges(); + status = STATUS_READING_ID; + // If we did not encounter msgctxt, we reset context to empty to reset it. + if (!entered_context) { + msg_context = ""; + } + msg_id = ""; + msg_id_plural = ""; + entered_context = false; + } + + if (l.begins_with("msgstr[")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_PLURAL, Vector<Vector<String>>(), + "Unexpected 'msgstr[]', was expecting 'msgid_plural' before 'msgstr[]' while parsing: " + path + ":" + itos(line)); + l = l.substr(9, l.length()).strip_edges(); + } else if (l.begins_with("msgstr")) { + ERR_FAIL_COND_V_MSG(status != STATUS_READING_ID, Vector<Vector<String>>(), + "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line)); + l = l.substr(6, l.length()).strip_edges(); + status = STATUS_READING_STRING; + } + + if (l.is_empty() || l.begins_with("#")) { + line++; + continue; // Nothing to read or comment. + } + + ERR_FAIL_COND_V_MSG(!l.begins_with("\"") || status == STATUS_NONE, Vector<Vector<String>>(), "Invalid line '" + l + "' while parsing: " + path + ":" + itos(line)); + + l = l.substr(1, l.length()); + // Find final quote, ignoring escaped ones (\"). + // The escape_next logic is necessary to properly parse things like \\" + // where the backslash is the one being escaped, not the quote. + int end_pos = -1; + bool escape_next = false; + for (int i = 0; i < l.length(); i++) { + if (l[i] == '\\' && !escape_next) { + escape_next = true; + continue; + } + + if (l[i] == '"' && !escape_next) { + end_pos = i; + break; + } + + escape_next = false; + } + + ERR_FAIL_COND_V_MSG(end_pos == -1, Vector<Vector<String>>(), "Expected '\"' at end of message while parsing: " + path + ":" + itos(line)); + + l = l.substr(0, end_pos); + l = l.c_unescape(); + + if (status == STATUS_READING_ID) { + msg_id += l; + } else if (status == STATUS_READING_CONTEXT) { + msg_context += l; + } else if (status == STATUS_READING_PLURAL) { + msg_id_plural += l; + } + + line++; } } etl++; } - return msgids; + return list; } diff --git a/editor/editor_translation.h b/editor/editor_translation.h index 4785495629..eee9e533c4 100644 --- a/editor/editor_translation.h +++ b/editor/editor_translation.h @@ -40,6 +40,6 @@ void load_editor_translations(const String &p_locale); void load_property_translations(const String &p_locale); void load_doc_translations(const String &p_locale); void load_extractable_translations(const String &p_locale); -List<StringName> get_extractable_message_list(); +Vector<Vector<String>> get_extractable_message_list(); #endif // EDITOR_TRANSLATION_H diff --git a/editor/pot_generator.cpp b/editor/pot_generator.cpp index 8323ae944b..76b6593f1d 100644 --- a/editor/pot_generator.cpp +++ b/editor/pot_generator.cpp @@ -34,7 +34,6 @@ #include "core/error/error_macros.h" #include "editor/editor_translation.h" #include "editor/editor_translation_parser.h" -#include "plugins/packed_scene_translation_parser_plugin.h" POTGenerator *POTGenerator::singleton = nullptr; @@ -66,8 +65,6 @@ void POTGenerator::generate_pot(const String &p_file) { // Clear all_translation_strings of the previous round. all_translation_strings.clear(); - List<StringName> extractable_msgids = get_extractable_message_list(); - // Collect all translatable strings according to files order in "POT Generation" setting. for (int i = 0; i < files.size(); i++) { Vector<String> msgids; @@ -92,8 +89,8 @@ void POTGenerator::generate_pot(const String &p_file) { } if (GLOBAL_GET("internationalization/locale/translation_add_builtin_strings_to_pot")) { - for (const StringName &extractable_msgid : extractable_msgids) { - _add_new_msgid(extractable_msgid, "", "", ""); + for (const Vector<String> &extractable_msgids : get_extractable_message_list()) { + _add_new_msgid(extractable_msgids[0], extractable_msgids[1], extractable_msgids[2], ""); } } diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 827273132c..e54209c1dd 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -2930,8 +2930,10 @@ void Node::_duplicate_properties_node(const Node *p_root, const Node *p_original } } - for (int i = 0; i < p_copy->get_child_count(); i++) { - _duplicate_properties_node(p_root, p_original->get_child(i), p_copy->get_child(i)); + for (int i = 0; i < p_original->get_child_count(); i++) { + Node *copy_child = p_copy->get_child(i); + ERR_FAIL_NULL_MSG(copy_child, "Child node disappeared while duplicating."); + _duplicate_properties_node(p_root, p_original->get_child(i), copy_child); } } diff --git a/tests/core/math/test_math_funcs.h b/tests/core/math/test_math_funcs.h index 0a9d9c97d9..f2eae3a20d 100644 --- a/tests/core/math/test_math_funcs.h +++ b/tests/core/math/test_math_funcs.h @@ -381,6 +381,9 @@ TEST_CASE_TEMPLATE("[Math] remap", T, float, double) { CHECK(Math::remap((T)-100.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)0.0)); CHECK(Math::remap((T)-200.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1000.0)); CHECK(Math::remap((T)-250.0, (T)-100.0, (T)-200.0, (T)0.0, (T)-1000.0) == doctest::Approx((T)-1500.0)); + + // Note: undefined behaviour can happen when `p_istart == p_istop`. We don't bother testing this as it will + // vary between hardware and compilers properly implementing IEEE 754. } TEST_CASE_TEMPLATE("[Math] angle_difference", T, float, double) { |