From 07b011aec7434180f38ddcb7b2a7871a917b9d22 Mon Sep 17 00:00:00 2001 From: Wyxaldir Date: Tue, 16 Jan 2024 14:41:58 -0500 Subject: [PATCH] Fixes #71243. Resources that have local_to_scene enabled now properly work inside arrays and dictionaries. --- scene/resources/packed_scene.cpp | 108 +++++++++++++++++++++++-------- scene/resources/packed_scene.h | 4 ++ 2 files changed, 86 insertions(+), 26 deletions(-) diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index de81b9d78522..905479fa96d9 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -327,35 +327,16 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { //handle resources that are local to scene by duplicating them if needed Ref res = value; if (res.is_valid()) { - if (res->is_local_to_scene()) { - if (n.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene. - value = get_remap_resource(res, resources_local_to_sub_scene, node->get(snames[nprops[j].name]), node); - } else { - HashMap, Ref>::Iterator E = resources_local_to_scene.find(res); - Node *base = i == 0 ? node : ret_nodes[0]; - if (E) { - value = E->value; - } else { - if (p_edit_state == GEN_EDIT_STATE_MAIN) { - //for the main scene, use the resource as is - res->configure_for_local_scene(base, resources_local_to_scene); - resources_local_to_scene[res] = res; - } else { - //for instances, a copy must be made - Ref local_dupe = res->duplicate_for_local_scene(base, resources_local_to_scene); - resources_local_to_scene[res] = local_dupe; - value = local_dupe; - } - } - } - //must make a copy, because this res is local to scene - } + value = make_local_resource(value, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state); } } if (value.get_type() == Variant::ARRAY) { Array set_array = value; + value = setup_resources_in_array(set_array, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state); + bool is_get_valid = false; Variant get_value = node->get(snames[nprops[j].name], &is_get_valid); + if (is_get_valid && get_value.get_type() == Variant::ARRAY) { Array get_array = get_value; if (!set_array.is_same_typed(get_array)) { @@ -363,8 +344,26 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } } - if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) { - value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor + if (value.get_type() == Variant::DICTIONARY) { + Dictionary dictionary = value; + const Array keys = dictionary.keys(); + const Array values = dictionary.values(); + + if (has_local_resource(values) || has_local_resource(keys)) { + Array duplicated_keys = keys.duplicate(true); + Array duplicated_values = values.duplicate(true); + + duplicated_keys = setup_resources_in_array(duplicated_keys, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state); + duplicated_values = setup_resources_in_array(duplicated_values, n, resources_local_to_sub_scene, node, snames[nprops[j].name], resources_local_to_scene, i, ret_nodes, p_edit_state); + + dictionary.clear(); + + for (int dictionary_index = 0; dictionary_index < keys.size(); dictionary_index++) { + dictionary[duplicated_keys[dictionary_index]] = duplicated_values[dictionary_index]; + } + + value = dictionary; + } } bool set_valid = true; @@ -379,6 +378,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (set_valid) { node->set(snames[nprops[j].name], value, &valid); } + if (p_edit_state == GEN_EDIT_STATE_INSTANCE && value.get_type() != Variant::OBJECT) { + value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor. + } } } if (!missing_resource_properties.is_empty()) { @@ -566,6 +568,50 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { return ret_nodes[0]; } +Variant SceneState::make_local_resource(Variant &p_value, const SceneState::NodeData &p_node_data, HashMap, Ref> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap, Ref> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const { + Ref res = p_value; + if (res.is_null() || !res->is_local_to_scene()) { + return p_value; + } + + if (p_node_data.instance >= 0) { // For the root node of a sub-scene, treat it as part of the sub-scene. + return get_remap_resource(res, p_resources_local_to_sub_scene, p_node->get(p_sname), p_node); + } else { + HashMap, Ref>::Iterator E = p_resources_local_to_scene.find(res); + Node *base = p_i == 0 ? p_node : p_ret_nodes[0]; + if (E) { + return E->value; + } else if (p_edit_state == GEN_EDIT_STATE_MAIN) { // For the main scene, use the resource as is + res->configure_for_local_scene(base, p_resources_local_to_scene); + p_resources_local_to_scene[res] = res; + return res; + } else { // For instances, a copy must be made. + Ref local_dupe = res->duplicate_for_local_scene(base, p_resources_local_to_scene); + p_resources_local_to_scene[res] = local_dupe; + return local_dupe; + } + } +} + +Array SceneState::setup_resources_in_array(Array &p_array_to_scan, const SceneState::NodeData &p_n, HashMap, Ref> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap, Ref> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const { + for (int i = 0; i < p_array_to_scan.size(); i++) { + if (p_array_to_scan[i].get_type() == Variant::OBJECT) { + p_array_to_scan[i] = make_local_resource(p_array_to_scan[i], p_n, p_resources_local_to_sub_scene, p_node, p_sname, p_resources_local_to_scene, p_i, p_ret_nodes, p_edit_state); + } + } + return p_array_to_scan; +} + +bool SceneState::has_local_resource(const Array &p_array) const { + for (int i = 0; i < p_array.size(); i++) { + Ref res = p_array[i]; + if (res.is_valid() && res->is_local_to_scene()) { + return true; + } + } + return false; +} + static int _nm_get_string(const String &p_string, HashMap &name_map) { if (name_map.has(p_string)) { return name_map[p_string]; @@ -730,8 +776,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has if (!pinned_props.has(name)) { bool is_valid_default = false; Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true); + if (is_valid_default && !PropertyUtils::is_property_value_different(value, default_value)) { - continue; + if (value.get_type() == Variant::ARRAY && has_local_resource(value)) { + // Save anyway + } else if (value.get_type() == Variant::DICTIONARY) { + Dictionary dictionary = value; + if (!has_local_resource(dictionary.values()) && !has_local_resource(dictionary.keys())) { + continue; + } + } else { + continue; + } } } diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h index e6cbc3e16ba2..2c8196f5e3b6 100644 --- a/scene/resources/packed_scene.h +++ b/scene/resources/packed_scene.h @@ -157,6 +157,10 @@ class SceneState : public RefCounted { bool can_instantiate() const; Node *instantiate(GenEditState p_edit_state) const; + Array setup_resources_in_array(Array &array_to_scan, const SceneState::NodeData &n, HashMap, Ref> &resources_local_to_sub_scene, Node *node, const StringName sname, HashMap, Ref> &resources_local_to_scene, int i, Node **ret_nodes, SceneState::GenEditState p_edit_state) const; + Variant make_local_resource(Variant &value, const SceneState::NodeData &p_node_data, HashMap, Ref> &p_resources_local_to_sub_scene, Node *p_node, const StringName p_sname, HashMap, Ref> &p_resources_local_to_scene, int p_i, Node **p_ret_nodes, SceneState::GenEditState p_edit_state) const; + bool has_local_resource(const Array &p_array) const; + Ref get_base_scene_state() const; void update_instance_resource(String p_path, Ref p_packed_scene);