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

Fix incorrect handling of exported typed dictionaries containing nodes in .NET #97948

Conversation

juanjp600
Copy link
Contributor

@juanjp600 juanjp600 commented Oct 7, 2024

This should fix #97850. This pull request consists of multiple commits because there are multiple distinct bugs that needed to be fixed.

As observed here, the scene is written with a copy of the node selected in the inspector instead of a path to it. The reason for this is that the property hint that the scene serializer looks for to rewrite node references as paths is PROPERTY_HINT_TYPE_STRING, but the code generation for exposing .NET properties to Godot does not account for this. That's fixed here.

With that first bug fixed, another one reveals itself: the nodes in the exported dictionary are all nulled out on the .NET side of things. Looking further with Get(nameof(Controls)), what is happening is that the node paths that are stored in the scene are not being turned back into node references. The scene reader is getting an untyped dictionary, which causes the relevant get_typed_*_builtin to not return Variant::OBJECT. The fix is to call set_typed on typed dictionaries from .NET so Godot also sees them as typed dictionaries.

These two fixes are still not enough because if no default non-null value is provided for the exported dictionary, which is the case in the MRP given, the scene reader still fails to convert node paths to the expected node type. That's fixed here.

Here's an updated MRP with a patched scene, because the first bug makes changes to the scene that are not easily reverted automatically: MRP-97850-patched-scene.zip

Do note that because of #97190, the scene will not load correctly if you open it before building the project, so make sure you do that.

@juanjp600 juanjp600 requested review from a team as code owners October 7, 2024 19:00
@juanjp600 juanjp600 changed the title Dotnet typed dictionary export fixes Fix various bugs with exported typed dictionaries in .NET Oct 7, 2024
@juanjp600 juanjp600 changed the title Fix various bugs with exported typed dictionaries in .NET Fix incorrect handling of exported typed dictionaries containing nodes in .NET Oct 7, 2024
@clayjohn clayjohn requested a review from raulsntos October 7, 2024 19:25
@clayjohn clayjohn added this to the 4.4 milestone Oct 7, 2024

_parse_dictionary_hint_string(dnp_base_prop.hint_string, key_subtype, key_subtype_hint, &key_class_name, value_subtype, value_subtype_hint, &value_class_name);

dict.set_typed(key_subtype, StringName(key_class_name), Variant(), value_subtype, StringName(value_class_name), Variant());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is not entirely correct if the type arguments for the dictionary are script types. However, the hint string does not include any information about scripts, so I'm not sure what the best way to address it is.

@Repiteo Repiteo assigned Repiteo and unassigned Repiteo Oct 12, 2024
@Repiteo Repiteo requested review from Repiteo and removed request for a team October 12, 2024 13:36
Copy link
Member

@raulsntos raulsntos left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution. You are totally right about the Dictionary hint being wrong, but I don't really understand the part about set_typed. We're not calling set_typed for typed Arrays and that works fine with arrays of nodes (e.g.: Godot.Collections.Array<Label>). So I feel like there may be a way to fix the bug without set_typed.

Calling set_typed is probably something that we should do anyway, and not just on Dictionary but on Array as well. However, I don't really like the current PR's implementation, it relays heavily on reflection which may be incompatible with trimming/NativeAOT, and may add substantial cost to typed Dictionaries which may be unacceptable to some users.

I have not looked at the changes to packed_scene.cpp at all, that's an area of Godot I'm not very familiar with so I don't think I can comment on it.

@@ -791,7 +795,7 @@ static bool GetStringArrayEnumHint(VariantType elementVariantType,
}
}

hint = PropertyHint.DictionaryType;
hint = containsNodeType ? PropertyHint.TypeString : PropertyHint.DictionaryType;
Copy link
Member

Choose a reason for hiding this comment

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

This should always be PropertyHint.TypeString, no need for the containsNodeType variable.

Suggested change
hint = containsNodeType ? PropertyHint.TypeString : PropertyHint.DictionaryType;
hint = PropertyHint.TypeString;

@juanjp600
Copy link
Contributor Author

juanjp600 commented Oct 12, 2024

I don't really understand the part about set_typed. We're not calling set_typed for typed Arrays and that works fine with arrays of nodes (e.g.: Godot.Collections.Array). So I feel like there may be a way to fix the bug without set_typed.

The reason why I think set_typed is necessary for dictionaries is because the conversion from NodePath to Node can be selective, i.e. it's possible for the conversion to apply to only keys or only values. Theoretically a dictionary of the form Dictionary[NodePath, Node] has to be converted back from an untyped dictionary containing pairs of the form [NodePath, NodePath], and as far as I'm aware there's no other mechanism to tell Godot what to convert. Arrays have only one type argument, so by simply establishing that a NodePath to Node conversion needs to happen, it's safe to infer that it applies to all elements in the array.

You are correct that, to be thorough, arrays should have a set_typed call. Would that be in scope for this pull request or should another be opened for that change?

@raulsntos
Copy link
Member

You are correct that, to be thorough, arrays should have a set_typed call. Would that be in scope for this pull request or should another be opened for that change?

Well, what I was getting at was that I would separate the property hint changes from the set_typed changes. Because, I'm very confident that the property hint changes are correct and would be willing to approve and merge that, but I'm not so confident about the set_typed changes, these may require further discussion.

But I think whatever PR includes the set_typed changes for Dictionary should probably also make the same changes to Array if we can, since we'll want to match the behavior for both types.

@juanjp600
Copy link
Contributor Author

Superseded by #98545 and #98546

@juanjp600 juanjp600 closed this Oct 26, 2024
@AThousandShips AThousandShips removed this from the 4.4 milestone Oct 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Nodes in typed dictionaries do not do not show changes in game made by code (4.4 dev 3 / C#)
5 participants