diff options
author | reduz <reduzio@gmail.com> | 2022-04-28 22:49:10 +0200 |
---|---|---|
committer | reduz <reduzio@gmail.com> | 2022-05-03 17:08:09 +0200 |
commit | 0a57f964a357976e023b638e872397ba94123776 (patch) | |
tree | 73b2d68b63b7634857150b6a31ae281b27cb392f /scene/resources/packed_scene.cpp | |
parent | d5d86cb26e65b89a00b644de6eef510d8ca06797 (diff) | |
download | redot-engine-0a57f964a357976e023b638e872397ba94123776.tar.gz |
Implement missing Node & Resource placeholders
Implemented by request of @neikeq to advance in the GDExtension version of Mono.
* If a Resource type is missing upon load, it will be remembered together with its data (Unless manually overriden).
* If a Node type is missing upon load, it will be also be remembered together with its data (unless deleted).
This feature makes working with GDExtension much easier, as it ensures that missing types no longer cause data loss.
Diffstat (limited to 'scene/resources/packed_scene.cpp')
-rw-r--r-- | scene/resources/packed_scene.cpp | 83 |
1 files changed, 68 insertions, 15 deletions
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index b991cb1abe..f7504974d2 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -33,11 +33,13 @@ #include "core/config/engine.h" #include "core/config/project_settings.h" #include "core/core_string_names.h" +#include "core/io/missing_resource.h" #include "core/io/resource_loader.h" #include "scene/2d/node_2d.h" #include "scene/3d/node_3d.h" #include "scene/gui/control.h" #include "scene/main/instance_placeholder.h" +#include "scene/main/missing_node.h" #include "scene/property_utils.h" #define PACKED_SCENE_VERSION 2 @@ -130,6 +132,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } Node *node = nullptr; + MissingNode *missing_node = nullptr; if (i == 0 && base_scene_idx >= 0) { //scene inheritance on root node @@ -184,24 +187,33 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { memdelete(obj); obj = nullptr; } - WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); - if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { - if (Object::cast_to<Control>(ret_nodes[n.parent])) { - obj = memnew(Control); - } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { - obj = memnew(Node2D); + + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled()) { + missing_node = memnew(MissingNode); + missing_node->set_original_class(snames[n.type]); + missing_node->set_recording_properties(true); + node = missing_node; + obj = missing_node; + } else { + WARN_PRINT(vformat("Node %s of type %s cannot be created. A placeholder will be created instead.", snames[n.name], snames[n.type]).ascii().get_data()); + if (n.parent >= 0 && n.parent < nc && ret_nodes[n.parent]) { + if (Object::cast_to<Control>(ret_nodes[n.parent])) { + obj = memnew(Control); + } else if (Object::cast_to<Node2D>(ret_nodes[n.parent])) { + obj = memnew(Node2D); #ifndef _3D_DISABLED - } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { - obj = memnew(Node3D); + } else if (Object::cast_to<Node3D>(ret_nodes[n.parent])) { + obj = memnew(Node3D); #endif // _3D_DISABLED + } } - } - if (!obj) { - obj = memnew(Node); - } + if (!obj) { + obj = memnew(Node); + } - node = Object::cast_to<Node>(obj); + node = Object::cast_to<Node>(obj); + } } } @@ -214,6 +226,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { if (nprop_count) { const NodeData::Property *nprops = &n.properties[0]; + Dictionary missing_resource_properties; + for (int j = 0; j < nprop_count; j++) { bool valid; ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); @@ -270,9 +284,24 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } else if (p_edit_state == GEN_EDIT_STATE_INSTANCE) { value = value.duplicate(true); // Duplicate arrays and dictionaries for the editor } - node->set(snames[nprops[j].name], value, &valid); + + bool set_valid = true; + if (ResourceLoader::is_creating_missing_resources_if_class_unavailable_enabled() && value.get_type() == Variant::OBJECT) { + Ref<MissingResource> mr = value; + if (mr.is_valid()) { + missing_resource_properties[snames[nprops[j].name]] = mr; + set_valid = false; + } + } + + if (set_valid) { + node->set(snames[nprops[j].name], value, &valid); + } } } + if (!missing_resource_properties.is_empty()) { + node->set_meta(META_MISSING_RESOURCES, missing_resource_properties); + } } //name @@ -324,6 +353,10 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (missing_node) { + missing_node->set_recording_properties(false); + } + ret_nodes[i] = node; if (node && gen_node_path_cache && ret_nodes[0]) { @@ -485,6 +518,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map p_node->get_property_list(&plist); Array pinned_props = _sanitize_node_pinned_properties(p_node); + Dictionary missing_resource_properties = p_node->get_meta(META_MISSING_RESOURCES, Dictionary()); for (const PropertyInfo &E : plist) { if (!(E.usage & PROPERTY_USAGE_STORAGE)) { @@ -493,6 +527,10 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map Variant forced_value; + if (E.name == META_PROPERTY_MISSING_RESOURCES) { + continue; //ignore this property when packing + } + // If instance or inheriting, not saving if property requested so, or it's meta if (states_stack.size()) { if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { @@ -511,6 +549,14 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map StringName name = E.name; Variant value = forced_value.get_type() == Variant::NIL ? p_node->get(name) : forced_value; + if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) { + // Was this missing resource overriden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[E.name]; + } + } + if (!pinned_props.has(name) && forced_value.get_type() == Variant::NIL) { bool is_valid_default = false; Variant default_value = PropertyUtils::get_property_default_value(p_node, name, &is_valid_default, &states_stack, true); @@ -566,11 +612,18 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map nd.owner = -1; } + MissingNode *missing_node = Object::cast_to<MissingNode>(p_node); + // Save the right type. If this node was created by an instance // then flag that the node should not be created but reused if (states_stack.is_empty() && !is_editable_instance) { //this node is not part of an instancing process, so save the type - nd.type = _nm_get_string(p_node->get_class(), name_map); + if (missing_node != nullptr) { + // Its a missing node (type non existant on load). + nd.type = _nm_get_string(missing_node->get_original_class(), name_map); + } else { + nd.type = _nm_get_string(p_node->get_class(), name_map); + } } else { // this node is part of an instantiated process, so do not save the type. // instead, save that it was instantiated |