diff options
Diffstat (limited to 'scene/resources/packed_scene.cpp')
-rw-r--r-- | scene/resources/packed_scene.cpp | 484 |
1 files changed, 286 insertions, 198 deletions
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index 60cda637ca..0e1b18d584 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,18 +33,43 @@ #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 "core/templates/local_vector.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 - +#define META_POINTER_PROPERTY_BASE "metadata/_editor_prop_ptr_" bool SceneState::can_instantiate() const { return nodes.size() > 0; } +static Array _sanitize_node_pinned_properties(Node *p_node) { + Array pinned = p_node->get_meta("_edit_pinned_properties_", Array()); + if (pinned.is_empty()) { + return Array(); + } + HashSet<StringName> storable_properties; + p_node->get_storable_properties(storable_properties); + int i = 0; + do { + if (storable_properties.has(pinned[i])) { + i++; + } else { + pinned.remove_at(i); + } + } while (i < pinned.size()); + if (pinned.is_empty()) { + p_node->remove_meta("_edit_pinned_properties_"); + } + return pinned; +} + Node *SceneState::instantiate(GenEditState p_edit_state) const { // nodes where instancing failed (because something is missing) List<Node *> stray_instances; @@ -82,12 +107,15 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { bool gen_node_path_cache = p_edit_state != GEN_EDIT_STATE_DISABLED && node_path_cache.is_empty(); - Map<Ref<Resource>, Ref<Resource>> resources_local_to_scene; + HashMap<Ref<Resource>, Ref<Resource>> resources_local_to_scene; + + LocalVector<DeferredNodePathProperties> deferred_node_paths; for (int i = 0; i < nc; i++) { const NodeData &n = nd[i]; Node *parent = nullptr; + String old_parent_path; if (i > 0) { ERR_FAIL_COND_V_MSG(n.parent == -1, nullptr, vformat("Invalid scene: node %s does not specify its parent node.", snames[n.name])); @@ -95,6 +123,8 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #ifdef DEBUG_ENABLED if (!nparent && (n.parent & FLAG_ID_IS_PATH)) { WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instancing: '" + get_path() + "'.").ascii().get_data()); + old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@"); + nparent = ret_nodes[0]; } #endif parent = nparent; @@ -105,6 +135,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 @@ -149,37 +180,44 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { #endif } } else { - Object *obj = nullptr; + //node belongs to this scene and must be created + Object *obj = ClassDB::instantiate(snames[n.type]); - if (ClassDB::is_class_enabled(snames[n.type])) { - //node belongs to this scene and must be created - obj = ClassDB::instantiate(snames[n.type]); - } + node = Object::cast_to<Node>(obj); - if (!Object::cast_to<Node>(obj)) { + if (!node) { if (obj) { 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); } if (node) { @@ -191,11 +229,32 @@ 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); + ERR_FAIL_INDEX_V(nprops[j].value, prop_count, nullptr); + if (nprops[j].name & FLAG_PATH_PROPERTY_IS_NODE) { + uint32_t name_idx = nprops[j].name & (FLAG_PATH_PROPERTY_IS_NODE - 1); + ERR_FAIL_UNSIGNED_INDEX_V(name_idx, (uint32_t)sname_count, nullptr); + if (Engine::get_singleton()->is_editor_hint()) { + // If editor, just set the metadata and be it + node->set(META_POINTER_PROPERTY_BASE + String(snames[name_idx]), props[nprops[j].value]); + } else { + // Do an actual deferred sed of the property path. + DeferredNodePathProperties dnp; + dnp.path = props[nprops[j].value]; + dnp.base = node; + dnp.property = snames[name_idx]; + deferred_node_paths.push_back(dnp); + } + continue; + } + + ERR_FAIL_INDEX_V(nprops[j].name, sname_count, nullptr); + if (snames[nprops[j].name] == CoreStringNames::get_singleton()->_script) { //work around to avoid old script variables from disappearing, should be the proper fix to: //https://github.com/godotengine/godot/issues/2958 @@ -220,14 +279,14 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { Ref<Resource> res = value; if (res.is_valid()) { if (res->is_local_to_scene()) { - Map<Ref<Resource>, Ref<Resource>>::Element *E = resources_local_to_scene.find(res); + HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res); if (E) { - value = E->get(); + value = E->value; } else { Node *base = i == 0 ? node : ret_nodes[0]; - if (p_edit_state == GEN_EDIT_STATE_MAIN) { + if (p_edit_state == GEN_EDIT_STATE_MAIN || p_edit_state == GEN_EDIT_STATE_MAIN_INHERITED) { //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; @@ -247,9 +306,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 @@ -283,12 +357,30 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + if (!old_parent_path.is_empty()) { + node->_set_name_nocheck(old_parent_path + "@" + node->get_name()); + } + if (n.owner >= 0) { NODE_FROM_ID(owner, n.owner); if (owner) { node->_set_owner_nocheck(owner); + if (node->data.unique_name_in_owner) { + node->_acquire_unique_name_in_owner(); + } } } + + // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting) + if (p_edit_state == GEN_EDIT_STATE_MAIN) { + _sanitize_node_pinned_properties(node); + } else { + node->remove_meta("_edit_pinned_properties_"); + } + } + + if (missing_node) { + missing_node->set_recording_properties(false); } ret_nodes[i] = node; @@ -299,6 +391,12 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } } + for (uint32_t i = 0; i < deferred_node_paths.size(); i++) { + const DeferredNodePathProperties &dnp = deferred_node_paths[i]; + Node *other = dnp.base->get_node_or_null(dnp.path); + dnp.base->set(dnp.property, other); + } + for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) { E.value->setup_local_to_scene(); } @@ -320,15 +418,26 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { continue; } - Vector<Variant> binds; - if (c.binds.size()) { - binds.resize(c.binds.size()); - for (int j = 0; j < c.binds.size(); j++) { - binds.write[j] = props[c.binds[j]]; + Callable callable(cto, snames[c.method]); + if (c.unbinds > 0) { + callable = callable.unbind(c.unbinds); + } else if (!c.binds.is_empty()) { + Vector<Variant> binds; + if (c.binds.size()) { + binds.resize(c.binds.size()); + for (int j = 0; j < c.binds.size(); j++) { + binds.write[j] = props[c.binds[j]]; + } + } + + const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * binds.size()); + for (int j = 0; j < binds.size(); j++) { + argptrs[j] = &binds[j]; } + callable = callable.bindp(argptrs, binds.size()); } - cfrom->connect(snames[c.signal], Callable(cto, snames[c.method]), binds, CONNECT_PERSIST | c.flags); + cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags); } //Node *s = ret_nodes[0]; @@ -349,7 +458,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { return ret_nodes[0]; } -static int _nm_get_string(const String &p_string, Map<StringName, int> &name_map) { +static int _nm_get_string(const String &p_string, HashMap<StringName, int> &name_map) { if (name_map.has(p_string)) { return name_map[p_string]; } @@ -369,7 +478,7 @@ static int _vm_get_variant(const Variant &p_variant, HashMap<Variant, int, Varia return idx; } -Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { +Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { // this function handles all the work related to properly packing scenes, be it // instantiated or inherited. // given the complexity of this process, an attempt will be made to properly @@ -384,7 +493,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // save the child instantiated scenes that are chosen as editable, so they can be restored // upon load back - if (p_node != p_owner && p_node->get_scene_file_path() != String() && p_owner->is_editable_instance(p_node)) { + if (p_node != p_owner && !p_node->get_scene_file_path().is_empty() && p_owner->is_editable_instance(p_node)) { editable_instances.push_back(p_owner->get_path_to(p_node)); // Node is the root of an editable instance. is_editable_instance = true; @@ -415,61 +524,22 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map // with the instance states, we can query for identical properties/groups // and only save what has changed - List<PackState> pack_state_stack; - - bool instantiated_by_owner = true; - - { - Node *n = p_node; - - while (n) { - if (n == p_owner) { - Ref<SceneState> state = n->get_scene_inherited_state(); - if (state.is_valid()) { - int node = state->find_node_by_path(n->get_path_to(p_node)); - if (node >= 0) { - //this one has state for this node, save - PackState ps; - ps.node = node; - ps.state = state; - pack_state_stack.push_back(ps); - instantiated_by_owner = false; - } - } - - if (p_node->get_scene_file_path() != String() && p_node->get_owner() == p_owner && instantiated_by_owner) { - if (p_node->get_scene_instance_load_placeholder()) { - //it's a placeholder, use the placeholder path - nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); - nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; - } else { - //must instance ourselves - Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); - if (!instance.is_valid()) { - return ERR_CANT_OPEN; - } + bool instantiated_by_owner = false; + Vector<SceneState::PackState> states_stack = PropertyUtils::get_node_states_stack(p_node, p_owner, &instantiated_by_owner); - nd.instance = _vm_get_variant(instance, variant_map); - } - } - n = nullptr; - } else { - if (n->get_scene_file_path() != String()) { - //is an instance - Ref<SceneState> state = n->get_scene_instance_state(); - if (state.is_valid()) { - int node = state->find_node_by_path(n->get_path_to(p_node)); - if (node >= 0) { - //this one has state for this node, save - PackState ps; - ps.node = node; - ps.state = state; - pack_state_stack.push_back(ps); - } - } - } - n = n->get_owner(); + if (!p_node->get_scene_file_path().is_empty() && p_node->get_owner() == p_owner && instantiated_by_owner) { + if (p_node->get_scene_instance_load_placeholder()) { + //it's a placeholder, use the placeholder path + nd.instance = _vm_get_variant(p_node->get_scene_file_path(), variant_map); + nd.instance |= FLAG_INSTANCE_IS_PLACEHOLDER; + } else { + //must instance ourselves + Ref<PackedScene> instance = ResourceLoader::load(p_node->get_scene_file_path()); + if (!instance.is_valid()) { + return ERR_CANT_OPEN; } + + nd.instance = _vm_get_variant(instance, variant_map); } } @@ -478,88 +548,51 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map List<PropertyInfo> plist; p_node->get_property_list(&plist); - StringName type = p_node->get_class(); - Ref<Script> script = p_node->get_script(); - if (Engine::get_singleton()->is_editor_hint() && script.is_valid()) { - // Should be called in the editor only and not at runtime, - // otherwise it can cause problems because of missing instance state support. - script->update_exports(); - } + 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)) { continue; } - String name = E.name; - Variant value = p_node->get(E.name); - - bool isdefault = false; - Variant default_value = ClassDB::class_get_default_property_value(type, name); - - if (default_value.get_type() != Variant::NIL) { - isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value)); + if (E.name == META_PROPERTY_MISSING_RESOURCES) { + continue; // Ignore this property when packing. } - - if (!isdefault && script.is_valid() && script->get_property_default_value(name, default_value)) { - isdefault = bool(Variant::evaluate(Variant::OP_EQUAL, value, default_value)); + if (E.name.begins_with(META_POINTER_PROPERTY_BASE)) { + continue; // do not save. } - // the version above makes more sense, because it does not rely on placeholder or usage flag - // in the script, just the default value function. - // if (E.usage & PROPERTY_USAGE_SCRIPT_DEFAULT_VALUE) { - // isdefault = true; //is script default value - // } - - if (pack_state_stack.size()) { - // we are on part of an instantiated subscene - // or part of instantiated scene. - // only save what has been changed - // only save changed properties in instance - - if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE) || E.name == "__meta__") { - //property has requested that no instance state is saved, sorry - //also, meta won't be overridden or saved + + // If instance or inheriting, not saving if property requested so. + if (!states_stack.is_empty()) { + if ((E.usage & PROPERTY_USAGE_NO_INSTANCE_STATE)) { continue; } + } - bool exists = false; - Variant original; - - for (List<PackState>::Element *F = pack_state_stack.back(); F; F = F->prev()) { - //check all levels of pack to see if the property exists somewhere - const PackState &ps = F->get(); + StringName name = E.name; + Variant value = p_node->get(name); + bool use_deferred_node_path_bit = false; - original = ps.state->get_property_value(ps.node, E.name, exists); - if (exists) { - break; - } + if (E.type == Variant::OBJECT && E.hint == PROPERTY_HINT_NODE_TYPE) { + value = p_node->get(META_POINTER_PROPERTY_BASE + E.name); + if (value.get_type() != Variant::NODE_PATH) { + continue; //was never set, ignore. } - - if (exists) { - //check if already exists and did not change - if (value.get_type() == Variant::FLOAT && original.get_type() == Variant::FLOAT) { - //this must be done because, as some scenes save as text, there might be a tiny difference in floats due to numerical error - float a = value; - float b = original; - - if (Math::is_equal_approx(a, b)) { - continue; - } - } else if (bool(Variant::evaluate(Variant::OP_EQUAL, value, original))) { - continue; - } + use_deferred_node_path_bit = true; + } else if (E.type == Variant::OBJECT && missing_resource_properties.has(E.name)) { + // Was this missing resource overridden? If so do not save the old value. + Ref<Resource> ures = value; + if (ures.is_null()) { + value = missing_resource_properties[E.name]; } + } - if (!exists && isdefault) { - //does not exist in original node, but it's the default value - //so safe to skip too. - continue; - } - - } else { - if (isdefault) { - //it's the default value, no point in saving it + 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; } } @@ -567,6 +600,9 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map NodeData::Property prop; prop.name = _nm_get_string(name, name_map); prop.value = _vm_get_variant(value, variant_map); + if (use_deferred_node_path_bit) { + prop.name |= FLAG_PATH_PROPERTY_IS_NODE; + } nd.properties.push_back(prop); } @@ -579,16 +615,11 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map if (!gi.persistent) { continue; } - /* - if (instance_state_node>=0 && instance_state->is_node_in_group(instance_state_node,gi.name)) - continue; //group was instantiated, don't add here - */ bool skip = false; - for (const PackState &F : pack_state_stack) { + for (const SceneState::PackState &ia : states_stack) { //check all levels of pack to see if the group was added somewhere - const PackState &ps = F; - if (ps.state->is_node_in_group(ps.node, gi.name)) { + if (ia.state->is_node_in_group(ia.node, gi.name)) { skip = true; break; } @@ -616,11 +647,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 (pack_state_stack.is_empty() && !is_editable_instance) { + 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) { + // It's a missing node (type non existent 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 @@ -635,7 +673,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map bool save_node = nd.properties.size() || nd.groups.size(); // some local properties or groups exist save_node = save_node || p_node == p_owner; // owner is always saved - save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instantiated + save_node = save_node || (p_node->get_owner() == p_owner && instantiated_by_owner); //part of scene and not instanced int idx = nodes.size(); int parent_node = NO_PARENT_SAVED; @@ -675,7 +713,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Map return OK; } -Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, Map<Node *, int> &node_map, Map<Node *, int> &nodepath_map) { +Error SceneState::_parse_connections(Node *p_owner, Node *p_node, HashMap<StringName, int> &name_map, HashMap<Variant, int, VariantHasher, VariantComparator> &variant_map, HashMap<Node *, int> &node_map, HashMap<Node *, int> &nodepath_map) { if (p_node != p_owner && p_node->get_owner() && p_node->get_owner() != p_owner && !p_owner->is_editable_instance(p_node->get_owner())) { return OK; } @@ -709,12 +747,32 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName continue; } + Vector<Variant> binds; + int unbinds = 0; + Callable base_callable; + + if (c.callable.is_custom()) { + CallableCustomBind *ccb = dynamic_cast<CallableCustomBind *>(c.callable.get_custom()); + if (ccb) { + binds = ccb->get_binds(); + base_callable = ccb->get_callable(); + } + + CallableCustomUnbind *ccu = dynamic_cast<CallableCustomUnbind *>(c.callable.get_custom()); + if (ccu) { + unbinds = ccu->get_unbinds(); + base_callable = ccu->get_callable(); + } + } else { + base_callable = c.callable; + } + //find if this connection already exists Node *common_parent = target->find_common_parent_with(p_node); ERR_CONTINUE(!common_parent); - if (common_parent != p_owner && common_parent->get_scene_file_path() == String()) { + if (common_parent != p_owner && common_parent->get_scene_file_path().is_empty()) { common_parent = common_parent->get_owner(); } @@ -734,7 +792,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName NodePath signal_from = common_parent->get_path_to(p_node); NodePath signal_to = common_parent->get_path_to(target); - if (ps->has_connection(signal_from, c.signal.get_name(), signal_to, c.callable.get_method())) { + if (ps->has_connection(signal_from, c.signal.get_name(), signal_to, base_callable.get_method())) { exists = true; break; } @@ -765,7 +823,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName if (from_node >= 0 && to_node >= 0) { //this one has state for this node, save - if (state->is_connection(from_node, c.signal.get_name(), to_node, c.callable.get_method())) { + if (state->is_connection(from_node, c.signal.get_name(), to_node, base_callable.get_method())) { exists2 = true; break; } @@ -774,7 +832,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName nl = nullptr; } else { - if (nl->get_scene_file_path() != String()) { + if (!nl->get_scene_file_path().is_empty()) { //is an instance Ref<SceneState> state = nl->get_scene_instance_state(); if (state.is_valid()) { @@ -783,7 +841,7 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName if (from_node >= 0 && to_node >= 0) { //this one has state for this node, save - if (state->is_connection(from_node, c.signal.get_name(), to_node, c.callable.get_method())) { + if (state->is_connection(from_node, c.signal.get_name(), to_node, base_callable.get_method())) { exists2 = true; break; } @@ -830,11 +888,13 @@ Error SceneState::_parse_connections(Node *p_owner, Node *p_node, Map<StringName ConnectionData cd; cd.from = src_id; cd.to = target_id; - cd.method = _nm_get_string(c.callable.get_method(), name_map); + cd.method = _nm_get_string(base_callable.get_method(), name_map); cd.signal = _nm_get_string(c.signal.get_name(), name_map); cd.flags = c.flags; - for (int i = 0; i < c.binds.size(); i++) { - cd.binds.push_back(_vm_get_variant(c.binds[i], variant_map)); + cd.unbinds = unbinds; + + for (int i = 0; i < binds.size(); i++) { + cd.binds.push_back(_vm_get_variant(binds[i], variant_map)); } connections.push_back(cd); } @@ -858,10 +918,10 @@ Error SceneState::pack(Node *p_scene) { Node *scene = p_scene; - Map<StringName, int> name_map; + HashMap<StringName, int> name_map; HashMap<Variant, int, VariantHasher, VariantComparator> variant_map; - Map<Node *, int> node_map; - Map<Node *, int> nodepath_map; + HashMap<Node *, int> node_map; + HashMap<Node *, int> nodepath_map; // If using scene inheritance, pack the scene it inherits from. if (scene->get_scene_inherited_state().is_valid()) { @@ -892,10 +952,10 @@ Error SceneState::pack(Node *p_scene) { } variants.resize(variant_map.size()); - const Variant *K = nullptr; - while ((K = variant_map.next(K))) { - int idx = variant_map[*K]; - variants.write[idx] = *K; + + for (const KeyValue<Variant, int> &E : variant_map) { + int idx = E.value; + variants.write[idx] = E.key; } node_paths.resize(nodepath_map.size()); @@ -932,7 +992,7 @@ void SceneState::clear() { base_scene_idx = -1; } -Ref<SceneState> SceneState::_get_base_scene_state() const { +Ref<SceneState> SceneState::get_base_scene_state() const { if (base_scene_idx >= 0) { Ref<PackedScene> ps = variants[base_scene_idx]; if (ps.is_valid()) { @@ -947,8 +1007,8 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built."); if (!node_path_cache.has(p_node)) { - if (_get_base_scene_state().is_valid()) { - int idx = _get_base_scene_state()->find_node_by_path(p_node); + if (get_base_scene_state().is_valid()) { + int idx = get_base_scene_state()->find_node_by_path(p_node); if (idx != -1) { int rkey = _find_base_scene_node_remap_key(idx); if (rkey == -1) { @@ -963,11 +1023,11 @@ int SceneState::find_node_by_path(const NodePath &p_node) const { int nid = node_path_cache[p_node]; - if (_get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { + if (get_base_scene_state().is_valid() && !base_scene_node_remap.has(nid)) { //for nodes that _do_ exist in current scene, still try to look for //the node in the instantiated scene, as a property may be missing //from the local one - int idx = _get_base_scene_state()->find_node_by_path(p_node); + int idx = get_base_scene_state()->find_node_by_path(p_node); if (idx != -1) { base_scene_node_remap[nid] = idx; } @@ -997,7 +1057,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property, const NodeData::Property *p = nodes[p_node].properties.ptr(); for (int i = 0; i < pc; i++) { - if (p_property == namep[p[i].name]) { + if (p_property == namep[p[i].name & FLAG_PROP_NAME_MASK]) { found = true; return variants[p[i].value]; } @@ -1007,7 +1067,7 @@ Variant SceneState::get_property_value(int p_node, const StringName &p_property, //property not found, try on instance if (base_scene_node_remap.has(p_node)) { - return _get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found); + return get_base_scene_state()->get_property_value(base_scene_node_remap[p_node], p_property, found); } return Variant(); @@ -1026,7 +1086,7 @@ bool SceneState::is_node_in_group(int p_node, const StringName &p_group) const { } if (base_scene_node_remap.has(p_node)) { - return _get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group); + return get_base_scene_state()->is_node_in_group(base_scene_node_remap[p_node], p_group); } return false; @@ -1065,7 +1125,7 @@ bool SceneState::is_connection(int p_node, const StringName &p_signal, int p_to_ } if (base_scene_node_remap.has(p_node) && base_scene_node_remap.has(p_to_node)) { - return _get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method); + return get_base_scene_state()->is_connection(base_scene_node_remap[p_node], p_signal, base_scene_node_remap[p_to_node], p_to_method); } return false; @@ -1388,7 +1448,19 @@ int SceneState::get_node_property_count(int p_idx) const { StringName SceneState::get_node_property_name(int p_idx, int p_prop) const { ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName()); ERR_FAIL_INDEX_V(p_prop, nodes[p_idx].properties.size(), StringName()); - return names[nodes[p_idx].properties[p_prop].name]; + return names[nodes[p_idx].properties[p_prop].name & FLAG_PROP_NAME_MASK]; +} + +Vector<String> SceneState::get_node_deferred_nodepath_properties(int p_idx) const { + Vector<String> ret; + ERR_FAIL_INDEX_V(p_idx, nodes.size(), ret); + for (int i = 0; i < nodes[p_idx].properties.size(); i++) { + uint32_t idx = nodes[p_idx].properties[i].name; + if (idx & FLAG_PATH_PROPERTY_IS_NODE) { + ret.push_back(names[idx & FLAG_PROP_NAME_MASK]); + } + } + return ret; } Variant SceneState::get_node_property_value(int p_idx, int p_prop) const { @@ -1447,6 +1519,11 @@ int SceneState::get_connection_flags(int p_idx) const { return connections[p_idx].flags; } +int SceneState::get_connection_unbinds(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, connections.size(), -1); + return connections[p_idx].unbinds; +} + Array SceneState::get_connection_binds(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, connections.size(), Array()); Array binds; @@ -1488,7 +1565,7 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p } } - ss = ss->_get_base_scene_state(); + ss = ss->get_base_scene_state(); } while (ss.is_valid()); return false; @@ -1529,13 +1606,16 @@ int SceneState::add_node(int p_parent, int p_owner, int p_type, int p_name, int return nodes.size() - 1; } -void SceneState::add_node_property(int p_node, int p_name, int p_value) { +void SceneState::add_node_property(int p_node, int p_name, int p_value, bool p_deferred_node_path) { ERR_FAIL_INDEX(p_node, nodes.size()); ERR_FAIL_INDEX(p_name, names.size()); ERR_FAIL_INDEX(p_value, variants.size()); NodeData::Property prop; prop.name = p_name; + if (p_deferred_node_path) { + prop.name |= FLAG_PATH_PROPERTY_IS_NODE; + } prop.value = p_value; nodes.write[p_node].properties.push_back(prop); } @@ -1551,7 +1631,7 @@ void SceneState::set_base_scene(int p_idx) { base_scene_idx = p_idx; } -void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, const Vector<int> &p_binds) { +void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method, int p_flags, int p_unbinds, const Vector<int> &p_binds) { ERR_FAIL_INDEX(p_signal, names.size()); ERR_FAIL_INDEX(p_method, names.size()); @@ -1564,6 +1644,7 @@ void SceneState::add_connection(int p_from, int p_to, int p_signal, int p_method c.signal = p_signal; c.method = p_method; c.flags = p_flags; + c.unbinds = p_unbinds; c.binds = p_binds; connections.push_back(c); } @@ -1572,6 +1653,10 @@ void SceneState::add_editable_instance(const NodePath &p_path) { editable_instances.push_back(p_path); } +String SceneState::get_meta_pointer_property(const String &p_property) { + return META_POINTER_PROPERTY_BASE + p_property; +} + Vector<String> SceneState::_get_node_groups(int p_idx) const { Vector<StringName> groups = get_node_groups(p_idx); Vector<String> ret; @@ -1606,10 +1691,12 @@ void SceneState::_bind_methods() { ClassDB::bind_method(D_METHOD("get_connection_method", "idx"), &SceneState::get_connection_method); ClassDB::bind_method(D_METHOD("get_connection_flags", "idx"), &SceneState::get_connection_flags); ClassDB::bind_method(D_METHOD("get_connection_binds", "idx"), &SceneState::get_connection_binds); + ClassDB::bind_method(D_METHOD("get_connection_unbinds", "idx"), &SceneState::get_connection_unbinds); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN); + BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED); } SceneState::SceneState() { @@ -1651,7 +1738,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const { s->set_scene_instance_state(state); } - if (get_path() != "" && get_path().find("::") == -1) { + if (!is_built_in()) { s->set_scene_file_path(get_path()); } @@ -1676,7 +1763,7 @@ void PackedScene::recreate_state() { #endif } -Ref<SceneState> PackedScene::get_state() { +Ref<SceneState> PackedScene::get_state() const { return state; } @@ -1692,7 +1779,7 @@ void PackedScene::_bind_methods() { ClassDB::bind_method(D_METHOD("pack", "path"), &PackedScene::pack); ClassDB::bind_method(D_METHOD("instantiate", "edit_state"), &PackedScene::instantiate, DEFVAL(GEN_EDIT_STATE_DISABLED)); ClassDB::bind_method(D_METHOD("can_instantiate"), &PackedScene::can_instantiate); - ClassDB::bind_method(D_METHOD("_set_bundled_scene"), &PackedScene::_set_bundled_scene); + ClassDB::bind_method(D_METHOD("_set_bundled_scene", "scene"), &PackedScene::_set_bundled_scene); ClassDB::bind_method(D_METHOD("_get_bundled_scene"), &PackedScene::_get_bundled_scene); ClassDB::bind_method(D_METHOD("get_state"), &PackedScene::get_state); @@ -1701,6 +1788,7 @@ void PackedScene::_bind_methods() { BIND_ENUM_CONSTANT(GEN_EDIT_STATE_DISABLED); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_INSTANCE); BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN); + BIND_ENUM_CONSTANT(GEN_EDIT_STATE_MAIN_INHERITED); } PackedScene::PackedScene() { |