Skip to content

Clarify Node::duplicate() semantics on #[var] and #[export] fields #1141

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

Merged
merged 2 commits into from
Jun 4, 2025

Conversation

DominikMendel
Copy link
Contributor

This currently doesn't compile because of an importer area. So please don't merge lol.

But should hopefully be fixed soon. Still will take any feedback. Like I'm unsure if it's wise in test cases to run unwrap() on the things that should be Some given the context.

Copy link
Member

@Bromeon Bromeon left a comment

Choose a reason for hiding this comment

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

Thanks a lot for your contribution, it looks really good for your first pull request! 😊

One thing I've noticed is that a lot of the test assertions aren't testing the duplicate() functionality itself, but a lot around that. We don't need to check if setting a field works or if cloning Gd functions correctly, as we have already dedicated tests for those things elsewhere. So it would be good if assert_eq statements could focus on checks after Node::duplicate() is invoked. Which you do at the end 👍

I also made a comment about Area2D. Let's try to get this problem reproduced with Area2D, and later we can see if we can reduce it even further, to show the problem with only Node. But for now, focus on the first step that demonstrates the problem with Area2D 🙂

Comment on lines 554 to 564
#[godot_api]
impl INode for SomeDuplicator {
fn init(_base: Base<Node>) -> Self {
SomeDuplicator {
int_val: 0,
optional_node: None,
export_area: OnEditor::default(),
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

As this uses default values for every field, it can be generated.

This can be done on the struct declaration above, by adding init:

#[derive(GodotClass)]
#[class(init, base=Node)]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed.

Comment on lines 580 to 588
obj.bind_mut()
.optional_node
.as_mut()
.unwrap()
.set_name("renamed");
assert_eq!(
obj.bind().optional_node.as_ref().unwrap().get_name(),
StringName::from("renamed")
);
Copy link
Member

Choose a reason for hiding this comment

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

Again, just tests what was set. The idea of adding this test is to check if Node::duplicate() copies the values, not if manually setting them works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Understood. Addressed.

Comment on lines 600 to 620
assert_eq!(duplicated_obj.bind().int_val, 5);
assert_eq!(
duplicated_obj
.bind()
.optional_node
.as_ref()
.unwrap()
.instance_id(),
obj_id
);
assert_eq!(
duplicated_obj
.bind()
.optional_node
.as_ref()
.unwrap()
.get_name(),
StringName::from("renamed")
);
assert_eq!(duplicated_obj.bind().export_area.get_collision_layer(), 1);
assert_eq!(duplicated_obj.bind().export_area.get_collision_mask(), 1);
Copy link
Member

Choose a reason for hiding this comment

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

You can extract this into a variable, and then use everywhere:

let duplicated = duplicated_obj.bind();

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@Bromeon Bromeon added bug c: engine Godot classes (nodes, resources, ...) labels Apr 25, 2025
@DominikMendel
Copy link
Contributor Author

@Bromeon I tried the changes to the codegen file but couldn't get it to compile. See previous comment thread : #1141 (comment)

Even with this removed I still couldn't get the test to pass, even without assert_eq!(). So I am not sure what I am doing wrong. I even added some free()s on all the objects I manually created. No luck. Open to any help.

@GodotRust
Copy link

API docs are being generated and will be shortly available at: https://godot-rust.github.io/docs/gdext/pr-1141

@DominikMendel
Copy link
Contributor Author

DominikMendel commented Apr 25, 2025

Here's the code failure after the duplication:

Run Godot integration tests...
  Focused run -- execute only selected Rust tests.
  Rust: found 1 tests in 1 files.

   property_test.rs:
   -- test_some_duplicator ... ERROR: [panic itest/rust/src/object_tests/property_test.rs:581]
  assertion `left == right` failed
    left: 0
   right: 5
  Context: itest `test_some_duplicator` failed
   at: godot_core::private::set_gdext_hook::{{closure}} (godot-core/src/private.rs:303)
(backtrace disabled, run application with `RUST_BACKTRACE=1` environment variable)
   -- test_some_duplicator ... FAILED

Test result: FAILED. 0 passed; 1 failed (focused run).
  Time: 0.00s.

  Failed tests:
  * property_test.rs > test_some_duplicator

Which is this line

        assert_eq!(duplicated.int_val, 5);

@Bromeon
Copy link
Member

Bromeon commented Apr 26, 2025

Hm... Node::duplicate() is documented as:

Duplicates the node, returning a new node with all of its properties, signals, groups, and children copied from the original. The behavior can be tweaked through the flags (see DuplicateFlags).

I wonder if this behavior is different in GDScript?
Or only applies to exported properties?

@DominikMendel
Copy link
Contributor Author

Hm... Node::duplicate() is documented as:

Duplicates the node, returning a new node with all of its properties, signals, groups, and children copied from the original. The behavior can be tweaked through the flags (see DuplicateFlags).

I wonder if this behavior is different in GDScript? Or only applies to exported properties?

Yeah I'm not too sure how to proceed from here. I haven't really used GDScript, so I don't think I can provide much input there.

But I did get my previous information from the link you provided about it duplicated all the properties.

@Bromeon Bromeon added this to the 0.3.x milestone Apr 27, 2025
@Bromeon Bromeon force-pushed the master branch 2 times, most recently from 72d898b to ef4ad67 Compare June 4, 2025 21:26
@Bromeon Bromeon added quality-of-life No new functionality, but improves ergonomics/internals and removed bug labels Jun 4, 2025
@Bromeon Bromeon changed the title Initial test of Godot duplicator with Area2D Clarify Node::duplicate() semantics on #[var] and #[export] fields Jun 4, 2025
@Bromeon Bromeon added the documentation Improvements or additions to documentation label Jun 4, 2025
@Bromeon
Copy link
Member

Bromeon commented Jun 4, 2025

@DominikMendel so I looked into this and debugged a bit.

Turns out duplicating user-defined #[var] properties works fine, we just need to mark them with the storage attribute. I cleaned up the test case and extended it with different ways to do this, plus a plain #[var] which is expected to fail.

Once we have the capability to add per-method custom docs, we can document this behavior for Node::duplicate(). Or maybe make this a proper method Gd::duplicate(), which is returns Gd<T> instead of Option<Gd<Node>>.

Thank you for the reproducible test case!

Additionally test `#[var(usage_flags = [STORAGE])]`, as well as basic `#[var]` which is ignored.

Remove Area2D from codegen again.
@Bromeon Bromeon enabled auto-merge June 4, 2025 21:37
@Bromeon Bromeon added this pull request to the merge queue Jun 4, 2025
@Bromeon Bromeon removed this pull request from the merge queue due to a manual request Jun 4, 2025
@Bromeon Bromeon force-pushed the master branch 2 times, most recently from f8d8204 to edfdc61 Compare June 4, 2025 22:08
@Bromeon Bromeon removed the documentation Improvements or additions to documentation label Jun 4, 2025
@Bromeon Bromeon enabled auto-merge June 4, 2025 22:09
@Bromeon Bromeon added this pull request to the merge queue Jun 4, 2025
Merged via the queue into godot-rust:master with commit 8d7d8eb Jun 4, 2025
18 checks passed
@Bromeon Bromeon added documentation Improvements or additions to documentation and removed quality-of-life No new functionality, but improves ergonomics/internals labels Jun 5, 2025
Bromeon added a commit that referenced this pull request Jun 9, 2025
Clarify `Node::duplicate()` semantics on `#[var]` and `#[export]` fields
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: engine Godot classes (nodes, resources, ...) documentation Improvements or additions to documentation
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants