From b487683216115a45ab2faf551b60dbeab95f97e6 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Wed, 23 Sep 2020 20:57:08 -0700 Subject: [PATCH 01/10] add smart component access, WIP --- examples/ffa_simulation.rs | 4 +- src/lib.rs | 4 +- src/query.rs | 232 +++++++++++++++++++++++++------------ src/query_one.rs | 25 ++-- src/world.rs | 28 ++++- tests/tests.rs | 16 +-- 6 files changed, 212 insertions(+), 97 deletions(-) diff --git a/examples/ffa_simulation.rs b/examples/ffa_simulation.rs index 20dc6a4a..407000dc 100644 --- a/examples/ffa_simulation.rs +++ b/examples/ffa_simulation.rs @@ -62,7 +62,7 @@ fn batch_spawn_entities(world: &mut World, n: usize) { fn system_integrate_motion(world: &mut World) { let mut rng = thread_rng(); - for (id, (pos, s)) in &mut world.query::<(&mut Position, &Speed)>() { + for (id, (mut pos, s)) in &mut world.query::<(&mut Position, &Speed)>() { let change = (rng.gen_range(-s.0, s.0), rng.gen_range(-s.0, s.0)); pos.x += change.0; pos.y += change.1; @@ -72,7 +72,7 @@ fn system_integrate_motion(world: &mut World) { // In this system entities find the closest entity and fire at them fn system_fire_at_closest(world: &mut World) { - for (id0, (pos0, dmg0, kc0)) in + for (id0, (pos0, dmg0, mut kc0)) in &mut world.query::>() { // Find closest: diff --git a/src/lib.rs b/src/lib.rs index 8fcb2415..aff94e9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -78,7 +78,9 @@ pub use entities::{Entity, NoSuchEntity}; pub use entity_builder::{BuiltEntity, EntityBuilder}; pub use query::{Access, BatchedIter, Query, QueryBorrow, QueryIter, With, Without}; pub use query_one::QueryOne; -pub use world::{ArchetypesGeneration, Component, ComponentError, Iter, SpawnBatchIter, World}; +pub use world::{ + ArchetypesGeneration, Component, ComponentError, Iter, SmartComponent, SpawnBatchIter, World, +}; // Unstable implementation details needed by the macros #[doc(hidden)] diff --git a/src/query.rs b/src/query.rs index e635e335..5d9bf1cc 100644 --- a/src/query.rs +++ b/src/query.rs @@ -12,21 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +use core::fmt; use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; use core::ptr::NonNull; use crate::archetype::Archetype; use crate::entities::EntityMeta; -use crate::{Component, Entity}; +use crate::{Component, Entity, SmartComponent}; /// A collection of component types to fetch from a `World` -pub trait Query { +pub trait Query { #[doc(hidden)] - type Fetch: for<'a> Fetch<'a>; + type Fetch: for<'q> Fetch<'q, C>; } /// Streaming iterators over contiguous homogeneous ranges of components -pub trait Fetch<'a>: Sized { +pub trait Fetch<'a, C>: Sized { /// Type of value to be fetched type Item; @@ -50,7 +52,7 @@ pub trait Fetch<'a>: Sized { /// - `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 next(&mut self) -> Self::Item; + unsafe fn next(&mut self, id: u32, context: &'a C) -> Self::Item; } /// Type of access a `Query` may have to an `Archetype` @@ -64,15 +66,44 @@ pub enum Access { Write, } -impl<'a, T: Component> Query for &'a T { +impl<'a, T: SmartComponent, C: 'static> Query for &'a T { type Fetch = FetchRead; } #[doc(hidden)] pub struct FetchRead(NonNull); -impl<'a, T: Component> Fetch<'a> for FetchRead { - type Item = &'a T; +pub struct Ref<'a, T, C> { + value: &'a T, + id: u32, + context: &'a C, +} + +impl<'a, T, C> Clone for Ref<'a, T, C> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T, C> Copy for Ref<'a, T, C> {} + +impl<'a, T: fmt::Debug, C> fmt::Debug for Ref<'a, T, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.value.fmt(f) + } +} + +impl<'a, T: SmartComponent, C> Deref for Ref<'a, T, C> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.value.on_borrow(self.id, self.context); + self.value + } +} + +impl<'a, T: SmartComponent, C: 'static> Fetch<'a, C> for FetchRead { + type Item = Ref<'a, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -85,31 +116,65 @@ impl<'a, T: Component> Fetch<'a> for FetchRead { fn borrow(archetype: &Archetype) { archetype.borrow::(); } + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) } + fn release(archetype: &Archetype) { archetype.release::(); } - unsafe fn next(&mut self) -> &'a T { + unsafe fn next(&mut self, id: u32, context: &'a C) -> Ref<'a, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); - &*x + Ref { + value: &*x, + id, + context, + } } } -impl<'a, T: Component> Query for &'a mut T { +impl<'a, T: SmartComponent, C: 'static> Query for &'a mut T { type Fetch = FetchWrite; } #[doc(hidden)] pub struct FetchWrite(NonNull); -impl<'a, T: Component> Fetch<'a> for FetchWrite { - type Item = &'a mut T; +pub struct RefMut<'a, T, C> { + value: &'a mut T, + id: u32, + context: &'a C, +} + +impl<'a, T: fmt::Debug, C> fmt::Debug for RefMut<'a, T, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.value.fmt(f) + } +} + +impl<'a, T: SmartComponent, C> Deref for RefMut<'a, T, C> { + type Target = T; + + fn deref(&self) -> &Self::Target { + (&*self.value).on_borrow(self.id, self.context); + self.value + } +} + +impl<'a, T: SmartComponent, C> DerefMut for RefMut<'a, T, C> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.value.on_borrow_mut(self.id, self.context); + self.value + } +} + +impl<'a, T: SmartComponent, C: 'a> Fetch<'a, C> for FetchWrite { + type Item = RefMut<'a, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -122,30 +187,36 @@ impl<'a, T: Component> Fetch<'a> for FetchWrite { fn borrow(archetype: &Archetype) { archetype.borrow_mut::(); } + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) } + fn release(archetype: &Archetype) { archetype.release_mut::(); } - unsafe fn next(&mut self) -> &'a mut T { + unsafe fn next(&mut self, id: u32, context: &'a C) -> RefMut<'a, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); - &mut *x + RefMut { + value: &mut *x, + id, + context, + } } } -impl Query for Option { +impl, C> Query for Option { type Fetch = TryFetch; } #[doc(hidden)] pub struct TryFetch(Option); -impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { +impl<'a, T: Fetch<'a, C>, C> Fetch<'a, C> for TryFetch { type Item = Option; fn access(archetype: &Archetype) -> Option { @@ -155,15 +226,17 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { fn borrow(archetype: &Archetype) { T::borrow(archetype) } + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(Self(T::get(archetype, offset))) } + fn release(archetype: &Archetype) { T::release(archetype) } - unsafe fn next(&mut self) -> Option { - Some(self.0.as_mut()?.next()) + unsafe fn next(&mut self, id: u32, context: &'a C) -> Option { + Some(self.0.as_mut()?.next(id, context)) } } @@ -186,14 +259,14 @@ impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { /// ``` pub struct Without(PhantomData<(Q, fn(T))>); -impl Query for Without { +impl, C> Query for Without { type Fetch = FetchWithout; } #[doc(hidden)] pub struct FetchWithout(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { +impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWithout { type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -217,8 +290,8 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { F::release(archetype) } - unsafe fn next(&mut self) -> F::Item { - self.0.next() + unsafe fn next(&mut self, id: u32, context: &'a C) -> F::Item { + self.0.next(id, context) } } @@ -243,14 +316,14 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWithout { /// ``` pub struct With(PhantomData<(Q, fn(T))>); -impl Query for With { +impl, C> Query for With { type Fetch = FetchWith; } #[doc(hidden)] pub struct FetchWith(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { +impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWith { type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -264,37 +337,41 @@ impl<'a, T: Component, F: Fetch<'a>> Fetch<'a> for FetchWith { fn borrow(archetype: &Archetype) { F::borrow(archetype) } + unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { if !archetype.has::() { return None; } Some(Self(F::get(archetype, offset)?, PhantomData)) } + fn release(archetype: &Archetype) { F::release(archetype) } - unsafe fn next(&mut self) -> F::Item { - self.0.next() + unsafe fn next(&mut self, id: u32, context: &'a C) -> F::Item { + self.0.next(id, context) } } /// A borrow of a `World` sufficient to execute the query `Q` /// /// Note that borrows are not released until this object is dropped. -pub struct QueryBorrow<'w, Q: Query> { +pub struct QueryBorrow<'w, Q: Query, C> { meta: &'w [EntityMeta], archetypes: &'w [Archetype], borrowed: bool, + context: &'w C, _marker: PhantomData, } -impl<'w, Q: Query> QueryBorrow<'w, Q> { - pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype]) -> Self { +impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { + pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype], context: &'w C) -> Self { Self { meta, archetypes, borrowed: false, + context, _marker: PhantomData, } } @@ -302,7 +379,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// Execute the query /// /// Must be called only once per query. - pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> { + pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q, C> { self.borrow(); QueryIter { borrow: self, @@ -314,7 +391,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// Like `iter`, but returns child iterators of at most `batch_size` elements /// /// Useful for distributing work over a threadpool. - pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q> { + pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q, C> { self.borrow(); BatchedIter { borrow: self, @@ -361,7 +438,7 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); /// ``` - pub fn with(self) -> QueryBorrow<'w, With> { + pub fn with(self) -> QueryBorrow<'w, With, C> { self.transform() } @@ -383,16 +460,17 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` - pub fn without(self) -> QueryBorrow<'w, Without> { + pub fn without(self) -> QueryBorrow<'w, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform(mut self) -> QueryBorrow<'w, R> { + fn transform>(mut self) -> QueryBorrow<'w, R, C> { let x = QueryBorrow { meta: self.meta, archetypes: self.archetypes, borrowed: self.borrowed, + context: self.context, _marker: PhantomData, }; // Ensure `Drop` won't fire redundantly @@ -401,10 +479,10 @@ impl<'w, Q: Query> QueryBorrow<'w, Q> { } } -unsafe impl<'w, Q: Query> Send for QueryBorrow<'w, Q> {} -unsafe impl<'w, Q: Query> Sync for QueryBorrow<'w, Q> {} +unsafe impl<'w, Q: Query, C: Sync> Send for QueryBorrow<'w, Q, C> {} +unsafe impl<'w, Q: Query, C: Sync> Sync for QueryBorrow<'w, Q, C> {} -impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { +impl<'w, Q: Query, C> Drop for QueryBorrow<'w, Q, C> { fn drop(&mut self) { if self.borrowed { for x in self.archetypes { @@ -416,9 +494,9 @@ impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> { } } -impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> { - type Item = (Entity, >::Item); - type IntoIter = QueryIter<'q, 'w, Q>; +impl<'q, 'w, Q: Query, C> IntoIterator for &'q mut QueryBorrow<'w, Q, C> { + type Item = (Entity, >::Item); + type IntoIter = QueryIter<'q, 'w, Q, C>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -426,17 +504,17 @@ impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> { } /// Iterator over the set of entities with the components in `Q` -pub struct QueryIter<'q, 'w, Q: Query> { - borrow: &'q mut QueryBorrow<'w, Q>, +pub struct QueryIter<'q, 'w, Q: Query, C> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, - iter: Option>, + iter: Option>, } -unsafe impl<'q, 'w, Q: Query> Send for QueryIter<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for QueryIter<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Send for QueryIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for QueryIter<'q, 'w, Q, C> {} -impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query, C> Iterator for QueryIter<'q, 'w, Q, C> { + type Item = (Entity, >::Item); #[inline] fn next(&mut self) -> Option { @@ -450,10 +528,11 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { entities: archetype.entities(), fetch, len: archetype.len(), + _context: PhantomData, }); } } - Some(ref mut iter) => match unsafe { iter.next() } { + Some(ref mut iter) => match unsafe { iter.next(self.borrow.context) } { None => { self.iter = None; continue; @@ -478,7 +557,7 @@ impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> { } } -impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> { +impl<'q, 'w, Q: Query, C> ExactSizeIterator for QueryIter<'q, 'w, Q, C> { fn len(&self) -> usize { self.borrow .archetypes @@ -489,38 +568,43 @@ impl<'q, 'w, Q: Query> ExactSizeIterator for QueryIter<'q, 'w, Q> { } } -struct ChunkIter { +struct ChunkIter, C> { entities: NonNull, fetch: Q::Fetch, len: u32, + _context: PhantomData<*const C>, } -impl ChunkIter { +impl, C> ChunkIter { #[inline] - unsafe fn next<'a>(&mut self) -> Option<(u32, >::Item)> { + unsafe fn next<'a>( + &mut self, + context: &'a C, + ) -> Option<(u32, >::Item)> { if self.len == 0 { return None; } self.len -= 1; let entity = self.entities.as_ptr(); + let id = *entity; self.entities = NonNull::new_unchecked(entity.add(1)); - Some((*entity, self.fetch.next())) + Some((id, self.fetch.next(id, context))) } } /// Batched version of `QueryIter` -pub struct BatchedIter<'q, 'w, Q: Query> { - borrow: &'q mut QueryBorrow<'w, Q>, +pub struct BatchedIter<'q, 'w, Q: Query, C> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, batch_size: u32, batch: u32, } -unsafe impl<'q, 'w, Q: Query> Send for BatchedIter<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for BatchedIter<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Send for BatchedIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for BatchedIter<'q, 'w, Q, C> {} -impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { - type Item = Batch<'q, 'w, Q>; +impl<'q, 'w, Q: Query, C> Iterator for BatchedIter<'q, 'w, Q, C> { + type Item = Batch<'q, 'w, Q, C>; fn next(&mut self) -> Option { loop { @@ -544,7 +628,9 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { }, fetch, len: self.batch_size.min(archetype.len() - offset), + _context: PhantomData, }, + context: self.borrow.context, }); } else { self.archetype_index += 1; @@ -559,17 +645,18 @@ impl<'q, 'w, Q: Query> Iterator for BatchedIter<'q, 'w, Q> { } /// A sequence of entities yielded by `BatchedIter` -pub struct Batch<'q, 'w, Q: Query> { +pub struct Batch<'q, 'w, Q: Query, C> { _marker: PhantomData<&'q ()>, meta: &'w [EntityMeta], - state: ChunkIter, + state: ChunkIter, + context: &'q C, } -impl<'q, 'w, Q: Query> Iterator for Batch<'q, 'w, Q> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query, C> Iterator for Batch<'q, 'w, Q, C> { + type Item = (Entity, >::Item); fn next(&mut self) -> Option { - let (id, components) = unsafe { self.state.next()? }; + let (id, components) = unsafe { self.state.next(self.context)? }; Some(( Entity { id, @@ -580,12 +667,12 @@ impl<'q, 'w, Q: Query> Iterator for Batch<'q, 'w, Q> { } } -unsafe impl<'q, 'w, Q: Query> Send for Batch<'q, 'w, Q> {} -unsafe impl<'q, 'w, Q: Query> Sync for Batch<'q, 'w, Q> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Send for Batch<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for Batch<'q, 'w, Q, C> {} macro_rules! tuple_impl { ($($name: ident),*) => { - impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { + impl<'a, Z, $($name: Fetch<'a, Z>),*> Fetch<'a, Z> for ($($name,)*) { type Item = ($($name::Item,)*); #[allow(unused_variables, unused_mut)] @@ -601,23 +688,26 @@ macro_rules! tuple_impl { fn borrow(archetype: &Archetype) { $($name::borrow(archetype);)* } + #[allow(unused_variables)] unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { Some(($($name::get(archetype, offset)?,)*)) } + #[allow(unused_variables)] fn release(archetype: &Archetype) { $($name::release(archetype);)* } - unsafe fn next(&mut self) -> Self::Item { + #[allow(unused_variables)] + unsafe fn next(&mut self, id: u32, context: &'a Z) -> Self::Item { #[allow(non_snake_case)] let ($($name,)*) = self; - ($($name.next(),)*) + ($($name.next(id, context),)*) } } - impl<$($name: Query),*> Query for ($($name,)*) { + impl),*> Query for ($($name,)*) { type Fetch = ($($name::Fetch,)*); } }; diff --git a/src/query_one.rs b/src/query_one.rs index 3e609a31..df83dd57 100644 --- a/src/query_one.rs +++ b/src/query_one.rs @@ -4,24 +4,26 @@ use crate::query::{Fetch, With, Without}; use crate::{Archetype, Component, Query}; /// A borrow of a `World` sufficient to execute the query `Q` on a single entity -pub struct QueryOne<'a, Q: Query> { +pub struct QueryOne<'a, Q: Query, C> { archetype: &'a Archetype, index: u32, borrowed: bool, + context: &'a C, _marker: PhantomData, } -impl<'a, Q: Query> QueryOne<'a, Q> { +impl<'a, Q: Query, C> QueryOne<'a, Q, C> { /// Construct a query accessing the entity in `archetype` at `index` /// /// # Safety /// /// `index` must be in-bounds for `archetype` - pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self { + pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32, context: &'a C) -> Self { Self { archetype, index, borrowed: false, + context, _marker: PhantomData, } } @@ -32,7 +34,7 @@ impl<'a, Q: Query> QueryOne<'a, Q> { /// /// Panics if called more than once or if it would construct a borrow that clashes with another /// pre-existing borrow. - pub fn get(&mut self) -> Option<>::Item> { + pub fn get(&mut self) -> Option<>::Item> { if self.borrowed { panic!("called QueryOnce::get twice; construct a new query instead"); } @@ -40,30 +42,31 @@ impl<'a, Q: Query> QueryOne<'a, Q> { let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?; self.borrowed = true; Q::Fetch::borrow(self.archetype); - Some(fetch.next()) + Some(fetch.next(self.index, self.context)) } } /// Transform the query into one that requires a certain component without borrowing it /// /// See `QueryBorrow::with` for details. - pub fn with(self) -> QueryOne<'a, With> { + pub fn with(self) -> QueryOne<'a, With, C> { self.transform() } /// Transform the query into one that skips entities having a certain component /// /// See `QueryBorrow::without` for details. - pub fn without(self) -> QueryOne<'a, Without> { + pub fn without(self) -> QueryOne<'a, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform(mut self) -> QueryOne<'a, R> { + fn transform>(mut self) -> QueryOne<'a, R, C> { let x = QueryOne { archetype: self.archetype, index: self.index, borrowed: self.borrowed, + context: self.context, _marker: PhantomData, }; // Ensure `Drop` won't fire redundantly @@ -72,7 +75,7 @@ impl<'a, Q: Query> QueryOne<'a, Q> { } } -impl Drop for QueryOne<'_, Q> { +impl, C> Drop for QueryOne<'_, Q, C> { fn drop(&mut self) { if self.borrowed { Q::Fetch::release(self.archetype); @@ -80,5 +83,5 @@ impl Drop for QueryOne<'_, Q> { } } -unsafe impl Send for QueryOne<'_, Q> {} -unsafe impl Sync for QueryOne<'_, Q> {} +unsafe impl, C: Sync> Send for QueryOne<'_, Q, C> {} +unsafe impl, C: Sync> Sync for QueryOne<'_, Q, C> {} diff --git a/src/world.rs b/src/world.rs index 6196c07b..ab85f399 100644 --- a/src/world.rs +++ b/src/world.rs @@ -244,8 +244,12 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` - pub fn query(&self) -> QueryBorrow<'_, Q> { - QueryBorrow::new(&self.entities.meta, &self.archetypes) + pub fn query(&self) -> QueryBorrow<'_, Q, ()> { + QueryBorrow::new(&self.entities.meta, &self.archetypes, &()) + } + + pub fn smart_query<'q, Q: Query, C>(&'q self, context: &'q C) -> QueryBorrow<'q, Q, C> { + QueryBorrow::new(&self.entities.meta, &self.archetypes, context) } /// Prepare a query against a single entity @@ -267,9 +271,18 @@ impl World { /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` - pub fn query_one(&self, entity: Entity) -> Result, NoSuchEntity> { + pub fn query_one(&self, entity: Entity) -> Result, NoSuchEntity> { let loc = self.entities.get(entity)?; - Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index) }) + Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, &()) }) + } + + pub fn smart_query_one<'q, Q: Query, C>( + &'q self, + entity: Entity, + context: &'q C, + ) -> Result, NoSuchEntity> { + let loc = self.entities.get(entity)?; + Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, context) }) } /// Borrow the `T` component of `entity` @@ -645,6 +658,13 @@ impl From for ComponentError { pub trait Component: Send + Sync + 'static {} impl Component for T {} +pub trait SmartComponent: Component { + fn on_borrow(&self, _id: u32, _x: &T) {} + fn on_borrow_mut(&mut self, _id: u32, _x: &T) {} +} + +impl SmartComponent<()> for T {} + /// Iterator over all of a world's entities pub struct Iter<'a> { archetypes: core::slice::Iter<'a, Archetype>, diff --git a/tests/tests.rs b/tests/tests.rs index 22b62a9d..5a108c6b 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -50,7 +50,7 @@ fn query_all() { let ents = world .query::<(&i32, &&str)>() .iter() - .map(|(e, (&i, &s))| (e, i, s)) + .map(|(e, (i, s))| (e, *i, *s)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, 123, "abc"))); @@ -70,7 +70,7 @@ fn query_single_component() { let ents = world .query::<&i32>() .iter() - .map(|(e, &i)| (e, i)) + .map(|(e, i)| (e, *i)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, 123))); @@ -93,7 +93,7 @@ fn query_sparse_component() { let ents = world .query::<&bool>() .iter() - .map(|(e, &b)| (e, b)) + .map(|(e, b)| (e, *b)) .collect::>(); assert_eq!(ents, &[(f, true)]); } @@ -106,7 +106,7 @@ fn query_optional_component() { let ents = world .query::<(Option<&bool>, &i32)>() .iter() - .map(|(e, (b, &i))| (e, b.copied(), i)) + .map(|(e, (b, i))| (e, b.as_deref().copied(), *i)) .collect::>(); assert_eq!(ents.len(), 2); assert!(ents.contains(&(e, None, 123))); @@ -139,7 +139,7 @@ fn dynamic_components() { world .query::<(&i32, &bool)>() .iter() - .map(|(e, (&i, &b))| (e, i, b)) + .map(|(e, (i, b))| (e, *i, *b)) .collect::>(), &[(e, 42, true)] ); @@ -148,7 +148,7 @@ fn dynamic_components() { world .query::<(&i32, &bool)>() .iter() - .map(|(e, (&i, &b))| (e, i, b)) + .map(|(e, (i, b))| (e, *i, *b)) .collect::>(), &[] ); @@ -156,7 +156,7 @@ fn dynamic_components() { world .query::<(&bool, &&str)>() .iter() - .map(|(e, (&b, &s))| (e, b, s)) + .map(|(e, (b, s))| (e, *b, *s)) .collect::>(), &[(e, true, "abc")] ); @@ -331,7 +331,7 @@ fn spawn_batch() { let entities = world .query::<&i32>() .iter() - .map(|(_, &x)| x) + .map(|(_, x)| *x) .collect::>(); assert_eq!(entities.len(), 100); } From 73c8373be0f22418d0b2943b9778f17b902eb34a Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 08:33:09 -0700 Subject: [PATCH 02/10] decouple context lifetime from query/world lifetime --- src/archetype.rs | 2 +- src/query.rs | 200 ++++++++++++++++++++++++++--------------------- src/query_one.rs | 26 +++--- src/world.rs | 24 +++--- 4 files changed, 140 insertions(+), 112 deletions(-) diff --git a/src/archetype.rs b/src/archetype.rs index 1f8a9c7c..5bef240d 100644 --- a/src/archetype.rs +++ b/src/archetype.rs @@ -301,7 +301,7 @@ impl Archetype { } /// How, if at all, `Q` will access entities in this archetype - pub fn access(&self) -> Option { + pub fn access<'w, Q: Query<'w, C>, C: Copy + 'w>(&self) -> Option { Q::Fetch::access(self) } } diff --git a/src/query.rs b/src/query.rs index 5d9bf1cc..ce6fbee3 100644 --- a/src/query.rs +++ b/src/query.rs @@ -22,13 +22,13 @@ use crate::entities::EntityMeta; use crate::{Component, Entity, SmartComponent}; /// A collection of component types to fetch from a `World` -pub trait Query { +pub trait Query<'c, C: Clone + 'c = ()> { #[doc(hidden)] - type Fetch: for<'q> Fetch<'q, C>; + type Fetch: for<'q> Fetch<'q, 'c, C>; } /// Streaming iterators over contiguous homogeneous ranges of components -pub trait Fetch<'a, C>: Sized { +pub trait Fetch<'q, 'c, C: Clone + 'c>: Sized { /// Type of value to be fetched type Item; @@ -41,7 +41,7 @@ pub trait Fetch<'a, C>: Sized { /// /// # Safety /// `offset` must be in bounds of `archetype` - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option; + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option; /// Release dynamic borrows acquired by `borrow` fn release(archetype: &Archetype); @@ -52,7 +52,7 @@ pub trait Fetch<'a, C>: Sized { /// - `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 next(&mut self, id: u32, context: &'a C) -> Self::Item; + unsafe fn next(&mut self, id: u32, context: C) -> Self::Item; } /// Type of access a `Query` may have to an `Archetype` @@ -66,7 +66,7 @@ pub enum Access { Write, } -impl<'a, T: SmartComponent, C: 'static> Query for &'a T { +impl<'a, T: SmartComponent, C: Clone + 'a> Query<'a, C> for &'a T { type Fetch = FetchRead; } @@ -76,16 +76,20 @@ pub struct FetchRead(NonNull); pub struct Ref<'a, T, C> { value: &'a T, id: u32, - context: &'a C, + context: C, } -impl<'a, T, C> Clone for Ref<'a, T, C> { +impl<'a, T, C: Clone> Clone for Ref<'a, T, C> { fn clone(&self) -> Self { - *self + Self { + value: self.value, + id: self.id, + context: self.context.clone(), + } } } -impl<'a, T, C> Copy for Ref<'a, T, C> {} +impl<'a, T, C: Copy> Copy for Ref<'a, T, C> {} impl<'a, T: fmt::Debug, C> fmt::Debug for Ref<'a, T, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -93,17 +97,17 @@ impl<'a, T: fmt::Debug, C> fmt::Debug for Ref<'a, T, C> { } } -impl<'a, T: SmartComponent, C> Deref for Ref<'a, T, C> { +impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { type Target = T; fn deref(&self) -> &Self::Target { - self.value.on_borrow(self.id, self.context); + self.value.on_borrow(self.id, &self.context); self.value } } -impl<'a, T: SmartComponent, C: 'static> Fetch<'a, C> for FetchRead { - type Item = Ref<'a, T, C>; +impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchRead { + type Item = Ref<'q, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -117,7 +121,7 @@ impl<'a, T: SmartComponent, C: 'static> Fetch<'a, C> for FetchRead { archetype.borrow::(); } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) @@ -127,7 +131,7 @@ impl<'a, T: SmartComponent, C: 'static> Fetch<'a, C> for FetchRead { archetype.release::(); } - unsafe fn next(&mut self, id: u32, context: &'a C) -> Ref<'a, T, C> { + unsafe fn next(&mut self, id: u32, context: C) -> Ref<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); Ref { @@ -138,7 +142,7 @@ impl<'a, T: SmartComponent, C: 'static> Fetch<'a, C> for FetchRead { } } -impl<'a, T: SmartComponent, C: 'static> Query for &'a mut T { +impl<'a, T: SmartComponent, C: Clone + 'a> Query<'a, C> for &'a mut T { type Fetch = FetchWrite; } @@ -148,7 +152,7 @@ pub struct FetchWrite(NonNull); pub struct RefMut<'a, T, C> { value: &'a mut T, id: u32, - context: &'a C, + context: C, } impl<'a, T: fmt::Debug, C> fmt::Debug for RefMut<'a, T, C> { @@ -157,24 +161,24 @@ impl<'a, T: fmt::Debug, C> fmt::Debug for RefMut<'a, T, C> { } } -impl<'a, T: SmartComponent, C> Deref for RefMut<'a, T, C> { +impl<'a, T: SmartComponent, C: Clone> Deref for RefMut<'a, T, C> { type Target = T; fn deref(&self) -> &Self::Target { - (&*self.value).on_borrow(self.id, self.context); + (&*self.value).on_borrow(self.id, &self.context); self.value } } -impl<'a, T: SmartComponent, C> DerefMut for RefMut<'a, T, C> { +impl<'a, T: SmartComponent, C: Clone> DerefMut for RefMut<'a, T, C> { fn deref_mut(&mut self) -> &mut Self::Target { - self.value.on_borrow_mut(self.id, self.context); + self.value.on_borrow_mut(self.id, &self.context); self.value } } -impl<'a, T: SmartComponent, C: 'a> Fetch<'a, C> for FetchWrite { - type Item = RefMut<'a, T, C>; +impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchWrite { + type Item = RefMut<'q, T, C>; fn access(archetype: &Archetype) -> Option { if archetype.has::() { @@ -188,7 +192,7 @@ impl<'a, T: SmartComponent, C: 'a> Fetch<'a, C> for FetchWrite { archetype.borrow_mut::(); } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { archetype .get::() .map(|x| Self(NonNull::new_unchecked(x.as_ptr().add(offset)))) @@ -198,7 +202,7 @@ impl<'a, T: SmartComponent, C: 'a> Fetch<'a, C> for FetchWrite { archetype.release_mut::(); } - unsafe fn next(&mut self, id: u32, context: &'a C) -> RefMut<'a, T, C> { + unsafe fn next(&mut self, id: u32, context: C) -> RefMut<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); RefMut { @@ -209,14 +213,14 @@ impl<'a, T: SmartComponent, C: 'a> Fetch<'a, C> for FetchWrite { } } -impl, C> Query for Option { +impl<'c, T: Query<'c, C>, C: Clone + 'c> Query<'c, C> for Option { type Fetch = TryFetch; } #[doc(hidden)] pub struct TryFetch(Option); -impl<'a, T: Fetch<'a, C>, C> Fetch<'a, C> for TryFetch { +impl<'q, 'c, T: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> for TryFetch { type Item = Option; fn access(archetype: &Archetype) -> Option { @@ -227,7 +231,7 @@ impl<'a, T: Fetch<'a, C>, C> Fetch<'a, C> for TryFetch { T::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { Some(Self(T::get(archetype, offset))) } @@ -235,7 +239,7 @@ impl<'a, T: Fetch<'a, C>, C> Fetch<'a, C> for TryFetch { T::release(archetype) } - unsafe fn next(&mut self, id: u32, context: &'a C) -> Option { + unsafe fn next(&mut self, id: u32, context: C) -> Option { Some(self.0.as_mut()?.next(id, context)) } } @@ -259,14 +263,16 @@ impl<'a, T: Fetch<'a, C>, C> Fetch<'a, C> for TryFetch { /// ``` pub struct Without(PhantomData<(Q, fn(T))>); -impl, C> Query for Without { +impl<'c, T: Component, Q: Query<'c, C>, C: Clone + 'c> Query<'c, C> for Without { type Fetch = FetchWithout; } #[doc(hidden)] pub struct FetchWithout(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWithout { +impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> + for FetchWithout +{ type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -280,7 +286,7 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWithout { fn borrow(archetype: &Archetype) { F::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { if archetype.has::() { return None; } @@ -290,7 +296,7 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWithout { F::release(archetype) } - unsafe fn next(&mut self, id: u32, context: &'a C) -> F::Item { + unsafe fn next(&mut self, id: u32, context: C) -> F::Item { self.0.next(id, context) } } @@ -316,14 +322,16 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWithout { /// ``` pub struct With(PhantomData<(Q, fn(T))>); -impl, C> Query for With { +impl<'c, T: Component, Q: Query<'c, C>, C: Clone + 'c> Query<'c, C> for With { type Fetch = FetchWith; } #[doc(hidden)] pub struct FetchWith(F, PhantomData); -impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWith { +impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> + for FetchWith +{ type Item = F::Item; fn access(archetype: &Archetype) -> Option { @@ -338,7 +346,7 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWith { F::borrow(archetype) } - unsafe fn get(archetype: &'a Archetype, offset: usize) -> Option { + unsafe fn get(archetype: &'q Archetype, offset: usize) -> Option { if !archetype.has::() { return None; } @@ -349,7 +357,7 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWith { F::release(archetype) } - unsafe fn next(&mut self, id: u32, context: &'a C) -> F::Item { + unsafe fn next(&mut self, id: u32, context: C) -> F::Item { self.0.next(id, context) } } @@ -357,16 +365,16 @@ impl<'a, T: Component, F: Fetch<'a, C>, C> Fetch<'a, C> for FetchWith { /// A borrow of a `World` sufficient to execute the query `Q` /// /// Note that borrows are not released until this object is dropped. -pub struct QueryBorrow<'w, Q: Query, C> { +pub struct QueryBorrow<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { meta: &'w [EntityMeta], archetypes: &'w [Archetype], borrowed: bool, - context: &'w C, - _marker: PhantomData, + context: C, + _marker: PhantomData<(Q, &'c ())>, } -impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { - pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype], context: &'w C) -> Self { +impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { + pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype], context: C) -> Self { Self { meta, archetypes, @@ -379,7 +387,7 @@ impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { /// Execute the query /// /// Must be called only once per query. - pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q, C> { + pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, 'c, Q, C> { self.borrow(); QueryIter { borrow: self, @@ -391,7 +399,7 @@ impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { /// Like `iter`, but returns child iterators of at most `batch_size` elements /// /// Useful for distributing work over a threadpool. - pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q, C> { + pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, 'c, Q, C> { self.borrow(); BatchedIter { borrow: self, @@ -433,12 +441,12 @@ impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { /// let entities = world.query::<&i32>() /// .with::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Copy out of the world + /// .map(|(e, &i)| (e, i)) // Clone out of the world /// .collect::>(); /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); /// ``` - pub fn with(self) -> QueryBorrow<'w, With, C> { + pub fn with(self) -> QueryBorrow<'w, 'c, With, C> { self.transform() } @@ -456,21 +464,21 @@ impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { /// let entities = world.query::<&i32>() /// .without::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Copy out of the world + /// .map(|(e, &i)| (e, i)) // Clone out of the world /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` - pub fn without(self) -> QueryBorrow<'w, Without, C> { + pub fn without(self) -> QueryBorrow<'w, 'c, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform>(mut self) -> QueryBorrow<'w, R, C> { + fn transform>(mut self) -> QueryBorrow<'w, 'c, R, C> { let x = QueryBorrow { meta: self.meta, archetypes: self.archetypes, borrowed: self.borrowed, - context: self.context, + context: self.context.clone(), _marker: PhantomData, }; // Ensure `Drop` won't fire redundantly @@ -479,10 +487,10 @@ impl<'w, Q: Query, C> QueryBorrow<'w, Q, C> { } } -unsafe impl<'w, Q: Query, C: Sync> Send for QueryBorrow<'w, Q, C> {} -unsafe impl<'w, Q: Query, C: Sync> Sync for QueryBorrow<'w, Q, C> {} +unsafe impl<'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'c> Send for QueryBorrow<'w, 'c, Q, C> {} +unsafe impl<'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'c> Sync for QueryBorrow<'w, 'c, Q, C> {} -impl<'w, Q: Query, C> Drop for QueryBorrow<'w, Q, C> { +impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Drop for QueryBorrow<'w, 'c, Q, C> { fn drop(&mut self) { if self.borrowed { for x in self.archetypes { @@ -494,9 +502,11 @@ impl<'w, Q: Query, C> Drop for QueryBorrow<'w, Q, C> { } } -impl<'q, 'w, Q: Query, C> IntoIterator for &'q mut QueryBorrow<'w, Q, C> { - type Item = (Entity, >::Item); - type IntoIter = QueryIter<'q, 'w, Q, C>; +impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> IntoIterator + for &'q mut QueryBorrow<'w, 'c, Q, C> +{ + type Item = (Entity, >::Item); + type IntoIter = QueryIter<'q, 'w, 'c, Q, C>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -504,17 +514,23 @@ impl<'q, 'w, Q: Query, C> IntoIterator for &'q mut QueryBorrow<'w, Q, C> { } /// Iterator over the set of entities with the components in `Q` -pub struct QueryIter<'q, 'w, Q: Query, C> { - borrow: &'q mut QueryBorrow<'w, Q, C>, +pub struct QueryIter<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { + borrow: &'q mut QueryBorrow<'w, 'c, Q, C>, archetype_index: u32, - iter: Option>, + iter: Option>, } -unsafe impl<'q, 'w, Q: Query, C: Sync> Send for QueryIter<'q, 'w, Q, C> {} -unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for QueryIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send + for QueryIter<'q, 'w, 'c, Q, C> +{ +} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync + for QueryIter<'q, 'w, 'c, Q, C> +{ +} -impl<'q, 'w, Q: Query, C> Iterator for QueryIter<'q, 'w, Q, C> { - type Item = (Entity, >::Item); +impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Iterator for QueryIter<'q, 'w, 'c, Q, C> { + type Item = (Entity, >::Item); #[inline] fn next(&mut self) -> Option { @@ -532,7 +548,7 @@ impl<'q, 'w, Q: Query, C> Iterator for QueryIter<'q, 'w, Q, C> { }); } } - Some(ref mut iter) => match unsafe { iter.next(self.borrow.context) } { + Some(ref mut iter) => match unsafe { iter.next(self.borrow.context.clone()) } { None => { self.iter = None; continue; @@ -557,7 +573,7 @@ impl<'q, 'w, Q: Query, C> Iterator for QueryIter<'q, 'w, Q, C> { } } -impl<'q, 'w, Q: Query, C> ExactSizeIterator for QueryIter<'q, 'w, Q, C> { +impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> ExactSizeIterator for QueryIter<'q, 'w, 'c, Q, C> { fn len(&self) -> usize { self.borrow .archetypes @@ -568,19 +584,19 @@ impl<'q, 'w, Q: Query, C> ExactSizeIterator for QueryIter<'q, 'w, Q, C> { } } -struct ChunkIter, C> { +struct ChunkIter<'c, Q: Query<'c, C>, C: Clone + 'c> { entities: NonNull, fetch: Q::Fetch, len: u32, _context: PhantomData<*const C>, } -impl, C> ChunkIter { +impl<'c, Q: Query<'c, C>, C: Clone + 'c> ChunkIter<'c, Q, C> { #[inline] unsafe fn next<'a>( &mut self, - context: &'a C, - ) -> Option<(u32, >::Item)> { + context: C, + ) -> Option<(u32, >::Item)> { if self.len == 0 { return None; } @@ -593,18 +609,24 @@ impl, C> ChunkIter { } /// Batched version of `QueryIter` -pub struct BatchedIter<'q, 'w, Q: Query, C> { - borrow: &'q mut QueryBorrow<'w, Q, C>, +pub struct BatchedIter<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { + borrow: &'q mut QueryBorrow<'w, 'c, Q, C>, archetype_index: u32, batch_size: u32, batch: u32, } -unsafe impl<'q, 'w, Q: Query, C: Sync> Send for BatchedIter<'q, 'w, Q, C> {} -unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for BatchedIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send + for BatchedIter<'q, 'w, 'c, Q, C> +{ +} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync + for BatchedIter<'q, 'w, 'c, Q, C> +{ +} -impl<'q, 'w, Q: Query, C> Iterator for BatchedIter<'q, 'w, Q, C> { - type Item = Batch<'q, 'w, Q, C>; +impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for BatchedIter<'q, 'w, 'c, Q, C> { + type Item = Batch<'q, 'w, 'c, Q, C>; fn next(&mut self) -> Option { loop { @@ -630,7 +652,7 @@ impl<'q, 'w, Q: Query, C> Iterator for BatchedIter<'q, 'w, Q, C> { len: self.batch_size.min(archetype.len() - offset), _context: PhantomData, }, - context: self.borrow.context, + context: self.borrow.context.clone(), }); } else { self.archetype_index += 1; @@ -645,18 +667,18 @@ impl<'q, 'w, Q: Query, C> Iterator for BatchedIter<'q, 'w, Q, C> { } /// A sequence of entities yielded by `BatchedIter` -pub struct Batch<'q, 'w, Q: Query, C> { +pub struct Batch<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> { _marker: PhantomData<&'q ()>, meta: &'w [EntityMeta], - state: ChunkIter, - context: &'q C, + state: ChunkIter<'c, Q, C>, + context: C, } -impl<'q, 'w, Q: Query, C> Iterator for Batch<'q, 'w, Q, C> { - type Item = (Entity, >::Item); +impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, 'c, Q, C> { + type Item = (Entity, >::Item); fn next(&mut self) -> Option { - let (id, components) = unsafe { self.state.next(self.context)? }; + let (id, components) = unsafe { self.state.next(self.context.clone())? }; Some(( Entity { id, @@ -667,12 +689,12 @@ impl<'q, 'w, Q: Query, C> Iterator for Batch<'q, 'w, Q, C> { } } -unsafe impl<'q, 'w, Q: Query, C: Sync> Send for Batch<'q, 'w, Q, C> {} -unsafe impl<'q, 'w, Q: Query, C: Sync> Sync for Batch<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send for Batch<'q, 'w, 'c, Q, C> {} +unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync for Batch<'q, 'w, 'c, Q, C> {} macro_rules! tuple_impl { ($($name: ident),*) => { - impl<'a, Z, $($name: Fetch<'a, Z>),*> Fetch<'a, Z> for ($($name,)*) { + impl<'a, 'z, Z: Clone + 'z, $($name: Fetch<'a, 'z, Z>),*> Fetch<'a, 'z, Z> for ($($name,)*) { type Item = ($($name::Item,)*); #[allow(unused_variables, unused_mut)] @@ -700,14 +722,14 @@ macro_rules! tuple_impl { } #[allow(unused_variables)] - unsafe fn next(&mut self, id: u32, context: &'a Z) -> Self::Item { + unsafe fn next(&mut self, id: u32, context: Z) -> Self::Item { #[allow(non_snake_case)] let ($($name,)*) = self; - ($($name.next(id, context),)*) + ($($name.next(id, context.clone()),)*) } } - impl),*> Query for ($($name,)*) { + impl<'z, Z: Clone + 'z, $($name: Query<'z, Z>),*> Query<'z, Z> for ($($name,)*) { type Fetch = ($($name::Fetch,)*); } }; diff --git a/src/query_one.rs b/src/query_one.rs index df83dd57..775663b8 100644 --- a/src/query_one.rs +++ b/src/query_one.rs @@ -4,21 +4,21 @@ use crate::query::{Fetch, With, Without}; use crate::{Archetype, Component, Query}; /// A borrow of a `World` sufficient to execute the query `Q` on a single entity -pub struct QueryOne<'a, Q: Query, C> { - archetype: &'a Archetype, +pub struct QueryOne<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> { + archetype: &'w Archetype, index: u32, borrowed: bool, - context: &'a C, - _marker: PhantomData, + context: C, + _marker: PhantomData<(Q, &'c ())>, } -impl<'a, Q: Query, C> QueryOne<'a, Q, C> { +impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> QueryOne<'w, 'c, Q, C> { /// Construct a query accessing the entity in `archetype` at `index` /// /// # Safety /// /// `index` must be in-bounds for `archetype` - pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32, context: &'a C) -> Self { + pub(crate) unsafe fn new(archetype: &'w Archetype, index: u32, context: C) -> Self { Self { archetype, index, @@ -34,7 +34,7 @@ impl<'a, Q: Query, C> QueryOne<'a, Q, C> { /// /// Panics if called more than once or if it would construct a borrow that clashes with another /// pre-existing borrow. - pub fn get(&mut self) -> Option<>::Item> { + pub fn get(&mut self) -> Option<>::Item> { if self.borrowed { panic!("called QueryOnce::get twice; construct a new query instead"); } @@ -49,19 +49,19 @@ impl<'a, Q: Query, C> QueryOne<'a, Q, C> { /// Transform the query into one that requires a certain component without borrowing it /// /// See `QueryBorrow::with` for details. - pub fn with(self) -> QueryOne<'a, With, C> { + pub fn with(self) -> QueryOne<'w, 'c, With, C> { self.transform() } /// Transform the query into one that skips entities having a certain component /// /// See `QueryBorrow::without` for details. - pub fn without(self) -> QueryOne<'a, Without, C> { + pub fn without(self) -> QueryOne<'w, 'c, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform>(mut self) -> QueryOne<'a, R, C> { + fn transform>(mut self) -> QueryOne<'w, 'c, R, C> { let x = QueryOne { archetype: self.archetype, index: self.index, @@ -75,7 +75,7 @@ impl<'a, Q: Query, C> QueryOne<'a, Q, C> { } } -impl, C> Drop for QueryOne<'_, Q, C> { +impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> Drop for QueryOne<'w, 'c, Q, C> { fn drop(&mut self) { if self.borrowed { Q::Fetch::release(self.archetype); @@ -83,5 +83,5 @@ impl, C> Drop for QueryOne<'_, Q, C> { } } -unsafe impl, C: Sync> Send for QueryOne<'_, Q, C> {} -unsafe impl, C: Sync> Sync for QueryOne<'_, Q, C> {} +unsafe impl<'w, 'c, Q: Query<'c, C>, C: Copy + Sync + 'c> Send for QueryOne<'w, 'c, Q, C> {} +unsafe impl<'w, 'c, Q: Query<'c, C>, C: Copy + Sync + 'c> Sync for QueryOne<'w, 'c, Q, C> {} diff --git a/src/world.rs b/src/world.rs index ab85f399..c5f3b601 100644 --- a/src/world.rs +++ b/src/world.rs @@ -244,11 +244,14 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` - pub fn query(&self) -> QueryBorrow<'_, Q, ()> { - QueryBorrow::new(&self.entities.meta, &self.archetypes, &()) + pub fn query<'q, Q: Query<'static>>(&'q self) -> QueryBorrow<'q, 'static, Q, ()> { + QueryBorrow::new(&self.entities.meta, &self.archetypes, ()) } - pub fn smart_query<'q, Q: Query, C>(&'q self, context: &'q C) -> QueryBorrow<'q, Q, C> { + pub fn smart_query<'q, 'c, Q: Query<'c, C>, C: Copy + 'c>( + &'q self, + context: C, + ) -> QueryBorrow<'q, 'c, Q, C> { QueryBorrow::new(&self.entities.meta, &self.archetypes, context) } @@ -271,16 +274,19 @@ impl World { /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` - pub fn query_one(&self, entity: Entity) -> Result, NoSuchEntity> { + pub fn query_one>( + &self, + entity: Entity, + ) -> Result, NoSuchEntity> { let loc = self.entities.get(entity)?; - Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, &()) }) + Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, ()) }) } - pub fn smart_query_one<'q, Q: Query, C>( + pub fn smart_query_one<'q, 'c, Q: Query<'c, C>, C: Copy + 'c>( &'q self, entity: Entity, - context: &'q C, - ) -> Result, NoSuchEntity> { + context: C, + ) -> Result, NoSuchEntity> { let loc = self.entities.get(entity)?; Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, context) }) } @@ -658,7 +664,7 @@ impl From for ComponentError { pub trait Component: Send + Sync + 'static {} impl Component for T {} -pub trait SmartComponent: Component { +pub trait SmartComponent: Component { fn on_borrow(&self, _id: u32, _x: &T) {} fn on_borrow_mut(&mut self, _id: u32, _x: &T) {} } From eee7b40f5c2afa48a0ebb15456b500762aca2657 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 08:44:47 -0700 Subject: [PATCH 03/10] reconsolidate lifetimes --- src/query.rs | 82 ++++++++++++++++++++---------------------------- src/query_one.rs | 20 ++++++------ src/world.rs | 27 +++++++++------- 3 files changed, 60 insertions(+), 69 deletions(-) diff --git a/src/query.rs b/src/query.rs index ce6fbee3..b248d8d8 100644 --- a/src/query.rs +++ b/src/query.rs @@ -365,15 +365,15 @@ impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> /// A borrow of a `World` sufficient to execute the query `Q` /// /// Note that borrows are not released until this object is dropped. -pub struct QueryBorrow<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { +pub struct QueryBorrow<'w, Q: Query<'w, C>, C: Clone + 'w> { meta: &'w [EntityMeta], archetypes: &'w [Archetype], borrowed: bool, context: C, - _marker: PhantomData<(Q, &'c ())>, + _marker: PhantomData, } -impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { +impl<'w, Q: Query<'w, C>, C: Clone + 'w> QueryBorrow<'w, Q, C> { pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype], context: C) -> Self { Self { meta, @@ -387,7 +387,7 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { /// Execute the query /// /// Must be called only once per query. - pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, 'c, Q, C> { + pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q, C> { self.borrow(); QueryIter { borrow: self, @@ -399,7 +399,7 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { /// Like `iter`, but returns child iterators of at most `batch_size` elements /// /// Useful for distributing work over a threadpool. - pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, 'c, Q, C> { + pub fn iter_batched<'q>(&'q mut self, batch_size: u32) -> BatchedIter<'q, 'w, Q, C> { self.borrow(); BatchedIter { borrow: self, @@ -446,7 +446,7 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); /// ``` - pub fn with(self) -> QueryBorrow<'w, 'c, With, C> { + pub fn with(self) -> QueryBorrow<'w, With, C> { self.transform() } @@ -468,12 +468,12 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` - pub fn without(self) -> QueryBorrow<'w, 'c, Without, C> { + pub fn without(self) -> QueryBorrow<'w, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform>(mut self) -> QueryBorrow<'w, 'c, R, C> { + fn transform>(mut self) -> QueryBorrow<'w, R, C> { let x = QueryBorrow { meta: self.meta, archetypes: self.archetypes, @@ -487,10 +487,10 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> QueryBorrow<'w, 'c, Q, C> { } } -unsafe impl<'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'c> Send for QueryBorrow<'w, 'c, Q, C> {} -unsafe impl<'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'c> Sync for QueryBorrow<'w, 'c, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for QueryBorrow<'w, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for QueryBorrow<'w, Q, C> {} -impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Drop for QueryBorrow<'w, 'c, Q, C> { +impl<'w, Q: Query<'w, C>, C: Clone + 'w> Drop for QueryBorrow<'w, Q, C> { fn drop(&mut self) { if self.borrowed { for x in self.archetypes { @@ -502,11 +502,9 @@ impl<'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Drop for QueryBorrow<'w, 'c, Q, C> } } -impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> IntoIterator - for &'q mut QueryBorrow<'w, 'c, Q, C> -{ - type Item = (Entity, >::Item); - type IntoIter = QueryIter<'q, 'w, 'c, Q, C>; +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> IntoIterator for &'q mut QueryBorrow<'w, Q, C> { + type Item = (Entity, >::Item); + type IntoIter = QueryIter<'q, 'w, Q, C>; fn into_iter(self) -> Self::IntoIter { self.iter() @@ -514,23 +512,17 @@ impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> IntoIterator } /// Iterator over the set of entities with the components in `Q` -pub struct QueryIter<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { - borrow: &'q mut QueryBorrow<'w, 'c, Q, C>, +pub struct QueryIter<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, - iter: Option>, + iter: Option>, } -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send - for QueryIter<'q, 'w, 'c, Q, C> -{ -} -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync - for QueryIter<'q, 'w, 'c, Q, C> -{ -} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for QueryIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for QueryIter<'q, 'w, Q, C> {} -impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Iterator for QueryIter<'q, 'w, 'c, Q, C> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for QueryIter<'q, 'w, Q, C> { + type Item = (Entity, >::Item); #[inline] fn next(&mut self) -> Option { @@ -573,7 +565,7 @@ impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> Iterator for QueryIter<'q, 'w, } } -impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> ExactSizeIterator for QueryIter<'q, 'w, 'c, Q, C> { +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> ExactSizeIterator for QueryIter<'q, 'w, Q, C> { fn len(&self) -> usize { self.borrow .archetypes @@ -609,24 +601,18 @@ impl<'c, Q: Query<'c, C>, C: Clone + 'c> ChunkIter<'c, Q, C> { } /// Batched version of `QueryIter` -pub struct BatchedIter<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'c> { - borrow: &'q mut QueryBorrow<'w, 'c, Q, C>, +pub struct BatchedIter<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { + borrow: &'q mut QueryBorrow<'w, Q, C>, archetype_index: u32, batch_size: u32, batch: u32, } -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send - for BatchedIter<'q, 'w, 'c, Q, C> -{ -} -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync - for BatchedIter<'q, 'w, 'c, Q, C> -{ -} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for BatchedIter<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for BatchedIter<'q, 'w, Q, C> {} -impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for BatchedIter<'q, 'w, 'c, Q, C> { - type Item = Batch<'q, 'w, 'c, Q, C>; +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for BatchedIter<'q, 'w, Q, C> { + type Item = Batch<'q, 'w, Q, C>; fn next(&mut self) -> Option { loop { @@ -667,15 +653,15 @@ impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for BatchedIter<'q, 'w } /// A sequence of entities yielded by `BatchedIter` -pub struct Batch<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> { +pub struct Batch<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> { _marker: PhantomData<&'q ()>, meta: &'w [EntityMeta], - state: ChunkIter<'c, Q, C>, + state: ChunkIter<'w, Q, C>, context: C, } -impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, 'c, Q, C> { - type Item = (Entity, >::Item); +impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, Q, C> { + type Item = (Entity, >::Item); fn next(&mut self) -> Option { let (id, components) = unsafe { self.state.next(self.context.clone())? }; @@ -689,8 +675,8 @@ impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, 'c, } } -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Send for Batch<'q, 'w, 'c, Q, C> {} -unsafe impl<'q, 'w, 'c, Q: Query<'c, C>, C: Clone + Sync + 'w> Sync for Batch<'q, 'w, 'c, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Send for Batch<'q, 'w, Q, C> {} +unsafe impl<'q, 'w, Q: Query<'w, C>, C: Clone + Sync + 'w> Sync for Batch<'q, 'w, Q, C> {} macro_rules! tuple_impl { ($($name: ident),*) => { diff --git a/src/query_one.rs b/src/query_one.rs index 775663b8..8057fa75 100644 --- a/src/query_one.rs +++ b/src/query_one.rs @@ -4,15 +4,15 @@ use crate::query::{Fetch, With, Without}; use crate::{Archetype, Component, Query}; /// A borrow of a `World` sufficient to execute the query `Q` on a single entity -pub struct QueryOne<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> { +pub struct QueryOne<'w, Q: Query<'w, C>, C: Copy + 'w> { archetype: &'w Archetype, index: u32, borrowed: bool, context: C, - _marker: PhantomData<(Q, &'c ())>, + _marker: PhantomData, } -impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> QueryOne<'w, 'c, Q, C> { +impl<'w, Q: Query<'w, C>, C: Copy + 'w> QueryOne<'w, Q, C> { /// Construct a query accessing the entity in `archetype` at `index` /// /// # Safety @@ -34,7 +34,7 @@ impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> QueryOne<'w, 'c, Q, C> { /// /// Panics if called more than once or if it would construct a borrow that clashes with another /// pre-existing borrow. - pub fn get(&mut self) -> Option<>::Item> { + pub fn get(&mut self) -> Option<>::Item> { if self.borrowed { panic!("called QueryOnce::get twice; construct a new query instead"); } @@ -49,19 +49,19 @@ impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> QueryOne<'w, 'c, Q, C> { /// Transform the query into one that requires a certain component without borrowing it /// /// See `QueryBorrow::with` for details. - pub fn with(self) -> QueryOne<'w, 'c, With, C> { + pub fn with(self) -> QueryOne<'w, With, C> { self.transform() } /// Transform the query into one that skips entities having a certain component /// /// See `QueryBorrow::without` for details. - pub fn without(self) -> QueryOne<'w, 'c, Without, C> { + pub fn without(self) -> QueryOne<'w, Without, C> { self.transform() } /// Helper to change the type of the query - fn transform>(mut self) -> QueryOne<'w, 'c, R, C> { + fn transform>(mut self) -> QueryOne<'w, R, C> { let x = QueryOne { archetype: self.archetype, index: self.index, @@ -75,7 +75,7 @@ impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> QueryOne<'w, 'c, Q, C> { } } -impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> Drop for QueryOne<'w, 'c, Q, C> { +impl<'w, Q: Query<'w, C>, C: Copy + 'w> Drop for QueryOne<'w, Q, C> { fn drop(&mut self) { if self.borrowed { Q::Fetch::release(self.archetype); @@ -83,5 +83,5 @@ impl<'w, 'c, Q: Query<'c, C>, C: Copy + 'c> Drop for QueryOne<'w, 'c, Q, C> { } } -unsafe impl<'w, 'c, Q: Query<'c, C>, C: Copy + Sync + 'c> Send for QueryOne<'w, 'c, Q, C> {} -unsafe impl<'w, 'c, Q: Query<'c, C>, C: Copy + Sync + 'c> Sync for QueryOne<'w, 'c, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Copy + Sync + 'w> Send for QueryOne<'w, Q, C> {} +unsafe impl<'w, Q: Query<'w, C>, C: Copy + Sync + 'w> Sync for QueryOne<'w, Q, C> {} diff --git a/src/world.rs b/src/world.rs index c5f3b601..9989be78 100644 --- a/src/world.rs +++ b/src/world.rs @@ -244,14 +244,15 @@ impl World { /// assert!(entities.contains(&(a, 123, true))); /// assert!(entities.contains(&(b, 456, false))); /// ``` - pub fn query<'q, Q: Query<'static>>(&'q self) -> QueryBorrow<'q, 'static, Q, ()> { + pub fn query<'q, Q: Query<'q>>(&'q self) -> QueryBorrow<'q, Q, ()> { QueryBorrow::new(&self.entities.meta, &self.archetypes, ()) } - pub fn smart_query<'q, 'c, Q: Query<'c, C>, C: Copy + 'c>( - &'q self, - context: C, - ) -> QueryBorrow<'q, 'c, Q, C> { + pub fn smart_query<'q, Q, C>(&'q self, context: C) -> QueryBorrow<'q, Q, C> + where + Q: Query<'q, C>, + C: Copy + 'q, + { QueryBorrow::new(&self.entities.meta, &self.archetypes, context) } @@ -274,19 +275,23 @@ impl World { /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` - pub fn query_one>( - &self, - entity: Entity, - ) -> Result, NoSuchEntity> { + pub fn query_one<'q, Q>(&'q self, entity: Entity) -> Result, NoSuchEntity> + where + Q: Query<'q>, + { let loc = self.entities.get(entity)?; Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, ()) }) } - pub fn smart_query_one<'q, 'c, Q: Query<'c, C>, C: Copy + 'c>( + pub fn smart_query_one<'q, Q, C>( &'q self, entity: Entity, context: C, - ) -> Result, NoSuchEntity> { + ) -> Result, NoSuchEntity> + where + Q: Query<'q, C>, + C: Copy + 'q, + { let loc = self.entities.get(entity)?; Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, context) }) } From 0425a5122b6b7969a548d287be6539f7477bc6e1 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 09:34:52 -0700 Subject: [PATCH 04/10] smart_ to _with_context rename, and smart entity/get/get_mut --- src/borrow.rs | 105 +++++++++++++++++++++++++++++----------- src/query.rs | 64 +++++++++++------------- src/query_one.rs | 14 ++++-- src/world.rs | 123 +++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 220 insertions(+), 86 deletions(-) diff --git a/src/borrow.rs b/src/borrow.rs index c42a4bb9..996361d0 100644 --- a/src/borrow.rs +++ b/src/borrow.rs @@ -18,7 +18,7 @@ use core::ptr::NonNull; use core::sync::atomic::{AtomicUsize, Ordering}; use crate::archetype::Archetype; -use crate::{Component, MissingComponent}; +use crate::{Component, Entity, MissingComponent, SmartComponent}; pub struct AtomicBorrow(AtomicUsize); @@ -63,15 +63,19 @@ const UNIQUE_BIT: usize = !(usize::max_value() >> 1); /// Shared borrow of an entity's component #[derive(Clone)] -pub struct Ref<'a, T: Component> { +pub struct Ref<'a, T: SmartComponent, C: Clone + 'a = ()> { archetype: &'a Archetype, target: NonNull, + entity: Entity, + context: C, } -impl<'a, T: Component> Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone + 'a> Ref<'a, T, C> { pub(crate) unsafe fn new( archetype: &'a Archetype, index: u32, + entity: Entity, + context: C, ) -> Result { let target = NonNull::new_unchecked( archetype @@ -81,36 +85,47 @@ impl<'a, T: Component> Ref<'a, T> { .add(index as usize), ); archetype.borrow::(); - Ok(Self { archetype, target }) + Ok(Self { + archetype, + target, + entity, + context, + }) } } -unsafe impl Send for Ref<'_, T> {} -unsafe impl Sync for Ref<'_, T> {} +unsafe impl, C: Clone + Sync> Send for Ref<'_, T, C> {} +unsafe impl, C: Clone + Sync> Sync for Ref<'_, T, C> {} -impl<'a, T: Component> Drop for Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Drop for Ref<'a, T, C> { fn drop(&mut self) { self.archetype.release::(); } } -impl<'a, T: Component> Deref for Ref<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { type Target = T; fn deref(&self) -> &T { - unsafe { self.target.as_ref() } + let value = unsafe { self.target.as_ref() }; + value.on_borrow(self.entity, &self.context); + value } } /// Unique borrow of an entity's component -pub struct RefMut<'a, T: Component> { +pub struct RefMut<'a, T: SmartComponent, C: Clone = ()> { archetype: &'a Archetype, target: NonNull, + entity: Entity, + context: C, } -impl<'a, T: Component> RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> RefMut<'a, T, C> { pub(crate) unsafe fn new( archetype: &'a Archetype, index: u32, + entity: Entity, + context: C, ) -> Result { let target = NonNull::new_unchecked( archetype @@ -120,14 +135,19 @@ impl<'a, T: Component> RefMut<'a, T> { .add(index as usize), ); archetype.borrow_mut::(); - Ok(Self { archetype, target }) + Ok(Self { + archetype, + target, + entity, + context, + }) } } -unsafe impl Send for RefMut<'_, T> {} -unsafe impl Sync for RefMut<'_, T> {} +unsafe impl, C: Clone + Sync> Send for RefMut<'_, T, C> {} +unsafe impl, C: Clone + Sync> Sync for RefMut<'_, T, C> {} -impl<'a, T: Component> Drop for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Drop for RefMut<'a, T, C> { fn drop(&mut self) { self.archetype.release_mut::(); } @@ -136,36 +156,51 @@ impl<'a, T: Component> Drop for RefMut<'a, T> { impl<'a, T: Component> Deref for RefMut<'a, T> { type Target = T; fn deref(&self) -> &T { - unsafe { self.target.as_ref() } + let value = unsafe { self.target.as_ref() }; + value.on_borrow(self.entity, &self.context); + value } } impl<'a, T: Component> DerefMut for RefMut<'a, T> { fn deref_mut(&mut self) -> &mut T { - unsafe { self.target.as_mut() } + let value = unsafe { self.target.as_mut() }; + value.on_borrow_mut(self.entity, &self.context); + value } } /// Handle to an entity with any component types #[derive(Copy, Clone)] -pub struct EntityRef<'a> { +pub struct EntityRef<'a, C: Clone = ()> { archetype: Option<&'a Archetype>, index: u32, + entity: Entity, + context: C, } -impl<'a> EntityRef<'a> { +impl<'a, C: Clone> EntityRef<'a, C> { /// Construct a `Ref` for an entity with no components - pub(crate) fn empty() -> Self { + pub(crate) fn empty(entity: Entity, context: C) -> Self { Self { archetype: None, index: 0, + entity, + context, } } - pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self { + pub(crate) unsafe fn new( + archetype: &'a Archetype, + index: u32, + entity: Entity, + context: C, + ) -> Self { Self { archetype: Some(archetype), index, + entity, + context, } } @@ -173,15 +208,31 @@ impl<'a> EntityRef<'a> { /// /// Panics if the component is already uniquely borrowed from another entity with the same /// components. - pub fn get(&self) -> Option> { - Some(unsafe { Ref::new(self.archetype?, self.index).ok()? }) + pub fn get>(&self) -> Option> { + Some(unsafe { + Ref::new( + self.archetype?, + self.index, + self.entity, + self.context.clone(), + ) + .ok()? + }) } /// Uniquely borrow the component of type `T`, if it exists /// /// Panics if the component is already borrowed from another entity with the same components. - pub fn get_mut(&self) -> Option> { - Some(unsafe { RefMut::new(self.archetype?, self.index).ok()? }) + pub fn get_mut>(&self) -> Option> { + Some(unsafe { + RefMut::new( + self.archetype?, + self.index, + self.entity, + self.context.clone(), + ) + .ok()? + }) } /// Enumerate the types of the entity's components @@ -197,5 +248,5 @@ impl<'a> EntityRef<'a> { } } -unsafe impl<'a> Send for EntityRef<'a> {} -unsafe impl<'a> Sync for EntityRef<'a> {} +unsafe impl<'a, C: Clone + Sync> Send for EntityRef<'a, C> {} +unsafe impl<'a, C: Clone + Sync> Sync for EntityRef<'a, C> {} diff --git a/src/query.rs b/src/query.rs index b248d8d8..47daacf0 100644 --- a/src/query.rs +++ b/src/query.rs @@ -52,7 +52,7 @@ pub trait Fetch<'q, 'c, C: Clone + 'c>: Sized { /// - `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 next(&mut self, id: u32, context: C) -> Self::Item; + unsafe fn next(&mut self, id: Entity, context: C) -> Self::Item; } /// Type of access a `Query` may have to an `Archetype` @@ -75,7 +75,7 @@ pub struct FetchRead(NonNull); pub struct Ref<'a, T, C> { value: &'a T, - id: u32, + id: Entity, context: C, } @@ -131,7 +131,7 @@ impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchRead archetype.release::(); } - unsafe fn next(&mut self, id: u32, context: C) -> Ref<'q, T, C> { + unsafe fn next(&mut self, id: Entity, context: C) -> Ref<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); Ref { @@ -151,7 +151,7 @@ pub struct FetchWrite(NonNull); pub struct RefMut<'a, T, C> { value: &'a mut T, - id: u32, + id: Entity, context: C, } @@ -202,7 +202,7 @@ impl<'q, 'c, T: SmartComponent, C: Clone + 'c> Fetch<'q, 'c, C> for FetchWrit archetype.release_mut::(); } - unsafe fn next(&mut self, id: u32, context: C) -> RefMut<'q, T, C> { + unsafe fn next(&mut self, id: Entity, context: C) -> RefMut<'q, T, C> { let x = self.0.as_ptr(); self.0 = NonNull::new_unchecked(x.add(1)); RefMut { @@ -239,7 +239,7 @@ impl<'q, 'c, T: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> for TryFetch Option { + unsafe fn next(&mut self, id: Entity, context: C) -> Option { Some(self.0.as_mut()?.next(id, context)) } } @@ -296,7 +296,7 @@ impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> F::release(archetype) } - unsafe fn next(&mut self, id: u32, context: C) -> F::Item { + unsafe fn next(&mut self, id: Entity, context: C) -> F::Item { self.0.next(id, context) } } @@ -357,7 +357,7 @@ impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> F::release(archetype) } - unsafe fn next(&mut self, id: u32, context: C) -> F::Item { + unsafe fn next(&mut self, id: Entity, context: C) -> F::Item { self.0.next(id, context) } } @@ -540,21 +540,15 @@ impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for QueryIter<'q, 'w, Q, C }); } } - Some(ref mut iter) => match unsafe { iter.next(self.borrow.context.clone()) } { - None => { - self.iter = None; - continue; + Some(ref mut iter) => { + match unsafe { iter.next(self.borrow.meta, self.borrow.context.clone()) } { + None => { + self.iter = None; + continue; + } + Some(item) => return Some(item), } - Some((id, components)) => { - return Some(( - Entity { - id, - generation: self.borrow.meta[id as usize].generation, - }, - components, - )); - } - }, + } } } } @@ -587,16 +581,21 @@ impl<'c, Q: Query<'c, C>, C: Clone + 'c> ChunkIter<'c, Q, C> { #[inline] unsafe fn next<'a>( &mut self, + meta: &'c [EntityMeta], context: C, - ) -> Option<(u32, >::Item)> { + ) -> Option<(Entity, >::Item)> { if self.len == 0 { return None; } self.len -= 1; - let entity = self.entities.as_ptr(); - let id = *entity; - self.entities = NonNull::new_unchecked(entity.add(1)); - Some((id, self.fetch.next(id, context))) + let entity_ptr = self.entities.as_ptr(); + self.entities = NonNull::new_unchecked(entity_ptr.add(1)); + let id = *entity_ptr; + let entity = Entity { + id, + generation: meta[id as usize].generation, + }; + Some((entity, self.fetch.next(entity, context))) } } @@ -664,14 +663,7 @@ impl<'q, 'w, Q: Query<'w, C>, C: Clone + 'w> Iterator for Batch<'q, 'w, Q, C> { type Item = (Entity, >::Item); fn next(&mut self) -> Option { - let (id, components) = unsafe { self.state.next(self.context.clone())? }; - Some(( - Entity { - id, - generation: self.meta[id as usize].generation, - }, - components, - )) + unsafe { self.state.next(self.meta, self.context.clone()) } } } @@ -708,7 +700,7 @@ macro_rules! tuple_impl { } #[allow(unused_variables)] - unsafe fn next(&mut self, id: u32, context: Z) -> Self::Item { + unsafe fn next(&mut self, id: Entity, context: Z) -> Self::Item { #[allow(non_snake_case)] let ($($name,)*) = self; ($($name.next(id, context.clone()),)*) diff --git a/src/query_one.rs b/src/query_one.rs index 8057fa75..952f8a7a 100644 --- a/src/query_one.rs +++ b/src/query_one.rs @@ -1,13 +1,14 @@ use core::marker::PhantomData; use crate::query::{Fetch, With, Without}; -use crate::{Archetype, Component, Query}; +use crate::{Archetype, Component, Entity, Query}; /// A borrow of a `World` sufficient to execute the query `Q` on a single entity pub struct QueryOne<'w, Q: Query<'w, C>, C: Copy + 'w> { archetype: &'w Archetype, index: u32, borrowed: bool, + entity: Entity, context: C, _marker: PhantomData, } @@ -18,11 +19,17 @@ impl<'w, Q: Query<'w, C>, C: Copy + 'w> QueryOne<'w, Q, C> { /// # Safety /// /// `index` must be in-bounds for `archetype` - pub(crate) unsafe fn new(archetype: &'w Archetype, index: u32, context: C) -> Self { + pub(crate) unsafe fn new( + archetype: &'w Archetype, + index: u32, + entity: Entity, + context: C, + ) -> Self { Self { archetype, index, borrowed: false, + entity, context, _marker: PhantomData, } @@ -42,7 +49,7 @@ impl<'w, Q: Query<'w, C>, C: Copy + 'w> QueryOne<'w, Q, C> { let mut fetch = Q::Fetch::get(self.archetype, self.index as usize)?; self.borrowed = true; Q::Fetch::borrow(self.archetype); - Some(fetch.next(self.index, self.context)) + Some(fetch.next(self.entity, self.context)) } } @@ -66,6 +73,7 @@ impl<'w, Q: Query<'w, C>, C: Copy + 'w> QueryOne<'w, Q, C> { archetype: self.archetype, index: self.index, borrowed: self.borrowed, + entity: self.entity, context: self.context, _marker: PhantomData, }; diff --git a/src/world.rs b/src/world.rs index 9989be78..039ce289 100644 --- a/src/world.rs +++ b/src/world.rs @@ -245,10 +245,10 @@ impl World { /// assert!(entities.contains(&(b, 456, false))); /// ``` pub fn query<'q, Q: Query<'q>>(&'q self) -> QueryBorrow<'q, Q, ()> { - QueryBorrow::new(&self.entities.meta, &self.archetypes, ()) + self.query_with_context(()) } - pub fn smart_query<'q, Q, C>(&'q self, context: C) -> QueryBorrow<'q, Q, C> + pub fn query_with_context<'q, Q, C>(&'q self, context: C) -> QueryBorrow<'q, Q, C> where Q: Query<'q, C>, C: Copy + 'q, @@ -279,11 +279,10 @@ impl World { where Q: Query<'q>, { - let loc = self.entities.get(entity)?; - Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, ()) }) + self.query_one_with_context(entity, ()) } - pub fn smart_query_one<'q, Q, C>( + pub fn query_one_with_context<'q, Q, C>( &'q self, entity: Entity, context: C, @@ -293,19 +292,33 @@ impl World { C: Copy + 'q, { let loc = self.entities.get(entity)?; - Ok(unsafe { QueryOne::new(&self.archetypes[loc.archetype as usize], loc.index, context) }) + Ok(unsafe { + QueryOne::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + ) + }) } /// Borrow the `T` component of `entity` /// /// Panics if the component is already uniquely borrowed from another entity with the same /// components. - pub fn get(&self, entity: Entity) -> Result, ComponentError> { + pub fn get(&self, entity: Entity) -> Result, ComponentError> { let loc = self.entities.get(entity)?; if loc.archetype == 0 { return Err(MissingComponent::new::().into()); } - Ok(unsafe { Ref::new(&self.archetypes[loc.archetype as usize], loc.index)? }) + Ok(unsafe { + Ref::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + (), + )? + }) } /// Uniquely borrow the `T` component of `entity` @@ -316,7 +329,14 @@ impl World { if loc.archetype == 0 { return Err(MissingComponent::new::().into()); } - Ok(unsafe { RefMut::new(&self.archetypes[loc.archetype as usize], loc.index)? }) + Ok(unsafe { + RefMut::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + (), + )? + }) } /// Access an entity regardless of its component types @@ -324,8 +344,71 @@ impl World { /// Does not immediately borrow any component. pub fn entity(&self, entity: Entity) -> Result, NoSuchEntity> { Ok(match self.entities.get(entity)? { - Location { archetype: 0, .. } => EntityRef::empty(), - loc => unsafe { EntityRef::new(&self.archetypes[loc.archetype as usize], loc.index) }, + Location { archetype: 0, .. } => EntityRef::empty(entity, ()), + loc => unsafe { + EntityRef::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + (), + ) + }, + }) + } + + pub fn get_with_context, C: Clone>( + &self, + entity: Entity, + context: C, + ) -> Result, ComponentError> { + let loc = self.entities.get(entity)?; + if loc.archetype == 0 { + return Err(MissingComponent::new::().into()); + } + Ok(unsafe { + Ref::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + )? + }) + } + + pub fn get_mut_with_context, C: Clone>( + &self, + entity: Entity, + context: C, + ) -> Result, ComponentError> { + let loc = self.entities.get(entity)?; + if loc.archetype == 0 { + return Err(MissingComponent::new::().into()); + } + Ok(unsafe { + RefMut::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + )? + }) + } + + pub fn entity_with_context( + &self, + entity: Entity, + context: C, + ) -> Result, NoSuchEntity> { + Ok(match self.entities.get(entity)? { + Location { archetype: 0, .. } => EntityRef::empty(entity, context), + loc => unsafe { + EntityRef::new( + &self.archetypes[loc.archetype as usize], + loc.index, + entity, + context, + ) + }, }) } @@ -670,8 +753,8 @@ pub trait Component: Send + Sync + 'static {} impl Component for T {} pub trait SmartComponent: Component { - fn on_borrow(&self, _id: u32, _x: &T) {} - fn on_borrow_mut(&mut self, _id: u32, _x: &T) {} + fn on_borrow(&self, _id: Entity, _x: &T) {} + fn on_borrow_mut(&mut self, _id: Entity, _x: &T) {} } impl SmartComponent<()> for T {} @@ -715,13 +798,13 @@ impl<'a> Iterator for Iter<'a> { let index = self.index; self.index += 1; let id = current.entity_id(index); - return Some(( - Entity { - id, - generation: self.entities.meta[id as usize].generation, - }, - unsafe { EntityRef::new(current, index) }, - )); + let entity = Entity { + id, + generation: self.entities.meta[id as usize].generation, + }; + return Some((entity, unsafe { + EntityRef::new(current, index, entity, ()) + })); } } } From 65871f276e3a6ac8ad3d8f24aeaef86458850cd0 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 10:23:48 -0700 Subject: [PATCH 05/10] clone context instead of borrow for SmartComponent hooks --- src/borrow.rs | 6 +++--- src/query.rs | 6 +++--- src/world.rs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/borrow.rs b/src/borrow.rs index 996361d0..d715af55 100644 --- a/src/borrow.rs +++ b/src/borrow.rs @@ -107,7 +107,7 @@ impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { type Target = T; fn deref(&self) -> &T { let value = unsafe { self.target.as_ref() }; - value.on_borrow(self.entity, &self.context); + value.on_borrow(self.entity, self.context.clone()); value } } @@ -157,7 +157,7 @@ impl<'a, T: Component> Deref for RefMut<'a, T> { type Target = T; fn deref(&self) -> &T { let value = unsafe { self.target.as_ref() }; - value.on_borrow(self.entity, &self.context); + value.on_borrow(self.entity, self.context.clone()); value } } @@ -165,7 +165,7 @@ impl<'a, T: Component> Deref for RefMut<'a, T> { impl<'a, T: Component> DerefMut for RefMut<'a, T> { fn deref_mut(&mut self) -> &mut T { let value = unsafe { self.target.as_mut() }; - value.on_borrow_mut(self.entity, &self.context); + value.on_borrow_mut(self.entity, self.context.clone()); value } } diff --git a/src/query.rs b/src/query.rs index 47daacf0..1b70ffc2 100644 --- a/src/query.rs +++ b/src/query.rs @@ -101,7 +101,7 @@ impl<'a, T: SmartComponent, C: Clone> Deref for Ref<'a, T, C> { type Target = T; fn deref(&self) -> &Self::Target { - self.value.on_borrow(self.id, &self.context); + self.value.on_borrow(self.id, self.context.clone()); self.value } } @@ -165,14 +165,14 @@ impl<'a, T: SmartComponent, C: Clone> Deref for RefMut<'a, T, C> { type Target = T; fn deref(&self) -> &Self::Target { - (&*self.value).on_borrow(self.id, &self.context); + (&*self.value).on_borrow(self.id, self.context.clone()); self.value } } impl<'a, T: SmartComponent, C: Clone> DerefMut for RefMut<'a, T, C> { fn deref_mut(&mut self) -> &mut Self::Target { - self.value.on_borrow_mut(self.id, &self.context); + self.value.on_borrow_mut(self.id, self.context.clone()); self.value } } diff --git a/src/world.rs b/src/world.rs index 039ce289..36d2eca2 100644 --- a/src/world.rs +++ b/src/world.rs @@ -753,8 +753,8 @@ pub trait Component: Send + Sync + 'static {} impl Component for T {} pub trait SmartComponent: Component { - fn on_borrow(&self, _id: Entity, _x: &T) {} - fn on_borrow_mut(&mut self, _id: Entity, _x: &T) {} + fn on_borrow(&self, _id: Entity, _x: T) {} + fn on_borrow_mut(&mut self, _id: Entity, _x: T) {} } impl SmartComponent<()> for T {} From 99ab07540970333279802fa23683320acccf34d9 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 10:42:49 -0700 Subject: [PATCH 06/10] fix tests --- src/lib.rs | 4 ++-- src/query.rs | 8 ++++---- src/world.rs | 4 ++-- tests/tests.rs | 18 ++++++++++++++---- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aff94e9f..0eb48495 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,8 +31,8 @@ //! let a = world.spawn((123, true, "abc")); //! let b = world.spawn((42, false)); //! // Systems can be simple for loops -//! for (id, (number, &flag)) in world.query::<(&mut i32, &bool)>().iter() { -//! if flag { *number *= 2; } +//! for (id, (mut number, flag)) in world.query::<(&mut i32, &bool)>().iter() { +//! if *flag { *number *= 2; } //! } //! // Random access is simple and safe //! assert_eq!(*world.get::(a).unwrap(), 246); diff --git a/src/query.rs b/src/query.rs index 1b70ffc2..a6815cac 100644 --- a/src/query.rs +++ b/src/query.rs @@ -257,7 +257,7 @@ impl<'q, 'c, T: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> for TryFetch>() /// .iter() -/// .map(|(e, &i)| (e, i)) +/// .map(|(e, i)| (e, *i)) /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` @@ -314,7 +314,7 @@ impl<'q, 'c, T: Component, F: Fetch<'q, 'c, C>, C: Clone + 'c> Fetch<'q, 'c, C> /// let c = world.spawn((42, "def")); /// let entities = world.query::>() /// .iter() -/// .map(|(e, &i)| (e, i)) +/// .map(|(e, i)| (e, *i)) /// .collect::>(); /// assert_eq!(entities.len(), 2); /// assert!(entities.contains(&(a, 123))); @@ -441,7 +441,7 @@ impl<'w, Q: Query<'w, C>, C: Clone + 'w> QueryBorrow<'w, Q, C> { /// let entities = world.query::<&i32>() /// .with::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Clone out of the world + /// .map(|(e, i)| (e, *i)) // Clone out of the world /// .collect::>(); /// assert!(entities.contains(&(a, 123))); /// assert!(entities.contains(&(b, 456))); @@ -464,7 +464,7 @@ impl<'w, Q: Query<'w, C>, C: Clone + 'w> QueryBorrow<'w, Q, C> { /// let entities = world.query::<&i32>() /// .without::() /// .iter() - /// .map(|(e, &i)| (e, i)) // Clone out of the world + /// .map(|(e, i)| (e, *i)) // Clone out of the world /// .collect::>(); /// assert_eq!(entities, &[(c, 42)]); /// ``` diff --git a/src/world.rs b/src/world.rs index 36d2eca2..320100e1 100644 --- a/src/world.rs +++ b/src/world.rs @@ -238,7 +238,7 @@ impl World { /// let c = world.spawn((42, "def")); /// let entities = world.query::<(&i32, &bool)>() /// .iter() - /// .map(|(e, (&i, &b))| (e, i, b)) // Copy out of the world + /// .map(|(e, (i, b))| (e, *i, *b)) // Copy out of the world /// .collect::>(); /// assert_eq!(entities.len(), 2); /// assert!(entities.contains(&(a, 123, true))); @@ -271,7 +271,7 @@ impl World { /// let a = world.spawn((123, true, "abc")); /// // The returned query must outlive the borrow made by `get` /// let mut query = world.query_one::<(&mut i32, &bool)>(a).unwrap(); - /// let (number, flag) = query.get().unwrap(); + /// let (mut number, flag) = query.get().unwrap(); /// if *flag { *number *= 2; } /// assert_eq!(*number, 246); /// ``` diff --git a/tests/tests.rs b/tests/tests.rs index 5a108c6b..22ae8ce2 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -342,12 +342,22 @@ fn query_one() { let a = world.spawn(("abc", 123)); let b = world.spawn(("def", 456)); let c = world.spawn(("ghi", 789, true)); - assert_eq!(world.query_one::<&i32>(a).unwrap().get(), Some(&123)); - assert_eq!(world.query_one::<&i32>(b).unwrap().get(), Some(&456)); + assert_eq!( + world.query_one::<&i32>(a).unwrap().get().as_deref(), + Some(&123) + ); + assert_eq!( + world.query_one::<&i32>(b).unwrap().get().as_deref(), + Some(&456) + ); assert!(world.query_one::<(&i32, &bool)>(a).unwrap().get().is_none()); assert_eq!( - world.query_one::<(&i32, &bool)>(c).unwrap().get(), - Some((&789, &true)) + world + .query_one::<(&i32, &bool)>(c) + .unwrap() + .get() + .map(|(x, y)| (*x, *y)), + Some((789, true)) ); world.despawn(a).unwrap(); assert!(world.query_one::<&i32>(a).is_err()); From da41dc9a839bfc3cf6a0c74dcb490b0f59208efa Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 24 Sep 2020 12:18:56 -0700 Subject: [PATCH 07/10] fix Deref/DerefMut for borrow::RefMut --- src/borrow.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/borrow.rs b/src/borrow.rs index d715af55..b65d1687 100644 --- a/src/borrow.rs +++ b/src/borrow.rs @@ -18,7 +18,7 @@ use core::ptr::NonNull; use core::sync::atomic::{AtomicUsize, Ordering}; use crate::archetype::Archetype; -use crate::{Component, Entity, MissingComponent, SmartComponent}; +use crate::{Entity, MissingComponent, SmartComponent}; pub struct AtomicBorrow(AtomicUsize); @@ -153,7 +153,7 @@ impl<'a, T: SmartComponent, C: Clone> Drop for RefMut<'a, T, C> { } } -impl<'a, T: Component> Deref for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> Deref for RefMut<'a, T, C> { type Target = T; fn deref(&self) -> &T { let value = unsafe { self.target.as_ref() }; @@ -162,7 +162,7 @@ impl<'a, T: Component> Deref for RefMut<'a, T> { } } -impl<'a, T: Component> DerefMut for RefMut<'a, T> { +impl<'a, T: SmartComponent, C: Clone> DerefMut for RefMut<'a, T, C> { fn deref_mut(&mut self) -> &mut T { let value = unsafe { self.target.as_mut() }; value.on_borrow_mut(self.entity, self.context.clone()); From 0e1489c9c7354ef439392dfcaab8db2df8f20ba8 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Sun, 27 Sep 2020 09:03:05 -0700 Subject: [PATCH 08/10] get_with_context --- src/world.rs | 40 ++++------------------------------------ 1 file changed, 4 insertions(+), 36 deletions(-) diff --git a/src/world.rs b/src/world.rs index 320100e1..aac24dfe 100644 --- a/src/world.rs +++ b/src/world.rs @@ -306,54 +306,22 @@ impl World { /// /// Panics if the component is already uniquely borrowed from another entity with the same /// components. - pub fn get(&self, entity: Entity) -> Result, ComponentError> { - let loc = self.entities.get(entity)?; - if loc.archetype == 0 { - return Err(MissingComponent::new::().into()); - } - Ok(unsafe { - Ref::new( - &self.archetypes[loc.archetype as usize], - loc.index, - entity, - (), - )? - }) + pub fn get(&self, entity: Entity) -> Result, ComponentError> { + self.get_with_context(entity, ()) } /// Uniquely borrow the `T` component of `entity` /// /// Panics if the component is already borrowed from another entity with the same components. pub fn get_mut(&self, entity: Entity) -> Result, ComponentError> { - let loc = self.entities.get(entity)?; - if loc.archetype == 0 { - return Err(MissingComponent::new::().into()); - } - Ok(unsafe { - RefMut::new( - &self.archetypes[loc.archetype as usize], - loc.index, - entity, - (), - )? - }) + self.get_mut_with_context(entity, ()) } /// Access an entity regardless of its component types /// /// Does not immediately borrow any component. pub fn entity(&self, entity: Entity) -> Result, NoSuchEntity> { - Ok(match self.entities.get(entity)? { - Location { archetype: 0, .. } => EntityRef::empty(entity, ()), - loc => unsafe { - EntityRef::new( - &self.archetypes[loc.archetype as usize], - loc.index, - entity, - (), - ) - }, - }) + self.entity_with_context(entity, ()) } pub fn get_with_context, C: Clone>( From 0e101b6046a612b4bf019b7206c8c1f3c8d03824 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Fri, 2 Oct 2020 09:48:38 -0700 Subject: [PATCH 09/10] Update bench. --- benches/bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benches/bench.rs b/benches/bench.rs index 685dbb0b..87fdaffe 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -66,7 +66,7 @@ fn iterate_100k(b: &mut Bencher) { world.spawn((Position(-(i as f32)), Velocity(i as f32))); } b.iter(|| { - for (_, (pos, vel)) in &mut world.query::<(&mut Position, &Velocity)>() { + for (_, (mut pos, vel)) in &mut world.query::<(&mut Position, &Velocity)>() { pos.0 += vel.0; } }) From 51a2f28cb0b101c8134756328d14f73a1840eb12 Mon Sep 17 00:00:00 2001 From: Sean Leffler Date: Thu, 8 Oct 2020 20:45:43 -0700 Subject: [PATCH 10/10] Document additions. --- src/world.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/src/world.rs b/src/world.rs index aac24dfe..3b7977fd 100644 --- a/src/world.rs +++ b/src/world.rs @@ -205,7 +205,7 @@ impl World { self.entities.contains(entity) } - /// Efficiently iterate over all entities that have certain components + /// Efficiently iterate over all entities that have certain components. /// /// Calling `iter` on the returned value yields `(Entity, Q)` tuples, where `Q` is some query /// type. A query type is `&T`, `&mut T`, a tuple of query types, or an `Option` wrapping a @@ -229,6 +229,12 @@ impl World { /// its dynamic borrows from the world to be released. Similarly, lifetime rules ensure that /// references obtained from a query cannot outlive the `QueryBorrow`. /// + /// If you need change detection of some sort, you will likely want to use [`query_with_context`] + /// instead; this method is actually shorthand for `.query_with_context(())`. See also [`SmartComponent`]. + /// + /// [`query_with_context`]: World::query_with_context + /// [`SmartComponent`]: SmartComponent + /// /// # Example /// ``` /// # use hecs::*; @@ -248,6 +254,13 @@ impl World { self.query_with_context(()) } + /// See [`query`]. This method augments `query` with a contex type, accessible by components + /// through the [`SmartComponent`] trait (`query` is actually implemented on top of this + /// method, using the empty context `()`.) If you are implementing an ECS on top of `hecs` + /// which needs some sort of change detection, this is likely the interface you want to use. + /// + /// [`query`]: World::query + /// [`SmartComponent`]: SmartComponent pub fn query_with_context<'q, Q, C>(&'q self, context: C) -> QueryBorrow<'q, Q, C> where Q: Query<'q, C>, @@ -264,6 +277,12 @@ impl World { /// /// Handy for accessing multiple components simultaneously. /// + /// If you need change detection of some sort, you will likely want to use [`query_one_with_context`] + /// instead; this method is actually shorthand for `.query_one_with_context(())`. See also [`SmartComponent`]. + /// + /// [`query_one_with_context`]: World::query_with_context + /// [`SmartComponent`]: SmartComponent + /// /// # Example /// ``` /// # use hecs::*; @@ -282,6 +301,13 @@ impl World { self.query_one_with_context(entity, ()) } + /// See [`query_one`]. This method augments `query_one` with a contex type, accessible by components + /// through the [`SmartComponent`] trait (`query_one` is actually implemented on top of this + /// method, using the empty context `()`.) If you are implementing an ECS on top of `hecs` + /// which needs some sort of change detection, this is likely the interface you want to use. + /// + /// [`query_one`]: World::query_one + /// [`SmartComponent`]: SmartComponent pub fn query_one_with_context<'q, Q, C>( &'q self, entity: Entity, @@ -324,6 +350,12 @@ impl World { self.entity_with_context(entity, ()) } + /// See [`get`]. This method allows `get` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component + /// is accessed immutably. See also [`SmartComponent`]. + /// + /// [`get`]: World::get + /// [`SmartComponent`]: SmartComponent pub fn get_with_context, C: Clone>( &self, entity: Entity, @@ -343,6 +375,12 @@ impl World { }) } + /// See [`get_mut`]. This method allows `get_mut` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component + /// is accessed mutably. See also [`SmartComponent`]. + /// + /// [`get_mut`]: World::get_mut + /// [`SmartComponent`]: SmartComponent pub fn get_mut_with_context, C: Clone>( &self, entity: Entity, @@ -362,6 +400,12 @@ impl World { }) } + /// See [`entity`]. This method allows `entity` to be augmented with a context type `C`, provided to + /// components which implement `SmartComponent`. This allows for detecting when the component is + /// accessed, and how. See also [`SmartComponent`]. + /// + /// [`entity`]: World::entity + /// [`SmartComponent`]: SmartComponent pub fn entity_with_context( &self, entity: Entity, @@ -720,8 +764,21 @@ impl From for ComponentError { pub trait Component: Send + Sync + 'static {} impl Component for T {} +/// `SmartComponent` is an additional layer on top of `Component` which optionally allows components +/// to be aware of when they are accessed. It is by default implemented for all components w.r.t. the +/// "empty context" `()` (all types that implement `Component` implement `SmartComponent<()>`.) +/// +/// The callbacks in this trait are called *when the reference returned from a query is dereferenced;* +/// in other words, these will not be called until the component is derefenced to an `&` or `&mut`. +/// +/// The type parameter `T` of `SmartComponent` is the "context" type. It's generally expected that +/// you'll use an immutable reference of some kind here, but anything clonable is fine. pub trait SmartComponent: Component { + /// Called when the component is immutably accessed. You probably won't ever need this one. fn on_borrow(&self, _id: Entity, _x: T) {} + /// Called when the component is mutably accessed. Useful for flagging changes to components. + /// For example, the context type `T` might be a `Mutex` type or similar (`AtomicBitSet`) + /// for which you might set the relevant bit on a mutable access to this component on an entity. fn on_borrow_mut(&mut self, _id: Entity, _x: T) {} }