From e20862b2d0d1808ca2f1bcf55d47e977af89c756 Mon Sep 17 00:00:00 2001 From: Nelson Earle Date: Tue, 10 Sep 2024 23:17:11 -0500 Subject: [PATCH] feat: single & first entity queries (take 2) (#454) Closes #289 Hello :wave: it's been a long while. This is take 2 of #386 which was reverted in cfce040 due to CI failures. ## Changes - Add `Entities::(get_)single_with` methods to get a single entity by a query - Add `Entities::(get_)first_with_bitset` methods to get the first entity in the given bitset - Add `Entities::(get_)first_with` methods to get the first entity in the given query ## Summary Add convenience methods to get a single entity (and one or more of its components) when there should only be one of the thing you're looking for. This could be useful, for example, to check if there is only one player alive in the game. You could use `Entities::get_single_with` which would allow you to detect when there is only one player (`Ok`) or when there are none/multiple (`Err`). All methods have a panicking and non-panicking variant -- e.g. `get_single_with` returns a `Result<_, QuerySingleError>`, while `single_with` panics if the return value is an `Err`. The `*first_with*` methods either return an `Option` or panic. --- .../bones_ecs/src/components/iterator.rs | 28 +- .../bones_ecs/src/components/typed.rs | 194 ++++++- .../bones_ecs/src/components/untyped.rs | 43 +- framework_crates/bones_ecs/src/entities.rs | 522 +++++++++++++++++- 4 files changed, 736 insertions(+), 51 deletions(-) diff --git a/framework_crates/bones_ecs/src/components/iterator.rs b/framework_crates/bones_ecs/src/components/iterator.rs index 50babfc8c6..c74ef38460 100644 --- a/framework_crates/bones_ecs/src/components/iterator.rs +++ b/framework_crates/bones_ecs/src/components/iterator.rs @@ -38,13 +38,13 @@ impl<'a> Iterator for UntypedComponentBitsetIterator<'a> { type Item = SchemaRef<'a>; fn next(&mut self) -> Option { let max_id = self.components.max_id; - while !(self.bitset.bit_test(self.current_id) - && self.components.bitset.bit_test(self.current_id)) - && self.current_id <= max_id + while self.current_id < max_id + && !(self.bitset.bit_test(self.current_id) + && self.components.bitset.bit_test(self.current_id)) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. Some(unsafe { SchemaRef::from_ptr_schema( @@ -71,11 +71,11 @@ impl<'a> Iterator for UntypedComponentOptionalBitsetIterator<'a> { fn next(&mut self) -> Option { // We stop iterating at bitset length, not component store length, as we want to iterate over // whole bitset and return None for entities that don't have this optional component. - let max_id = self.bitset.bit_len() - 1; - while !self.bitset.bit_test(self.current_id) && self.current_id <= max_id { + let max_id = self.bitset.bit_len(); + while self.current_id < max_id && !self.bitset.bit_test(self.current_id) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. if self.components.bitset.bit_test(self.current_id) { Some(Some(unsafe { @@ -110,13 +110,13 @@ impl<'a> Iterator for UntypedComponentBitsetIteratorMut<'a> { type Item = SchemaRefMut<'a>; fn next(&mut self) -> Option { let max_id = self.components.max_id; - while !(self.bitset.bit_test(self.current_id) - && self.components.bitset.bit_test(self.current_id)) - && self.current_id <= max_id + while self.current_id < max_id + && !(self.bitset.bit_test(self.current_id) + && self.components.bitset.bit_test(self.current_id)) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: We know that the index is within bounds, and we know that the pointer will be // valid for the new lifetime. Some(unsafe { @@ -144,11 +144,11 @@ impl<'a> Iterator for UntypedComponentOptionalBitsetIteratorMut<'a> { fn next(&mut self) -> Option { // We do not stop iterating at component store length, as we want to iterate over // whole bitset and return None for entities that don't have this optional component. - let max_id = self.bitset.bit_len() - 1; - while !self.bitset.bit_test(self.current_id) && self.current_id <= max_id { + let max_id = self.bitset.bit_len(); + while self.current_id < max_id && !self.bitset.bit_test(self.current_id) { self.current_id += 1; } - let ret = if self.current_id <= max_id { + let ret = if self.current_id < max_id { // SAFE: Here we are just getting a pointer, not doing anything unsafe with it. if self.components.bitset.bit_test(self.current_id) { Some(Some(unsafe { diff --git a/framework_crates/bones_ecs/src/components/typed.rs b/framework_crates/bones_ecs/src/components/typed.rs index 23578ac186..7decc444cd 100644 --- a/framework_crates/bones_ecs/src/components/typed.rs +++ b/framework_crates/bones_ecs/src/components/typed.rs @@ -83,7 +83,7 @@ impl ComponentStore { self.untyped.get_mut_or_insert(entity, f) } - /// Get mutable references s to the component data for multiple entities at the same time. + /// Get mutable references to the component data for multiple entities at the same time. /// /// # Panics /// @@ -110,6 +110,27 @@ impl ComponentStore { self.untyped.remove(entity) } + /// Gets an immutable reference to the component if there is exactly one instance of it. + #[inline] + pub fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError> { + // SOUND: we know the schema matches. + self.untyped + .get_single_with_bitset(bitset) + .map(|x| unsafe { x.cast_into_unchecked() }) + } + + /// Gets a mutable reference to the component if there is exactly one instance of it. + #[inline] + pub fn get_single_with_bitset_mut( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError> { + // SOUND: we know the schema matches. + self.untyped + .get_single_with_bitset_mut(bitset) + .map(|x| unsafe { x.cast_into_mut_unchecked() }) + } + /// Iterates immutably over all components of this type. /// Very fast but doesn't allow joining with other component types. #[inline] @@ -137,6 +158,15 @@ impl ComponentStore { /// /// Automatically implemented for [`ComponentStore`]. pub trait ComponentIterBitset<'a, T: HasSchema> { + /// Gets an immutable reference to the component if there is exactly one instance of it. + fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError>; + + /// Gets a mutable reference to the component if there is exactly one instance of it. + fn get_single_mut_with_bitset( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError>; + /// Iterates immutably over the components of this type where `bitset` /// indicates the indices of entities. /// Slower than `iter()` but allows joining between multiple component types. @@ -174,6 +204,27 @@ pub trait ComponentIterBitset<'a, T: HasSchema> { } impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { + /// Gets an immutable reference to the component if there is exactly one instance of it. + fn get_single_with_bitset(&self, bitset: Rc) -> Result<&T, QuerySingleError> { + // SOUND: we know the schema matches. + fn map(r: SchemaRef) -> &T { + unsafe { r.cast_into_unchecked() } + } + self.untyped.get_single_with_bitset(bitset).map(map) + } + + /// Gets a mutable reference to the component if there is exactly one instance of it. + fn get_single_mut_with_bitset( + &mut self, + bitset: Rc, + ) -> Result<&mut T, QuerySingleError> { + // SOUND: we know the schema matches. + fn map(r: SchemaRefMut) -> &mut T { + unsafe { r.cast_into_mut_unchecked() } + } + self.untyped.get_single_with_bitset_mut(bitset).map(map) + } + /// Iterates immutably over the components of this type where `bitset` /// indicates the indices of entities. /// Slower than `iter()` but allows joining between multiple component types. @@ -207,7 +258,7 @@ impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { #[inline] fn iter_mut_with_bitset(&mut self, bitset: Rc) -> ComponentBitsetIteratorMut { // SOUND: we know the schema matches. - fn map(r: SchemaRefMut<'_>) -> &mut T { + fn map(r: SchemaRefMut) -> &mut T { unsafe { r.cast_into_mut_unchecked() } } @@ -250,14 +301,16 @@ impl<'a, T: HasSchema> ComponentIterBitset<'a, T> for ComponentStore { #[cfg(test)] mod tests { + use std::rc::Rc; + use crate::prelude::*; + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct A(String); + #[test] fn create_remove_components() { - #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] - #[repr(C)] - struct A(String); - let mut entities = Entities::default(); let e1 = entities.create(); let e2 = entities.create(); @@ -276,10 +329,6 @@ mod tests { #[test] fn get_mut_or_insert() { - #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] - #[repr(C)] - struct A(String); - let mut entities = Entities::default(); let e1 = entities.create(); @@ -299,4 +348,129 @@ mod tests { // Test that existing component is retrieved assert_eq!(comp.0, "Test2"); } + + #[test] + fn single_returns_none_when_empty() { + let storage = ComponentStore::::default(); + let bitset = Rc::new({ + let mut entities = Entities::default(); + entities.create(); + entities.bitset().clone() + }); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::NoEntities)); + } + + #[test] + fn single_returns_some_single() { + let mut storage = ComponentStore::::default(); + let mut entities = Entities::default(); + + // Create some dummies so that the target entity isn't 0 + (0..3).map(|_| entities.create()).count(); + + let e = entities.create(); + let a = A("a".to_string()); + storage.insert(e, a.clone()); + + let bitset = Rc::new(entities.bitset().clone()); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Ok(&a)); + } + + #[test] + fn single_returns_none_when_more_than_1() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + (0..3) + .map(|i| storage.insert(entities.create(), A(i.to_string()))) + .count(); + + let bitset = Rc::new(entities.bitset().clone()); + + let maybe_comp = storage.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::MultipleEntities)); + } + + #[test] + fn iter_with_bitset() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + { + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset(bitset.clone()); + assert_eq!(comp_iter.next(), None); + + let mut comp_mut_iter = storage.iter_mut_with_bitset(bitset); + assert_eq!(comp_mut_iter.next(), None); + } + + { + let e = entities.create(); + let mut a = A("e".to_string()); + storage.insert(e, a.clone()); + + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset(bitset.clone()); + assert_eq!(comp_iter.next(), Some(&a)); + + let mut comp_mut_iter = storage.iter_mut_with_bitset(bitset); + assert_eq!(comp_mut_iter.next(), Some(&mut a)); + + entities.kill(e); + } + } + + #[test] + fn iter_with_bitset_optional() { + let mut entities = Entities::default(); + let mut storage = ComponentStore::::default(); + + { + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), None); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), None); + } + + { + let e = entities.create(); + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), Some(None)); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), Some(None)); + + entities.kill(e); + } + + { + let e = entities.create(); + let mut a = A("e".to_string()); + storage.insert(e, a.clone()); + let bitset = Rc::new(entities.bitset().clone()); + + let mut comp_iter = storage.iter_with_bitset_optional(bitset.clone()); + assert_eq!(comp_iter.next(), Some(Some(&a))); + + let mut comp_mut_iter = storage.iter_mut_with_bitset_optional(bitset); + assert_eq!(comp_mut_iter.next(), Some(Some(&mut a))); + + entities.kill(e); + } + } } diff --git a/framework_crates/bones_ecs/src/components/untyped.rs b/framework_crates/bones_ecs/src/components/untyped.rs index e5bf23e246..70697f6f64 100644 --- a/framework_crates/bones_ecs/src/components/untyped.rs +++ b/framework_crates/bones_ecs/src/components/untyped.rs @@ -295,12 +295,10 @@ impl UntypedComponentStore { entity: Entity, f: impl FnOnce() -> T, ) -> &mut T { - if self.bitset.bit_test(entity.index() as usize) { - return self.get_mut(entity).unwrap(); - } else { + if !self.bitset.bit_test(entity.index() as usize) { self.insert(entity, f()); - self.get_mut(entity).unwrap() } + self.get_mut(entity).unwrap() } /// Get a [`SchemaRefMut`] to the component for the given [`Entity`] @@ -492,6 +490,37 @@ impl UntypedComponentStore { } } + /// Get a reference to the component store if there is exactly one instance of the component. + pub fn get_single_with_bitset( + &self, + bitset: Rc, + ) -> Result { + let len = self.bitset().bit_len(); + let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i)); + let i = iter.next().ok_or(QuerySingleError::NoEntities)?; + if iter.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + // TODO: add unchecked variant to avoid redundant validation + self.get_idx(i).ok_or(QuerySingleError::NoEntities) + } + + /// Get a mutable reference to the component store if there is exactly one instance of the + /// component. + pub fn get_single_with_bitset_mut( + &mut self, + bitset: Rc, + ) -> Result { + let len = self.bitset().bit_len(); + let mut iter = (0..len).filter(|&i| bitset.bit_test(i) && self.bitset().bit_test(i)); + let i = iter.next().ok_or(QuerySingleError::NoEntities)?; + if iter.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + // TODO: add unchecked variant to avoid redundant validation + self.get_idx_mut(i).ok_or(QuerySingleError::NoEntities) + } + /// Iterates immutably over all components of this type. /// /// Very fast but doesn't allow joining with other component types. @@ -600,9 +629,8 @@ impl<'a> Iterator for UntypedComponentStoreIter<'a> { if let Some(ptr) = self.store.get_idx(self.idx) { self.idx += 1; break Some(ptr); - } else { - self.idx += 1; } + self.idx += 1; } else { break None; } @@ -627,9 +655,8 @@ impl<'a> Iterator for UntypedComponentStoreIterMut<'a> { break Some(unsafe { SchemaRefMut::from_ptr_schema(ptr.as_ptr(), ptr.schema()) }); - } else { - self.idx += 1; } + self.idx += 1; } else { break None; } diff --git a/framework_crates/bones_ecs/src/entities.rs b/framework_crates/bones_ecs/src/entities.rs index d8b1f838b1..53eb3ff688 100644 --- a/framework_crates/bones_ecs/src/entities.rs +++ b/framework_crates/bones_ecs/src/entities.rs @@ -91,10 +91,28 @@ pub trait QueryItem { type Iter: Iterator; /// Modify the iteration bitset fn apply_bitset(&self, bitset: &mut BitSetVec); + /// Return the item that matches the query within the given bitset if there is exactly one + /// entity that matches this query item. + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError>; /// Return an iterator over the provided bitset. fn iter_with_bitset(self, bitset: Rc) -> Self::Iter; } +/// An error that may occur when querying for a single entity. For example, via +/// [`Entities::get_single_with`], or more directly with +/// [`ComponentStore::get_single_with_bitset`] or +/// [`ComponentStore::get_single_mut_with_bitset`]. +#[derive(Debug, PartialEq, Eq)] +pub enum QuerySingleError { + /// No entity matches the query. + NoEntities, + /// More than one entity matches the query. + MultipleEntities, +} + /// Wrapper for the [`Comp`] [`SystemParam`] used as [`QueryItem`] to iterate /// over entities optionally retrieving components from [`ComponentStore`]. /// Entities iterated over will not be filtered by [`OptionalQueryItem`]. @@ -153,10 +171,18 @@ where impl<'a> QueryItem for &'a Ref<'a, UntypedComponentStore> { type Iter = UntypedComponentBitsetIterator<'a>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + UntypedComponentStore::get_single_with_bitset(self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { UntypedComponentStore::iter_with_bitset(self, bitset) } @@ -164,10 +190,18 @@ impl<'a> QueryItem for &'a Ref<'a, UntypedComponentStore> { impl<'a, 'q, T: HasSchema> QueryItem for &'a Comp<'q, T> { type Iter = ComponentBitsetIterator<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset(&**self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_with_bitset(&**self, bitset) } @@ -175,10 +209,18 @@ impl<'a, 'q, T: HasSchema> QueryItem for &'a Comp<'q, T> { impl<'a, 'q, T: HasSchema> QueryItem for &'a CompMut<'q, T> { type Iter = ComponentBitsetIterator<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset(&**self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_with_bitset(&**self, bitset) } @@ -186,10 +228,18 @@ impl<'a, 'q, T: HasSchema> QueryItem for &'a CompMut<'q, T> { impl<'a, 'q, T: HasSchema> QueryItem for &'a mut CompMut<'q, T> { type Iter = ComponentBitsetIteratorMut<'a, T>; + fn apply_bitset(&self, bitset: &mut BitSetVec) { bitset.bit_and(self.bitset()); } + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + ComponentStore::get_single_with_bitset_mut(self, bitset) + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { ComponentStore::iter_mut_with_bitset(self, bitset) } @@ -203,8 +253,20 @@ where S: std::ops::Deref + 'a, { type Iter = ComponentBitsetOptionalIterator<'a, T>; + fn apply_bitset(&self, _bitset: &mut BitSetVec) {} + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + match self.0.get_single_with_bitset(bitset) { + Ok(single) => Ok(Some(single)), + Err(QuerySingleError::NoEntities) => Ok(None), + Err(err) => Err(err), + } + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { self.0.iter_with_bitset_optional(bitset) } @@ -217,8 +279,20 @@ where S: std::ops::DerefMut + 'a, { type Iter = ComponentBitsetOptionalIteratorMut<'a, T>; + fn apply_bitset(&self, _bitset: &mut BitSetVec) {} + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + match self.0.get_single_mut_with_bitset(bitset) { + Ok(x) => Ok(Some(x)), + Err(QuerySingleError::NoEntities) => Ok(None), + Err(err) => Err(err), + } + } + fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { self.0.iter_mut_with_bitset_optional(bitset) } @@ -298,6 +372,32 @@ macro_rules! impl_query { )* } + #[allow(non_snake_case)] + fn get_single_with_bitset( + self, + bitset: Rc, + ) -> Result<::Item, QuerySingleError> { + let ( + $( + $args, + )* + ) = self; + let mut query = MultiQueryIter { + data: ( + $( + $args.iter_with_bitset(bitset.clone()), + )* + ) + }; + let first = query.next(); + let has_second = query.next().is_some(); + match (first, has_second) { + (None, _) => Err(QuerySingleError::NoEntities), + (Some(items), false) => Ok(items), + (Some(_), true) => Err(QuerySingleError::MultipleEntities), + } + } + #[allow(non_snake_case)] fn iter_with_bitset(self, bitset: Rc) -> Self::Iter { let ( @@ -363,6 +463,89 @@ impl<'a, I: Iterator> Iterator for EntitiesIterWith<'a, I> { } impl Entities { + /// Get a single entity and components in the given query if there is exactly one entity + /// matching the query. + /// + /// # Panics + /// + /// This method panics if the number of matching entities is not *exactly one*. + pub fn single_with( + &self, + query: Q, + ) -> (Entity, <::Iter as Iterator>::Item) { + self.get_single_with(query).unwrap() + } + + /// Get a single entity and components in the given query if there is exactly one entity + /// matching the query. + pub fn get_single_with( + &self, + query: Q, + ) -> Result<(Entity, <::Iter as Iterator>::Item), QuerySingleError> { + let mut bitset = self.bitset().clone(); + query.apply_bitset(&mut bitset); + + let entity = { + let mut ids = (0..self.next_id).filter(|&i| bitset.bit_test(i)); + let id = ids.next().ok_or(QuerySingleError::NoEntities)?; + if ids.next().is_some() { + return Err(QuerySingleError::MultipleEntities); + } + Entity::new(id as u32, self.generation[id]) + }; + + let bitset = Rc::new(bitset); + + query + .get_single_with_bitset(bitset) + .map(|item| (entity, item)) + } + + /// Get the first entity in the given bitset. + /// + /// # Panics + /// + /// This method panics if there are no entities in the bitset. + pub fn first_with_bitset(&self, bitset: &BitSetVec) -> Entity { + self.get_first_with_bitset(bitset).unwrap() + } + + /// Get the first entity in the given bitset. + pub fn get_first_with_bitset(&self, bitset: &BitSetVec) -> Option { + self.iter_with_bitset(bitset).next() + } + + /// Get the first entity and components in the given query. + /// + /// # Panics + /// + /// This method panics if there are no entities that match the query. + pub fn first_with( + &self, + query: Q, + ) -> (Entity, <::Iter as Iterator>::Item) { + self.get_first_with(query).unwrap() + } + + /// Get the first entity and components in the given query. + pub fn get_first_with( + &self, + query: Q, + ) -> Option<(Entity, <::Iter as Iterator>::Item)> { + self.iter_with(query).next() + } + + /// Iterates over entities using the provided bitset. + pub fn iter_with_bitset<'a>(&'a self, bitset: &'a BitSetVec) -> EntityIterator { + EntityIterator { + current_id: 0, + next_id: self.next_id, + entities: &self.alive, + generations: &self.generation, + bitset, + } + } + /// Iterate over the entities and components in the given query. /// /// The [`QueryItem`] trait is automatically implemented for references to [`Comp`] and @@ -531,17 +714,6 @@ impl Entities { &self.alive } - /// Iterates over entities using the provided bitset. - pub fn iter_with_bitset<'a>(&'a self, bitset: &'a BitSetVec) -> EntityIterator { - EntityIterator { - current_id: 0, - next_id: self.next_id, - entities: &self.alive, - generations: &self.generation, - bitset, - } - } - /// Iterates over all alive entities. pub fn iter(&self) -> EntityIterator { EntityIterator { @@ -587,12 +759,155 @@ impl<'a> Iterator for EntityIterator<'a> { #[cfg(test)] mod tests { - use std::collections::HashSet; + #![allow(non_snake_case)] + + use std::{collections::HashSet, rc::Rc}; use crate::prelude::*; + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct A(String); + + #[derive(Debug, Clone, PartialEq, Eq, HasSchema, Default)] + #[repr(C)] + struct B(String); + #[test] - fn create_kill_entities() { + fn query_item__get_single_with_one_required() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + { + let comp = state.borrow_mut(); + let query = ∁ + + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::NoEntities)); + } + + { + let mut comp = state.borrow_mut(); + + let e = entities.create(); + let a = A("e".to_string()); + comp.insert(e, a.clone()); + + let query = ∁ + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Ok(&a)); + + entities.kill(e); + } + + { + let mut comp = state.borrow_mut(); + + let e1 = entities.create(); + comp.insert(e1, A("e1".to_string())); + + let e2 = entities.create(); + comp.insert(e2, A("e2".to_string())); + + let query = ∁ + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::MultipleEntities)); + + entities.kill(e1); + entities.kill(e2); + } + } + + #[test] + fn query_item__get_single_with_multiple_required() { + let mut entities = Entities::default(); + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + { + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::NoEntities)); + } + + { + let e = entities.create(); + let a = A("e".to_string()); + let b = B("e".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + state_b.borrow_mut().insert(e, b.clone()); + + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Ok((&a, &b))); + + entities.kill(e); + } + + { + let e1 = entities.create(); + state_a.borrow_mut().insert(e1, A("e1".to_string())); + state_b.borrow_mut().insert(e1, B("e1".to_string())); + + let e2 = entities.create(); + state_a.borrow_mut().insert(e2, A("e2".to_string())); + state_b.borrow_mut().insert(e2, B("e2".to_string())); + + let query = (&state_a.borrow(), &state_b.borrow()); + let mut bitset = entities.bitset().clone(); + query.apply_bitset(&mut bitset); + + let maybe_comps = query.get_single_with_bitset(Rc::new(bitset)); + + assert_eq!(maybe_comps, Err(QuerySingleError::MultipleEntities)); + + entities.kill(e1); + entities.kill(e2); + } + } + + #[test] + fn query_item__get_single_with_bitset__uses_bitset() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + let e = entities.create(); + state.borrow_mut().insert(e, A("unexpected".to_string())); + + let query = &state.borrow(); + let bitset = Rc::new({ + let mut bitset = BitSetVec::default(); + bitset.bit_set(99); + bitset + }); + + let maybe_comp = query.get_single_with_bitset(bitset); + + assert_eq!(maybe_comp, Err(QuerySingleError::NoEntities)); + } + + #[test] + fn entities__create_kill() { let mut entities = Entities::default(); let e1 = entities.create(); let e2 = entities.create(); @@ -620,7 +935,7 @@ mod tests { } #[test] - fn test_interleaved_create_kill() { + fn entities__interleaved_create_kill() { let mut entities = Entities::default(); let e1 = entities.create(); @@ -644,7 +959,7 @@ mod tests { #[test] /// Exercise basic operations on entities to increase code coverage - fn clone_debug_hash() { + fn entities__clone_debug_hash() { let mut entities = Entities::default(); let e1 = entities.create(); // Clone @@ -661,7 +976,7 @@ mod tests { /// /// Exercises a code path not tested according to code coverage. #[test] - fn force_generate_next_section() { + fn entities__force_generate_next_section() { let mut entities = Entities::default(); // Create enough entities to fil up the first section of the bitset for _ in 0..256 { @@ -678,7 +993,7 @@ mod tests { #[cfg(not(miri))] // This test is very slow on miri and not critical to test for. #[test] #[should_panic(expected = "Exceeded maximum amount")] - fn force_max_entity_panic() { + fn entities__force_max_entity_panic() { let mut entities = Entities::default(); for _ in 0..(BITSET_SIZE + 1) { entities.create(); @@ -688,7 +1003,7 @@ mod tests { #[cfg(not(miri))] // This test is very slow on miri and not critical to test for. #[test] #[should_panic(expected = "Exceeded maximum amount")] - fn force_max_entity_panic2() { + fn entities__force_max_entity_panic2() { let mut entities = Entities::default(); let mut e = None; for _ in 0..BITSET_SIZE { @@ -701,7 +1016,7 @@ mod tests { } #[test] - fn iter_with_empty_bitset() { + fn entities__iter_with_empty_bitset() { let mut entities = Entities::default(); // Create a couple entities @@ -712,4 +1027,173 @@ mod tests { let bitset = BitSetVec::default(); assert_eq!(entities.iter_with_bitset(&bitset).count(), 0); } + + #[test] + fn entities__get_single__with_one_required__ok() { + let mut entities = Entities::default(); + (0..3).map(|_| entities.create()).count(); + let e = entities.create(); + let a = A("a".to_string()); + + let state = AtomicCell::new(ComponentStore::::default()); + state.borrow_mut().insert(e, a.clone()); + + let comp = state.borrow(); + + assert_eq!(entities.get_single_with(&comp), Ok((e, &a))); + } + + #[test] + fn entities__get_single__with_one_required__none() { + let mut entities = Entities::default(); + (0..3).map(|_| entities.create()).count(); + + let state = AtomicCell::new(ComponentStore::::default()); + let comp = state.borrow(); + + assert_eq!( + entities.get_single_with(&comp), + Err(QuerySingleError::NoEntities) + ); + } + + #[test] + fn entities__get_single__with_one_required__too_many() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + for i in 0..3 { + let e = entities.create(); + let a = A(i.to_string()); + state.borrow_mut().insert(e, a.clone()); + } + + let comp = state.borrow(); + + assert_eq!( + entities.get_single_with(&comp), + Err(QuerySingleError::MultipleEntities) + ); + } + + #[test] + fn entities__get_single__with_multiple_required() { + let mut entities = Entities::default(); + + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + let _e1 = entities.create(); + + let e2 = entities.create(); + state_a.borrow_mut().insert(e2, A("a2".to_string())); + + let e3 = entities.create(); + state_b.borrow_mut().insert(e3, B("b3".to_string())); + + let e4 = entities.create(); + let a4 = A("a4".to_string()); + let b4 = B("b4".to_string()); + state_a.borrow_mut().insert(e4, a4.clone()); + state_b.borrow_mut().insert(e4, b4.clone()); + + let comp_a = state_a.borrow(); + let comp_b = state_b.borrow(); + assert_eq!( + entities.get_single_with((&comp_a, &comp_b)), + Ok((e4, (&a4, &b4))) + ); + } + + #[test] + fn entities__get_single__with_one_optional() { + let mut entities = Entities::default(); + let state = AtomicCell::new(ComponentStore::::default()); + + { + let e = entities.create(); + + let mut comp = state.borrow_mut(); + + assert_eq!(entities.get_single_with(&Optional(&comp)), Ok((e, None))); + + assert_eq!( + entities.get_single_with(&mut OptionalMut(&mut comp)), + Ok((e, None)) + ); + + entities.kill(e); + } + + { + let e = entities.create(); + let mut a = A("a".to_string()); + state.borrow_mut().insert(e, a.clone()); + + let mut comp = state.borrow_mut(); + + assert_eq!( + entities.get_single_with(&Optional(&comp)), + Ok((e, Some(&a))) + ); + + assert_eq!( + entities.get_single_with(&mut OptionalMut(&mut comp)), + Ok((e, Some(&mut a))) + ); + + entities.kill(e); + } + } + + #[test] + fn entities__get_single__with_required_and_optional() { + let mut entities = Entities::default(); + let state_a = AtomicCell::new(ComponentStore::::default()); + let state_b = AtomicCell::new(ComponentStore::::default()); + + { + let e = entities.create(); + let a = A("a".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + + let comp_a = state_a.borrow(); + let mut comp_b = state_b.borrow_mut(); + + assert_eq!( + entities.get_single_with((&comp_a, &Optional(&comp_b))), + Ok((e, (&a, None))) + ); + + assert_eq!( + entities.get_single_with((&comp_a, &mut OptionalMut(&mut comp_b))), + Ok((e, (&a, None))) + ); + + entities.kill(e); + } + + { + let e = entities.create(); + let a = A("a".to_string()); + let mut b = B("b".to_string()); + state_a.borrow_mut().insert(e, a.clone()); + state_b.borrow_mut().insert(e, b.clone()); + + let comp_a = state_a.borrow(); + let mut comp_b = state_b.borrow_mut(); + + assert_eq!( + entities.get_single_with((&comp_a, &Optional(&comp_b))), + Ok((e, (&a, Some(&b)))) + ); + + assert_eq!( + entities.get_single_with((&comp_a, &mut OptionalMut(&mut comp_b))), + Ok((e, (&a, Some(&mut b)))) + ); + + entities.kill(e); + } + } }