Skip to content
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

PackedScene: Avoid saving signal connections twice #100965

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

cixil
Copy link
Contributor

@cixil cixil commented Dec 31, 2024

Fixes #48064 and fixes #86532

When packing a tree of nodes to a PackedScene, signal connections originating from the root node of a scene instance within that tree would get duplicated into the PackedScene and cause "duplicate signal" errors when instantiating it.


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 use GEN_EDIT_STATE_MAIN instead of GEN_EDIT_STATE_INSTANCE, I've verified this change does not reopen #66154

@cixil cixil requested a review from a team as a code owner December 31, 2024 01:25
@cixil
Copy link
Contributor Author

cixil commented Dec 31, 2024

@akien-mga

@cixil cixil changed the title avoid-saving-signals-twice Avoid saving signals twice Dec 31, 2024
@Chaosus Chaosus added this to the 4.4 milestone Dec 31, 2024
@@ -822,7 +822,7 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
if (type == "PackedScene") { // Its a scene.
Ref<PackedScene> ps = ResourceLoader::load(p_path, "PackedScene", ResourceFormatLoader::CACHE_MODE_IGNORE);
ERR_FAIL_COND_V(ps.is_null(), p_path);
Node *node = ps->instantiate(PackedScene::GEN_EDIT_STATE_INSTANCE); // Make sure the child scene root gets the correct inheritance chain.
Node *node = ps->instantiate(PackedScene::GEN_EDIT_STATE_MAIN); // Make sure scene root gets the correct inheritance chain.
Copy link
Contributor Author

@cixil cixil Jan 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding of the code (which could be wrong) is that this line is calling instantiate on every packed scene in the game as part of some necessary processing when exporting the game to a binary file. Since we are just processing the scenes alone by themselves, it makes sense to use GEN_EDIT_STATE_MAIN instead of INSTANCE here.

The CONNECT_INHERITED flag is applied here depending on the GEN_EDIT_STATE

cfrom->connect(snames[c.signal], callable, CONNECT_PERSIST | c.flags | (p_edit_state == GEN_EDIT_STATE_MAIN ? 0 : CONNECT_INHERITED));

I believe passing GEN_EDIT_STATE_INSTANCE to instantiate was applying the CONNECT_INHERITED flag falsely to signal connections and saving them. It was not game breaking, so went unnoticed.

But by changing the GEN_EDIT_STATE here, we can use the CONNECT_INHERITED flag to correctly process signals when packing a scene.

But this should be verified by someone more familiar with the engine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to be 100% sure about the implications without detailedly debugging all this myself, as it's long since I last touched this code, but I'd say your logic is good.

The use of GEN_EDIT_STATE_INSTANCE was in fact discussed (see 6973abd). The reason stated there for prefering it was a bit weak. and there wasn't a strong preference for either. Now we have a solid argument to back why GEN_EDIT_STATE_MAIN is preferable. I agree with you that it's the right choice in this context, where the scene is being instantiated in a standalone fashion, which warrants it to be treated as the main one.

@akien-mga akien-mga requested review from KoBeWi and a team January 7, 2025 22:02
Copy link
Member

@RandomShaper RandomShaper left a 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.

@akien-mga akien-mga changed the title Avoid saving signals twice PackedScene: Avoid saving signal connections twice Jan 8, 2025
@akien-mga
Copy link
Member

Could you amend the commit message to formatted like a normal sentence/commit title? E.g. like this PR's title.

@cixil cixil force-pushed the avoid-saving-signals-twice branch from 2f80214 to 9bac8bb Compare January 8, 2025 15:31
@cixil cixil force-pushed the avoid-saving-signals-twice branch from 9bac8bb to 2a31298 Compare January 8, 2025 15:34
@cixil
Copy link
Contributor Author

cixil commented Jan 8, 2025

This and #97303 added tests to make sure signals are saved properly by PackedScene's pack function.

I wanted to add a test for the GEN_EDIT_STATE_MAIN thing, but I had no idea how to approach that, as I couldn't find how exports are tested.
The test would need to verify that signal connections are present in exported projects in addition to the issue in #66154

@KoBeWi
Copy link
Member

KoBeWi commented Jan 8, 2025

This causes the issue I mentioned in #100097 (comment)
In my plugin I remove part of the scene, pack it and then re-instantiate (sort of built-in PackedScene). This PR makes the signals stripped with this operation.
MRP:
Brokensignals.zip
Open the Bug bottom panel and click the buttons.

I'm not sure how this could be resolved though. Maybe don't apply CONNECT_INHERITED when edit state is 0? Though it's such a corner-case that it's probably not blocking this PR.
My workaround is manually stripping the flag from signals. It's unexposed though, which makes it even more hacky.

@cixil
Copy link
Contributor Author

cixil commented Jan 9, 2025

This causes the issue I mentioned in #100097 (comment)

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

@export var button_scene:PackedScene
...
	for i in 10:
		add_child(button_scene.instantiate())

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.

@cixil
Copy link
Contributor Author

cixil commented Jan 9, 2025

Did #100160 solve this case?

@KoBeWi
Copy link
Member

KoBeWi commented Jan 9, 2025

Yes.

I feel like its better practice to save the button as a separate scene and just instantiate that

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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment