-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Guard against VisibilityClass duplicates #20891
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: main
Are you sure you want to change the base?
Conversation
if let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) | ||
&& !visibility_class.contains(&TypeId::of::<C>()) | ||
{ | ||
visibility_class.push(TypeId::of::<C>()); | ||
} |
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.
We can reuse the TypeId
, we should probably have some warning when the entity does not have VisibilityClass
too.
let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) else {
return;
};
let type_id = TypeId::of::<C>();
if !visibility_class.contains(&type_id) {
visibility_class.push(type_id);
}
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 thought about doing it this way but didn't for some reason. If there were to be a warning, what would you expect it to tell you? It seems like the most likely scenario is that it gets automatically handled when someone adds a different component. I'm not sure in what scenario it would warn in the real world and if that would just confuse people.
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 updated the code match what you have. I think it's cleaner to do it this way but I'm not sure about the conventions of warning users so I'd like to get some feedback on that before making that call.
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'm not sure in what scenario it would warn in the real world and if that would just confuse people.
It could happen in case someone used this hook but forgot to #[require(VisibilityClass)]
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.
Maybe something similar to:
bevy/crates/bevy_ecs/src/hierarchy.rs
Lines 463 to 472 in 1dfcde6
warn!( | |
"warning[B0004]: {}{name} with the {ty_name} component has a parent ({parent}) without {ty_name}.\n\ | |
This will cause inconsistent behaviors! See: https://bevy.org/learn/errors/b0004", | |
caller.map(|c| format!("{c}: ")).unwrap_or_default(), | |
ty_name = debug_name.shortname(), | |
name = name.map_or_else( | |
|| format!("Entity {entity}"), | |
|s| format!("The {s} entity") | |
), | |
); |
I wrote a test for this, we can include it to make sure it works as expected: #[derive(Component, Default, Clone, Reflect)]
#[require(VisibilityClass)]
#[reflect(Component, Default, Clone)]
#[component(on_add = add_visibility_class::<Self>)]
struct TestVisibilityClassHook;
#[test]
fn test_add_visibility_class_hook() {
let mut world = World::new();
let entity = world.spawn(TestVisibilityClassHook).id();
let entity_clone = world.spawn_empty().id();
world
.entity_mut(entity)
.clone_with_opt_out(entity_clone, |_| {});
let entity_visibility_class = world.entity(entity).get::<VisibilityClass>().unwrap();
assert_eq!(entity_visibility_class.len(), 1);
let entity_clone_visibility_class =
world.entity(entity_clone).get::<VisibilityClass>().unwrap();
assert_eq!(entity_clone_visibility_class.len(), 1);
} |
Co-authored-by: Dimitrios Loukadakis <[email protected]>
I added this test and also added you as a co-author. Thanks for the assists. I've been trying to get into Bevy contributions so it's nice to have the help. I'm still considering the warning situation and I'll report back when I make a decision for that. |
No worries, I wonder if failing loudly with a panic is more appropriate, because if this hook runs on an entity without a We could panic like this to include some debug information: pub fn add_visibility_class<C>(
mut world: DeferredWorld<'_>,
HookContext { entity, caller, .. }: HookContext,
) where
C: 'static,
{
let Some(mut visibility_class) = world.get_mut::<VisibilityClass>(entity) else {
let component_debug_name = DebugName::type_name::<C>();
let visibility_class_debug_name = DebugName::type_name::<VisibilityClass>();
panic!(
"{}Entity {entity} with the {} component must have the {} component",
caller.map(|c| format!("{c}: ")).unwrap_or_default(),
component_debug_name.shortname(),
visibility_class_debug_name.shortname(),
);
};
let type_id = TypeId::of::<C>();
if !visibility_class.contains(&type_id) {
visibility_class.push(type_id);
}
} Also, even though it's out of scope for this pull request, it's worth noting that nothing currently removes the visibility class once it has been added. Maybe we should track this as a separate issue? |
Objective
clone_and_spawn
a Mesh3D, you get a duplicate in the VisibilityClass Component. This is especially noticable with transaparent materials.Solution
Testing
bevy_inspector_egui
Before the fix:

After the fix:

Sorry for the janky screenshots