Skip to content
Merged
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
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub mod prelude {
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
name::{Name, NameOrEntity},
observer::{Observer, Trigger},
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
query::{Added, Allows, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
related,
relationship::RelationshipTarget,
removal_detection::RemovedComponents,
Expand Down
4 changes: 3 additions & 1 deletion crates/bevy_ecs/src/query/access.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,10 @@ impl<T: SparseSetIndex> Access<T> {
/// This is for components whose values are not accessed (and thus will never cause conflicts),
/// but whose presence in an archetype may affect query results.
///
/// Currently, this is only used for [`Has<T>`].
/// Currently, this is only used for [`Has<T>`] and [`Allows<T>`].
///
/// [`Has<T>`]: crate::query::Has
/// [`Allows<T>`]: crate::query::filter::Allows
pub fn add_archetypal(&mut self, index: T) {
self.archetypal.grow_and_insert(index.sparse_set_index());
}
Expand Down Expand Up @@ -499,6 +500,7 @@ impl<T: SparseSetIndex> Access<T> {
self.resource_read_and_writes
.union_with(&other.resource_read_and_writes);
self.resource_writes.union_with(&other.resource_writes);
self.archetypal.union_with(&other.archetypal);
}

/// Returns `true` if the access and `other` can be active at the same time,
Expand Down
57 changes: 57 additions & 0 deletions crates/bevy_ecs/src/query/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,63 @@ all_tuples!(
S
);

/// Allows a query to contain entities with the component `T`, bypassing [`DefaultQueryFilters`].
///
/// [`DefaultQueryFilters`]: crate::entity_disabling::DefaultQueryFilters
pub struct Allows<T>(PhantomData<T>);

/// SAFETY:
/// `update_component_access` does not add any accesses.
/// This is sound because [`QueryFilter::filter_fetch`] does not access any components.
/// `update_component_access` adds an archetypal filter for `T`.
/// This is sound because it doesn't affect the query
unsafe impl<T: Component> WorldQuery for Allows<T> {
type Fetch<'w> = ();
type State = ComponentId;

fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {}

#[inline]
unsafe fn init_fetch(_: UnsafeWorldCell, _: &ComponentId, _: Tick, _: Tick) {}

// Even if the component is sparse, this implementation doesn't do anything with it
const IS_DENSE: bool = true;

#[inline]
unsafe fn set_archetype(_: &mut (), _: &ComponentId, _: &Archetype, _: &Table) {}

#[inline]
unsafe fn set_table(_: &mut (), _: &ComponentId, _: &Table) {}

#[inline]
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
access.access_mut().add_archetypal(id);
}

fn init_state(world: &mut World) -> ComponentId {
world.register_component::<T>()
}

fn get_state(components: &Components) -> Option<Self::State> {
components.component_id::<T>()
}

fn matches_component_set(_: &ComponentId, _: &impl Fn(ComponentId) -> bool) -> bool {
// Allows<T> always matches
true
}
}

// SAFETY: WorldQuery impl performs no access at all
unsafe impl<T: Component> QueryFilter for Allows<T> {
const IS_ARCHETYPAL: bool = true;

#[inline(always)]
unsafe fn filter_fetch(_: &mut Self::Fetch<'_>, _: Entity, _: TableRow) -> bool {
true
}
}

/// A filter on a component that only retains results the first time after they have been added.
///
/// A common use for this filter is one-time initialization.
Expand Down
4 changes: 4 additions & 0 deletions crates/bevy_ecs/src/query/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2232,6 +2232,10 @@ mod tests {
let mut query = QueryState::<Has<C>>::new(&mut world);
assert_eq!(3, query.iter(&world).count());

// Allows should bypass the filter entirely
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to write or update a test or example explaining why allows is different than has?

let mut query = QueryState::<(), Allows<C>>::new(&mut world);
assert_eq!(3, query.iter(&world).count());

// Other filters should still be respected
let mut query = QueryState::<Has<C>, Without<B>>::new(&mut world);
assert_eq!(1, query.iter(&world).count());
Expand Down