-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Let FilteredEntity(Ref|Mut) receive access when nested.
#18236
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
Changes from 2 commits
1a44e14
bdd75d4
5ca8704
51d945e
fee46fe
54d0950
c50edbe
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -248,11 +248,9 @@ impl<'w, D: QueryData, F: QueryFilter> QueryBuilder<'w, D, F> { | |
| pub fn transmute_filtered<NewD: QueryData, NewF: QueryFilter>( | ||
| &mut self, | ||
| ) -> &mut QueryBuilder<'w, NewD, NewF> { | ||
| let mut fetch_state = NewD::init_state(self.world); | ||
| let fetch_state = NewD::init_state(self.world); | ||
| let filter_state = NewF::init_state(self.world); | ||
|
|
||
| NewD::set_access(&mut fetch_state, &self.access); | ||
|
|
||
| let mut access = FilteredAccess::default(); | ||
| NewD::update_component_access(&fetch_state, &mut access); | ||
| NewF::update_component_access(&filter_state, &mut access); | ||
|
|
@@ -275,7 +273,10 @@ impl<'w, D: QueryData, F: QueryFilter> QueryBuilder<'w, D, F> { | |
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use crate::{prelude::*, world::FilteredEntityRef}; | ||
| use crate::{ | ||
| prelude::*, | ||
| world::{EntityMutExcept, EntityRefExcept, FilteredEntityMut, FilteredEntityRef}, | ||
| }; | ||
| use std::dbg; | ||
|
|
||
| #[derive(Component, PartialEq, Debug)] | ||
|
|
@@ -422,6 +423,89 @@ mod tests { | |
| } | ||
| } | ||
|
|
||
| #[test] | ||
| fn builder_provide_access() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. great tests! |
||
| let mut world = World::new(); | ||
| world.spawn((A(0), B(1))); | ||
|
|
||
| let mut query = | ||
| QueryBuilder::<(Entity, FilteredEntityRef, FilteredEntityMut)>::new(&mut world) | ||
| .data::<&mut A>() | ||
| .data::<&B>() | ||
| .build(); | ||
|
|
||
| // The `FilteredEntityRef` only has read access, so the `FilteredEntityMut` can have read access without conflicts | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The access is provided from left to right, so if the tuple had been (FilteredEntityMut, FilteredEntityRef), the Ref wouldn't have read access to the mutable component? It is not super obvious that the order matters, maybe this should be documented slightly more in the FilteredEntityRef/Mut docstrings?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, that's right!
I wanted to ensure that it was sound to use more than one
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's reasonable |
||
| let (_entity, entity_ref_1, mut entity_ref_2) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref_1.get::<A>().is_some()); | ||
| assert!(entity_ref_1.get::<B>().is_some()); | ||
| assert!(entity_ref_2.get::<A>().is_some()); | ||
| assert!(entity_ref_2.get_mut::<A>().is_none()); | ||
| assert!(entity_ref_2.get::<B>().is_some()); | ||
| assert!(entity_ref_2.get_mut::<B>().is_none()); | ||
|
|
||
| let mut query = | ||
| QueryBuilder::<(Entity, FilteredEntityMut, FilteredEntityMut)>::new(&mut world) | ||
| .data::<&mut A>() | ||
| .data::<&B>() | ||
| .build(); | ||
|
|
||
| // The first `FilteredEntityMut` has write access to A, so the second one cannot have write access | ||
| let (_entity, mut entity_ref_1, mut entity_ref_2) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref_1.get::<A>().is_some()); | ||
| assert!(entity_ref_1.get_mut::<A>().is_some()); | ||
| assert!(entity_ref_1.get::<B>().is_some()); | ||
| assert!(entity_ref_1.get_mut::<B>().is_none()); | ||
| assert!(entity_ref_2.get::<A>().is_none()); | ||
| assert!(entity_ref_2.get_mut::<A>().is_none()); | ||
| assert!(entity_ref_2.get::<B>().is_some()); | ||
| assert!(entity_ref_2.get_mut::<B>().is_none()); | ||
|
|
||
| let mut query = QueryBuilder::<(FilteredEntityMut, &mut A, &B)>::new(&mut world) | ||
| .data::<&mut A>() | ||
| .data::<&mut B>() | ||
| .build(); | ||
|
|
||
| // Any `A` access would conflict with `&mut A`, and write access to `B` would conflict with `&B`. | ||
| let (mut entity_ref, _a, _b) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref.get::<A>().is_none()); | ||
| assert!(entity_ref.get_mut::<A>().is_none()); | ||
| assert!(entity_ref.get::<B>().is_some()); | ||
| assert!(entity_ref.get_mut::<B>().is_none()); | ||
|
|
||
| let mut query = QueryBuilder::<(FilteredEntityMut, &mut A, &B)>::new(&mut world) | ||
| .data::<EntityMut>() | ||
| .build(); | ||
|
|
||
| // Same as above, but starting from "all" access | ||
| let (mut entity_ref, _a, _b) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref.get::<A>().is_none()); | ||
| assert!(entity_ref.get_mut::<A>().is_none()); | ||
| assert!(entity_ref.get::<B>().is_some()); | ||
| assert!(entity_ref.get_mut::<B>().is_none()); | ||
|
|
||
| let mut query = QueryBuilder::<(FilteredEntityMut, EntityMutExcept<A>)>::new(&mut world) | ||
| .data::<EntityMut>() | ||
| .build(); | ||
|
|
||
| // Removing `EntityMutExcept<A>` just leaves A | ||
| let (mut entity_ref_1, _entity_ref_2) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref_1.get::<A>().is_some()); | ||
| assert!(entity_ref_1.get_mut::<A>().is_some()); | ||
| assert!(entity_ref_1.get::<B>().is_none()); | ||
| assert!(entity_ref_1.get_mut::<B>().is_none()); | ||
|
|
||
| let mut query = QueryBuilder::<(FilteredEntityMut, EntityRefExcept<A>)>::new(&mut world) | ||
| .data::<EntityMut>() | ||
| .build(); | ||
|
|
||
| // Removing `EntityRefExcept<A>` just leaves A, plus read access | ||
| let (mut entity_ref_1, _entity_ref_2) = query.single_mut(&mut world).unwrap(); | ||
| assert!(entity_ref_1.get::<A>().is_some()); | ||
| assert!(entity_ref_1.get_mut::<A>().is_some()); | ||
| assert!(entity_ref_1.get::<B>().is_some()); | ||
| assert!(entity_ref_1.get_mut::<B>().is_none()); | ||
| } | ||
|
|
||
| /// Regression test for issue #14348 | ||
| #[test] | ||
| fn builder_static_dense_dynamic_sparse() { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.