Fix recursive resource local to scene

Any resource that contains other local to scene resources inside of
arrays or dictionaries will now be duplicated and configured.

The case where a scene's node has an exported array/dictionary
property containing local to scene resources is NOT handled here.
This commit is contained in:
RedMser 2023-01-17 21:14:19 +01:00
parent 12ee58d8bc
commit 608b5d2e07
2 changed files with 98 additions and 27 deletions

View file

@ -204,7 +204,58 @@ void Resource::reload_from_file() {
copy_from(s); copy_from(s);
} }
Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { void Resource::_dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
switch (r_variant.get_type()) {
case Variant::ARRAY: {
Array a = r_variant;
for (int i = 0; i < a.size(); i++) {
_dupe_sub_resources(a[i], p_for_scene, p_remap_cache);
}
} break;
case Variant::DICTIONARY: {
Dictionary d = r_variant;
List<Variant> keys;
d.get_key_list(&keys);
for (Variant &k : keys) {
if (k.get_type() == Variant::OBJECT) {
// Replace in dictionary key.
Ref<Resource> sr = k;
if (sr.is_valid() && sr->is_local_to_scene()) {
if (p_remap_cache.has(sr)) {
d[p_remap_cache[sr]] = d[k];
d.erase(k);
} else {
Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
d[dupe] = d[k];
d.erase(k);
p_remap_cache[sr] = dupe;
}
}
} else {
_dupe_sub_resources(k, p_for_scene, p_remap_cache);
}
_dupe_sub_resources(d[k], p_for_scene, p_remap_cache);
}
} break;
case Variant::OBJECT: {
Ref<Resource> sr = r_variant;
if (sr.is_valid() && sr->is_local_to_scene()) {
if (p_remap_cache.has(sr)) {
r_variant = p_remap_cache[sr];
} else {
Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, p_remap_cache);
r_variant = dupe;
p_remap_cache[sr] = dupe;
}
}
} break;
default: {
}
}
}
Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
List<PropertyInfo> plist; List<PropertyInfo> plist;
get_property_list(&plist); get_property_list(&plist);
@ -217,21 +268,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
if (!(E.usage & PROPERTY_USAGE_STORAGE)) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) {
continue; continue;
} }
Variant p = get(E.name); Variant p = get(E.name).duplicate(true);
if (p.get_type() == Variant::OBJECT) {
Ref<Resource> sr = p; _dupe_sub_resources(p, p_for_scene, p_remap_cache);
if (sr.is_valid()) {
if (sr->is_local_to_scene()) {
if (remap_cache.has(sr)) {
p = remap_cache[sr];
} else {
Ref<Resource> dupe = sr->duplicate_for_local_scene(p_for_scene, remap_cache);
p = dupe;
remap_cache[sr] = dupe;
}
}
}
}
r->set(E.name, p); r->set(E.name, p);
} }
@ -239,7 +278,35 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref
return r; return r;
} }
void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache) { void Resource::_find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found) {
switch (p_variant.get_type()) {
case Variant::ARRAY: {
Array a = p_variant;
for (int i = 0; i < a.size(); i++) {
_find_sub_resources(a[i], p_resources_found);
}
} break;
case Variant::DICTIONARY: {
Dictionary d = p_variant;
List<Variant> keys;
d.get_key_list(&keys);
for (const Variant &k : keys) {
_find_sub_resources(k, p_resources_found);
_find_sub_resources(d[k], p_resources_found);
}
} break;
case Variant::OBJECT: {
Ref<Resource> r = p_variant;
if (r.is_valid()) {
p_resources_found.insert(r);
}
} break;
default: {
}
}
}
void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache) {
List<PropertyInfo> plist; List<PropertyInfo> plist;
get_property_list(&plist); get_property_list(&plist);
@ -251,14 +318,15 @@ void Resource::configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource
continue; continue;
} }
Variant p = get(E.name); Variant p = get(E.name);
if (p.get_type() == Variant::OBJECT) {
Ref<Resource> sr = p; HashSet<Ref<Resource>> sub_resources;
if (sr.is_valid()) { _find_sub_resources(p, sub_resources);
for (Ref<Resource> sr : sub_resources) {
if (sr->is_local_to_scene()) { if (sr->is_local_to_scene()) {
if (!remap_cache.has(sr)) { if (!p_remap_cache.has(sr)) {
sr->configure_for_local_scene(p_for_scene, remap_cache); sr->configure_for_local_scene(p_for_scene, p_remap_cache);
remap_cache[sr] = sr; p_remap_cache[sr] = sr;
}
} }
} }
} }

View file

@ -74,6 +74,9 @@ private:
SelfList<Resource> remapped_list; SelfList<Resource> remapped_list;
void _dupe_sub_resources(Variant &r_variant, Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
void _find_sub_resources(const Variant &p_variant, HashSet<Ref<Resource>> &p_resources_found);
protected: protected:
virtual void _resource_path_changed(); virtual void _resource_path_changed();
static void _bind_methods(); static void _bind_methods();
@ -111,8 +114,8 @@ public:
String get_scene_unique_id() const; String get_scene_unique_id() const;
virtual Ref<Resource> duplicate(bool p_subresources = false) const; virtual Ref<Resource> duplicate(bool p_subresources = false) const;
Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache); Ref<Resource> duplicate_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &remap_cache); void configure_for_local_scene(Node *p_for_scene, HashMap<Ref<Resource>, Ref<Resource>> &p_remap_cache);
void set_local_to_scene(bool p_enable); void set_local_to_scene(bool p_enable);
bool is_local_to_scene() const; bool is_local_to_scene() const;