// Copyright 2019 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // modified by Bevy contributors use core::{ marker::PhantomData, ops::{Deref, DerefMut}, ptr::NonNull, }; use std::vec; use crate::{access::QueryAccess, archetype::Archetype, Component, Entity, MissingComponent}; /// A collection of component types to fetch from a `World` pub trait Query { #[doc(hidden)] type Fetch: for<'a> Fetch<'a>; } /// A fetch that is read only. This should only be implemented for read-only fetches. pub unsafe trait ReadOnlyFetch {} /// A fetch that will always match every entity in an archetype (aka Fetch::should_skip always returns false) pub trait UnfilteredFetch {} /// Streaming iterators over contiguous homogeneous ranges of components pub trait Fetch<'a>: Sized { /// Type of value to be fetched type Item; /// A value on which `get` may never be called #[allow(clippy::declare_interior_mutable_const)] // no const fn in traits const DANGLING: Self; /// How this query will access `archetype`, if at all fn access() -> QueryAccess; /// Construct a `Fetch` for `archetype` if it should be traversed /// /// # Safety /// `offset` must be in bounds of `archetype` unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option; /// if this returns true, the nth item should be skipped during iteration /// /// # Safety /// shouldn't be called if there is no current item unsafe fn should_skip(&self, _n: usize) -> bool { false } /// Access the `n`th item in this archetype without bounds checking /// /// # Safety /// - Must only be called after `borrow` /// - `release` must not be called while `'a` is still live /// - Bounds-checking must be performed externally /// - Any resulting borrows must be legal (e.g. no &mut to something another iterator might access) unsafe fn fetch(&self, n: usize) -> Self::Item; } #[derive(Copy, Clone, Debug)] pub struct EntityFetch(NonNull); unsafe impl ReadOnlyFetch for EntityFetch {} impl UnfilteredFetch for EntityFetch {} impl Query for Entity { type Fetch = EntityFetch; } impl<'a> Fetch<'a> for EntityFetch { type Item = Entity; const DANGLING: Self = Self(NonNull::dangling()); #[inline] unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(EntityFetch(NonNull::new_unchecked( archetype.entities().as_ptr().add(offset), ))) } #[inline] unsafe fn fetch(&self, n: usize) -> Self::Item { *self.0.as_ptr().add(n) } #[inline] fn access() -> QueryAccess { QueryAccess::None } } impl<'a, T: Component> Query for &'a T { type Fetch = FetchRead; } #[doc(hidden)] pub struct FetchRead(NonNull); unsafe impl ReadOnlyFetch for FetchRead {} impl UnfilteredFetch for FetchRead {} impl<'a, T: Component> Fetch<'a> for FetchRead { type Item = &'a T; const DANGLING: Self = Self(NonNull::dangling()); unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) } #[inline] unsafe fn fetch(&self, n: usize) -> &'a T { &*self.0.as_ptr().add(n) } #[inline] fn access() -> QueryAccess { QueryAccess::read::() } } impl<'a, T: Component> Query for &'a mut T { type Fetch = FetchMut; } impl Query for Option { type Fetch = TryFetch; } /// Unique borrow of an entity's component pub struct Mut<'a, T: Component> { pub(crate) value: &'a mut T, pub(crate) mutated: &'a mut bool, } impl<'a, T: Component> Mut<'a, T> { /// Creates a new mutable reference to a component. This is unsafe because the index bounds are not checked. /// /// # Safety /// This doesn't check the bounds of index in archetype pub unsafe fn new(archetype: &'a Archetype, index: usize) -> Result { let (target, type_state) = archetype .get_with_type_state::() .ok_or_else(MissingComponent::new::)?; Ok(Self { value: &mut *target.as_ptr().add(index), mutated: &mut *type_state.mutated().as_ptr().add(index), }) } } unsafe impl Send for Mut<'_, T> {} unsafe impl Sync for Mut<'_, T> {} impl<'a, T: Component> Deref for Mut<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { self.value } } impl<'a, T: Component> DerefMut for Mut<'a, T> { #[inline] fn deref_mut(&mut self) -> &mut T { *self.mutated = true; self.value } } impl<'a, T: Component + core::fmt::Debug> core::fmt::Debug for Mut<'a, T> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.value.fmt(f) } } impl<'a, T: Component> Query for Mut<'a, T> { type Fetch = FetchMut; } #[doc(hidden)] pub struct FetchMut(NonNull, NonNull); impl UnfilteredFetch for FetchMut {} impl<'a, T: Component> Fetch<'a> for FetchMut { type Item = Mut<'a, T>; const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get_with_type_state::() .map(|(components, type_state)| { Self( NonNull::new_unchecked(components.as_ptr().add(offset)), NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)), ) }) } #[inline] unsafe fn fetch(&self, n: usize) -> Mut<'a, T> { Mut { value: &mut *self.0.as_ptr().add(n), mutated: &mut *self.1.as_ptr().add(n), } } #[inline] fn access() -> QueryAccess { QueryAccess::write::() } } macro_rules! impl_or_query { ( $( $T:ident ),+ ) => { impl<$( $T: Query ),+> Query for Or<($( $T ),+)> { type Fetch = FetchOr<($( $T::Fetch ),+)>; } impl<'a, $( $T: Fetch<'a> ),+> Fetch<'a> for FetchOr<($( $T ),+)> { type Item = ($( $T::Item ),+); const DANGLING: Self = Self(($( $T::DANGLING ),+)); fn access() -> QueryAccess { QueryAccess::union(vec![ $($T::access(),)+ ]) } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(Self(( $( $T::get(archetype, offset)?),+ ))) } #[allow(non_snake_case)] unsafe fn fetch(&self, n: usize) -> Self::Item { let ($( $T ),+) = &self.0; ($( $T.fetch(n) ),+) } #[allow(non_snake_case)] unsafe fn should_skip(&self, n: usize) -> bool { let ($( $T ),+) = &self.0; true $( && $T.should_skip(n) )+ } } unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for Or<($( $T ),+)> {} unsafe impl<$( $T: ReadOnlyFetch ),+> ReadOnlyFetch for FetchOr<($( $T ),+)> {} }; } impl_or_query!(Q1, Q2); impl_or_query!(Q1, Q2, Q3); impl_or_query!(Q1, Q2, Q3, Q4); impl_or_query!(Q1, Q2, Q3, Q4, Q5); impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6); impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7); impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8); impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9); impl_or_query!(Q1, Q2, Q3, Q4, Q5, Q6, Q7, Q8, Q9, Q10); /// Query transformer performing a logical or on a pair of queries /// Intended to be used on Mutated or Changed queries. /// # Example /// ``` /// # use bevy_hecs::*; /// let mut world = World::new(); /// world.spawn((123, true, 1., Some(1))); /// world.spawn((456, false, 2., Some(0))); /// for mut b in world.query_mut::>().skip(1).take(1) { /// *b += 1; /// } /// let components = world /// .query_mut::, Mutated, Mutated, Mutated>)>>() /// .map(|(b, i, f, o)| (*b, *i)) /// .collect::>(); /// assert_eq!(components, &[(false, 457)]); /// ``` pub struct Or(PhantomData); //pub struct Or(PhantomData<(Q1, Q2, Q3)>); #[doc(hidden)] pub struct FetchOr(T); /// Query transformer that retrieves components of type `T` that have been mutated since the start of the frame. /// Added components do not count as mutated. pub struct Mutated<'a, T> { value: &'a T, } impl<'a, T: Component> Deref for Mutated<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { self.value } } impl<'a, T: Component> Query for Mutated<'a, T> { type Fetch = FetchMutated; } #[doc(hidden)] pub struct FetchMutated(NonNull, NonNull); unsafe impl ReadOnlyFetch for FetchMutated {} impl<'a, T: Component> Fetch<'a> for FetchMutated { type Item = Mutated<'a, T>; const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); #[inline] fn access() -> QueryAccess { QueryAccess::read::() } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get_with_type_state::() .map(|(components, type_state)| { Self( NonNull::new_unchecked(components.as_ptr().add(offset)), NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)), ) }) } unsafe fn should_skip(&self, n: usize) -> bool { // skip if the current item wasn't mutated !*self.1.as_ptr().add(n) } #[inline] unsafe fn fetch(&self, n: usize) -> Self::Item { Mutated { value: &*self.0.as_ptr().add(n), } } } /// Query transformer that retrieves components of type `T` that have been added since the start of the frame. pub struct Added<'a, T> { value: &'a T, } impl<'a, T: Component> Deref for Added<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { self.value } } impl<'a, T: Component> Query for Added<'a, T> { type Fetch = FetchAdded; } #[doc(hidden)] pub struct FetchAdded(NonNull, NonNull); unsafe impl ReadOnlyFetch for FetchAdded {} impl<'a, T: Component> Fetch<'a> for FetchAdded { type Item = Added<'a, T>; const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); #[inline] fn access() -> QueryAccess { QueryAccess::read::() } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get_with_type_state::() .map(|(components, type_state)| { Self( NonNull::new_unchecked(components.as_ptr().add(offset)), NonNull::new_unchecked(type_state.added().as_ptr().add(offset)), ) }) } unsafe fn should_skip(&self, n: usize) -> bool { // skip if the current item wasn't added !*self.1.as_ptr().add(n) } #[inline] unsafe fn fetch(&self, n: usize) -> Self::Item { Added { value: &*self.0.as_ptr().add(n), } } } /// Query transformer that retrieves components of type `T` that have either been mutated or added since the start of the frame. pub struct Changed<'a, T> { value: &'a T, } impl<'a, T: Component> Deref for Changed<'a, T> { type Target = T; #[inline] fn deref(&self) -> &T { self.value } } impl<'a, T: Component> Query for Changed<'a, T> { type Fetch = FetchChanged; } #[doc(hidden)] pub struct FetchChanged(NonNull, NonNull, NonNull); unsafe impl ReadOnlyFetch for FetchChanged {} impl<'a, T: Component> Fetch<'a> for FetchChanged { type Item = Changed<'a, T>; const DANGLING: Self = Self( NonNull::dangling(), NonNull::dangling(), NonNull::dangling(), ); #[inline] fn access() -> QueryAccess { QueryAccess::read::() } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get_with_type_state::() .map(|(components, type_state)| { Self( NonNull::new_unchecked(components.as_ptr().add(offset)), NonNull::new_unchecked(type_state.added().as_ptr().add(offset)), NonNull::new_unchecked(type_state.mutated().as_ptr().add(offset)), ) }) } unsafe fn should_skip(&self, n: usize) -> bool { // skip if the current item wasn't added or mutated !*self.1.as_ptr().add(n) && !*self.2.as_ptr().add(n) } #[inline] unsafe fn fetch(&self, n: usize) -> Self::Item { Changed { value: &*self.0.as_ptr().add(n), } } } #[doc(hidden)] pub struct TryFetch(Option); unsafe impl ReadOnlyFetch for TryFetch where T: ReadOnlyFetch {} impl UnfilteredFetch for TryFetch where T: UnfilteredFetch {} impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { type Item = Option; const DANGLING: Self = Self(None); #[inline] fn access() -> QueryAccess { QueryAccess::optional(T::access()) } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(Self(T::get(archetype, offset))) } unsafe fn fetch(&self, n: usize) -> Option { Some(self.0.as_ref()?.fetch(n)) } unsafe fn should_skip(&self, n: usize) -> bool { self.0.as_ref().map_or(false, |fetch| fetch.should_skip(n)) } } /// Query transformer skipping entities that have a `T` component /// /// See also `QueryBorrow::without`. /// /// # Example /// ``` /// # use bevy_hecs::*; /// let mut world = World::new(); /// let a = world.spawn((123, true, "abc")); /// let b = world.spawn((456, false)); /// let c = world.spawn((42, "def")); /// let entities = world.query::>() /// .map(|(e, &i)| (e, i)) /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` pub struct Without(PhantomData<(Q, fn(T))>); impl Query for Without { type Fetch = FetchWithout; } #[doc(hidden)] pub struct FetchWithout(F, PhantomData); unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWithout where F: ReadOnlyFetch { } impl UnfilteredFetch for FetchWithout where F: UnfilteredFetch {} impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { type Item = F::Item; const DANGLING: Self = Self(F::DANGLING, PhantomData); #[inline] fn access() -> QueryAccess { QueryAccess::without::(F::access()) } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { if archetype.has::() { return None; } Some(Self(F::get(archetype, offset)?, PhantomData)) } unsafe fn fetch(&self, n: usize) -> F::Item { self.0.fetch(n) } unsafe fn should_skip(&self, n: usize) -> bool { self.0.should_skip(n) } } /// Query transformer skipping entities that do not have a `T` component /// /// See also `QueryBorrow::with`. /// /// # Example /// ``` /// # use bevy_hecs::*; /// let mut world = World::new(); /// let a = world.spawn((123, true, "abc")); /// let b = world.spawn((456, false)); /// let c = world.spawn((42, "def")); /// let entities = world.query::>() /// .map(|(e, &i)| (e, i)) /// .collect::>(); /// assert_eq!(entities.len(), 2); /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); /// ``` pub struct With(PhantomData<(Q, fn(T))>); impl Query for With { type Fetch = FetchWith; } #[doc(hidden)] pub struct FetchWith(F, PhantomData); unsafe impl<'a, T: Component, F: Fetch<'a>> ReadOnlyFetch for FetchWith where F: ReadOnlyFetch {} impl UnfilteredFetch for FetchWith where F: UnfilteredFetch {} impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { type Item = F::Item; const DANGLING: Self = Self(F::DANGLING, PhantomData); #[inline] fn access() -> QueryAccess { QueryAccess::with::(F::access()) } unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { if !archetype.has::() { return None; } Some(Self(F::get(archetype, offset)?, PhantomData)) } unsafe fn fetch(&self, n: usize) -> F::Item { self.0.fetch(n) } unsafe fn should_skip(&self, n: usize) -> bool { self.0.should_skip(n) } } struct ChunkInfo { fetch: Q::Fetch, len: usize, } /// Iterator over the set of entities with the components in `Q` pub struct QueryIter<'w, Q: Query> { archetypes: &'w [Archetype], archetype_index: usize, chunk_info: ChunkInfo, chunk_position: usize, } impl<'w, Q: Query> QueryIter<'w, Q> { // #[allow(clippy::declare_interior_mutable_const)] // no trait bounds on const fns // const EMPTY: Q::Fetch = Q::Fetch::DANGLING; const EMPTY: ChunkInfo = ChunkInfo { fetch: Q::Fetch::DANGLING, len: 0, }; /// Creates a new QueryIter #[inline] pub(crate) fn new(archetypes: &'w [Archetype]) -> Self { Self { archetypes, archetype_index: 0, chunk_info: Self::EMPTY, chunk_position: 0, } } } impl<'w, Q: Query> Iterator for QueryIter<'w, Q> { type Item = >::Item; #[inline] fn next(&mut self) -> Option { unsafe { loop { if self.chunk_position == self.chunk_info.len { let archetype = self.archetypes.get(self.archetype_index)?; self.archetype_index += 1; self.chunk_position = 0; self.chunk_info = Q::Fetch::get(archetype, 0) .map(|fetch| ChunkInfo { fetch, len: archetype.len(), }) .unwrap_or(Self::EMPTY); continue; } if self .chunk_info .fetch .should_skip(self.chunk_position as usize) { self.chunk_position += 1; continue; } let item = Some(self.chunk_info.fetch.fetch(self.chunk_position as usize)); self.chunk_position += 1; return item; } } } } // if the Fetch is an UnfilteredFetch, then we can cheaply compute the length of the query by getting // the length of each matching archetype impl<'w, Q: Query> ExactSizeIterator for QueryIter<'w, Q> where Q::Fetch: UnfilteredFetch, { fn len(&self) -> usize { self.archetypes .iter() .filter(|&archetype| unsafe { Q::Fetch::get(archetype, 0).is_some() }) .map(|x| x.len()) .sum() } } struct ChunkIter { fetch: Q::Fetch, position: usize, len: usize, } impl ChunkIter { unsafe fn next<'a>(&mut self) -> Option<>::Item> { loop { if self.position == self.len { return None; } if self.fetch.should_skip(self.position as usize) { self.position += 1; continue; } let item = Some(self.fetch.fetch(self.position as usize)); self.position += 1; return item; } } } /// Batched version of `QueryIter` pub struct BatchedIter<'w, Q: Query> { archetypes: &'w [Archetype], archetype_index: usize, batch_size: usize, batch: usize, _marker: PhantomData, } impl<'w, Q: Query> BatchedIter<'w, Q> { pub(crate) fn new(archetypes: &'w [Archetype], batch_size: usize) -> Self { Self { archetypes, archetype_index: 0, batch_size, batch: 0, _marker: Default::default(), } } } unsafe impl<'w, Q: Query> Send for BatchedIter<'w, Q> {} unsafe impl<'w, Q: Query> Sync for BatchedIter<'w, Q> {} impl<'w, Q: Query> Iterator for BatchedIter<'w, Q> { type Item = Batch<'w, Q>; fn next(&mut self) -> Option { loop { let archetype = self.archetypes.get(self.archetype_index)?; let offset = self.batch_size * self.batch; if offset >= archetype.len() { self.archetype_index += 1; self.batch = 0; continue; } if let Some(fetch) = unsafe { Q::Fetch::get(archetype, offset) } { self.batch += 1; return Some(Batch { _marker: PhantomData, state: ChunkIter { fetch, position: 0, len: self.batch_size.min(archetype.len() - offset), }, }); } else { self.archetype_index += 1; debug_assert_eq!( self.batch, 0, "query fetch should always reject at the first batch or not at all" ); continue; } } } } /// A sequence of entities yielded by `BatchedIter` pub struct Batch<'q, Q: Query> { _marker: PhantomData<&'q ()>, state: ChunkIter, } impl<'q, 'w, Q: Query> Iterator for Batch<'q, Q> { type Item = >::Item; fn next(&mut self) -> Option { let components = unsafe { self.state.next()? }; Some(components) } } unsafe impl<'q, Q: Query> Send for Batch<'q, Q> {} unsafe impl<'q, Q: Query> Sync for Batch<'q, Q> {} macro_rules! tuple_impl { ($($name: ident),*) => { impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { type Item = ($($name::Item,)*); const DANGLING: Self = ($($name::DANGLING,)*); #[allow(unused_variables, unused_mut)] fn access() -> QueryAccess { QueryAccess::union(vec![ $($name::access(),)* ]) } #[allow(unused_variables)] unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(($($name::get(archetype, offset)?,)*)) } #[allow(unused_variables)] unsafe fn fetch(&self, n: usize) -> Self::Item { #[allow(non_snake_case)] let ($($name,)*) = self; ($($name.fetch(n),)*) } #[allow(unused_variables)] unsafe fn should_skip(&self, n: usize) -> bool { #[allow(non_snake_case)] let ($($name,)*) = self; $($name.should_skip(n)||)* false } } impl<$($name: Query),*> Query for ($($name,)*) { type Fetch = ($($name::Fetch,)*); } unsafe impl<$($name: ReadOnlyFetch),*> ReadOnlyFetch for ($($name,)*) {} impl<$($name: UnfilteredFetch),*> UnfilteredFetch for ($($name,)*) {} }; } smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A); #[cfg(test)] mod tests { use crate::{Entity, Mut, Mutated, World}; use std::{vec, vec::Vec}; use super::*; struct A(usize); struct B(usize); struct C; #[test] fn added_queries() { let mut world = World::default(); let e1 = world.spawn((A(0),)); fn get_added(world: &World) -> Vec { world .query::<(Added, Entity)>() .map(|(_added, e)| e) .collect::>() }; assert_eq!(get_added::(&world), vec![e1]); world.insert(e1, (B(0),)).unwrap(); assert_eq!(get_added::(&world), vec![e1]); assert_eq!(get_added::(&world), vec![e1]); world.clear_trackers(); assert!(get_added::(&world).is_empty()); let e2 = world.spawn((A(1), B(1))); assert_eq!(get_added::(&world), vec![e2]); assert_eq!(get_added::(&world), vec![e2]); let added = world .query::<(Entity, Added, Added)>() .map(|a| a.0) .collect::>(); assert_eq!(added, vec![e2]); } #[test] fn mutated_trackers() { let mut world = World::default(); let e1 = world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); let e3 = world.spawn((A(0), B(0))); world.spawn((A(0), B)); for (i, mut a) in world.query_mut::>().enumerate() { if i % 2 == 0 { a.0 += 1; } } fn get_mutated_a(world: &mut World) -> Vec { world .query_mut::<(Mutated, Entity)>() .map(|(_a, e)| e) .collect::>() }; assert_eq!(get_mutated_a(&mut world), vec![e1, e3]); // ensure changing an entity's archetypes also moves its mutated state world.insert(e1, (C,)).unwrap(); assert_eq!(get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change (although the order will due to archetype moves)"); // spawning a new A entity should not change existing mutated state world.insert(e1, (A(0), B)).unwrap(); assert_eq!( get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing an unchanged entity should not change mutated state world.despawn(e2).unwrap(); assert_eq!( get_mutated_a(&mut world), vec![e3, e1], "changed entities list should not change" ); // removing a changed entity should remove it from enumeration world.despawn(e1).unwrap(); assert_eq!( get_mutated_a(&mut world), vec![e3], "e1 should no longer be returned" ); world.clear_trackers(); assert!(world .query_mut::<(Mutated, Entity)>() .map(|(_a, e)| e) .next() .is_none()); let e4 = world.spawn(()); world.insert_one(e4, A(0)).unwrap(); assert!(get_mutated_a(&mut world).is_empty()); world.insert_one(e4, A(1)).unwrap(); assert_eq!(get_mutated_a(&mut world), vec![e4]); world.clear_trackers(); // ensure inserting multiple components set mutated state for // already existing components and set added state for // non existing components even when changing archetype. world.insert(e4, (A(0), B(0))).unwrap(); let added_a = world .query::<(Added, Entity)>() .iter() .map(|(_, e)| e) .next(); assert!(added_a.is_none()); assert_eq!(get_mutated_a(&mut world), vec![e4]); let added_b = world .query::<(Added, Entity)>() .iter() .map(|(_, e)| e) .next(); assert!(added_b.is_some()); let mutated_b = world .query_mut::<(Mutated, Entity)>() .iter() .map(|(_, e)| e) .next(); assert!(mutated_b.is_none()); } #[test] fn multiple_mutated_query() { let mut world = World::default(); world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); world.spawn((A(0), B(0))); for mut a in world.query_mut::>() { a.0 += 1; } for mut b in world.query_mut::>().skip(1).take(1) { b.0 += 1; } let a_b_mutated = world .query_mut::<(Mutated, Mutated, Entity)>() .map(|(_a, _b, e)| e) .collect::>(); assert_eq!(a_b_mutated, vec![e2]); } #[test] fn or_mutated_query() { let mut world = World::default(); let e1 = world.spawn((A(0), B(0))); let e2 = world.spawn((A(0), B(0))); let e3 = world.spawn((A(0), B(0))); let _e4 = world.spawn((A(0), B(0))); // Mutate A in entities e1 and e2 for mut a in world.query_mut::>().take(2) { a.0 += 1; } // Mutate B in entities e2 and e3 for mut b in world.query_mut::>().skip(1).take(2) { b.0 += 1; } let a_b_mutated = world .query_mut::<(Or<(Mutated, Mutated)>, Entity)>() .map(|((_a, _b), e)| e) .collect::>(); // e1 has mutated A, e3 has mutated B, e2 has mutated A and B, _e4 has no mutated component assert_eq!(a_b_mutated, vec![e1, e2, e3]); } #[test] fn changed_query() { let mut world = World::default(); let e1 = world.spawn((A(0), B(0))); fn get_changed(world: &World) -> Vec { world .query::<(Changed, Entity)>() .map(|(_a, e)| e) .collect::>() }; assert_eq!(get_changed(&world), vec![e1]); world.clear_trackers(); assert_eq!(get_changed(&world), vec![]); *world.get_mut(e1).unwrap() = A(1); assert_eq!(get_changed(&world), vec![e1]); } #[test] fn exact_size_query() { let mut world = World::default(); world.spawn((A(0), B(0))); world.spawn((A(0), B(0))); world.spawn((C,)); assert_eq!(world.query::<(&A, &B)>().len(), 2); // the following example shouldn't compile because Changed is not an UnfilteredFetch // assert_eq!(world.query::<(Changed, &B)>().len(), 2); } }