summaryrefslogtreecommitdiffstats
path: root/scene/resources/packed_scene.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/resources/packed_scene.cpp')
-rw-r--r--scene/resources/packed_scene.cpp198
1 files changed, 134 insertions, 64 deletions
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index ac67e6e5e9..514e7eb260 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -1,32 +1,32 @@
-/*************************************************************************/
-/* packed_scene.cpp */
-/*************************************************************************/
-/* This file is part of: */
-/* GODOT ENGINE */
-/* https://godotengine.org */
-/*************************************************************************/
-/* 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 */
-/* "Software"), to deal in the Software without restriction, including */
-/* without limitation the rights to use, copy, modify, merge, publish, */
-/* distribute, sublicense, and/or sell copies of the Software, and to */
-/* permit persons to whom the Software is furnished to do so, subject to */
-/* the following conditions: */
-/* */
-/* The above copyright notice and this permission notice shall be */
-/* included in all copies or substantial portions of the Software. */
-/* */
-/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
-/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
-/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
-/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
-/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
-/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
-/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
-/*************************************************************************/
+/**************************************************************************/
+/* packed_scene.cpp */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
#include "packed_scene.h"
@@ -71,7 +71,7 @@ static Array _sanitize_node_pinned_properties(Node *p_node) {
}
Node *SceneState::instantiate(GenEditState p_edit_state) const {
- // nodes where instancing failed (because something is missing)
+ // Nodes where instantiation failed (because something is missing.)
List<Node *> stray_instances;
#define NODE_FROM_ID(p_name, p_id) \
@@ -122,7 +122,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
NODE_FROM_ID(nparent, n.parent);
#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());
+ WARN_PRINT(String("Parent path '" + String(node_paths[n.parent & FLAG_MASK]) + "' for node '" + String(snames[n.name]) + "' has vanished when instantiating: '" + get_path() + "'.").ascii().get_data());
old_parent_path = String(node_paths[n.parent & FLAG_MASK]).trim_prefix("./").replace("/", "@");
nparent = ret_nodes[0];
}
@@ -131,7 +131,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
} else {
// i == 0 is root node.
ERR_FAIL_COND_V_MSG(n.parent != -1, nullptr, vformat("Invalid scene: root node %s cannot specify a parent node.", snames[n.name]));
- ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANCED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name]));
+ ERR_FAIL_COND_V_MSG(n.type == TYPE_INSTANTIATED && base_scene_idx < 0, nullptr, vformat("Invalid scene: root node %s in an instance, but there's no base scene.", snames[n.name]));
}
Node *node = nullptr;
@@ -150,15 +150,15 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
} else if (n.instance >= 0) {
//instance a scene into this node
if (n.instance & FLAG_INSTANCE_IS_PLACEHOLDER) {
- String path = props[n.instance & FLAG_MASK];
+ String scene_path = props[n.instance & FLAG_MASK];
if (disable_placeholders) {
- Ref<PackedScene> sdata = ResourceLoader::load(path, "PackedScene");
+ Ref<PackedScene> sdata = ResourceLoader::load(scene_path, "PackedScene");
ERR_FAIL_COND_V(!sdata.is_valid(), nullptr);
node = sdata->instantiate(p_edit_state == GEN_EDIT_STATE_DISABLED ? PackedScene::GEN_EDIT_STATE_DISABLED : PackedScene::GEN_EDIT_STATE_INSTANCE);
ERR_FAIL_COND_V(!node, nullptr);
} else {
InstancePlaceholder *ip = memnew(InstancePlaceholder);
- ip->set_instance_path(path);
+ ip->set_instance_path(scene_path);
node = ip;
}
node->set_scene_instance_load_placeholder(true);
@@ -169,7 +169,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
ERR_FAIL_COND_V(!node, nullptr);
}
- } else if (n.type == TYPE_INSTANCED) {
+ } else if (n.type == TYPE_INSTANTIATED) {
//get the node from somewhere, it likely already exists from another instance
if (parent) {
node = parent->_get_child_by_name(snames[n.name]);
@@ -279,25 +279,36 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
Ref<Resource> res = value;
if (res.is_valid()) {
if (res->is_local_to_scene()) {
- HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res);
-
- if (E) {
- value = E->value;
+ // In a situation where a local-to-scene resource is used in a child node of a non-editable instance,
+ // we need to avoid the parent scene from overriding the resource potentially also used in the root
+ // of the instantiated scene. That would to the instance having two different instances of the resource.
+ // Since at this point it's too late to propagate the resource instance in the parent scene to all the relevant
+ // nodes in the instance (and that would require very complex bookkepping), what we do instead is
+ // tampering the resource object already there with the values from the node in the parent scene and
+ // then tell this node to reference that resource.
+ if (n.instance >= 0) {
+ Ref<Resource> node_res = node->get(snames[nprops[j].name]);
+ if (node_res.is_valid()) {
+ node_res->copy_from(res);
+ node_res->configure_for_local_scene(node, resources_local_to_scene);
+ value = node_res;
+ }
} else {
+ HashMap<Ref<Resource>, Ref<Resource>>::Iterator E = resources_local_to_scene.find(res);
Node *base = i == 0 ? node : ret_nodes[0];
-
- 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;
-
+ if (E) {
+ value = E->value;
} else {
- //for instances, a copy must be made
- Node *base2 = i == 0 ? node : ret_nodes[0];
- Ref<Resource> local_dupe = res->duplicate_for_local_scene(base2, resources_local_to_scene);
- resources_local_to_scene[res] = local_dupe;
- res = local_dupe;
- value = local_dupe;
+ 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<Resource> 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
@@ -334,7 +345,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
node->add_to_group(snames[n.groups[j]], true);
}
- if (n.instance >= 0 || n.type != TYPE_INSTANCED || i == 0) {
+ if (n.instance >= 0 || n.type != TYPE_INSTANTIATED || i == 0) {
//if node was not part of instance, must set its name, parenthood and ownership
if (i > 0) {
if (parent) {
@@ -371,7 +382,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
}
- // we only want to deal with pinned flag if instancing as pure main (no instance, no inheriting)
+ // We only want to deal with pinned flag if instantiating as pure main (no instance, no inheriting.)
if (p_edit_state == GEN_EDIT_STATE_MAIN) {
_sanitize_node_pinned_properties(node);
} else {
@@ -398,7 +409,9 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
}
for (KeyValue<Ref<Resource>, Ref<Resource>> &E : resources_local_to_scene) {
- E.value->setup_local_to_scene();
+ if (E.value->get_local_scene() == ret_nodes[0]) {
+ E.value->setup_local_to_scene();
+ }
}
//do connections
@@ -437,7 +450,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
callable = callable.bindp(argptrs, binds.size());
}
- cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags);
+ cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED));
}
//Node *s = ret_nodes[0];
@@ -652,7 +665,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
// 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
+ //This node is not part of an instantiation process, so save the type.
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);
@@ -662,7 +675,7 @@ Error SceneState::_parse_node(Node *p_owner, Node *p_node, int p_parent_idx, Has
} else {
// this node is part of an instantiated process, so do not save the type.
// instead, save that it was instantiated
- nd.type = TYPE_INSTANCED;
+ nd.type = TYPE_INSTANTIATED;
}
// determine whether to save this node or not
@@ -925,8 +938,8 @@ Error SceneState::pack(Node *p_scene) {
// If using scene inheritance, pack the scene it inherits from.
if (scene->get_scene_inherited_state().is_valid()) {
- String path = scene->get_scene_inherited_state()->get_path();
- Ref<PackedScene> instance = ResourceLoader::load(path);
+ String scene_path = scene->get_scene_inherited_state()->get_path();
+ Ref<PackedScene> instance = ResourceLoader::load(scene_path);
if (instance.is_valid()) {
base_scene_idx = _vm_get_variant(instance, variant_map);
}
@@ -992,6 +1005,37 @@ void SceneState::clear() {
base_scene_idx = -1;
}
+Error SceneState::copy_from(const Ref<SceneState> &p_scene_state) {
+ ERR_FAIL_COND_V(p_scene_state.is_null(), ERR_INVALID_PARAMETER);
+
+ clear();
+
+ for (const StringName &E : p_scene_state->names) {
+ names.append(E);
+ }
+ for (const Variant &E : p_scene_state->variants) {
+ variants.append(E);
+ }
+ for (const SceneState::NodeData &E : p_scene_state->nodes) {
+ nodes.append(E);
+ }
+ for (const SceneState::ConnectionData &E : p_scene_state->connections) {
+ connections.append(E);
+ }
+ for (KeyValue<NodePath, int> &E : p_scene_state->node_path_cache) {
+ node_path_cache.insert(E.key, E.value);
+ }
+ for (const NodePath &E : p_scene_state->node_paths) {
+ node_paths.append(E);
+ }
+ for (const NodePath &E : p_scene_state->editable_instances) {
+ editable_instances.append(E);
+ }
+ base_scene_idx = p_scene_state->base_scene_idx;
+
+ return OK;
+}
+
Ref<SceneState> SceneState::get_base_scene_state() const {
if (base_scene_idx >= 0) {
Ref<PackedScene> ps = variants[base_scene_idx];
@@ -1338,7 +1382,7 @@ int SceneState::get_node_count() const {
StringName SceneState::get_node_type(int p_idx) const {
ERR_FAIL_INDEX_V(p_idx, nodes.size(), StringName());
- if (nodes[p_idx].type == TYPE_INSTANCED) {
+ if (nodes[p_idx].type == TYPE_INSTANTIATED) {
return StringName();
}
return names[nodes[p_idx].type];
@@ -1533,7 +1577,7 @@ Array SceneState::get_connection_binds(int p_idx) const {
return binds;
}
-bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method) {
+bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p_signal, const NodePath &p_node_to, const StringName &p_method, bool p_no_inheritance) {
// this method cannot be const because of this
Ref<SceneState> ss = this;
@@ -1565,6 +1609,10 @@ bool SceneState::has_connection(const NodePath &p_node_from, const StringName &p
}
}
+ if (p_no_inheritance) {
+ break;
+ }
+
ss = ss->get_base_scene_state();
} while (ss.is_valid());
@@ -1720,6 +1768,28 @@ void PackedScene::clear() {
state->clear();
}
+void PackedScene::reload_from_file() {
+ String path = get_path();
+ if (!path.is_resource_file()) {
+ return;
+ }
+
+ Ref<PackedScene> s = ResourceLoader::load(ResourceLoader::path_remap(path), get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE);
+ if (!s.is_valid()) {
+ return;
+ }
+
+ // Backup the loaded_state
+ Ref<SceneState> loaded_state = s->get_state();
+ // This assigns a new state to s->state
+ // We do this because of the next step
+ s->recreate_state();
+ // This has a side-effect to clear s->state
+ copy_from(s);
+ // Then, we copy the backed-up loaded_state to state
+ state->copy_from(loaded_state);
+}
+
bool PackedScene::can_instantiate() const {
return state->can_instantiate();
}
@@ -1742,7 +1812,7 @@ Node *PackedScene::instantiate(GenEditState p_edit_state) const {
s->set_scene_file_path(get_path());
}
- s->notification(Node::NOTIFICATION_INSTANCED);
+ s->notification(Node::NOTIFICATION_SCENE_INSTANTIATED);
return s;
}
@@ -1779,7 +1849,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);