Merge pull request #57606 from SaracenOne/update_on_reimport

Update instances of scenes which have been reimported.
This commit is contained in:
Rémi Verschelde 2023-01-23 15:35:44 +01:00
commit 11e2278844
No known key found for this signature in database
GPG Key ID: C3336907360768E1
8 changed files with 538 additions and 2 deletions

View File

@ -909,6 +909,9 @@
<constant name="NOTIFICATION_ENABLED" value="29"> <constant name="NOTIFICATION_ENABLED" value="29">
Notification received when the node is enabled again after being disabled. See [constant PROCESS_MODE_DISABLED]. Notification received when the node is enabled again after being disabled. See [constant PROCESS_MODE_DISABLED].
</constant> </constant>
<constant name="NOTIFICATION_NODE_RECACHE_REQUESTED" value="30">
Notification received when other nodes in the tree may have been removed/replaced and node pointers may require re-caching.
</constant>
<constant name="NOTIFICATION_EDITOR_PRE_SAVE" value="9001"> <constant name="NOTIFICATION_EDITOR_PRE_SAVE" value="9001">
Notification received right before the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects. Notification received right before the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects.
</constant> </constant>

View File

@ -201,7 +201,6 @@ public:
String get_scene_type(int p_idx) const; String get_scene_type(int p_idx) const;
void set_scene_path(int p_idx, const String &p_path); void set_scene_path(int p_idx, const String &p_path);
Ref<Script> get_scene_root_script(int p_idx) const; Ref<Script> get_scene_root_script(int p_idx) const;
void set_edited_scene_version(uint64_t version, int p_scene_idx = -1);
void set_scene_modified_time(int p_idx, uint64_t p_time); void set_scene_modified_time(int p_idx, uint64_t p_time);
uint64_t get_scene_modified_time(int p_idx) const; uint64_t get_scene_modified_time(int p_idx) const;
void clear_edited_scenes(); void clear_edited_scenes();

View File

@ -58,6 +58,7 @@
#include "scene/gui/tab_bar.h" #include "scene/gui/tab_bar.h"
#include "scene/gui/tab_container.h" #include "scene/gui/tab_container.h"
#include "scene/main/window.h" #include "scene/main/window.h"
#include "scene/property_utils.h"
#include "scene/resources/packed_scene.h" #include "scene/resources/packed_scene.h"
#include "servers/display_server.h" #include "servers/display_server.h"
#include "servers/navigation_server_3d.h" #include "servers/navigation_server_3d.h"
@ -997,6 +998,7 @@ void EditorNode::_resources_reimported(const Vector<String> &p_resources) {
for (const String &E : scenes) { for (const String &E : scenes) {
reload_scene(E); reload_scene(E);
reload_instances_with_path_in_edited_scenes(E);
} }
scene_tabs->set_current_tab(current_tab); scene_tabs->set_current_tab(current_tab);
@ -3899,7 +3901,7 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
Ref<SceneState> state = sdata->get_state(); Ref<SceneState> state = sdata->get_state();
state->set_path(lpath); state->set_path(lpath);
new_scene->set_scene_inherited_state(state); new_scene->set_scene_inherited_state(state);
new_scene->set_scene_file_path(String()); new_scene->set_scene_file_path(lpath);
} }
new_scene->set_scene_instance_state(Ref<SceneState>()); new_scene->set_scene_instance_state(Ref<SceneState>());
@ -3933,6 +3935,134 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b
return OK; return OK;
} }
HashMap<StringName, Variant> EditorNode::get_modified_properties_for_node(Node *p_node) {
HashMap<StringName, Variant> modified_property_map;
List<PropertyInfo> pinfo;
p_node->get_property_list(&pinfo);
for (const PropertyInfo &E : pinfo) {
if (E.usage & PROPERTY_USAGE_STORAGE) {
bool is_valid_revert = false;
Variant revert_value = EditorPropertyRevert::get_property_revert_value(p_node, E.name, &is_valid_revert);
Variant current_value = p_node->get(E.name);
if (is_valid_revert) {
if (PropertyUtils::is_property_value_different(current_value, revert_value)) {
modified_property_map[E.name] = current_value;
}
}
}
}
return modified_property_map;
}
void EditorNode::update_diff_data_for_node(
Node *p_edited_scene,
Node *p_root,
Node *p_node,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
List<AdditiveNodeEntry> &p_addition_list) {
bool node_part_of_subscene = p_node != p_edited_scene &&
p_edited_scene->get_scene_inherited_state().is_valid() &&
p_edited_scene->get_scene_inherited_state()->find_node_by_path(p_edited_scene->get_path_to(p_node)) >= 0;
// Loop through the owners until either we reach the root node or nullptr
Node *valid_node_owner = p_node->get_owner();
while (valid_node_owner) {
if (valid_node_owner == p_root) {
break;
}
valid_node_owner = valid_node_owner->get_owner();
}
if ((valid_node_owner == p_root && (p_root != p_edited_scene || !p_edited_scene->get_scene_file_path().is_empty())) || node_part_of_subscene || p_node == p_root) {
HashMap<StringName, Variant> modified_properties = get_modified_properties_for_node(p_node);
// Find all valid connections to other nodes.
List<Connection> connections_to;
p_node->get_all_signal_connections(&connections_to);
List<ConnectionWithNodePath> valid_connections_to;
for (const Connection &c : connections_to) {
Node *connection_target_node = Object::cast_to<Node>(c.callable.get_object());
if (connection_target_node) {
// TODO: add support for reinstating custom callables
if (!c.callable.is_custom()) {
ConnectionWithNodePath connection_to;
connection_to.connection = c;
connection_to.node_path = p_node->get_path_to(connection_target_node);
valid_connections_to.push_back(connection_to);
}
}
}
// Find all valid connections from other nodes.
List<Connection> connections_from;
p_node->get_signals_connected_to_this(&connections_from);
List<Connection> valid_connections_from;
for (const Connection &c : connections_from) {
Node *source_node = Object::cast_to<Node>(c.signal.get_object());
Node *valid_source_owner = nullptr;
if (source_node) {
valid_source_owner = source_node->get_owner();
while (valid_source_owner) {
if (valid_source_owner == p_root) {
break;
}
valid_source_owner = valid_source_owner->get_owner();
}
}
if (!source_node || valid_source_owner == nullptr) {
// TODO: add support for reinstating custom callables
if (!c.callable.is_custom()) {
valid_connections_from.push_back(c);
}
}
}
// Find all node groups.
List<Node::GroupInfo> groups;
p_node->get_groups(&groups);
if (!modified_properties.is_empty() || !valid_connections_to.is_empty() || !valid_connections_from.is_empty() || !groups.is_empty()) {
ModificationNodeEntry modification_node_entry;
modification_node_entry.property_table = modified_properties;
modification_node_entry.connections_to = valid_connections_to;
modification_node_entry.connections_from = valid_connections_from;
modification_node_entry.groups = groups;
p_modification_table[p_root->get_path_to(p_node)] = modification_node_entry;
}
} else {
AdditiveNodeEntry new_additive_node_entry;
new_additive_node_entry.node = p_node;
new_additive_node_entry.parent = p_root->get_path_to(p_node->get_parent());
new_additive_node_entry.owner = p_node->get_owner();
new_additive_node_entry.index = p_node->get_index();
Node2D *node_2d = Object::cast_to<Node2D>(p_node);
if (node_2d) {
new_additive_node_entry.transform_2d = node_2d->get_relative_transform_to_parent(node_2d->get_parent());
}
Node3D *node_3d = Object::cast_to<Node3D>(p_node);
if (node_3d) {
new_additive_node_entry.transform_3d = node_3d->get_relative_transform(node_3d->get_parent());
}
p_addition_list.push_back(new_additive_node_entry);
return;
}
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *child = p_node->get_child(i);
update_diff_data_for_node(p_edited_scene, p_root, child, p_modification_table, p_addition_list);
}
}
//
void EditorNode::open_request(const String &p_path) { void EditorNode::open_request(const String &p_path) {
if (!opening_prev) { if (!opening_prev) {
List<String>::Element *prev_scene_item = previous_scenes.find(p_path); List<String>::Element *prev_scene_item = previous_scenes.find(p_path);
@ -5789,6 +5919,352 @@ void EditorNode::reload_scene(const String &p_path) {
scene_tabs->set_current_tab(current_tab); scene_tabs->set_current_tab(current_tab);
} }
void EditorNode::find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list) {
String scene_file_path = p_node->get_scene_file_path();
// This is going to get messy...
if (p_node->get_scene_file_path() == p_instance_path) {
p_instance_list.push_back(p_node);
} else {
Node *current_node = p_node;
Ref<SceneState> inherited_state = current_node->get_scene_inherited_state();
while (inherited_state.is_valid()) {
String inherited_path = inherited_state->get_path();
if (inherited_path == p_instance_path) {
p_instance_list.push_back(p_node);
break;
}
inherited_state = inherited_state->get_base_scene_state();
}
}
for (int i = 0; i < p_node->get_child_count(); i++) {
Node *child = p_node->get_child(i);
find_all_instances_inheriting_path_in_node(p_root, child, p_instance_path, p_instance_list);
}
}
void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_instance_path) {
int original_edited_scene_idx = editor_data.get_edited_scene();
HashMap<int, List<Node *>> edited_scene_map;
// Walk through each opened scene to get a global list of all instances which match
// the current reimported scenes.
for (int i = 0; i < editor_data.get_edited_scene_count(); i++) {
if (editor_data.get_scene_path(i) != p_instance_path) {
Node *edited_scene_root = editor_data.get_edited_scene_root(i);
if (edited_scene_root) {
List<Node *> valid_nodes;
find_all_instances_inheriting_path_in_node(edited_scene_root, edited_scene_root, p_instance_path, valid_nodes);
if (valid_nodes.size() > 0) {
edited_scene_map[i] = valid_nodes;
}
}
}
}
if (edited_scene_map.size() > 0) {
// Reload the new instance.
Error err;
Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err);
instance_scene_packed_scene->set_path(p_instance_path, true);
ERR_FAIL_COND(err != OK);
ERR_FAIL_COND(instance_scene_packed_scene.is_null());
HashMap<String, Ref<PackedScene>> local_scene_cache;
local_scene_cache[p_instance_path] = instance_scene_packed_scene;
for (const KeyValue<int, List<Node *>> &edited_scene_map_elem : edited_scene_map) {
// Set the current scene.
int current_scene_idx = edited_scene_map_elem.key;
editor_data.set_edited_scene(current_scene_idx);
Node *current_edited_scene = editor_data.get_edited_scene_root(current_scene_idx);
// Clear the history for this tab (should we allow history to be retained?).
EditorUndoRedoManager::get_singleton()->clear_history();
// Update the version
editor_data.is_scene_changed(current_scene_idx);
for (Node *original_node : edited_scene_map_elem.value) {
// Walk the tree for the current node and extract relevant diff data, storing it in the modification table.
// For additional nodes which are part of the current scene, they get added to the addition table.
HashMap<NodePath, ModificationNodeEntry> modification_table;
List<AdditiveNodeEntry> addition_list;
update_diff_data_for_node(current_edited_scene, original_node, original_node, modification_table, addition_list);
// Disconnect all relevant connections, all connections from and persistent connections to.
for (const KeyValue<NodePath, ModificationNodeEntry> &modification_table_entry : modification_table) {
for (Connection conn : modification_table_entry.value.connections_from) {
conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
}
for (ConnectionWithNodePath cwnp : modification_table_entry.value.connections_to) {
Connection conn = cwnp.connection;
if (conn.flags & CONNECT_PERSIST) {
conn.signal.get_object()->disconnect(conn.signal.get_name(), conn.callable);
}
}
}
// Store all the paths for any selected nodes which are ancestors of the node we're replacing.
List<NodePath> selected_node_paths;
for (Node *selected_node : editor_selection->get_selected_node_list()) {
if (selected_node == original_node || original_node->is_ancestor_of(selected_node)) {
selected_node_paths.push_back(original_node->get_path_to(selected_node));
editor_selection->remove_node(selected_node);
}
}
// Remove all nodes which were added as additional elements (they will be restored later).
for (AdditiveNodeEntry additive_node_entry : addition_list) {
Node *addition_node = additive_node_entry.node;
addition_node->get_parent()->remove_child(addition_node);
}
// Clear ownership of the nodes (kind of hack to workaround an issue with
// replace_by when called on nodes in other tabs).
List<Node *> nodes_owned_by_original_node;
original_node->get_owned_by(original_node, &nodes_owned_by_original_node);
for (Node *owned_node : nodes_owned_by_original_node) {
owned_node->set_owner(nullptr);
}
// Delete all the remaining node children.
while (original_node->get_child_count()) {
Node *child = original_node->get_child(0);
original_node->remove_child(child);
child->queue_free();
}
// Reset the editable instance state.
bool is_editable = true;
Node *owner = original_node->get_owner();
if (owner) {
is_editable = owner->is_editable_instance(original_node);
}
// Load a replacement scene for the node.
Ref<PackedScene> current_packed_scene;
if (original_node->get_scene_file_path() == p_instance_path) {
// If the node file name directly matches the scene we're replacing,
// just load it since we already cached it.
current_packed_scene = instance_scene_packed_scene;
} else {
// Otherwise, check the inheritance chain, reloading and caching any scenes
// we require along the way.
List<String> required_load_paths;
String scene_path = original_node->get_scene_file_path();
// Do we need to check if the paths are empty?
if (!scene_path.is_empty()) {
required_load_paths.push_front(scene_path);
}
Ref<SceneState> inherited_state = original_node->get_scene_inherited_state();
while (inherited_state.is_valid()) {
String inherited_path = inherited_state->get_path();
// Do we need to check if the paths are empty?
if (!inherited_path.is_empty()) {
required_load_paths.push_front(inherited_path);
}
inherited_state = inherited_state->get_base_scene_state();
}
// Ensure the inheritance chain is loaded in the correct order so that cache can
// be properly updated.
for (String path : required_load_paths) {
if (!local_scene_cache.find(path)) {
current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err);
current_packed_scene->set_path(path, true);
local_scene_cache[path] = current_packed_scene;
} else {
current_packed_scene = local_scene_cache[path];
}
}
}
ERR_FAIL_COND(current_packed_scene.is_null());
// Instantiate the node.
Node *instantiated_node = nullptr;
if (current_packed_scene.is_valid()) {
instantiated_node = current_packed_scene->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE);
}
ERR_FAIL_COND(!instantiated_node);
bool original_node_is_displayed_folded = original_node->is_displayed_folded();
bool original_node_scene_instance_load_placeholder = original_node->get_scene_instance_load_placeholder();
// Update the name to match
instantiated_node->set_name(original_node->get_name());
// Is this replacing the edited root node?
String original_node_file_path = original_node->get_scene_file_path();
if (current_edited_scene == original_node) {
instantiated_node->set_scene_instance_state(original_node->get_scene_instance_state());
// Fix unsaved inherited scene
if (original_node_file_path.is_empty()) {
Ref<SceneState> state = current_packed_scene->get_state();
state->set_path(current_packed_scene->get_path());
instantiated_node->set_scene_inherited_state(state);
}
editor_data.set_edited_scene_root(instantiated_node);
current_edited_scene = instantiated_node;
if (original_node->is_inside_tree()) {
SceneTreeDock::get_singleton()->set_edited_scene(current_edited_scene);
original_node->get_tree()->set_edited_scene_root(instantiated_node);
}
}
// Replace the original node with the instantiated version.
original_node->replace_by(instantiated_node, false);
// Mark the old node for deletion.
original_node->queue_free();
// Restore the folded and placeholder state from the original node.
instantiated_node->set_display_folded(original_node_is_displayed_folded);
instantiated_node->set_scene_instance_load_placeholder(original_node_scene_instance_load_placeholder);
if (owner) {
Ref<SceneState> ss_inst = owner->get_scene_instance_state();
if (ss_inst.is_valid()) {
ss_inst->update_instance_resource(p_instance_path, current_packed_scene);
}
owner->set_editable_instance(instantiated_node, is_editable);
}
// Attempt to re-add all the additional nodes.
for (AdditiveNodeEntry additive_node_entry : addition_list) {
Node *parent_node = instantiated_node->get_node_or_null(additive_node_entry.parent);
if (!parent_node) {
parent_node = current_edited_scene;
}
parent_node->add_child(additive_node_entry.node);
parent_node->move_child(additive_node_entry.node, additive_node_entry.index);
// If the additive node's owner was the node which got replaced, update it.
if (additive_node_entry.owner == original_node) {
additive_node_entry.owner = instantiated_node;
}
additive_node_entry.node->set_owner(additive_node_entry.owner);
// If the parent node was lost, attempt to restore the original global transform.
{
Node2D *node_2d = Object::cast_to<Node2D>(additive_node_entry.node);
if (node_2d) {
node_2d->set_transform(additive_node_entry.transform_2d);
}
Node3D *node_3d = Object::cast_to<Node3D>(additive_node_entry.node);
if (node_3d) {
node_3d->set_transform(additive_node_entry.transform_3d);
}
}
}
// Restore the selection.
if (selected_node_paths.size()) {
for (NodePath selected_node_path : selected_node_paths) {
Node *selected_node = instantiated_node->get_node_or_null(selected_node_path);
if (selected_node) {
editor_selection->add_node(selected_node);
}
}
editor_selection->update();
}
// Attempt to restore the modified properties and signals for the instantitated node and all its owned children.
for (KeyValue<NodePath, ModificationNodeEntry> &E : modification_table) {
NodePath new_current_path = E.key;
Node *modifiable_node = instantiated_node->get_node_or_null(new_current_path);
if (modifiable_node) {
// Get properties for this node.
List<PropertyInfo> pinfo;
modifiable_node->get_property_list(&pinfo);
// Get names of all valid property names (TODO: make this more efficent).
List<String> property_names;
for (const PropertyInfo &E2 : pinfo) {
if (E2.usage & PROPERTY_USAGE_STORAGE) {
property_names.push_back(E2.name);
}
}
// Restore the modified properties for this node.
for (const KeyValue<StringName, Variant> &E2 : E.value.property_table) {
if (property_names.find(E2.key)) {
modifiable_node->set(E2.key, E2.value);
}
}
// Restore the connections to other nodes.
for (const ConnectionWithNodePath &E2 : E.value.connections_to) {
Connection conn = E2.connection;
// Get the node the callable is targetting.
Node *target_node = cast_to<Node>(conn.callable.get_object());
// If the callable object no longer exists or is marked for deletion,
// attempt to reaccquire the closest match by using the node path
// we saved earlier.
if (!target_node || !target_node->is_queued_for_deletion()) {
target_node = modifiable_node->get_node_or_null(E2.node_path);
}
if (target_node) {
// Reconstruct the callable.
Callable new_callable = Callable(target_node, conn.callable.get_method());
if (!modifiable_node->is_connected(conn.signal.get_name(), new_callable)) {
ERR_FAIL_COND(modifiable_node->connect(conn.signal.get_name(), new_callable, conn.flags) != OK);
}
}
}
// Restore the connections from other nodes.
for (const Connection &E2 : E.value.connections_from) {
Connection conn = E2;
bool valid = modifiable_node->has_method(conn.callable.get_method()) || Ref<Script>(modifiable_node->get_script()).is_null() || Ref<Script>(modifiable_node->get_script())->has_method(conn.callable.get_method());
ERR_CONTINUE_MSG(!valid, vformat("Attempt to connect signal '%s.%s' to nonexistent method '%s.%s'.", conn.signal.get_object()->get_class(), conn.signal.get_name(), conn.callable.get_object()->get_class(), conn.callable.get_method()));
// Get the object which the signal is connected from.
Object *source_object = conn.signal.get_object();
if (source_object) {
ERR_FAIL_COND(source_object->connect(conn.signal.get_name(), Callable(modifiable_node, conn.callable.get_method()), conn.flags) != OK);
}
}
// Re-add the groups.
for (const Node::GroupInfo &E2 : E.value.groups) {
modifiable_node->add_to_group(E2.name, E2.persistent);
}
}
}
}
// Cleanup the history of the changes.
editor_history.cleanup_history();
current_edited_scene->propagate_notification(NOTIFICATION_NODE_RECACHE_REQUESTED);
}
edited_scene_map.clear();
}
editor_data.set_edited_scene(original_edited_scene_idx);
_edit_current();
}
int EditorNode::plugin_init_callback_count = 0; int EditorNode::plugin_init_callback_count = 0;
void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) { void EditorNode::add_plugin_init_callback(EditorPluginInitializeCallback p_callback) {

View File

@ -87,6 +87,7 @@ class ImportDock;
class LinkButton; class LinkButton;
class MenuBar; class MenuBar;
class MenuButton; class MenuButton;
class Node2D;
class NodeDock; class NodeDock;
class OptionButton; class OptionButton;
class OrphanResourcesDialog; class OrphanResourcesDialog;
@ -818,6 +819,37 @@ public:
Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false); Error load_scene(const String &p_scene, bool p_ignore_broken_deps = false, bool p_set_inherited = false, bool p_clear_errors = true, bool p_force_open_imported = false, bool p_silent_change_tab = false);
Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false); Error load_resource(const String &p_resource, bool p_ignore_broken_deps = false);
HashMap<StringName, Variant> get_modified_properties_for_node(Node *p_node);
struct AdditiveNodeEntry {
Node *node = nullptr;
NodePath parent = NodePath();
Node *owner = nullptr;
int index = 0;
// Used if the original parent node is lost
Transform2D transform_2d;
Transform3D transform_3d;
};
struct ConnectionWithNodePath {
Connection connection;
NodePath node_path;
};
struct ModificationNodeEntry {
HashMap<StringName, Variant> property_table;
List<ConnectionWithNodePath> connections_to;
List<Connection> connections_from;
List<Node::GroupInfo> groups;
};
void update_diff_data_for_node(
Node *p_edited_scene,
Node *p_root,
Node *p_node,
HashMap<NodePath, ModificationNodeEntry> &p_modification_table,
List<AdditiveNodeEntry> &p_addition_list);
bool is_scene_open(const String &p_path); bool is_scene_open(const String &p_path);
void set_current_scene(int p_idx); void set_current_scene(int p_idx);
@ -870,6 +902,9 @@ public:
void reload_scene(const String &p_path); void reload_scene(const String &p_path);
void find_all_instances_inheriting_path_in_node(Node *p_root, Node *p_node, const String &p_instance_path, List<Node *> &p_instance_list);
void reload_instances_with_path_in_edited_scenes(const String &p_path);
bool is_exiting() const { return exiting; } bool is_exiting() const { return exiting; }
Button *get_pause_button() { return pause_button; } Button *get_pause_button() { return pause_button; }

View File

@ -2947,6 +2947,7 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE); BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE);
BIND_CONSTANT(NOTIFICATION_DISABLED); BIND_CONSTANT(NOTIFICATION_DISABLED);
BIND_CONSTANT(NOTIFICATION_ENABLED); BIND_CONSTANT(NOTIFICATION_ENABLED);
BIND_CONSTANT(NOTIFICATION_NODE_RECACHE_REQUESTED);
BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE); BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE);
BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE); BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE);

View File

@ -270,6 +270,7 @@ public:
NOTIFICATION_POST_ENTER_TREE = 27, NOTIFICATION_POST_ENTER_TREE = 27,
NOTIFICATION_DISABLED = 28, NOTIFICATION_DISABLED = 28,
NOTIFICATION_ENABLED = 29, NOTIFICATION_ENABLED = 29,
NOTIFICATION_NODE_RECACHE_REQUESTED = 30,
//keep these linked to node //keep these linked to node
NOTIFICATION_WM_MOUSE_ENTER = 1002, NOTIFICATION_WM_MOUSE_ENTER = 1002,

View File

@ -1046,6 +1046,25 @@ Ref<SceneState> SceneState::get_base_scene_state() const {
return Ref<SceneState>(); return Ref<SceneState>();
} }
void SceneState::update_instance_resource(String p_path, Ref<PackedScene> p_packed_scene) {
ERR_FAIL_COND(p_packed_scene.is_null());
for (const NodeData &nd : nodes) {
if (nd.instance >= 0) {
if (!(nd.instance & FLAG_INSTANCE_IS_PLACEHOLDER)) {
int instance_id = nd.instance & FLAG_MASK;
Ref<PackedScene> original_packed_scene = variants[instance_id];
if (original_packed_scene.is_valid()) {
if (original_packed_scene->get_path() == p_path) {
variants.remove_at(instance_id);
variants.insert(instance_id, p_packed_scene);
}
}
}
}
}
}
int SceneState::find_node_by_path(const NodePath &p_node) const { 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."); ERR_FAIL_COND_V_MSG(node_path_cache.size() == 0, -1, "This operation requires the node cache to have been built.");

View File

@ -150,6 +150,8 @@ public:
Ref<SceneState> get_base_scene_state() const; Ref<SceneState> get_base_scene_state() const;
void update_instance_resource(String p_path, Ref<PackedScene> p_packed_scene);
//unbuild API //unbuild API
int get_node_count() const; int get_node_count() const;