From 1fae837ec916859f318dd8b45389a68476174ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=A3=8E=E9=9D=92=E5=B1=B1?= Date: Thu, 29 Jan 2026 00:18:19 +0800 Subject: [PATCH] Fix `resource_local_to_scene` may fail during instantiating scene - Array: improved, - Dictionary: slightly improved. --- scene/resources/packed_scene.cpp | 89 +++++++++++++++++++++++++++++--- scene/resources/packed_scene.h | 2 + 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index c25994a3d3..1ed2111e0d 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -37,6 +37,7 @@ #include "core/object/script_language.h" #include "core/templates/local_vector.h" #include "core/variant/callable_bind.h" +#include "core/variant/container_type_validate.h" #include "scene/2d/node_2d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" @@ -78,6 +79,85 @@ static Array _sanitize_node_pinned_properties(Node *p_node) { return pinned; } +Variant SceneState::_duplicate_recursive(const Variant &p_variant, HashMap, Ref>> &p_remap_cache, const Variant &p_fallback, Node *p_for_scene) { + switch (p_variant.get_type()) { + case Variant::OBJECT: { + // The local-to-scene subresource instance is preserved, thus maintaining the previous sharing relationship. + // This is mainly used when the sub-scene root is reset in the main scene. + Ref sub_res_of_from = p_variant; + if (sub_res_of_from.is_valid() && sub_res_of_from->is_local_to_scene()) { + return get_remap_resource(sub_res_of_from, p_remap_cache, p_fallback, p_for_scene); + } + } break; + case Variant::ARRAY: { + const Array &src = p_variant; + const Array &fallback = p_fallback; + + bool has_fallback = true; + Array dst; + if (src.is_typed()) { + const ContainerType &scr_type = src.get_element_type(); + dst.set_typed(scr_type); + has_fallback = false; + if (fallback.is_typed()) { + const ContainerType &fallback_type = fallback.get_element_type(); + has_fallback = + scr_type.builtin_type == fallback_type.builtin_type && + scr_type.class_name == fallback_type.class_name && + scr_type.script == fallback_type.script; + } + } + dst.resize(src.size()); + for (int i = 0; i < src.size(); i++) { + Variant ele_fallback; + if (has_fallback && fallback.size() > i) { + ele_fallback = fallback[i]; + } + dst[i] = _duplicate_recursive(src[i], p_remap_cache, ele_fallback, p_for_scene); + } + return dst; + } break; + case Variant::DICTIONARY: { + const Dictionary &src = p_variant; + const Dictionary &fallback = p_fallback; + + bool has_fallback = true; + Dictionary dst; + if (src.is_typed()) { + dst.set_typed(src.get_typed_key_builtin(), src.get_typed_key_class_name(), src.get_typed_key_script(), src.get_typed_value_builtin(), src.get_typed_value_class_name(), src.get_typed_value_script()); + has_fallback = false; + if (fallback.is_typed()) { + has_fallback = + src.get_typed_key_builtin() == fallback.get_typed_key_builtin() && + src.get_typed_key_class_name() == fallback.get_typed_key_class_name() && + src.get_typed_key_script() == fallback.get_typed_key_script() && + src.get_typed_value_builtin() == fallback.get_typed_value_builtin() && + src.get_typed_value_class_name() == fallback.get_typed_value_class_name() && + src.get_typed_value_script() == fallback.get_typed_value_script(); + } + } + + for (const KeyValue &KV : src) { + const Variant &k = KV.key; + const Variant &v = KV.value; + Variant val_fallback; + // FIXME: as both `src` and `fallback` are remapped values, so if the local-to-scene + // resource is used as the key, it is difficult to find its fallback value. + if (has_fallback && fallback.has(k)) { + val_fallback = fallback[k]; + } + dst.set( + _duplicate_recursive(k, p_remap_cache, Variant(), p_for_scene), + _duplicate_recursive(v, p_remap_cache, val_fallback, p_for_scene)); + } + return dst; + } break; + default: { + } + } + return p_variant; +} + Ref SceneState::get_remap_resource(const Ref &p_resource, HashMap, Ref>> &remap_cache, const Ref &p_fallback, Node *p_for_scene) { ERR_FAIL_COND_V(p_resource.is_null(), Ref()); @@ -112,14 +192,7 @@ Ref SceneState::get_remap_resource(const Ref &p_resource, Ha continue; // Do not change path. } - Variant value = p_resource->get(E.name); - - // The local-to-scene subresource instance is preserved, thus maintaining the previous sharing relationship. - // This is mainly used when the sub-scene root is reset in the main scene. - Ref sub_res_of_from = value; - if (sub_res_of_from.is_valid() && sub_res_of_from->is_local_to_scene()) { - value = get_remap_resource(sub_res_of_from, remap_cache, p_fallback->get(E.name), p_fallback->get_local_scene()); - } + Variant value = _duplicate_recursive(p_resource->get(E.name), remap_cache, p_fallback->get(E.name), p_for_scene); p_fallback->set(E.name, value); } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index 74ebbd8b61..6dd3ec77de 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -105,6 +105,8 @@ class SceneState : public RefCounted { Node *_recover_node_path_index(Node *p_base, int p_idx) const; + static Variant _duplicate_recursive(const Variant &p_variant, HashMap, Ref>> &p_remap_cache, const Variant &p_fallback, Node *p_for_scene); + #ifdef TOOLS_ENABLED public: typedef void (*InstantiationWarningNotify)(const String &p_warning);