Skip to content

Commit c72ac48

Browse files
committed
Add Allows filter to bypass DefaultQueryFilters
1 parent cc69fdd commit c72ac48

File tree

4 files changed

+65
-2
lines changed

4 files changed

+65
-2
lines changed

crates/bevy_ecs/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ pub mod prelude {
7979
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
8080
name::{Name, NameOrEntity},
8181
observer::{Observer, Trigger},
82-
query::{Added, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
82+
query::{Added, Allows, AnyOf, Changed, Has, Or, QueryBuilder, QueryState, With, Without},
8383
related,
8484
relationship::RelationshipTarget,
8585
removal_detection::RemovedComponents,

crates/bevy_ecs/src/query/access.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,10 @@ impl<T: SparseSetIndex> Access<T> {
257257
/// This is for components whose values are not accessed (and thus will never cause conflicts),
258258
/// but whose presence in an archetype may affect query results.
259259
///
260-
/// Currently, this is only used for [`Has<T>`].
260+
/// Currently, this is only used for [`Has<T>`] and [`Allows<T>`].
261261
///
262262
/// [`Has<T>`]: crate::query::Has
263+
/// [`Allows<T>`]: crate::query::filter::Allows
263264
pub fn add_archetypal(&mut self, index: T) {
264265
self.archetypal.grow_and_insert(index.sparse_set_index());
265266
}
@@ -499,6 +500,7 @@ impl<T: SparseSetIndex> Access<T> {
499500
self.resource_read_and_writes
500501
.union_with(&other.resource_read_and_writes);
501502
self.resource_writes.union_with(&other.resource_writes);
503+
self.archetypal.union_with(&other.archetypal);
502504
}
503505

504506
/// Returns `true` if the access and `other` can be active at the same time,

crates/bevy_ecs/src/query/filter.rs

+57
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,63 @@ all_tuples!(
555555
S
556556
);
557557

558+
/// Allows a query to contain entities with the component `T`, bypassing [`DefaultQueryFilters`].
559+
///
560+
/// [`DefaultQueryFilters`]: crate::entity_disabling::DefaultQueryFilters
561+
pub struct Allows<T>(PhantomData<T>);
562+
563+
/// SAFETY:
564+
/// `update_component_access` does not add any accesses.
565+
/// This is sound because [`QueryFilter::filter_fetch`] does not access any components.
566+
/// `update_component_access` adds an archetypal filter for `T`.
567+
/// This is sound because it doesn't affect the query
568+
unsafe impl<T: Component> WorldQuery for Allows<T> {
569+
type Fetch<'w> = ();
570+
type State = ComponentId;
571+
572+
fn shrink_fetch<'wlong: 'wshort, 'wshort>(_: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {}
573+
574+
#[inline]
575+
unsafe fn init_fetch(_: UnsafeWorldCell, _: &ComponentId, _: Tick, _: Tick) {}
576+
577+
// Even if the component is sparse, this implementation doesn't do anything with it
578+
const IS_DENSE: bool = true;
579+
580+
#[inline]
581+
unsafe fn set_archetype(_: &mut (), _: &ComponentId, _: &Archetype, _: &Table) {}
582+
583+
#[inline]
584+
unsafe fn set_table(_: &mut (), _: &ComponentId, _: &Table) {}
585+
586+
#[inline]
587+
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
588+
access.access_mut().add_archetypal(id);
589+
}
590+
591+
fn init_state(world: &mut World) -> ComponentId {
592+
world.register_component::<T>()
593+
}
594+
595+
fn get_state(components: &Components) -> Option<Self::State> {
596+
components.component_id::<T>()
597+
}
598+
599+
fn matches_component_set(_: &ComponentId, _: &impl Fn(ComponentId) -> bool) -> bool {
600+
// Allows<T> always matches
601+
true
602+
}
603+
}
604+
605+
// SAFETY: WorldQuery impl performs no access at all
606+
unsafe impl<T: Component> QueryFilter for Allows<T> {
607+
const IS_ARCHETYPAL: bool = true;
608+
609+
#[inline(always)]
610+
unsafe fn filter_fetch(_: &mut Self::Fetch<'_>, _: Entity, _: TableRow) -> bool {
611+
true
612+
}
613+
}
614+
558615
/// A filter on a component that only retains results the first time after they have been added.
559616
///
560617
/// A common use for this filter is one-time initialization.

crates/bevy_ecs/src/query/state.rs

+4
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,10 @@ mod tests {
22322232
let mut query = QueryState::<Has<C>>::new(&mut world);
22332233
assert_eq!(3, query.iter(&world).count());
22342234

2235+
// Allows should bypass the filter entirely
2236+
let mut query = QueryState::<(), Allows<C>>::new(&mut world);
2237+
assert_eq!(3, query.iter(&world).count());
2238+
22352239
// Other filters should still be respected
22362240
let mut query = QueryState::<Has<C>, Without<B>>::new(&mut world);
22372241
assert_eq!(1, query.iter(&world).count());

0 commit comments

Comments
 (0)