-
-
Notifications
You must be signed in to change notification settings - Fork 22.5k
PackedScene: Avoid saving signal connections twice #100965
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somehow TIWAGOSly, I'm leaving my approval as the changes look good to me and I gues it will help keep things moving forward. It'd be great to have this thing covered in tests, though.
Could you amend the commit message to formatted like a normal sentence/commit title? E.g. like this PR's title. |
2f80214
to
9bac8bb
Compare
9bac8bb
to
2a31298
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This causes the issue I mentioned in #100097 (comment) I'm not sure how this could be resolved though. Maybe don't apply |
I feel like its better practice to save the button as a separate scene and just instantiate that, which solves the issue. Unless that somehow doesn't work for your original use case
But I'd hate to break something else by fixing something. This probably deserves a little bit more testing to see if this case affects any other scenarios before its merged. |
Did #100160 solve this case? |
Yes.
The point is to avoid excessive scenes, like with built-in scripts. It's a niche use-case though, so it's something that can be fixed later (or not, as there is workaround). |
This comment was marked as outdated.
This comment was marked as outdated.
I have not found a resolution to that bug but I think I have found other places where connections are falsely ignored. This PR should NOT be merged until it is updated . |
PR UpdateI redid the whole PR. The current solution fixes the issues and does not cause the bug mentioned by Kobewi above. ProblemSignals are being saved to packed scene when they should be ignored in these cases:
ReasoningConsider the case where the root node of an instanced scene has a signal connected to itself. There can be such a connection within the scene itself, and also such a connection made by the scene containing it. Therefor when packing the scene, there must be a way to distinguish which scene a connection belongs to based on the connection itself, not just the node it comes from and goes to. SolutionWhen instantiate is called, a unique id for the scene is assigned to and shared by every node and signal connection in that scene. There is one unique id per scene instance. This id is different from |
This is ready for feedback. The code is finished but the tests for signal packing from my old PR need rewritten. I wanted to get feedback that this is a viable solution before writing tests. |
abe2378
to
5feeb11
Compare
// FIXME: This subcase requires GH-48064 to be fixed. | ||
/* TODO needs fixed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Closing the issue does not fix this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I started working on tests/scene/test_packed_scene.h
but decided to wait for feedback on the rest of the code because I wasn't sure if the solution in this PR would be too complex.
so tests/scene/test_packed_scene.h
should be ignored for now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The tests were for a solution that used CONNECT_INHERITED
, so they won't work for this PR now
|
||
//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().is_empty()) { | ||
common_parent = common_parent->get_owner(); | ||
} | ||
|
||
bool exists = false; | ||
|
||
//go through ownership chain to see if this exists | ||
while (common_parent) { | ||
Ref<SceneState> ps; | ||
|
||
if (common_parent == p_owner) { | ||
ps = common_parent->get_scene_inherited_state(); | ||
} else { | ||
ps = common_parent->get_scene_instance_state(); | ||
} | ||
|
||
if (ps.is_valid()) { | ||
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, base_callable.get_method())) { | ||
exists = true; | ||
break; | ||
} | ||
} | ||
|
||
if (common_parent == p_owner) { | ||
break; | ||
} else { | ||
common_parent = common_parent->get_owner(); | ||
} | ||
} | ||
|
||
if (exists) { //already exists (comes from instance or inheritance), so don't save | ||
continue; | ||
} | ||
|
||
{ | ||
Node *nl = p_node; | ||
|
||
bool exists2 = false; | ||
|
||
while (nl) { | ||
if (nl == p_owner) { | ||
Ref<SceneState> state = nl->get_scene_inherited_state(); | ||
if (state.is_valid()) { | ||
int from_node = state->find_node_by_path(nl->get_path_to(p_node)); | ||
int to_node = state->find_node_by_path(nl->get_path_to(target)); | ||
|
||
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, base_callable.get_method())) { | ||
exists2 = true; | ||
break; | ||
} | ||
} | ||
} | ||
|
||
nl = nullptr; | ||
} else { | ||
if (!nl->get_scene_file_path().is_empty()) { | ||
//is an instance | ||
Ref<SceneState> state = nl->get_scene_instance_state(); | ||
if (state.is_valid()) { | ||
int from_node = state->find_node_by_path(nl->get_path_to(p_node)); | ||
int to_node = state->find_node_by_path(nl->get_path_to(target)); | ||
|
||
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, base_callable.get_method())) { | ||
exists2 = true; | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
nl = nl->get_owner(); | ||
} | ||
} | ||
|
||
if (exists2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
worth mentioning that this code seems to solve the issue for the editor since there's never an error when saving a scene in the editor. When packing a scene it checks to see if the connection was already saved, and if it was, not pack it again.
When calling pack
from code, however, this doesn't work as the connections are saved multiple times. I think scene packing was originally just meant to be used by the editor, not in code, which could be why this bug was not caught when this code was written. I admittedly did not figure out exactly why this block wasn't working despite spending a couple days trying to debug it.
However I've been using a custom build of 4.4 with the changes in this PR almost daily for about a month now without any problems.
It doesn't, the scene still gets spammed with invalid connections. |
Summary
Fixes #48064 fixes #86532 fixes #85372 (all variations of the same issue)
(could also close #42161, but no way to test)
When packing a tree of nodes to a PackedScene, signal connections were getting saved twice in some circumstances, when they should have been ignored.
See PR update below for a better explanation of the problem and solution.
PR history
I originally made PR #97303 which was partially reverted by #100235 because it inadvertently caused #100097.
This fixes #48064 without causing #100097 using @KoBeWi's suggestion from 100097.It changes the line committed in #66289 which fixed 66154 to useGEN_EDIT_STATE_MAIN
instead ofGEN_EDIT_STATE_INSTANCE
, I've verified this change does not reopen #66154