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

Use register_dynamic for merging #18028

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions crates/bevy_ecs/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2113,7 +2113,7 @@ impl RequiredComponents {
unsafe { self.register_dynamic(component_id, erased, inheritance_depth) };
}

/// Iterates the ids of all required components. This includes recursive required components.
/// Iterates the ids of all required components' ids. This includes recursive required components.
pub fn iter_ids(&self) -> impl Iterator<Item = ComponentId> + '_ {
self.0.keys().copied()
}
Expand All @@ -2131,8 +2131,21 @@ impl RequiredComponents {
// Merges `required_components` into this collection. This only inserts a required component
// if it _did not already exist_.
pub(crate) fn merge(&mut self, required_components: &RequiredComponents) {
for (id, constructor) in &required_components.0 {
self.0.entry(*id).or_insert_with(|| constructor.clone());
for (
component_id,
RequiredComponent {
constructor,
inheritance_depth,
},
) in required_components
.0
.iter()
.map(|(id, req)| (*id, req.clone()))
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer to avoid cloning here, but I don't see a way to do that without making register_dynamic's signature worse.

{
// SAFETY: This exact registration must have been done on `required_components`, so safety is ensured by that caller.
unsafe {
self.register_dynamic(component_id, constructor, inheritance_depth);
}
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2635,6 +2635,37 @@ mod tests {
assert_eq!(to_vec(required_z), vec![(b, 0), (c, 1)]);
}

#[test]
fn required_components_inheritance_depth_bias() {
#[derive(Component, PartialEq, Eq, Clone, Copy, Debug)]
struct MyRequired(bool);

#[derive(Component, Default)]
#[require(MyRequired(|| MyRequired(false)))]
struct MiddleMan;

#[derive(Component, Default)]
#[require(MiddleMan)]
struct ConflictingRequire;

#[derive(Component, Default)]
#[require(MyRequired(|| MyRequired(true)))]
struct MyComponent;

let mut world = World::new();
let order_a = world
.spawn((ConflictingRequire, MyComponent))
.get::<MyRequired>()
.cloned();
let order_b = world
.spawn((MyComponent, ConflictingRequire))
.get::<MyRequired>()
.cloned();

assert_eq!(order_a, Some(MyRequired(true)));
assert_eq!(order_b, Some(MyRequired(true)));
}

#[test]
#[should_panic = "Recursive required components detected: A → B → C → B\nhelp: If this is intentional, consider merging the components."]
fn required_components_recursion_errors() {
Expand Down